B站 XMCVE Pwn入门课程学习笔记(5)
回顾:
这段看着会很枯燥,具体的可以看我以前的博客
对于pwn,二进制的漏洞的利用。在现实中,pwn是用来寻找一个程序或者一个计算机上的服务的二进制漏洞,进而对这个二进制漏洞进行利用,进而攻击这个漏洞。
在CTF比赛中,大部分情况下是云端服务器上运行某个端口的一个io接口,这个由以恶搞程序挂着,举办方会给一个二进制程序,通过反编译反汇编这个脚本,寻找漏洞,进而编写脚本,向比赛中运行这个程序的服务器对应的端口发送恶意数据,将服务攻陷,控制设备,获取flag
我们缩攻击的是已经编译成机器码的二进制程序,可以由很多语言生成,最多是C语言,它比较接近硬件底层的语言,可以操控内存
C语言在编译和汇编之后得到的是可执行文件,linux是ELF
一个程序要想运行需要是在内存中。
有可执行不可写的text段,可写的是data段
虚拟内存mmap段中的动态链接库只在内存中单独装在一份
就是很多个程序用到同一个代码,那么这段代码只用在物理内存中装载一份
虚拟内存:
在攻击和调试时用到的都是虚拟内存,一般程序都开了保护。方便攻击
需要学会虚拟内存结构,知道如何利用漏洞,更好地攻击
小端序:
数据读写总是从低地址到高地址读写,低位存放在低地址,高位存放在高地址;大端序相反
CPU,寄存器,cache,内存,外存
寄存器:
静态链接:
启动一个用户程序,会经历如下过程:
操作系统会申请一个系统调用execve,是一个新的程序,准备空间,布置环境。回到用户态代码执行,main,start函数在banary已经写好,此时程序控制流又回到了用户态。
动态链接:
多了几步,而且可以在运行时将C语言代码“借过来”,
ld.so是装载器。
结构:
核心是.plt和.got.plt,.plt里面是表,表里是代码;而.got.plt里是数据,其实是地址
具体来说system表象有一个地址,里面还有一个地址,这个地址才是指向某个区域内的数据
puts函数在got函数的表象的地址,可以用gdb
栈漏洞:
栈是在用户最高地址的区域,push向栈顶添加数据,pop数据只能从栈顶移除
栈从高地址向低地址增长,下面是栈顶,ebp,esp指的是当前栈帧的栈顶或栈底
出栈使ebp或esp向上移动1个单位,入栈使其向下延伸1个单位;一个栈帧会对应一个function的调用状态,如上一个函数(复函数)的返回地址、上一个函数的栈底、当前函数的所需的各种各样的局部变量的空间、当前函数的子函数所需的参数
可以思考一下为什么要用栈保存函数状态,函数和栈都是“后来先走”,就是后调用的函数先返回
main函数使my_puts函数的调用者,main是父函数
在 X86下,my_puts函数用到的参数是保存在main函数里。就比如儿子要买房,main函数就要先准备一点钱,就是把参数压栈,再真正的调用my_puts函数。相应的,my_puts函数也会有自己的子函数。在my_puts函数里,局部变量保存于自己的空间里
调用的时候需要注意:
ebp指向pre ebp,控制其开头,ebp和esp指向就是当前栈帧
ret函数紧邻arg函数,后面会涉及到,retlibc要找到子函数参数和返回地址,怎样组织才能正确返回到攻击目的
栈溢出:
发生在子函数和父函数的矛盾。最基本的,攻击者写入超长数据,包括覆盖了父函数,ret address。在实际攻击中填写ret shellcode/backdoor,使子函数返回到不应该的位置,eip会写恶意地址。程序就会执行恶意代码
更难一点的,上一篇写到的返回导向编程
gadget存放在text段,把所有所需要的地址全部向栈溢出的那部分区域一个一个填进去,在其之间将参数布置进去
第一个Canary,第二个PIE。一般先关闭保护措施
ret2libc1:
64位高地址是6个字节,32位是4个字节
起始位置,64位是0X400000,32位是0X8048000(关闭PIE)
gadget布置环境,通过控制返回地址,回到gadget。一切gadget执行完后,攻击环境被布置完毕,回到libc里。ROP会在ret address溢出更远位置构造payload
padding就是写的内容溢出
我们并不知道shellcode的真实地址,因为开了ASLR,需要用到一种技术
nop滑梯:
nop是一个汇编指令。nop的机器码是0x90。见下图,假如没有开启站保护,在shellcode下方空白区域全部填上nop,那么大概率会指向nop区域,不管栈怎样上下移动,一直执行nop直到shellcode
看栈溢出的汇编代码执行到ret,但是已经被system覆盖了,指针会跳转到system函数。system函数会有自己的栈帧,它的参数在其上方两个字长
在下图中科一看到system函数上面有ret addr和prev ebp两个字长,而上图中只有exit一个字长
解释:system函数第一句是push ebp,先把ebp加上。ebp是由子函数自己压进去的。但是返回函数是由父函数压进去的。ret addr是call指令放进栈里,父函数call子函数,call指令会把ret addr放入栈中。ebp在子函数的栈中。父函数保存了子函数的参数和返回的内容,子函数保存了父函数的ebp。system上面是返回地址,返回地址下面还有一个pre ebp,是在自己执行时自己压进去,所以是两个字长,还有puts函数,这就是更复杂的攻击情况
分界线:上面是父函数的内容,下面是子函数
构造payload:
先看溢出数据,画图理解:
通过ret addr,system函数弹出到eip,自己可以压一个ebp
先走过ebp,再走ret addr
下一步就是找system和exit的地址,这俩是库函数,并且攻击的是动态链接
回到ret2libc1题,先checksec检查一下保护,32位小端,没有canary保护,栈不可执行权限打开了。放入ida,进入main函数
看secure,这里system是有用的,重点要获取返回到的地址
这里讲了一下找system的地址,执行动态链接函数的执行流
首先text段里有一个call system,之后来到程序的plt,实际上是system的plt表象,本质上是一段代码,got会问你system地址,有两个方向,首次调用system时会沿着1,第二次直接从上到下
要将eip控制到system的plt表象位置,libc中system一定会被加载
这道题添加了system表象,降低了难度
回到题目,找溢出距离
可以看到8个A,eax到ebp,计算一下,108加上ebp的4个字节,就是112
需要构造3个字长,并且2个需要设计,例如:
这和我们最开始的通用攻击不一样,这个简单,不用exit
exit是用来退出程序的,在这里拿到shell关掉io即可,不需要
写代码攻击
补充:io process是动态运行
创建elf是静态分析,猛一看还以为checksec
获得flag,要把process改为remote,输入题目地址
获取system plt的地址
这道题有bin/sh,还有一个技巧,看是否有bin/sh,linux自带的工具,有输出就是有bin/sh
这里切换一下,在逆向题中,一般flag会在程序中,有的会进行算法加密,最简单的就可以用以上命令grep flag得到
再次回到这题,获得bin/sh的地址
构造payload并且连接交互,就可以获得shell