ret2text

上一篇博客是对栈溢出的基本学习,做了几个题练习了一下,师兄给的建议是多用gdb,更直观,动态也可以查看寄存器和栈的变化

一、如何使用gdb

1.peda插件的安装

为了方便查看堆栈和寄存器,最好是安装peda插件,命令如下

sudo apt install gdb    //安装gdb
git clone https://github.com/longld/peda.git ~/peda   //使用git命令从GitHub克隆peda插件到本地
echo "source ~/peda/peda.py" >> ~/.gdbinit   //加入配置文件中,每次启动GDB时都会自动加载peda
cat ~/.gdbinit    //查看是否添加成功

这里我是之前就以及安装好gdb了

克隆

配置

查看配置文件,里面确实存在peda插件

2.gdb简介

GDB(GUN Debugger)是一个用来调试C/C++程序的功能强大的调试器,是Linux系统开发C/C++最常用的调试器

GDB主要功能:

1>设置断点(断点可以是条件表达式)

2>使程序在指定的代码行上暂停执行,便于观察

3>单步执行程序,便于调试

4>查看程序中变量值的变化

5>动态改变程序的执行环境

6>分析崩溃程序产生的core文件

3.常用命令

(gdb)help(h)        # 查看命令帮助,具体命令查询在gdb中输入help + 命令 
(gdb)run(r)         # 重新开始运行文件(run-text:加载文本文件,run-bin:加载二进制文件)
(gdb)start          # 单步执行,运行程序,停在第一行执行语句 
(gdb)list(l)        # 查看原代码(list-n,从第n行开始查看代码。list+ 函数名:查看具体函数)
(gdb)set            # 设置变量的值
(gdb)next(n)        # 单步调试(逐过程,函数直接执行)
(gdb)step(s)        # 单步调试(逐语句:跳入自定义函数内部执行)
(gdb)backtrace(bt)  # 查看函数的调用的栈帧和层级关系
(gdb)frame(f)       # 切换函数的栈帧
(gdb)info(i)        # 查看函数内部局部变量的数值
(gdb)finish         # 结束当前函数,返回到函数调用点
(gdb)continue(c)    # 继续运行
(gdb)print(p)       # 打印值及地址
(gdb)quit(q)        # 退出gdb
(gdb)break+num(b)                 # 在第num行设置断点
(gdb)info breakpoints             # 查看当前设置的所有断点
(gdb)delete breakpoints num(d)    # 删除第num个断点
(gdb)display                      # 追踪查看具体变量值
(gdb)undisplay                    # 取消追踪观察变量
(gdb)watch                        # 被设置观察点的变量发生修改时,打印显示
(gdb)i watch                      # 显示观察点
(gdb)enable breakpoints           # 启用断点
(gdb)disable breakpoints          # 禁用断点
(gdb)x                            # 查看内存x/20xw 显示20个单元,16进制,4字节每单元
(gdb)run argv[1] argv[2]          # 调试时命令行传参
(gdb)set follow-fork-mode child   # Makefile项目管理:选择跟踪父子进程(fork())

后面那些有括号的是命令的简化版

Linux·gdb用法详解-CSDN博客具体命令可以参考这篇文章,非常详细

4.调试

准备

gcc -g test.c -o test.out     //进行编译
gdb ./test.out     //进行调试

二、ret2text

1.简介

ret2text就是执行程序中已有的代码,例如程序中写有system等系统的调用函数,在这种攻击方法中,攻击者可以控制程序执行若干不相邻的代码段(即gadgets),这就是我们常说的ROP(Return-Oriented Programming)

补充:

ROP主要思想是在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程

有以下一些概念:

1.rop:在栈缓冲区溢出的基础上,利用程序中已有的小片段 (gadgets) 来改变某些寄存器或者变量的值,从而控制程序的执行流程。

2.gadgets:在程序中的指令片段,有时我们为了达到我们执行命令的目的,需要多个gadget来完成我们的功能。gadget最后一般都有ret,因为我们需要将程序控制权(EIP)给下一个gadget。即让程序自动持续的选择堆栈中的指令依次执行。

3.ropgadgets:一个pwntools的一个命令行工具,用来具体寻找gadgets的。例如:我们从pop、ret序列当中寻找其中的eax
ROPgadget --binary ./7.exe --only "pop|ret" | grep "eax"

4、在linux系统中,函数的调用是有一个系统调用号的。例如execve("/bin/sh",null,null)函数其系统调用号是11,即十六进制0xb。

2.攻击原理

通过篡改栈帧上的返回地址为程序中已有的后门函数。攻击者需要知道对应返回代码的位置,并构造合适的payload来触发栈溢出,从而改变返回地址,执行后门函数‌。

3.主要过程

确定溢出点‌:首先要找到程序中的溢出点,通常是通过分析程序的反汇编代码来定位。常见的溢出点包括gets(buf)、strcpy(dest,sec)、scanf("%s",buf)、strcat(buf,buf2) 、read(0,buf,size)等函数调用‌。

‌构造ROP链‌:一旦找到溢出点,攻击者需要构造一个ROP链。ROP链是由一系列的"gadgets"组成,这些gadgets是程序中已有的、以ret指令结尾的指令序列。攻击者通过这些gadgets来控制程序的执行流程‌。

‌覆盖返回地址‌:在构造好ROP链后,攻击者会通过溢出覆盖函数的返回地址,使其指向ROP链的起始地址。这样,当函数执行完毕后,控制流会跳转到ROP链,执行攻击者控制的代码‌。

‌执行特定代码‌:ROP链的最终目的是执行特定的代码。在ret2text攻击中,攻击者通常会选择执行程序已有的代码段(.text段)中的函数或代码片段。例如,如果攻击者想要执行system("/bin/sh"),他们会在ROP链中插入相应的gadgets来调用system函数‌。

‌绕过安全机制‌:在某些情况下,程序可能启用了如PIE(位置无关执行)和Canary(栈保护)等安全机制。攻击者需要通过格式化字符串漏洞等技巧来泄露栈上的信息,从而绕过这些安全机制‌。

三、练习

云曦24秋季学期期末考-pwn-真正的签到题

先检查一下文件,发现没有缓冲区溢出

nc连接看看

发现不行

用ida打开附件,可以看到有个give_flag

在下面的start_shell中得知输入69470可执行give_flag

得到flag

NSSCTF-pwn-[SWPUCTF 2021 新生赛]gift_pwn

例行发现可能存在栈溢出

用ida打开,在gift函数中发现存在后门

查看main函数,这个是程序的入口,从这里可以看程序的执行流程,会发现执行了vlun函数

查看vuln,看图会发现read读取64个字节,超过了栈的大小

双击buf,查看一下栈内,0x10就是数组的大小,然后后面就是保存的rbp(32位是ebp)以及返回地址。s是存储的rbp,代表这个函数结束以后rbp要被修改为什么。r是返回地址,代表这个函数结束以后程序会跳转到哪个地址继续执行,大多数情况下都是利用这个返回地址来进行操作。

栈的生长方向是由高地址往低地址增长的,可以看到左边是变量所在的相对位置,以s为基准。在read读入字符的时候,第一个字符会被放在相对地址是0x10的位置,第二个字符会被放在0x0F的位置,当读入的字符超过了buf的长度,那么会接着向下(高地址)存放,这个时候就会覆盖掉原来保存在高地址的s和r,以让程序跳转到不同的地方。

read函数可以读入0x64个字符,而buf的长度为16,s为8,r为8,可以修改s和r,(在32位程序中s和r为4,64位程序中s和r为8,一个字符长度是一个字节,一个字节的长度是8位(即长度为8的二进制数),32/8 = 4,64/8 = 8)

目的是为了跳转到gift函数处,那么r是返回地址,也就是说要让r的地址成为gift起始地址

找到gift函数地址

然后构造payload

payload = b'a' * 16 + b'a' * 8

payload += p64(0x4005B6)

接下来写exp

from pwn import *

p = remote("node4.anna.nssctf.cn",28035)
payload = b'a' * 16 + b'a' * 8
payload += p64(0x4005B6)
p.send(payload)
p.interactive()

得到flag

相关推荐
iOS开发-上海4 分钟前
HarmonyOS学习 --- Mac电脑获取手机UDID
学习·harmony os
跃渊Yuey7 分钟前
【C++笔记】AVL树
c++·笔记
凌鲨9 分钟前
IOS学习路线图
学习·ios·cocoa
虾球xz1 小时前
游戏引擎学习第52天
学习·游戏引擎
半夏知半秋1 小时前
unity中获取游戏物体和组件的方式
笔记·学习·游戏·unity·c#·游戏引擎
studying_mmr1 小时前
Introduction to NoSQL Systems
数据库·笔记·nosql数据库·database
Fairy要carry2 小时前
矩阵论-第三章:矩阵的标准型
学习·矩阵
柠檬味的薄荷心2 小时前
【JavaSE基础】第十七章:反射+设计模式
笔记
练小杰2 小时前
我在广州学 Mysql 系列之 数据“表”的基本操作
android·数据库·学习·mysql·adb