B站 XMCVE Pwn入门课程学习笔记(7)
ret2libc3:
构造ROP,直接带脚本:
补充:
sendlineafter函数,当我接收到第一个参数时,就会把后面的内容加换行符发送过去
recvuntil函数,接收远程数据,直到遇到参数里给的字符
对应的代码即是,接收到空格+冒号时,就send给read,继续接收,一直到See_something
将空格+冒号之前的接收到缓冲区,接着接受地址
回到代码继续有recvuntil,后面还会有其它数据,如%p/n
drop=True,就是until的标记数据不保留。那么这一行就是接收直到遇到换行符并且不保留换行符,实际要的是%p
success函数是尝试攻击一下,确保此次攻击得到libc字符串是正确的。下面是另一种攻击方式,需要另一种工具
再看看偏移量是多少
要写的垃圾数据是56+4
cyclic(60):生成一定字节的垃圾数据,且每四个字节为一个标记字符。生成垃圾数据更方便
后面的话就是生成垃圾数据,生成bin/sh等
oneGadget:
这是一个工具,用来去查找动态链接库里execve("/bin/sh", rsp+0x70, environ)函数的地址的
one_gadget 下载 安装 与使用_gadget配置下载-CSDN博客
这里是有一些偏移地址。libc是一个动态链接的文件,它是连接进入其它的程序虚拟内存空间
一个gadget就可以满足一切需求,地址是libc里的地址,进入代码,满足约束条件,就可以直接获取shell。但是对程序环境要求比较高。如以上图。得一个一个向上试
前三个都不太行。所以还是老老实实构造吧,此题毕
练习:
pwn0:
checksec
64位,没有保护
拖入ida64
先打印,又func函数,跟进
明显栈溢出,再判断攻击手法
有callsystem函数,后门函数,bin/sh。就是ret2text,但是这个跟之前不一样,是64位
32位填充4个字节数据,64位填充8个字节数据
0X400开头是64位没有保护的默认载入地址
试一下24
这里看到中间有很多都是0,直接省略了
计算写入的到rbp距离,即填充垃圾数据的内容再加上8个字节,返回callsystem的地址
128+8=136的垃圾数据,16进制为0X80
开始攻击
构造payload:
查一下callsystem的地址
获得shell
pwn1:
checksec
栈本身可读可写可执行
进入ida32
告诉了我们buf缓冲区地址,为什么呢?
需要我们把shellcode往栈上写,但是由于ASLR,我们不知道shellcode地址是什么。所以就可以将shellcode写入这个地址中,然后控制程序返回到这里。就是之前的ret2shellcode
调试查看栈溢出的距离,在50可以看到。计算距离,转换为16进制
构造payload:
向buf中写入数据,写shellcode,再写垃圾数据,还有p32(buf_addr)
用0X90也就是nop填充
获得shell
pwn2:
checksec
拖入ida32,进入main函数
在plt里增加了一个system表象。进入vuln_func函数
不能在栈上执行shellcode,不知道栈的地址。就会想到ret2libc,这里有system的plt地址
需要我们手动构造一个rop链,然后将程序返回到system这里。先查看溢出的距离
0X88+0X4,接着构造bin/sh调用链。有system,可以看到已经给我们写好了
现在可以攻击了。
回到vuln_func函数,echo Input
构造payload,先是垃圾数据,然后是返回地址,system_addr。要知道system地址,通过plt。再构造payload
链接获得shell,下面是完整的
构造ROP链:
ROP注意:
64位和32位不一样的是参数传递
32位:
在调用新的函数之前,子函数的参数已经被保存在父函数的栈帧里了,并且逆序压入
有一个read函数,先压入10,再压入buf地址,继续stand_in,call指令压入一个返回地址。
就开始执行read函数,push ebp。
64位:
在main函数read,call之前,有三个寄存器,依次放入rdi,rsi,sdx中,相应的放入rad的三个参数
如果参数特别多长,假设七个,前六个已经放在寄存器里,那么超过六个的就会陆续放入栈中
故在64位构造ROP链的时候,给system传参,不能到栈中,而是放到寄存器中
如何放?怎样用栈中溢出的数据操控寄存器?就是pop,return gadget,并且加上bin/sh的地址
当这个指令执行完之后,bin/sh的地址就会存弹入rdi寄存器中,之后运行system bin/sh
构造payload:
在攻击之前,先要在rdi里面填上bin/sh的地址
找pop_rdi_ret地址,通过pop gadget
写脚本的时候,py2和py3不一样。而且把要获得的地址写全,如下过程获得shell
问题:为何要先pop_edi_ret,再system?
就是要先把参数放进栈中,再执行函数
遭受攻击的栈
执行到ret,把当前栈顶的值,即pop_rdi_ret弹到rip,那么程序就会执行。下一条就会把bin/sh弹到rdi,完成参数传递。继续ret把system plt地址弹到rip里。就达到了攻击的目的
其实父函数调用子函数也是一样的
而对于32位的栈,对比一下
构造payload时地址在前,传参在后,因为栈式相反的。高地址先被传入栈,但是写数据是低地址向高地址增长
ROP技巧:
ret2csu:
有一些大量连续pop和ret,如果控制执行流到上一行,就可以控制下面所有的寄存器
在别的地方还有寄存器的赋值操作,其实下面都可以控制
jz,z是1就跳转,z是0就继续向下执行。到ret回到nop继续向下执行
X64需要注意只可以控制edi,也就是rdi的低32位。64位地址分为4字节和6字节,其中栈和动态链接库地址一般是6字节。我们可以灵活用IDA查看相应地址长度,有时候edi完全够用
pwn3:
跟之前的ret2libc3很像,但是难上了不少。需要自己构造ROP链泄露内存内容
checksec
这道题还给了一个libc,说明此时需要获取这个程序里某个函数在内存里的真实地址
跟之前很像,vulner_func,read函数,读入过长数据造成栈溢出,没有bin/sh,system
首先要获得system的地址,通过ROP。然后获得bin/sh地址。可以通过一些特殊符号如末尾有sh或者ROP,也可以泄露libc,那么任意的都可以使用
有的,在程序中找一下。这里必须保证sh后面存在截断符。如果后面内容没有被截断那就不行了。很明显这个不行
看看libc中有没有,一般都会有的。就可以用了
有system函数,那么就剩泄露libc的地址了,用ROP
泄露:
回顾一下ROP,连续的执行我们想要的一些代码片段,让片段泄露
可以尝试构造write系统调用,这里的write本质就是系统调用,对应了一个system
构造pop链,连续的用ret为寄存器赋值,第二个参数传got表象的地址,把got表的内容通过系统调用泄露出来
用ROP gadget
不行。一般碰到动态链接的能用的概率很小
可以用write函数,就在plt表象里。直接ret到write的plt表象并传参。就能通过ROP完成write向屏幕显示数据的过程。write要写got表的地址
在ROP到write之前要想write哪个函数的真实地址,必须是一个已经被调用过的函数
就是vulner_func函数。通过ROP劫持程序执行流到write的plt地址,同时把write对应的got里面的内容写出来,就能知道write函数在内存中的真实地址,利用它减去它在libc中的偏移,就是libc的基地址,同时加上system的偏移和bin/sh的偏移,就能知道system和bin/sh的真实地址
攻击构造的ROP链就和X64构造是一样的
我的天,太难了!