1. Checksec工具入门安全防护的第一道防线第一次接触CTF-Pwn题目时很多新手会直接运行程序就开始尝试输入这就像不戴护具就去攀岩一样危险。Checksec就是我们分析二进制文件安全防护机制的安全扫描仪它能快速告诉我们目标程序开启了哪些防护功能。我在刚开始玩CTF时就因为忽略这个步骤浪费了好几个小时在已经失效的攻击方法上。安装Checksec非常简单推荐使用GitHub上的最新版本。打开终端执行以下命令git clone https://github.com/slimm609/checksec.sh.git cd checksec.sh sudo ln -sf checksec /usr/bin/checksec这样就能全局使用checksec命令了。有些同学可能会问我的gdb-peda里自带checksec为什么要装新的老版本的checksec会漏掉一些新出现的防护机制检测就像用旧版杀毒软件查新型病毒一样不靠谱。使用方式极其简单checksec ./vulnerable_program这条命令会输出类似这样的信息Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)这短短几行信息实际上包含了我们制定攻击策略的关键依据。比如看到NX enabled就知道栈上的shellcode执行不了必须考虑ROP攻击看到Canary found就要准备绕过或泄露栈保护值。2. 深度解析RELRO保护机制RELRO(Relocation Read-Only)保护是很多CTF选手容易忽视但实际非常重要的防护机制。它主要防护的是对GOT(Global Offset Table)表的篡改攻击。记得我第一次遇到Full RELRO保护时原本计划好的GOT覆盖攻击完全失效不得不重新思考攻击路线。RELRO分为三个等级No RELROGOT表可读可写这是最危险的状态Partial RELROGOT表在程序启动后变为只读但延迟绑定机制仍然存在风险Full RELRO所有符号在程序启动时立即解析GOT表完全只读编译测试程序时可以这样控制RELRO级别gcc -z norelro -o test test.c # 关闭RELRO gcc -z lazy -o test test.c # 部分RELRO(默认) gcc -z now -o test test.c # 完全RELRO在实际CTF比赛中Partial RELRO是最常见的情况。这时候我们可以利用以下攻击手法修改.got.plt表中的函数指针如将atoi的GOT项改为system地址通过格式化字符串漏洞修改GOT表利用UAF等漏洞修改GOT表指针但遇到Full RELRO时这些方法都会失效。这时候就需要转向其他攻击面比如利用栈溢出结合ROP攻击堆结构寻找其他内存破坏漏洞3. Stack Canary栈溢出的守门人Stack Canary就像是在栈上安插的哨兵专门防范缓冲区溢出攻击。它的工作原理是在函数开始时在栈上放置一个随机值(canary)在函数返回前检查这个值是否被修改。如果发现被篡改程序会立即终止。Canary检测在汇编层面看起来是这样的mov rax,QWORD PTR fs:0x28 # 从fs段获取canary值 mov QWORD PTR [rbp-0x8],rax # 将canary存入栈中 ... # 函数主体代码 mov rdx,QWORD PTR [rbp-0x8] # 取出栈中的canary sub rdx,QWORD PTR fs:0x28 # 与原始值比较 je 0x4005d7 # 相同则跳转 call 0x4004c0 __stack_chk_fail # 否则调用失败处理在CTF中绕过Canary主要有三种方法泄露Canary值通过格式化字符串漏洞或信息泄露漏洞获取canary值然后在溢出时保持该值不变逐字节爆破Canary由于canary通常以null字节结尾可以逐个字节尝试劫持__stack_chk_fail修改这个函数的GOT项让它不终止程序编译时控制Canary的选项gcc -fno-stack-protector -o test test.c # 禁用栈保护 gcc -fstack-protector -o test test.c # 对含char数组的函数启用保护 gcc -fstack-protector-all -o test test.c # 对所有函数启用保护4. NX保护与执行权限控制NX(No-eXecute)保护是现代操作系统最重要的安全特性之一。它通过将数据区域标记为不可执行有效阻断了直接在栈或堆上执行shellcode的攻击方式。这就像是在说这里只能存放东西不能当做工厂来用。检查NX状态的简单方法readelf -l vulnerable_program | grep GNU_STACK输出中出现RWE表示栈可执行只有RW则表示NX保护开启。绕过NX保护的常用技术是ROP(Return-Oriented Programming)。通过串联程序中已有的代码片段(gadgets)构造出需要的功能链。比如构造system(/bin/sh)的调用找到pop rdi; ret的gadget找到/bin/sh字符串地址找到system函数地址构造payloadpadding pop_rdi_addr binsh_addr system_addr控制NX保护的编译选项gcc -z execstack -o test test.c # 禁用NX gcc -z noexecstack -o test test.c # 启用NX(默认)5. PIE与地址随机化PIE(Position-Independent Executable)技术让程序的所有段(代码、数据等)在加载时都使用随机地址这大大增加了攻击难度。就像每次运行程序时所有函数和变量都会搬家攻击者无法提前知道它们的具体位置。PIE通常与ASLR(Address Space Layout Randomization)配合使用。检查PIE状态file vulnerable_program输出中出现pie executable表示PIE开启。当遇到PIE保护时攻击通常需要分两步信息泄露通过漏洞泄露某个关键地址计算基址根据泄露的地址计算其他所需地址编译选项控制PIEgcc -no-pie -o test test.c # 禁用PIE gcc -fpie -pie -o test test.c # 开启PIE(级别1) gcc -fPIE -pie -o test test.c # 开启PIE(级别2)6. 新版Checksec的增强功能随着安全技术的发展新版Checksec增加了对更多防护机制的检测。这些功能在老版本中是没有的这也是我推荐使用最新版的原因。RPATH/RUNPATH检测 这两个选项关系到程序运行时查找共享库的路径顺序。不安全的配置可能导致加载恶意库。检测到问题时可以尝试patchelf --remove-rpath vulnerable_program patchelf --set-rpath /safe/path vulnerable_programFORTIFY_SOURCE 这是GCC提供的源码级保护会将危险的字符串操作函数替换为安全版本。比如strcpy → __strcpy_chkmemcpy → __memcpy_chksprintf → __sprintf_chk开启FORTIFY的方法gcc -D_FORTIFY_SOURCE2 -O1 -o test test.c在实际漏洞利用中遇到FORTIFY保护时需要避免使用被保护的函数寻找其他未受保护的函数链确保缓冲区操作长度严格可控7. 综合分析与实战策略拿到一个CTF题目时我通常会按照以下流程进行分析运行checksec获取防护概况根据防护组合制定攻击路线使用gdb验证漏洞可行性构建完整的exploit常见的防护组合及应对策略防护组合典型特征攻击思路全保护RELRO FULL, Canary, NX, PIE需要信息泄露ROP部分保护Partial RELRO, NX可尝试GOT覆盖弱保护只有NX考虑shellcode注入无保护所有保护关闭直接栈溢出注入shellcode在真实漏洞利用时有几点经验值得注意32位和64位程序在参数传递、栈布局上有很大差异不同libc版本提供的gadgets可能完全不同网络赛题要注意字节序和输入过滤本地测试成功的exploit可能因为环境差异在远程失败
CTF-Pwn安全防护机制解析——Checksec实战指南
1. Checksec工具入门安全防护的第一道防线第一次接触CTF-Pwn题目时很多新手会直接运行程序就开始尝试输入这就像不戴护具就去攀岩一样危险。Checksec就是我们分析二进制文件安全防护机制的安全扫描仪它能快速告诉我们目标程序开启了哪些防护功能。我在刚开始玩CTF时就因为忽略这个步骤浪费了好几个小时在已经失效的攻击方法上。安装Checksec非常简单推荐使用GitHub上的最新版本。打开终端执行以下命令git clone https://github.com/slimm609/checksec.sh.git cd checksec.sh sudo ln -sf checksec /usr/bin/checksec这样就能全局使用checksec命令了。有些同学可能会问我的gdb-peda里自带checksec为什么要装新的老版本的checksec会漏掉一些新出现的防护机制检测就像用旧版杀毒软件查新型病毒一样不靠谱。使用方式极其简单checksec ./vulnerable_program这条命令会输出类似这样的信息Arch: amd64-64-little RELRO: Partial RELRO Stack: Canary found NX: NX enabled PIE: No PIE (0x400000)这短短几行信息实际上包含了我们制定攻击策略的关键依据。比如看到NX enabled就知道栈上的shellcode执行不了必须考虑ROP攻击看到Canary found就要准备绕过或泄露栈保护值。2. 深度解析RELRO保护机制RELRO(Relocation Read-Only)保护是很多CTF选手容易忽视但实际非常重要的防护机制。它主要防护的是对GOT(Global Offset Table)表的篡改攻击。记得我第一次遇到Full RELRO保护时原本计划好的GOT覆盖攻击完全失效不得不重新思考攻击路线。RELRO分为三个等级No RELROGOT表可读可写这是最危险的状态Partial RELROGOT表在程序启动后变为只读但延迟绑定机制仍然存在风险Full RELRO所有符号在程序启动时立即解析GOT表完全只读编译测试程序时可以这样控制RELRO级别gcc -z norelro -o test test.c # 关闭RELRO gcc -z lazy -o test test.c # 部分RELRO(默认) gcc -z now -o test test.c # 完全RELRO在实际CTF比赛中Partial RELRO是最常见的情况。这时候我们可以利用以下攻击手法修改.got.plt表中的函数指针如将atoi的GOT项改为system地址通过格式化字符串漏洞修改GOT表利用UAF等漏洞修改GOT表指针但遇到Full RELRO时这些方法都会失效。这时候就需要转向其他攻击面比如利用栈溢出结合ROP攻击堆结构寻找其他内存破坏漏洞3. Stack Canary栈溢出的守门人Stack Canary就像是在栈上安插的哨兵专门防范缓冲区溢出攻击。它的工作原理是在函数开始时在栈上放置一个随机值(canary)在函数返回前检查这个值是否被修改。如果发现被篡改程序会立即终止。Canary检测在汇编层面看起来是这样的mov rax,QWORD PTR fs:0x28 # 从fs段获取canary值 mov QWORD PTR [rbp-0x8],rax # 将canary存入栈中 ... # 函数主体代码 mov rdx,QWORD PTR [rbp-0x8] # 取出栈中的canary sub rdx,QWORD PTR fs:0x28 # 与原始值比较 je 0x4005d7 # 相同则跳转 call 0x4004c0 __stack_chk_fail # 否则调用失败处理在CTF中绕过Canary主要有三种方法泄露Canary值通过格式化字符串漏洞或信息泄露漏洞获取canary值然后在溢出时保持该值不变逐字节爆破Canary由于canary通常以null字节结尾可以逐个字节尝试劫持__stack_chk_fail修改这个函数的GOT项让它不终止程序编译时控制Canary的选项gcc -fno-stack-protector -o test test.c # 禁用栈保护 gcc -fstack-protector -o test test.c # 对含char数组的函数启用保护 gcc -fstack-protector-all -o test test.c # 对所有函数启用保护4. NX保护与执行权限控制NX(No-eXecute)保护是现代操作系统最重要的安全特性之一。它通过将数据区域标记为不可执行有效阻断了直接在栈或堆上执行shellcode的攻击方式。这就像是在说这里只能存放东西不能当做工厂来用。检查NX状态的简单方法readelf -l vulnerable_program | grep GNU_STACK输出中出现RWE表示栈可执行只有RW则表示NX保护开启。绕过NX保护的常用技术是ROP(Return-Oriented Programming)。通过串联程序中已有的代码片段(gadgets)构造出需要的功能链。比如构造system(/bin/sh)的调用找到pop rdi; ret的gadget找到/bin/sh字符串地址找到system函数地址构造payloadpadding pop_rdi_addr binsh_addr system_addr控制NX保护的编译选项gcc -z execstack -o test test.c # 禁用NX gcc -z noexecstack -o test test.c # 启用NX(默认)5. PIE与地址随机化PIE(Position-Independent Executable)技术让程序的所有段(代码、数据等)在加载时都使用随机地址这大大增加了攻击难度。就像每次运行程序时所有函数和变量都会搬家攻击者无法提前知道它们的具体位置。PIE通常与ASLR(Address Space Layout Randomization)配合使用。检查PIE状态file vulnerable_program输出中出现pie executable表示PIE开启。当遇到PIE保护时攻击通常需要分两步信息泄露通过漏洞泄露某个关键地址计算基址根据泄露的地址计算其他所需地址编译选项控制PIEgcc -no-pie -o test test.c # 禁用PIE gcc -fpie -pie -o test test.c # 开启PIE(级别1) gcc -fPIE -pie -o test test.c # 开启PIE(级别2)6. 新版Checksec的增强功能随着安全技术的发展新版Checksec增加了对更多防护机制的检测。这些功能在老版本中是没有的这也是我推荐使用最新版的原因。RPATH/RUNPATH检测 这两个选项关系到程序运行时查找共享库的路径顺序。不安全的配置可能导致加载恶意库。检测到问题时可以尝试patchelf --remove-rpath vulnerable_program patchelf --set-rpath /safe/path vulnerable_programFORTIFY_SOURCE 这是GCC提供的源码级保护会将危险的字符串操作函数替换为安全版本。比如strcpy → __strcpy_chkmemcpy → __memcpy_chksprintf → __sprintf_chk开启FORTIFY的方法gcc -D_FORTIFY_SOURCE2 -O1 -o test test.c在实际漏洞利用中遇到FORTIFY保护时需要避免使用被保护的函数寻找其他未受保护的函数链确保缓冲区操作长度严格可控7. 综合分析与实战策略拿到一个CTF题目时我通常会按照以下流程进行分析运行checksec获取防护概况根据防护组合制定攻击路线使用gdb验证漏洞可行性构建完整的exploit常见的防护组合及应对策略防护组合典型特征攻击思路全保护RELRO FULL, Canary, NX, PIE需要信息泄露ROP部分保护Partial RELRO, NX可尝试GOT覆盖弱保护只有NX考虑shellcode注入无保护所有保护关闭直接栈溢出注入shellcode在真实漏洞利用时有几点经验值得注意32位和64位程序在参数传递、栈布局上有很大差异不同libc版本提供的gadgets可能完全不同网络赛题要注意字节序和输入过滤本地测试成功的exploit可能因为环境差异在远程失败