1.1 大致方向
通过修改栈和内存来劫持程序的执行流,
%n 转换指示符 ,将当前成功写入流的缓冲区的字符串个数 存储 到参数指定的整数中。
1.2继续花式hello world
#include
int main()
{
int i;
char str[] = "hello";
printf("%s %n\n",str,&i);
printf("%d\n",i);
}
运行:
root@MSI:/mnt/c/Users/13013/Desktop/PWN/pwn 字符串格式化漏洞/02-覆盖栈的内容# ./example
hello
6
1.3
- 分析:
这里的 i没有被附有初值,但是输出是6 - 原因:
转换指示符之前写入了6个(包括空格)字符(h e l l o ' '),没有长度修饰的时候,默认写入一个int类型的值,
1.4 一般利用情况:
通常情况下,我们要需要覆写的值是一个 shellcode 的地址,而这个地址往往是一 个很大的数字。这时我们就需要通过使用具体的宽度或精度的转换规范来控制写入 的字符个数,即在格式字符串中加上一个十进制整数来表示输出的最小位数,如果 实际位数大于定义的宽度,则按实际位数输出,反之则以空格或 0 补齐
just like this:
#include
int main()
{
int i;
printf("%10u%n\n",1,&i);
printf("%d\n",i);
printf("%.50u%n\n",1,&i);
printf("%d\n",i);
printf("%0100u%n\n",1,&i);
printf("%d\n",i);
}
运行结果:
root@MSI:/mnt/c/Users/13013/Desktop/PWN/pwn 字符串格式化漏洞/02-覆盖栈的内容# ./shellcode
1
10
00000000000000000000000000000000000000000000000001
50
0000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001
100
仔细思考一下,我就就可以把一个地址写入内存
注:
- 在64位中,RDI被用于传递格式字符串,所以我们就无法改写参数了
- 格式字符串("aa%15$na")这种,一定要8字节对齐
2. 关于参数位置的计算
(要改变的地址 - 输入的地址)/ 8 + gdb中的偏移 = 参数位置
这个地方可以顺便讲讲canry绕过
参考题:
攻防世界 Mary_Morton(ASIS-CTF-Finals-2017)
WP:(推荐)
Mary Morton wp
WP讲的比较详细了
3.pwntools中相关工具:
3.1pwntools pwnlib.fmtstr
1- 自动化的字符串漏洞的利用:
class pwnlib.fmtstr.FmtStr(execute_fmt, offset=None, padlen=0, n umbwritten=0)
- excute_fmt(funtion):与漏洞进程进行交互
- offset(int):你控制的第一个程序的偏移量
- padlen(int):在payload前添加pad大小
- numbwritten(int):已写入字节数
2- 自动生成payload
pwnlib.fmtstr.fmtstr_payload(offset, writes, numbwritten=0, writ e_size='byte')
- offset (int):你控制的第一个格式化程序的偏移量
- writes (dict):格式为 {addr: value, addr2: value2},用于往 addr 里写入 value的值(常用:{printf_got})
- numbwritten (int):已经由 printf 函数写入的字节数
- write_size (str):必须是 byte,short 或 int。告诉你是要逐 byte 写,逐 short 写还是逐 int 写(hhn,hn或n)
================施工未完成===============
3.2直接上题
3.2.1源代码:
#include
int main()
{
char str[1024];
while(1)
{
memset(str,'\0',1024);
read(0,str,1024);
printf(str);
fflush(stdout);
}
}
3.2.2常规套路:
root@MSI:/mnt/c/Users/13013/Desktop/PWN/pwn 字符串格式化漏洞/02-覆盖栈的内容# rabin2 -I fmt
arch x86
baddr 0x400000
binsz 6814
bintype elf
bits 64
canary false
class ELF64
compiler GCC: (Ubuntu 5.4.0-6ubuntu1~16.04.11) 5.4.0 20160609
crypto false
endian little
havecode true
intrp /lib64/ld-linux-x86-64.so.2
laddr 0x0
lang c
linenum true
lsyms true
machine AMD x86-64 architecture
maxopsz 16
minopsz 1
nx true
os linux
pcalign 0
pic false
relocs true
relro partial
rpath NONE
sanitiz false
static false
stripped false
subsys linux
va true
3.2.3思路:
我们的思路是将 printf() 函数的地址改 成 system() 函数的地址,这样当我们再次输入 /bin/sh 时,就可以获得 shell 了。
====================================
4.1 CVE-2012-0809
官方解释
EXP思路
以后在CVE合集里更新吧
4.2 补充
格式化字符串函数
常见的有格式化字符串函数有
- 输入
- scanf
- 输出
函数 | 基本介绍 |
---|---|
printf | 输出到stdout |
fprintf | 输出到指定FILE流 |
vprintf | 根据参数列表格式化输出到 stdout |
vfprintf | 根据参数列表格式化输出到指定FILE流 |
sprintf | 输出到字符串 |
snprintf | 输出指定字节数到字符串 |
vsprintf | 根据参数列表格式化输出到字符串 |
vsnprintf | 根据参数列表格式化输出指定字节到字符串 |
setproctitle | 设置argv |
syslog | 输出日志 |
err, verr, warn, vwarn等 | 。。。 |
-----------转自ctfwiki