Pwn之CGfab的WirteUp

首先去攻防世界平台下载附件 CGfsb 发现还有一个创建环境,点击之后发现是叫我们用nc这个工具连接111.198.29.45的30233端口(由于这个平台不允许完成题目的人在此创建环境,所以就没有截图了)连接之后发现这个端口的服务与我们下载的elf文件的执行流程一样,所以我判断flag应该不在我们下载的elf文件里,而是在远端服务器,我们的任务就是通过分析这个elf找出其中的漏洞,并利用这个漏洞获取flag。

首先运行一下elf文件 ./cgfsb ,发现不能执行,首先想到可能是这儿elf文件没有可执行权限,使用ls -l 命令查看
在这里插入图片描述
发现的确是没有可执行权限(eXecutable=1),只有可读(Read=4)可写(Write=2)权限,使用chmod 777 cgfsb 把所有权限给它
在这里插入图片描述
然后执行 ./cgfsb,输入输出如下:
在这里插入图片描述

了解程序的大致功能和输入输出之后,再在kali下使用file指令查看elf文件的基本属性,结果如下图
在这里插入图片描述可以看到这个elf文件是32位的。
我们要获取这个这个elf文件的功能就必须要知道他的代码(机器指令和汇编代码可读性太差),因此可以考虑使用ida进行反汇编操作得到相应的C语言代码,这里需要注意的是ida分为32位的和64位的,我的要分析的是32位elf文件,那就用32位的ida打开
在这里插入图片描述左侧是elf文件中函数名字,右侧是elf文件的汇编代码,点击相应的函数名可以直接跳转到相应的汇编代码处,我们首先看程序的入口main函数(其实程序的入口并不是main函数,而是_start()函数,在此处我们不用管它,如果想要了解的建议看《程序员的自我修养 装载、链接与库》)然后把光标放在main函数的对应汇编代码处按F5键就可以的到了main函数的C代码
在这里插入图片描述
通过源码我发现要得到flag就必须使pwnme这个变量等于8
但是我们通过汇编代码发现pwnme这个变量没有定义,只是声明了,值是不确定的
在这里插入图片描述因此只有找到漏洞修改pwnme的值才行
我一开始没找出来,后来看了别人做的才发现漏洞在printf(&s)处。
printf函数很特殊,是可变参数函数,也就是说参数的个数不确定
我们平时的使用方法是 printf(’%s\n’,“bug,pwn”),但是现在成了print(一个字符串),更关键的是这个字符串是可控的,也就是说可以被我们任意输入,由此导致了格式化字符串漏洞,对于这个漏洞详情可以阅读这篇博客https://blog.csdn.net/qq_43394612/article/details/84900668
和ctf wiki的介绍https://ctf-wiki.github.io/ctf-wiki/pwn/linux/fmtstr/fmtstr_intro/
在这里插入图片描述

本题中最主要的就是%<数字>$ n的使用,i表示第几个参数,eg
.%11$n表示把打印的字节数写到第十一个参数(四字节为一个参数)的内容所表示的地址处 ,因为%n对应的参数与%s一样应该是一个指针

那么也就是说只需printf(“八字节的数据%$n”)第num个参数为pwnme的地址就好了。

现在还有两个问题,num的确定 和 pwnme的地址确定

pwnme的地址确定:
首先看看这个elf是否开启了ASLR(Address space layout randomization)地址空间布局随机化
在这里插入图片描述
发现是 No PIE ,说明没有开启,那么每一次运行这个程序pwnme的地址都不会变,
那么在ida中找到pwnme,点击pwnme变量就自动跳转到pwnme的声明处,看到pwnme的地址为 0x0804A068
在这里插入图片描述
num的确定需要了解栈和函数调用的知识,可以阅读**《0day:软件漏洞分析技术》《程序员的自我修养 装载、链接与库》**

需要注意的是
1)局部变量s是在main函数的栈帧中,其他函数对s的使用都是通过s的地址使用它的;
2)在调用函数之前(call指令之前),都会先将被调函数的参数(一般从右到左)压入栈中然后压入返回地址。
在这里我们只需要关注三个函数调用时的栈的情况
main() fgets() printf()
main调用之后,fgets调用之前
fgets函数调用后,printf调用前
printf调用时使用gdb调试程序
0x080486CD是call printf函数的地址,在这之前已经把printf的参数入栈了,我们在此处下一个断点,执行到此处时停止执行,esp指向printf的参数&s处
我们给字符串s输入aaaaaaaa

在这里插入图片描述
使用x/16wx $esp查看esp处(printf参数&s)处之后的16个参数(暂且当作参数看)的值,发现第10个参数就是我们输入的aaaa ,所以num应该就是10

这里要注意一点程序给变量分配了100字节,(本题中地址为0xffffd1d8~0xffffd242),
写入字符串时,先写低地址,再写高地址,其中每四个字节(32位机)又采用小端存储或大端存储(因操纵系统而定)

payload的要求如下:
8个字节;包含pwnme的地址(四字节);通过%$n写入数据

payload=p32(‘pwnme的地址’)+‘任意四字节数据’+’%11$n’
或者

payload=‘任意四字节数据’+p32(‘pwnme的地址’)+’%11$n’

p32()是pwntool里面的函数,用于数据打包,地址不能直接输进内存,因为内存采用的是小端存储,具体的可以阅读pwntools使用手册

脚本如下:
在这里插入图片描述
一些技术细节并没有说得很明白,希望对大家有点用,如果有错希望能够指出

猜你喜欢

转载自blog.csdn.net/qq_37862949/article/details/88748043
pwn
今日推荐