B站 XMCVE Pwn入门课程学习笔记(8)

发布于:2025-08-29 ⋅ 阅读:(16) ⋅ 点赞:(0)

这个视频讲的比较难,我花了比较长时间来分析,甚至一个点反复很多次,这也是在学PWN的过程中不可避免的,需要坚持和毅力

pwn3:

没有system,通过ROP调用write的plt入口,执行write函数,并且将gots里的内容打印出来,泄露libc

远程libc和本地不同。有些大的版本更新会使libc发生大变,所以不能用一个脚本。

改成本地。用一下命令可以看到libc的路径

so.6只是软连接,通过以下可以获得真实路径

这一题是真正需要我们自己泄露内容的题目。这就需要泄露的过程,但是一次不能把ROP链构造完。

先一次构造完试一下,在构造payload时后面应该加上system地址,但是现在并不知道执行完write返回的system地址。其实这里就可以填vulnerable_func函数的地址,这样就会在执行完后返回read造成栈溢出

再通过payload,重新执行write,打印出想要的内容,程序再执行一次vulnerable_func函数

相当于程序执行流被我们劫持了,我们可以去到已知的地址任意一个地方,一会会返回这个函数,还有一次溢出。

下一步加write参数(三个),elf got表里的write对应表象。第一个参数在低地址,后面在高地址

这是第一次栈溢出,并且执行完之后会重新返回到有漏洞的地方,为了下一次溢出

到这里,我猜前段是为了在write写入write的libc地址,然后让程序自己把libc真实地址泄露出来

补充:

p32函数是将整数转换为字节型数据,可以用u32逆转换,再转为16进制。这就是write在内存中的真实地址。p是pack打包,u是unpack解包

继续找system和bin/sh的地址。这两个都在libc中,要得到基地址,是用write真实地址-write在libc中的偏移。那么system的地址就是libc的基地址+system在libc中的偏移;bin/sh就是libc的基地址+第一个bin/sh在libc中的偏移

这是第一个payload,执行完后会回到vulnerable_func函数,这就要再次构造payload

就可以获得shell

花式栈溢出:

栈中有很多栈帧,每个栈帧对应一个函数的执行状态。但是在栈中所有的内容不都是栈帧,第一个是main函数,在其之前没有栈帧,在这之前保存了什么内容?

找一道题,checksec,一个动态链接的程序

复习:

可以直接带到这个ida中

白色背景直接存在于程序中,而粉红色只存在于plt表象

start函数用于布置环境,没有C语言;栈在栈帧之外存在的内容,如当前执行程序的名字

由于动态链接库,libc start main函数先来到plt,第一次调用,先解析真实地址

ret到start函数末尾

这道题没有说main函数,但是可以在ida中找到,地址4006D0

这题带有Canary

这道题需要溢出的数据很大,得几百

侦测到了缓冲区溢出,但是输出的内容不一样,就是因为打开了Canary保护

没有这个保护,就直接向上溢出

虽然也是可以直接覆盖,但是返回时先检查Canary,如果和最开始放的值不同,就会触发stack chk fail函数,输出一些东西并且强行退出程序

补充  canary:

如何在ida里判断是否开启canary保护?

黄色区域这一行代码就是对应的canary

对应汇编实现

把一个十六进制数mov到rax,再移到栈上。就是放置canary,在v4

最后原来canary的随机数会和v4异或,值得是0,就是每一位都相等

汇编里有一个jnz指令,在之前的博客里讲过

例题(smashes):

其实这道题就是上面分析的,ctrl+shifft+12,好像又一个flag

flag的意思是服务器上的flag所占用的位置

返回到原代码

第10行写入数据,第14行输出一行“你好,请复写flag”,第17行读取输入,如果什么都没有输入,会到LABEL_9,强行退出程序;如果是换行符,就会跳出程序。第22行存的数据就是之前flag内容,这会导致我们输入的内容将服务器上真正的flag给复写掉。如果是跳出程序,之后也会将flag清零。

就是说如果按照它的程序是肯定拿不到flag

补充:elf很小的时候,可以在编译的时候通过某些选项,elf中的某些节和段会在虚拟内存中映射两份

在600D40有一份,在0X400d20这里也有一份

data段被映射到了这两个位置,就是即使600被复写了,400还会有,写的内容一样

按理说应该不能栈溢出,但是已经写好的payload里有一次

补充:这里的main函数没有被特殊符号标记,使用了strip,使gdb辨别不出来有main函数。需要通过ida里函数地址

这里可能还有其它保护,致使没法调试,这里老师遇到了点问题。可能是这道题的libc太老,而新的会把漏洞给修复掉

还是要学习一下这道题的知识

这里变成了0,应该就是漏洞被修复的位置

这行应该是程序名,所对应的字符串的地址,向上溢出到canary,触发造成程序强行终止,在终止前会将程序名称打印出来。然后取这个地址,到这个地方寻找程序绝对路径名称的字符串。把这个指向路径名的地址篡改成指向flag的地址,报错绝对路径名,会把flag的值当作程序路径名打印出来

栈迁移:

会碰到下面情况

长度不够:到prev ebp就结束了

思路:把这个栈的内存区域转移到另一个内存区域

只覆盖ebp,还能劫持执行流。汇编后面两个指令leave和ret,leave就是相当于mov esp,ebp和pop ebp,之后esp继续抬高一个字长,ebp就会指向刚开辟的可控制的区域,就不在原来的栈里

第二个子函数后面也是这样

先用ppt展示一下

最简单的情况就是溢出的并不只是一个长度(不只ebp),还能向上溢出一些距离。任务就是要把栈迁移到另一个区域,控制了栈的两个寄存器ebp,esp,就可以欺骗操作系统,控制到另一个区域。另一个区域有我们设计的gadget,能完成一次完整的攻击

怎样迁移呢?会有一个函数栈溢出,覆盖上面的内容。函数最后使leave,ret,会将ebp篡改为想要的值。进行溢出时会pop ebp到恶意覆盖的地址,还有esp。就需要依靠返回地址创造的gadget,可以leave或pop,将esp移到ebp。可以在新的空间内构造数据完成攻击流程

这里可以发现ebp帮助esp标记了一个返回的位置,而且数据读写都在栈底。就是说不管怎样,只要把esp控制了就行

大体是这样,其实有很多种,比这难,听着很乏味,需要看具体题目

例题 PWN3 X64:

拖入ida

跟上一题逻辑,漏洞一样,就是变成了64位

构造两次ROP,调用write,打印write的plt地址,再返回这里,再溢出,ROP,调用system,bin/sh。就是构造payload时传参会有不同,参数用寄存器来存放

payload:

第一个payload调用write,并写入got表。从ret addr开始,写入三个参数,rdi,rsi,rdx,需要相应的gadget控制。

找到了rdi;rsi是r15 ret,写入write got表地址,pop r15要写入一些垃圾数据;第三个参数是向屏幕输出的参数的最大长度,没有rdx,只能赌运气,就是rdx的值>8。回到vulner func

第二个payload,先rdi ret,然后bin/sh,最后system

格式化字符串漏洞:

以一个代码为例

对于这个printf,输出一些参数,%加数字加字母,这个就是格式化字符串

后面的一些给删去,还是会输出,就是printf函数的缺陷

有时候后方并没有检查格式化参数数量和printf后方接收到的参数数量是否一样

补充:

这个命令是打开脚本的调试模式

调试运行一下,其中gcc -g可以带着源码

会显示发送的数据和接受的数据,还按字长对齐了。每一步很清楚

调试一下上面代码,这里出问题了,另举例

printf输出时,前面有几个%x,后面有几个参数,最好是整型,那么就会按照十六进制格式打印出来

但是把后面参数去掉,会打印出来一些奇怪的数据,对照一下

再没有给任何参数的情况下,直接把栈上的内容打印了出来

这是为什么呢?在X86的情况下,printf栈帧,先向上数2个字长(ebp,ret addr),接着找自己的参数,例如%x%x%x%x,要向上找四个字长,就会跳过那两个字长,向上四个字长的值作为参数,而这四个字长应该是由父函数放置(子函数的参数由父函数压入)。

在call时如果后面没有参数(a,b,c,d),高地址就没有这四个值,但是父函数的内容还在。还是会向上找四个参数,而这现在就是父函数的内容,就会把父函数的一些地址给泄露出来。这是格式化字符串的基本原理

后面会更加深入的讲格式化字符串,再往后就会讲堆。


网站公告

今日签到

点亮在社区的每一天
去签到