首页
社区
课程
招聘
[原创]从0开始CTF-PWN(三)没有system怎么办?构造你的shellcode
发表于: 2020-5-25 00:39 15723

[原创]从0开始CTF-PWN(三)没有system怎么办?构造你的shellcode

2020-5-25 00:39
15723

作者:dxbaicai

为了降低入门难度,会关闭操作系统的地址空间随机化(ASLR),这是针对栈溢出漏洞被操作系统广泛采用的防御措施。

在实验环境创建.c源代码文件,使用如下命令进行编译。

知识点-编译参数说明

我们来看这样一段程序:

这次我们就只有一个main函数,main函数通过strcpy拷贝argv[1]到事先定义的buf数组中,然后将buf打印出来。根据上一节所学的知识,我们知道当argv[1]的长度超过128时,就会发生栈溢出,但是没有system函数可以给我们提供shell了,要怎么办呢?

stack_3

调用结束时,栈变化的核心任务是弹出被调用函数(callee)的状态,并将整个栈恢复到调用函数(caller)的状态。首先弹出被调用函数(callee)的局部变量,然后将栈上存储的调用函数(caller)的基地址从栈内弹出,并重新保存到ebp寄存器中,这样调用函数的基地址信息得以恢复,此时栈顶会指向返回地址。最后将返回地址从栈顶弹出,并保存到eip寄存器内,这样调用函数的eip指令信息得以恢复,指向了调用函数后的下一条语句。

stack3-1

**注意:此时虽然栈被弹出了,但只是栈顶指针的位置发生了变化,之前的写入内存的buf数组其数据并没有被清理(根据cdecl调用约定退出main函数时才被清理),所以我们可以用于跳转。

shellcode就是一串可以返回shell的机器指令码,在linux上典型的有:
Linux/x86 - execve(/bin/sh) + Polymorphic Shellcode (48 bytes)
对应代码为:

shellcode本质就是就是一串机器码,执行后提供shell。

根据上面的分析,我们需要如下计算步骤:

思路明确,我们现在开始来逐步调试。

debug3_1

查看函数汇编代码后在strcpy的下一行下断点,并输入r(run)指令执行,这个时候已经完成了函数调用,另外在ret指令下第二个断点,从上一篇中我们知道,此时栈顶即为返回地址。

debug3_2

执行并观察断点1处时的栈空间:

debug3_3

可以看到buf的起始地址为0xffffd580,记录下来。

debug3_4

可以看到确实是我们猜测的地址,在栈上的位置为0xffffd60c,另外也说明了可以通过类似<__libc_start_main+241>的关键字去找返回地址。

计算偏移量差值,覆盖返回地址
于是我们可以计算偏移量差值为140:

尝试构造Payload
于是我们可以构造如下的payload结构,这里buf的起始地址位置就是填在返回地址所在位置:
shellcode + padding + buf的起始地址

在命令行里执行,却发现报错了,是我们哪里出错了吗?

debug3_5

输入144位长度的字符作为参数,并检查返回地址是否被正确覆盖。这里输入:

debug3_6

重新执行上面的下断点步骤查看buf起始地址和函数返回地址,发现输入长度变化后确实起始地址也跟着变化了,同时验证了原来是main函数返回地址的位置已经被替换成了我们预计的CCCC,证明偏移量是没有发生变化的。

debug3_7

得到144输入长度下buf的起始地址应该为0xffffd4f0

debug3_8

但是你会发现如果不在gdb中执行,直接外部执行——又报错了,这又是为啥呢?

知识点1-NOP指令
NOP指令,也称作“空指令”,在x86的CPU中机器码为0x90(144)。NOP不执行操作,但占一个程序步。——也就是说当遇到NOP指令的时候,程序不会做任何事,而是继续执行下一条指令。

我们可以改造一下payload,在头部放上一段NOP指令,然后再跟上shellcode,并适当偏移之前的buf起始地址,这样当返回地址指向这段NOP指令中的任意一个地址时,因为NOP空指令的关系,会一直找下去,直到遇到shellcode,这样就大大提高了命中率。对于栈可执行程序而言,这是一种很有效的命中方式。

debug3_9

知识点2-使用pwntools.cyclic()快速定位偏移量
这里补充一个快速定位偏移量的好工具cyclic()

在本例中,这样使用:

然后将这个串作为参数输入程序:

会看到如下输出:

cyclic3_1

这里的0x6261616b表示函数返回到这个地址了,我们把这个放到cyclic_find()里找一下,可以看到返回了正确的偏移量。

这里提供了pwntools攻击的实现。

我们再来回顾下我们是如何自己构造一个shell的:

我们现在知道了怎么构造自己的shell了,但是这一切都是建立在栈上代码可以执行这一基础上的,现在的应用大部分都不太可能打开-z execstack参数了,那么我们继续思考,当栈不可执行时,我们又要怎么获得Shell呢?

 
 
 
 
 
 
 
 
 
 
 
 
 
 
 

[注意]APP应用上架合规检测服务,协助应用顺利上架!

最后于 2020-5-25 10:09 被dxbaicai编辑 ,原因: 修改图片链接
上传的附件:
收藏
免费 3
支持
分享
最新回复 (5)
雪    币: 1931
活跃值: (442)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
2
请问gdb调试与常态运行的区别在哪里可以找到资料
2020-5-30 09:42
0
雪    币: 1110
活跃值: (569)
能力值: ( LV3,RANK:35 )
在线值:
发帖
回帖
粉丝
3
我这里生成的弄起来好像有点问题。。。。可能是环境没弄对。。。。
2020-6-19 19:49
0
雪    币: 2886
活跃值: (599)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
4
王嘟嘟 我这里生成的弄起来好像有点问题。。。。可能是环境没弄对。。。。
可以先按第一篇里的设置下环境,如果还有问题,可以把具体的报错贴出来
2020-6-22 23:11
0
雪    币: 2886
活跃值: (599)
能力值: ( LV5,RANK:70 )
在线值:
发帖
回帖
粉丝
5
TopGreen 请问gdb调试与常态运行的区别在哪里可以找到资料
gdb调试我的理解就是逐步执行,执行过程中你可以方便的观察甚至改动。gdb的资料可以看看这个:
https://www.cnblogs.com/arnoldlu/p/9633254.html
2020-6-22 23:13
0
雪    币: 1931
活跃值: (442)
能力值: ( LV2,RANK:10 )
在线值:
发帖
回帖
粉丝
6
dxbaicai gdb调试我的理解就是逐步执行,执行过程中你可以方便的观察甚至改动。gdb的资料可以看看这个: https://www.cnblogs.com/arnoldlu/p/9633254.html
但是你会发现如果不在gdb中执行,直接外部执行——又报错了,这又是为啥呢?

增加NOP链
这是因为gdb在运行时,会往栈上添加许多进程使用的环境变量,导致栈的地址变低了,但是直接运行时,没有这些环境变量,所以地址会比gdb中查询获得的高。对于这个问题,我们可以NOP链来绕过 
我想问的是这个
2020-7-14 19:45
0
游客
登录 | 注册 方可回帖
返回
//