PWN入门实战:SHCTF栈溢出与整数溢出利用全复盘
周六上午打开比赛平台的时候我还在犹豫要不要碰PWN。前几次CTF里我都是被分到了Web和MiscPWN一直属于听说过没摸过的状态。这次SHCTF的题目列表拉下来PWN题有两道分数都不低而且一眼扫过去就能猜到考点方向一道栈溢出一道整数溢出。这两类题放在同一场赛事里其实挺典型的也特别适合作为入门选手的练手对象。赛后我把做题的过程和心理活动重新捋了一遍发现最值得记录下来的反而不是最后拿没拿到flag而是环境怎么搭题目怎么读利用怎么一步步试出来的完整思路。这篇就当作一份给同样卡在PWN门口的人看的复盘笔记重点聊那些坑、细节和判断。1. 赛前先想清楚SHCTF的PWN到底在考察什么很多初学者一听到PWN这个词脑子里浮现的都是电影里那种飞快敲键盘的画面总觉得这辈子都学不会。其实放到CTF里PWN的门槛并没有想象中那么高尤其是学生赛和入门赛里出的题目考察的核心就三块栈溢出、整数溢出、格式化字符串。SHCTF这类赛事更是如此出题人不会为难你能拿出来的基本就是经典考点换个壳。PWN本质上是程序分析加漏洞利用的合体。你拿到的是一个编译好的二进制可执行文件需要先通过静态分析和动态调试搞清楚它内部发生了什么再找出它对输入的处理缺陷最后构造一段特殊输入让程序做出超出设计者的预期动作——最常见的就是弹出一个shell或者控制它的输出内容。整个过程可以被拆成独立的步骤每一步都有对应的工具和套路只要按顺序来新手也能稳住。赛前盘点让我心里稍微有个底但真正到手做题时第一道坎其实不是题目本身而是运行环境。pwn环境配置这件事很多人以为装个Kali就万事大吉实际上远没那么简单。工具版本、调试器插件、Python依赖互相打架的情况非常多我这次就在环境上耽误了差不多半小时所以第二部分单独说一说。2. pwn环境配置用Kali搭一套不乱打架的工具链PWN做题目前体系很成熟基础工具就那几样Python 3、pwntools、gdb、checksec。其中pwntools和gdb是绝对的主力前者负责收发数据、构造payload和计算偏移量后者负责在进程崩溃的瞬间查看寄存器和内存状态。许多新手死在这一步是因为gdb里没有装任何增强插件导致反汇编输出可读性很差寄存器变化看得一头雾水。我的做法是先装gdb多平台调试插件再装pwntools顺序不能反。因为pwntools在导入后可能会覆盖一些gdb的默认行为如果插件后装有时会遇到符号表加载异常的问题。Kali下具体操作大致是这样sudo apt update sudo apt install -y python3 python3-pip gdb netcat-traditional git clone https://github.com/pwndbg/pwndbg.git cd pwndbg ./setup.sh pip3 install pwntools这里有个小细节值得说明Kali自带的Python 3版本通常够用直接用pip3装pwntools就行。如果你之前装了别的工具链导致pip环境被搞乱过建议用虚拟环境隔离不要在全局环境里硬怼。我第一次就是因为电脑里有一个旧版Capstone库跟pwntools里的反汇编功能冲突导入pwn模块时直接报错。后来在venv里重装了一遍才正常。python3 -m venv ~/pwnenv source ~/pwnenv/bin/activate pip install pwntools装完以后启动gdb验证一下pwndbg是否生效。如果命令行提示符变成了pwndbg说明插件加载成功。再接着用checksec命令看二进制文件的保护机制。注意这个checksec在pwndbg里也内置了不需要特意另装直接运行即可。环境配好之后我通常会在本地写一个最小测试程序来验证工具链是否畅通。比如写一个简单的gets函数读输入的C程序编译后扔给pwntools发送一长串字符看程序是否Segmentation fault。这一步很值它能提前确认你的gdb、pwntools和系统glibc之间没有暗坑否则等到比赛题目真正打开时才追环境问题节奏就全乱了。3. 拿到PWN题先别慌从file和checksec读出出题人的思路环境就绪以后面对一个陌生的ELF文件我习惯按固定顺序操作。第一步用file命令看文件类型第二步用checksec看防护机制第三步用strings扫一遍可见字符串第四步再做反汇编。file ./pwn_test checksec --file./pwn_test为什么顺序这么重要因为file的结果决定你要不要换架构。很多新人看到32位的ELF和64位的ELF默认按同一种套路处理结果payload构造时栽了大跟头。32位程序参数通过栈传递64位程序前几个参数走的是寄存器栈对齐要求也不同。仅这一条就足以让不检查架构的人白熬夜。checksec给出的信息更是直接关系到利用方案的选择常见保护项和应对思路可以整理成一张对照表保护机制含义对利用方式的影响NX开启栈不可执行不能直接执行shellcode考虑ret2libcPIE开启程序基址随机化需要先泄露地址或利用部分覆盖Canary开启栈金丝雀保护溢出前必须绕过Canary校验RELRO完全开启GOT表只读格式化字符串改GOT的方案受限我这次在SHCTF的栈溢出题里遇到的组合就是典型的NX开启、无Canary、PIE未开。看到这个组合脑子里立刻知道方向不需要费劲去泄露地址重点是如何在栈不可执行的约束下构造ROP链。strings这一步经常被忽略但它特别有用。程序里残留的打印提示、函数名、甚至调试信息都能帮你快速锁定漏洞入口。比如题目里如果出现/bin/sh这样的字符串那多半是在暗示你往shellcode或ret2libc的方向考虑。如果出现类似Too long!的提示大概率对应某个长度检查逻辑后面可能会隐藏整数溢出。真正打开反汇编界面后第一要找的是危险函数。gets、strcpy、strcat、sprintf这一族都是经典溢出入口。用pwndbg反汇编主函数重点观察栈空间分配大小和拷贝长度这些数字直接决定payload里的填充长度。4. 栈溢出利用的完整链路从偏移量计算到拿到shell栈溢出的套路本身并不复杂但它像组装模型一样每一步都必须精确。我以这次做的题目为例把整条链路串一遍。这道题的伪代码逻辑大概是程序先打印一行提示然后调用gets读取用户输入到局部缓冲区接着判断一个全局变量是否会变成某个特定值不做任何长度限制。编译时关闭了Canary开启了NX未开PIE。这个配置对新手特别友好因为即使被溢出了也不会被Canary打断不需要猜测随机基址。第一步确定缓冲区到返回地址的偏移量。这一步我直接用pwntools的cyclic来完成from pwn import * context.log_level debug io process(./pwn_stack) payload cyclic(200) io.sendline(payload) io.wait() # 从core dump或gdb中读取RIP的实际值出现Segmentation fault后用gdb查看崩溃时的地址。pwndbg会自动提示RIP: cyclic offset或者你可以在gdb里直接运行cyclic_find。比如崩溃地址是0x6161616a对应的偏移量就是42。这个数很关键它表示从缓冲区起始位置到返回地址之间需要填充42字节。第二步寻找可以利用的函数或后门。很多入门题的代码里都会留一个未调用的函数里面封装了system(/bin/sh)或者cat flag的操作。反汇编里找到这个函数的地址比如0x08048456。如果题目没留后门就得走ret2libc路线先泄露puts地址再算出libc基址。第三步构造返回地址覆盖。由于NX开启栈里不能执行shellcode所以直接跳到后门函数即可。payload长度非常短payload bA * 42 p32(0x08048456) io.sendline(payload) io.interactive()发送之后程序会重新走到那个函数打开一个shellflag就都在眼前了。第一次真正跑通的时候屏幕上的$提示符出现的一瞬间那种成就感确实很提气。不过栈溢出还有一个隐藏考点就是返回地址覆盖后你能跳到哪里并不等于你能控制完整的调用链。有时候后门函数是先打印再退出那没问题但如果你想实现任意代码执行还得考虑栈对齐问题。64位程序里调用system(/bin/sh)时如果栈没对齐会直接崩溃这是新手最容易踩的大坑。解决办法就是在payload里多加一个ret指令的地址让栈在进入system之前先对齐到16字节边界。这种硬试出来的经验比教程里看到的更让人印象深刻。5. 整数溢出的隐蔽性对比长度检查后发生的越界第二道题考察整数溢出这题比栈溢出更考验对代码逻辑的理解。很多时候漏洞的表象是栈溢出但触发它的前提条件却是整数溢出造成的一次错误判断。题目程序里有一段逻辑输入一个长度值然后根据这个值决定从输入里拷贝多少数据到缓冲区。乍一看长度被限制了怎么想都不应该溢出。但如果这个长度值被存储在一个有符号变量里而实际参与比较或拷贝时被转换成了无符号类型情况就完全不同了。举个例子一个32位的有符号整数如果输入的是0xFFFFFFFF那么它在有符号视角下是-1。当代码执行if (len 10)这样的判断时-1确实小于10检查通过了。可是紧接着拷贝时计算的是memcpy(dest, src, len)参数len被隐式转换成了无符号数也就是4294967295。于是memcpy会尝试拷贝将近4GB的数据缓冲区早就被撑爆了。这就是整型溢出在PWN里的经典玩法表面限制形同虚设溢出也就必然发生。我在Solving时先用file确认程序是64位然后在源码级别确认长度变量是int类型。随后用一段测试输入验证栈上缓冲区的覆盖情况。当时我写了一段Python脚本循环尝试不同的长度边界from pwn import * # 依次尝试 0x7fffffff, 0xffffffff 等特殊值 for num in [0x7fffffff, 0x7ffffffe, 0xffffffff, 0xfffffffe]: r process(./pwn_int) r.recvuntil(blength:) r.sendline(str(num).encode()) r.recvuntil(bdata:) r.send(bA * 200) print(r.poll(blockFalse)) r.close()当发送0xffffffff时进程明显退出异常说明检查被绕过拷贝长度判断已经超限。这时再利用溢出控制返回地址思路就和第一部分完全一样了。整数溢出最迷惑人的地方在于题目可能连特殊值都不给提示。你需要手动试出哪个数值能让逻辑失效。判断方法也不难就是穷举几个有代表性的数字比如0x7FFFFFFF、0x80000000、0xFFFFFFFF观察程序行为的变化。在gdb里配合观察比较跳转指令的分支情况基本上十分钟内就能定位漏洞。这类题想巩固的话要理解C语言里的隐式类型转换规则。signed和unsigned混用时到底谁被转成谁是跟操作数的位宽和类型等级有关的。我刚入门时一直记反后来就用一个小口诀有符号和无符号打架时通常无符号赢。这个口诀虽然不严格但在绝大多数CTF题目里都够用。6. 调试与利用过程中真正让人崩溃的五分钟整个做题过程不是一路顺畅的。我印象最深的一处卡壳是在栈溢出题已经构造好payload、send出去、屏幕上却没有出现shell。我当时第一反应是地址算错了翻来覆去地核对调用后门函数的地址。结果问题根本不是地址而是本地程序的路径带了argv[0]参数问题——我直接用process(./pwn_stack)启动时二进制文件依赖的某个so找不到导致程序在进入主逻辑之前就崩溃了。这类环境型bug在PWN里特别常见。Kali自带工具虽然全但glibc版本和题目里打包的libc版本未必一致这时候最简单的办法是下载题目自带的libc然后用pwninit或patchelf把链接器指过去。具体命令可以参考patchelf --set-interpreter ./ld-2.27.so ./pwn_stack patchelf --set-rpath . ./pwn_stack还有一次崩溃是忘记考虑64位程序的栈对齐。system函数在glibc内部会用到SSE指令如果调用时栈指针恰好是16的倍数而非8的倍数movaps就会触发Segmentation fault。这种问题在本地调试时会表现得非常莫名其妙地址对、函数对、参数对但就是崩。解决办法就是在发送payload时在返回地址后面多塞一个ret gadget。这个细节非常值得记下来因为十次做ret2libc有七次新手崩溃原因都是它。另外pwndbg的cyclic和gdb.attach组合用起来很方便。我发现有些时候脚本里写上gdb.attach(io)程序会停住等你手动调试。这种模式下可以逐步观察payload的结构在栈上的排布情况比事后分析core dump直观得多。不过每场比赛我都会把握一个度不能在调试器里陷太久——如果某一步连续试了四十分钟还不行果断换一个角度重新读源码很可能你纠结的地方根本不是漏洞点。7. 给同样卡在PWN门口的人三个回归实操的建议打完这次SHCTF的两道PWN题我最真实的感受是PWN并没有那么遥不可及但它是一道需要重复练习才能建立手感的技术。入门阶段给自己定几条务实的路线比到处收藏教程有用得多。第一把工具链练到条件反射的程度。拿到题目第一反应就应该是file、checksec、strings接着打开pwndbg。这些操作不需要犹豫练到像吃饭喝水一样自然就对了。第二一定要自己动手跑完至少十个经典的栈溢出入门题。光看writeup和只看解题视频效果都很差因为每个程序保护组合不一样调试中遇到的问题也各不相同这些经验只能靠实战积攒。第三养成赛后写复盘笔记的习惯尤其是把你卡壳的地方原原本本记下来哪怕是很蠢的忘记加ret对齐一周后回头看会发现这些问题解决了你的水平也就上去了。我个人的体会是入门PWN最需要的不是聪明而是耐心。环境配置出错就慢慢排查偏移量算错就重新跑一次整数溢出试不出来就多换几个边界值。这些动作本身都不难难的是愿意一遍遍坐下来磨。等到某一天你发现自己闭着眼睛都能把payload拼出来那一刻回头看第一次碰PWN时的手忙脚乱就会觉得一切都很值得。

相关新闻

屏幕隐私保护新思路:用网页遮罩层与快捷键打造CSDN风伪装工具

屏幕隐私保护新思路:用网页遮罩层与快捷键打造CSDN风伪装工具

你有没有过这样的瞬间:正在处理一件私事,背后突然有人经过,手忙脚乱切窗口,还是被扫到了一角。在办公室、图书馆、咖啡馆这类半公共空间,屏幕内容被旁人无意扫到,确实是很多人的真实痛点。我做的这个“CSDN…

2026/10/12 0:26:20 阅读更多 →
Python解析Human3.6M:从BVH到3D关键点坐标的完整指南

Python解析Human3.6M:从BVH到3D关键点坐标的完整指南

简介:面向计算机视觉与机器学习研究者的Human3.6M三维人体姿态数据集获取工具包,专门解决从官方渠道下载、解压到数据预处理的完整流程中遇到的常见问题,适合需要批量使用该标准数据集的初学者或工程团队。资源共14个文件,压缩包大…

2026/10/12 0:26:43 阅读更多 →
STM32F4 OTA升级:单App与双App方案优缺点对比

STM32F4 OTA升级:单App与双App方案优缺点对比

1. 引言在嵌入式产品开发中,OTA(Over-The-Air)升级已成为一项关键能力。对于基于 STM32F4 系列 MCU 的产品,OTA 升级方案的设计直接关系到系统的稳定性、可靠性和用户体验。目前主流的方案分为单 App 升级和双 App 升级&#xff0…

2026/10/12 0:26:49 阅读更多 →

最新新闻

数据结构 - > 排序算法

数据结构 - > 排序算法

1. 排序的概念1.1 常见的排序算法1.2 排序算法的评价指标复杂度:评价排序算法的第一大指标就是时间复杂度和空间复杂度,它衡量算法的时间效率和空间效率。稳定性:假定在待排序的数据元素中有两个元素 Ri 和 Rj,它们对应的关键字为…

2026/10/12 3:39:10 阅读更多 →
ccg-workflow Shell 技能指南:Bash 脚本自动化、系统管理与多模型协作实战

ccg-workflow Shell 技能指南:Bash 脚本自动化、系统管理与多模型协作实战

【免费下载链接】ccg-workflow 多模型协作工作流引擎 — /ccg:go 一个命令,AI 自动分析意图、选择策略、编排 Codex Gemini Claude 协作执行 项目地址: https://gitcode.com/gh_mirrors/cc/ccg-workflow 点击查看 免费下载 导读 本文基于 ccg-workfl…

2026/10/12 3:39:10 阅读更多 →
2026年软件测试趋势:AI Agent、质量内建与可观测性重塑质量保障

2026年软件测试趋势:AI Agent、质量内建与可观测性重塑质量保障

做测试这行,每年年底都在猜明年的技术方向,但2026年这次不太一样。我最近和不少测试负责人、开发团队聊下来,大家最焦虑的已经不是又冒出了什么新工具,而是整个质量体系正在被 AI 和平台工程重构,很多沿用多年的测试方…

2026/10/12 3:39:10 阅读更多 →
netdxf实战:DXF文字注释与尺寸标注的创建与修改

netdxf实战:DXF文字注释与尺寸标注的创建与修改

接触过DXF开发的人应该都有这种感觉:画直线、画圆、画多段线都属于“基本功”,真正让图纸变得可读、可传递设计意图的,是文字注释和尺寸标注。这一篇是整个netdxf系列里我比较想写的一篇,因为注释和标注的处理逻辑和普通几何实体完…

2026/10/12 3:39:10 阅读更多 →
文华财经主升浪买点指标公式拆解:多条件共振识别趋势启动

文华财经主升浪买点指标公式拆解:多条件共振识别趋势启动

1. 文华财经主升浪买点指标的实战拆解做期货日内或者波段的朋友,应该都听过“主升浪”这个词。行情走主升浪的时候,速度最快、幅度最大,但也是最难拿得住的一段。很多朋友在文华财经软件里翻遍了各类指标公式,要么信号滞后&#x…

2026/10/12 3:39:10 阅读更多 →
ppt-master 的 IBM 品牌身份预设解析:从 Carbon Blue 设计规范到可执行的 design_spec

ppt-master 的 IBM 品牌身份预设解析:从 Carbon Blue 设计规范到可执行的 design_spec

AI 技能人工智能 【免费下载链接】ppt-master AI 把任意文档生成真正可编辑的 PowerPoint —— 原生形状与动画、演讲者备注可合成音频旁白、还能参考你自己的 .pptx 模板,而不是一张张图片 何雨果出品 项目地址: https://gitcode.com/hugohe3/ppt-master 点击查看…

2026/10/12 3:38:09 阅读更多 →

日新闻

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

复古胶片颗粒感噪点合成器:Canvas ImageData 像素高斯杂色注入算法

在数码相机、高清显示屏与现代矢量图形技术高度发达的今天,画面可以做到绝对的锐利、平滑与无瑕。然而,当一张秋日手账插画或拍立得照片过于“平整无瑕”时,往往会散发出一种冰冷生硬的“数码塑料感(Digital Plasticity&#xff0…

2026/10/12 0:00:59 阅读更多 →
活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

活字印刷古籍线装排版:Canvas 竖排文字与栏线自适应算法

在现代网页与移动端设计中,横排(Horizontal Layout)早已经成为了绝对的主流。然而,当我们翻开泛黄的线装古籍、宋版木刻诗集,或是欣赏一张茶道雅集的手写便签时,那种**自上而下纵向书写、自右向左逐列铺展&…

2026/10/12 0:00:59 阅读更多 →
周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

周日晚间的“精神松绑减震器”:无压力情绪倾倒箱与温和轻声陪伴

每到周日的晚上八点到十点,很多人心里都会悄悄亮起一盏警示灯。 在心理学上,这种现象有一个专门的称谓——“周日夜晚焦虑症(Sunday Scaries)”。明天又是周一,闹钟又要重新在七点响彻卧房;脑海里仿佛有一个…

2026/10/12 0:00:59 阅读更多 →

周新闻

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

流感时间序列预测实战:ARIMA/LSTM全流程拆解与避坑指南

简介:基于 ARIMA、LSTM、Transformer 等模型的流感时间序列预测 Python 源码,面向计算机相关专业课程设计与期末大作业学生,以及项目实战学习者。内容覆盖预处理、平稳性检验、定阶、残差分析、多模型对比预测的完整时序建模流程,…

2026/10/12 0:16:30 阅读更多 →
影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别

影刀RPA新手教程:键盘模拟输入实战——输入文本与模拟按键的区别 做影刀RPA自动化,十个新手有八个栽在"往输入框里填东西"这件事上:要么填不进去,要么填了一半,要么直接把原来内容追加在后面。这背后的根因&…

2026/10/12 0:16:38 阅读更多 →
影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容

影刀RPA新手教程:阅文起点小说数据采集实战——书籍信息与章节内容 1. 认识影刀:什么场景该用RPA采小说数据 起点中文网的页面结构相对稳定——分类榜单、书籍详情、章节内容三块独立页面,跳转链路清晰。这种场景非常适合影刀自动化&#x…

2026/10/12 0:16:43 阅读更多 →

月新闻

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

我发现了一个新思路:用 Remotion + Claude Code 像写代码一样自动化生成短视频

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 10:45:37 阅读更多 →
Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

Windows下 Codex 中 Chrome 和 Computer Use 插件不可用问题排查及解决参考方式:TaoToken 统一 Key 配置与验证

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:53 阅读更多 →
黑夜航拍船只数据集训练YOLOV5模型全流程解析

黑夜航拍船只数据集训练YOLOV5模型全流程解析

/* MD / 富文本中的 .toc(含博客园搬家等嵌套结构);.toc-box 在侧栏,不受影响 */#content_views .toc,/* 编辑器常在目录前后插入空 p(:empty 仍占 20px),一并去掉避免顶空隙 */#content_views.markdown_views > p:empty:has(+ .toc),#content_views.markdown_views …

2026/10/11 14:36:54 阅读更多 →