はじめに
前回のProxyパターン、ライブラリの差し替え部分でLD_PRELOAD
を使って色々なproxyパターン的な例を考えてみたのですが、調べているうちに
凄さと恐ろしさを感じたため、別件としてまとめさせていただきました。
基本的なLinuxの動的ライブラリリンクに関する仕組み
何よりもまず、Linuxの動的ライブラリがリンクされる際の仕組みについて整理しましょう。
(一旦LD_PRELOAD
は忘れてください。)
まずライブラリリンクの検索は、ld.so
というライブラリによって検索されるそうです。
manpageよりld.so
の仕様を確認すると、検索順は
-
DT_RUNPATH
,DT_RPATH(非推奨)
(ビルド時の-rpathで指定されたパス) - 環境変数
LD_LIBRARY_PATH
- /etc/ld.so.cacheファイル内のパス
(基本は/etc/ld.so.conf, /etc/ld.so.conf.d/*, デフォルトのパス)
となるとのこと。(1の解釈が怪しいかもです)
デフォルトは/lib、 次いで /usr/lib。
ビルド時に-z nodeflib
を指定すると、ここからデフォルトのパスが除かれます。
rpathの指定には癖があり、Xlinker
でくくらないと効果が出ないようです。
-Xlinker -rpath -Xlinker $(libdir)
リンクされているライブラリについては、ldd
コマンドで確認できます。
こんな感じに
$ ldd /usr/bin/curl
linux-vdso.so.1 (0x00007ffe307ac000)
libcurl.so.4 => /usr/lib/x86_64-linux-gnu/libcurl.so.4 (0x00007f85cce4e000)
...
libssl.so.1.1 => /usr/lib/x86_64-linux-gnu/libssl.so.1.1 (0x00007f85cbb4b000)
libcrypto.so.1.1 => /usr/lib/x86_64-linux-gnu/libcrypto.so.1.1 (0x00007f85cb6d3000)
...
libm.so.6 => /lib/x86_64-linux-gnu/libm.so.6 (0x00007f85c6c95000)
色々な設定に合わせて優先度を変えてリンクされるライブラリですが、
この順番をぶっ飛ばして最優先に使用されるのがこのLD_PRELOADです!
LD_PRELOADの凄まじさ
最近Qiitaをはじめて一番最初に衝撃を受けた記事が、@koara-localさんが紹介されていた記事でした。
再コンパイルなしでprintfが差し替えられるだって!
自作ならまだしも標準関数のprintf
がLD_PRELOADを使って差し替えるというのです。
このLD_PRELOAD
は環境変数で、ここにライブラリのパスを設定すると、そのパスがライブラリのリンク先検索最上位になります。
しかもstaticな関数をフックする方法を紹介している方までいる。なんでもありです。
こりゃすごいと、この機能で色々proxyパターンを表現しようと思ったところ、既に@koara-localさんがここで紹介されてました。そっか残念と思いつつ使い方を思い返していると、徐々にこのLD_PRELOAD
に対して怖さを感じ始めます。
LD_PRELOADの恐ろしさ
先ほどの話を整理しましょう。
-
LD_PRELOAD
は環境変数でパスを指定するだけで好きな関数をラップ出来る - 再コンパイルの必要がないので既存動作しているプログラムにも適用される。
さて、どんなことが出来ますかね
###試してみた
OpenSSL
はいわずと知れたSSLに関するライブラリを持っています。
色々なHTTPS通信に用いられており、ユーザープログラムとしては意識せず、ライブラリが暗号化を行ってくれます。
私の知っている使い方は、なんやかんや初期処理を行いクライアントサーバー間で認証が通った後は、SSL_read
, SSL_write
を使えばライブラリ側で勝手に暗号化/復号をしてくれます。全部OpenSSLが暗号化処理をしてくれるなんて楽ちんですね。
…read/writeは全部これなんですよね。
試しにHTTP系通信に使用する定番のクライアントコマンド、curlのソースを確認。
やっぱり使ってますね、両方。サンプルに倣い、それぞれデコード前後にログを出力。
やり方は参考元を参照ください。
export LD_PRELOAD=XXX/libssl_dummy.so
でライブラリのパスを通します。ライブラリの置き場はどこでもいいようです。(root権限のない所でも)
試しにyahooにアクセスしてみました。
普通はこんな感じ
$ curl --url "https://yahoo.co.jp" -o /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 3191 100 3191 0 0 2818 0 0:00:01 0:00:01 --:--:-- 2818
出力しなければ中身も見えないし、パケットログをみてみてもhttpのやり取りは見えません。
curlの引数で通信ログを出力しないようにすれば、その内容は確認できないはず。
ですが、LD_PRELOAD
の力を借りてdlsymを使うと…
$ curl --url "https://yahoo.co.jp" -o /dev/null
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
0 0 0 0 0 0 0 0 --:--:-- --:--:-- --:--:-- 0
#############Show write buffer before encrypt!!!
GET / HTTP/1.1
Host: yahoo.co.jp
User-Agent: curl/7.58.0
Accept: */*
...
#############Show read buffer after decrypt!!!
<!doctype html public "-//W3C//DTD HTML 4.01//EN" "http://www.w3.org/TR/html4/strict.dtd">
<html><head><title>Yahoo - 301 Moved Permanently</title><style>
...
<p class="more">Please try <strong><a href="http://us.rd.yahoo.com/301/*http://help.yahoo.com">Yahoo Help Central</a></strong> if you need more assistance.</p>
</div><div id="ft"><p>Copyright © 2018 Yahoo Inc. All rights reserved. <a href="http://us.rd.yahoo.com/301/*http://privacy.yahoo.com">Privacy Policy</a> - <a href="http://us.rd.yahoo.com/301/*http://info.yahoo.com/legal/us/yahoo/utos/terms/">Terms of Service</a></p></div>
</div></body></html>
送信も受信も完全に平文が覗けちゃっています。
これはただprintf
しただけですけど、例えば誰かのアドレスへのメール送信コードが埋め込まれていて、気付かずクレジットカード登録したなんて日には大変なことに。
しかもこれproxyパターンで作ってるからちゃんとcurlの機能を満たしているのでばれにくい気がします。
大事なファイルは全部root権限のある場所にあるからと安心して、
特定のパスは開けっ放しにしているなんて方、いらっしゃいませんか?
万が一にでもいらっしゃったら、なにか危険なライブラリが組み込まれているかもしれませんよ?
###環境変数はどこまで影響があるか
じゃあLD_PRELOAD
の力がどこまで及ぶのか、環境変数の仕様についても調べてみました。
環境変数は、以下の順で設定ファイルが読み込まれ、設定されます。
1, /etc/profile
2, /etc/profile/profile.d
3, ~/.bash_profile
4, ~/.bash_login
5, ~/.profile
6, ~/.bashrc
7, /etc/bashrc
その他には実行時に変数を設定する方法もあります。
なので、仕込まれるとしたら上記設定ファイルか利用スクリプトくらい。
/etcや利用スクリプトがちゃんとroot権限のままになっていれば、環境変数はそのユーザーのホームディレクトリのもの、つまりそのユーザーにしか影響はないようです。
外から環境変数を設定する手段については流石にないと思いますが、用意されている機能は計り知れないので油断はできませんね。
最後に
最初にLD_PRELOAD
の記事を見た時は、なんて凄い機能だと感心していましたが、dlsym
という別の強力機能と組み合わせて悪用すれば、恐ろしいハック道具にもなってしまいます。
気になるサーバー管理者の方は各環境変数の設定ファイルをご確認ください。(私も確認しなきゃ)
参考
ld.so manpage
https://linuxjm.osdn.jp/html/LDP_man-pages/man8/ld.so.8.html
LD_PRELOAD参考元
https://qiita.com/koara-local/items/d5205f94decade3ffbf1
https://qiita.com/koara-local/items/221508d4691c4502adce
LD_PRELOADでstaticな関数もフック出来るという話
http://neocat.hatenablog.com/entry/20111225/1324823705
環境変数の件
https://qiita.com/bokotomo/items/674e06da84c921f5407a