PWN的知识之栈溢出

栈溢出

什么是栈溢出?

栈溢出(Stack Overflow)是指在程序运行过程中,向栈中存放的数据量超过了栈的最大容量,从而导致程序出现异常行为的情况。可以比作一个箱子原本只能容纳一定数量的物品,如果强行往里面塞入更多的东西,最终物品会“溢出来”,进而影响到箱子周围的其他物品,甚至破坏整个存放系统的正常运作。

在计算机程序的运行时内存布局中,栈是一种用于存储局部变量、函数调用信息(如返回地址、参数等)的数据结构。它遵循后进先出(LIFO)的原则进行操作。当程序不断地向栈中压入数据(例如频繁调用函数,每次函数调用都会在栈上分配空间以存储局部变量和调用相关信息),却没有及时进行出栈操作以释放空间时,一旦栈的使用超出了其最大容量范围,就会发生栈溢出。

什么情况下会出现栈溢出?

这里以C语言为例,举几种不同的例子

无限循环中的函数调用

在一个无限循环体内不断地进行函数调用,且每个函数调用都会在栈上分配空间,这样持续下去也会导致栈溢出。下面是一个典型的因为无限循环导致栈溢出的

#include <stdio.h>

// 递归函数,没有合理的终止条件
void infinite_recursion() {
    int local_variable = 0; // 局部变量,占用栈空间
    printf("Calling function, stack address: %p\n", &local_variable);
    infinite_recursion(); // 函数调用自身
}

int main() {
    infinite_recursion(); // 开始递归调用
    return 0;
}

infinite_recursion 函数在内部调用自身每次调用时都会在栈上分配一个局部变量(local_variable)并打印出来来直观显示栈的增长,并且没有终止条件,运行之后会因为没有终止条件而进行无限调用,栈空间持续占用,最后直至超出栈的空间,造成栈溢出

递归调用过深

个函数在其内部不断地调用自身,并且没有合理的终止条件或者终止条件很难达到时,就会导致栈不断地增长,最终可能引发栈溢出。以下是一个计算阶乘的递归函数示例,当输入的数值较大时,就可能出现栈溢出的情况:

#include <stdio.h>

int factorial(int n) {
    if (n == 0 || n == 1) {
        return 1;
    }
    return n * factorial(n - 1); // 递归调用
}

int main() {
    int n;
    printf("请输入一个整数来计算其阶乘: ");
    scanf("%d", &n); 

    int result = factorial(n); 
    printf("阶乘结果: %d\n", result);

    return 0;
}

如果用户输入一个很大的数就会因为调用次数过多,栈空间会被耗尽,导致栈溢出,程序崩溃。

栈溢出如何利用?

这里用一道题目来说明一下(题目来源ctfshow):

check一下

32位程序开启NX,部分开启RELRO

IDA32位打开查看一下main函数

打开ctfshow_flag文件,读取其中的内容,根据命令行参数决定打印不同的消息。如果命令行参数个数小于等于 1,则提示用户重试,否则调用 ctfshow 函数处理用户输入的命令行参数,并进行输出

跟进一下ctfshow函数

简单解读一下:

char dest: 声明一个名为dest的字符变量。

return strcpy(dest, src)使用 strcpy

数将 src 字符串复制到dest ,并返回指向 dest 的指针。

strcpy函数这个函数是一个典型的可以用来利用溢出的函数。所以我们可以在这里进行栈溢出。

注意到signal(11, (__sighandler_t)sigsegv_handler);函数发生对存储的无效访问时,会把stderr打印输出,即将flag的值打印输出,那么我们直接输入超长数据就会溢出,程序就会崩溃进而打印出flag

<think>嗯,用户想学习CTFShow平台上的PWN入门级栈溢出攻击技巧,包括教程、原理和实例。好的,我需要先回顾栈溢出的基本原理,然后结合CTFShow的题目例子来解释。首先,栈溢出是覆盖返回地址来控制程序流程,对吧?比如覆盖返回地址为system函数,然后传入/bin/sh的参数。 那我要分步骤来讲解。可能先讲栈的结构,比如函数调用时的返回地址、局部变量这些。然后,如何通过溢出覆盖返回地址。可能需要提到缓冲区溢出,比如使用gets、scanf等不安全的函数导致输入超出缓冲区大小。 接下来,结合CTFShow的题目,比如pwn41到pwn48,这些题目可能涉及不同的利用技巧。例如,引用4提到bss段的buf2可以用来存储输入数据,传入/bin/sh给system。这可能是一个例子,需要具体说明如何构造payload,将参数放入合适的位置,并跳转到system函数。 用户可能还需要了解如何找到system函数的地址,以及如何获取/bin/sh字符串的地址。可能需要用到ROP技术,或者题目中已经提供了这些地址。另外,要注意内存保护机制,比如NX、ASLR,但入门题目可能这些保护都没开,所以可以直接执行栈上的代码或者覆盖返回地址。 然后,可能需要给出一个具体的例子,比如如何利用栈溢出执行system("/bin/sh")。步骤大概是:确定溢出点,计算偏移量,找到system地址,找到/bin/sh字符串地址,构造payload填充缓冲区并覆盖返回地址为system,同时传递参数。 此外,用户可能需要知道如何调试,使用gdb或者其他工具来确定偏移量和地址是否正确。还有,如何构造payload,比如使用Python的struct模块来打包地址为大端或小端格式,取决于目标架构。 可能的问题点包括:确定偏移量的正确位置,处理地址中的空字符,确保参数正确传递(比如32位和64位传参方式不同)。例如,32位下参数在栈中,而64位可能使用寄存器。但CTFShow的题目可能都是32位的,或者需要根据具体情况调整。 最后,要提醒用户注意实践时的细节,比如输入的处理,避免截断,以及利用现有的函数和字符串。可能还需要提到如何查找gadgets,但入门题可能不需要复杂的ROP链,直接覆盖返回地址即可。 总结一下,回答的结构应该是先讲原理,再结合实例分步骤说明,最后给出相关问题和引用。需要确保LaTeX格式正确,比如寄存器用$eax$,地址用$0xdeadbeef$这样的格式。代码块用Python示例如何生成payload,可能还要提到如何利用题目中的特定函数或缓冲区。</think>### CTFShow PWN入门 栈溢出攻击技巧解析 #### 一、栈溢出基本原理 栈溢出是通过覆盖函数返回地址实现控制流劫持的技术。当程序使用不安全函数(如`gets`/`scanf`)时,若未对输入长度做限制,攻击者可构造超长输入覆盖栈帧中的返回地址,使其指向恶意代码或系统函数。例如,覆盖返回地址为`system("/bin/sh")`可实现获取shell[^4]。 栈帧结构示例: $$ \text{低地址} \rightarrow \text{缓冲区} \rightarrow \text{EBP} \rightarrow \text{返回地址} \rightarrow \text{高地址} $$ #### 二、攻击步骤拆解 1. **确定溢出点**:通过调试工具(如`gdb`)计算缓冲区到返回地址的偏移量。 - 使用`pattern create`生成测试字符串,观察崩溃时寄存器的值。 2. **获取关键地址**: - `system`函数地址:通过`objdump -d`或`gdb`的`p system`获取。 - `/bin/sh`字符串地址:若程序中存在可直接引用,否则需自行写入。 3. **构造Payload**: - 填充缓冲区至返回地址位置。 - 覆盖返回地址为`system`地址。 - 传递参数(如`/bin/sh`地址)。 #### 三、实例分析(以CTFShow pwn41为例) **题目场景**: 程序中存在`buf2`缓冲区(位于.bss段),可利用其存储`/bin/sh`字符串,并通过栈溢出调用`system`。 **攻击流程**: 1. **计算偏移量**: 通过调试确定`buf`到返回地址的偏移为`140`字节。 2. **构造Payload**: ```python from pwn import * system_addr = 0x08048460 # system函数地址 buf2_addr = 0x0804A080 # buf2地址 payload = b'A' * 140 + p32(system_addr) + p32(0xdeadbeef) + p32(buf2_addr) # p32(0xdeadbeef)为system返回地址,此处无意义 # p32(buf2_addr)为system的参数地址 ``` 3. **写入`/bin/sh`到buf2**: 若程序提供输入点,可提前将`/bin/sh`写入`buf2`。 #### 四、关键知识点 1. **函数调用约定**: - 32位程序:参数通过栈传递,调用格式为`system(返回地址, 参数)`。 - 64位程序:参数优先通过寄存器(`rdi`, `rsi`等)传递,需用ROP链控制。 2. **内存保护绕过**: - 若开启NX(栈不可执行),需通过ROP实现攻击。 - 若开启ASLR,需泄露地址或使用固定地址函数。 #### 五、防御与优化 - 使用安全函数(如`fgets`替代`gets`)。 - 开启编译保护选项(`-fstack-protector`)。 - 地址随机化(ASLR)和栈不可执行(NX)。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值