Linux安全防护机制详解:从ASLR、NX到Canary绕过与ROP利用
搞PWN的人绕不开Linux安全防护机制。每次拿到一个二进制我第一件事就是丢进checksec看它开了哪些保护——这几乎成了肌肉记忆。因为PWN的本质是让程序执行我们期望的代码而Linux安全防护机制就是堆在漏洞和利用之间的一堵墙。你不仅要懂漏洞更要知道哪堵墙能凿穿、哪堵墙需要绕甚至哪堵墙本身就是可利用的支点。这篇文章我想系统拆解一下Linux下常见的防护机制从原理到绕过思路再到实际调试中的经验适合刚接触PWN的CTF选手也适合想在二进制安全方向深入的研究者。1. 防护机制全景先看清对手到底设了几道防线1.1 为什么PWN的本质就是和防护机制博弈我见过很多入门者拿到题目只会找栈溢出发现溢出点后直接往栈里塞shellcode跑了发现Segmentation fault就不知道怎么办了。这背后其实是NX防护挡住了shellcode执行。也就是说漏洞是在的但防护机制改变了利用条件。现代Linux系统默认开启大量防护PWN的利用过程早已变成漏洞利用能力 防护绕过能力的结合。换个角度理解防护机制把内存抽象成了一个个隔离区栈、堆、代码段、数据段、GOT表、TLS区域。每个区域都有自己的守卫规则。比如栈里有Canary守卫堆有对元数据的校验动态链接库的GOT被只读保护页面被标为不可执行。攻击者每想前进一步都需要考虑当前区域设了哪道关卡。我的习惯是先列出一个检查清单每个机制分别思考它保护了哪个区域、由谁负责实现、在什么条件下失效、有哪些经典绕过方案。下面这张表就是我的基础备忘。防护机制保护目标主要实现方失败条件/绕过思路NX栈、堆、数据页不可执行CPU与内核ROP、ret2libc、利用可执行段ASLR地址空间随机化内核信息泄露、partial overwrite、爆破PIE程序自身基址随机化编译器内核泄露ELF地址、盲打Canary栈返回地址完整性编译器泄露canary、fork爆破、劫持处理函数RELROGOT/动态链接数据结构完整性链接器在RELRO完全生效前覆盖或改hookFortify编译期检测常见缓冲区溢出编译器利用检测不覆盖的路径SELinux/AppArmor系统级强制访问控制内核发行版策略实际CTF中常关闭真实系统中影响后续命令执行1.2 checksec、readelf与file开局三连拿到题目我一般会先跑三个命令。第一是file看它是32位还是64位、动态还是静态、是否strip。第二是checksec这个工具现在集成在pwntools里直接checksec --file./pwn就能输出所有主要防护项。第三是readelf -h可以查看ELF头的类型判断是否开启了PIE。这里要注意一个容易混淆的点ASLR是系统机制PIE是程序机制。只有程序编译为PIE时内核的ASLR才会对程序基址做随机化如果程序是非PIE的ELF通常是ET_EXEC类型那么即使系统开了ASLR程序自身的代码段、数据段基址也是固定的。栈、堆、共享库地址则会随机化。我在实际调试中经常用readelf -h来看Type字段ET_EXEC就是非PIEET_DYN就是PIE。很多老题都是非PIE意味着代码段地址完全固定直接复用程序内部函数或gadget非常方便。检查完之后还要确认一个问题程序是静态链接还是动态链接有没有开Full RELRO。这直接决定了利用路径。我见过有人拿着一个静态链接的程序还在想怎么leak libc结果发现根本没有libc可leak这说明连程序类型都没分析清楚。1.3 防护机制组合带来的现实处境不同的防护组合会让同一类漏洞的利用难度天差地别。比如32位程序通常地址空间小ASLR爆破的成功率较高64位程序地址空间大爆破不现实必须依赖信息泄露。实际中我用一个原则来衡量难度能不能泄露地址决定了你能不能精确控制执行流有没有可写可执行的内存决定了你能不能直接上shellcode。大多数现代PWN题的默认配置是NX开启、Canary开启、Full RELRO、PIE开启、ASLR系统开启。这种五险一金全开的题目单纯靠栈溢出的传统打法已经很难奏效通常需要组合链先泄露Canary再泄露libc或程序基址然后构造ROP链最后通过调用system(/bin/sh)或execve拿shell。整条链的每一步都是在跟某一道防线打交道。2. 内存随机化ASLR与PIE的纠缠关系2.1 地址随机化到底随机了什么ASLR全称是Address Space Layout Randomization由内核实现。它每次加载程序时会将共享库、栈、堆、mmap区域的起始地址加上一个随机偏移。早期Linux的ASLR强度不高随机位数有限尤其是在32位系统上可能只需要爆破几次就能撞中64位系统虽然随机范围大但实际随机位数受限于页面大小和mmap布局并非完全不可预测。PIE则是编译器在链接时生成的与位置无关的可执行程序。它使代码段、数据段的加载基址可以被随机化。需要理解的是PIE依赖ASLR来提供随机源但两者不是一回事。一个非PIE程序其ELF加载基址是固定的0x40000064位常见无论ASLR怎么开代码段地址都不变。而PIE程序基址则会在每次运行时变化但ELF内部各段之间的相对偏移是固定的。这意味着只要泄露了任意一个代码段地址就能算出完整的程序基址。我在做笔记时会把地址空间分成两部分程序自身空间和系统库空间。程序自身空间包括代码段、全局变量段、GOT表系统库空间主要是libc、ld、堆、栈。泄露程序自身地址配合PIE的固定偏移就能定位代码地址泄露libc地址配合system与/bin/sh的偏移就能构造ret2libc。2.2 开启、关闭与查看ASLR在Linux下ASLR由/proc/sys/kernel/randomize_va_space控制。0表示关闭1表示开启部分随机化2表示开启完整随机化。日常调试时为了复现确定性我经常用setarch -R ./pwn来关闭当前进程的ASLR注意这只是让当前程序运行时关闭不修改系统全局配置。# 查看当前ASLR级别 cat /proc/sys/kernel/randomize_va_space # 临时关闭ASLR仅当前命令有效 setarch -R ./pwn # 如果想要全局关闭不推荐 sudo sysctl -w kernel.randomize_va_space0编译阶段PIE由-pie和-no-pie控制。GCC默认很多发行版开启PIE但CTF题目为了各种难度会有不同的编译选项。写汇编或C代码时可以这样指定gcc -o vuln vuln.c -fno-stack-protector -no-pie gcc -o vuln_pie vuln.c -fno-stack-protector -fpie -pie我建议初学者一定要亲手编译两个版本一个开PIE一个不开然后用gdb看它们每次运行的加载地址差异。看到0x555555554000这种基址反复变化就会对随机化有直观感受。2.3 绕过ASLR/PIE的常见思路绕过ASLR本质上就是解决我该把跳转目标设成多少这个问题。主要有三条路。第一是信息泄露这是最正统的手法。利用格式化字符串、越界读、use-after-free等漏洞读出某个地址再根据已知偏移计算出目标地址。比如格式化字符串用%p可以泄露栈上残留的返回地址返回地址属于libc或程序从而确定基址。我遇到的大多数PWN题信息泄露都不是靠运气而是程序本身就存在读接口只是需要你找到合适的索引。第二是部分覆盖partial overwrite。在PIE和ASLR开启时地址的高位已经随机化但低位保留且在不同进程间有一定规律。如果你能修改返回地址低1~2字节可以在不泄露完整地址的情况下跳转到相邻的函数或指令。这个技巧非常适合32位程序也常用于64位程序的目标地址与源地址在同一页面的情况。第三是爆破主要适用于32位程序或地址随机位很少的情况。32位系统ASLR的mmap基址随机位数往往只有8位左右而栈地址随机到0xff初始多开几次进程有一定概率能命中已经写死的地址。对于PIE如果随机化了3字节爆破成功率大概在1/4096左右只有在服务端不退出、fork出来的子进程共享父进程内存布局时爆破才有可行性。2.4 我踩过的坑PIE与gdb断点地址的错觉调试PIE程序时有个常见问题gdb默认在没有断点前会禁用ASLR以便调试吗不一定。不同gdb配置不一样。我经常遇到的情况是在gdb里运行PIE程序看到断点地址是0x555555554abc但直接运行程序时地址又变成了一串随机的地址。这是因为gdb在默认情况下为了让调试方便可能会关闭ASLR。如果你在gdb里调试成功但脱离gdb再跑exploit就失败很可能就是ASLR差异导致。解决方式是在gdb里执行set disable-randomization off让它保持和正常运行时一致的随机化然后再调试。还有一个小习惯用p main查看函数地址时注意gdb显示的是运行时地址而readelf -s查看的是链接后未重定位的偏移。两者之间差一个基址。3. NX与ROP禁止执行之后代码从哪来3.1 NX原理与检查方法NX全称No-eXecute也叫DEP数据执行保护在Windows上的对应物。它利用CPU的NX/XD标志配合内核将栈、堆等数据页标记为不可执行。当指令寄存器试图从这些页面取指时CPU会触发异常进程直接被杀死。这也意味着经典的往栈里塞shellcode然后跳过去的思路在现代系统上天然失效。判断一个二进制是否开NX最简单的方式是checksec。它会显示NX enabled或NX disabled。也可以用readelf -l查看段的权限标志readelf -l ./pwn | grep GNU_STACK如果GNU_STACK这行的权限是RW表示栈不可执行NX开启如果权限是RWX则表示栈可执行。注意某些老程序可能为了特殊用途比如JIT打开可执行栈但这类程序在CTF里反而成了福利题。3.2 没有可执行栈那就执行已有代码ROP的核心思想NX开启后攻击者无法注入并执行新代码但程序本身和共享库里有大量现成指令片段。这些片段以pop、ret、mov、add等指令结尾称为gadget。通过精心构造栈上的返回地址序列让CPU依次执行这些gadget就能拼凑出我们想要的功能。这就是ROPReturn-Oriented Programming。打个比方正常的程序执行像是在直路上开车ROP则像是不断打方向盘进匝道每段匝道都很短但连接在一起就能绕到目的地。ROP的核心是控制栈因为每个ret指令都会把栈顶的值弹出到指令指针所以如果攻击者控制了栈内容就控制了执行流。一个最简单的ROP链是ret2libc利用程序或libc中的pop rdi; retgadget把/bin/sh字符串地址传给rdi然后调用system函数。这样即使栈不可执行也能弹 shell。这里有一个重要前提你需要知道libc中system、/bin/sh和某个gadget的地址通常通过泄露libc基址获得。下面是一个典型的pwn利用脚本框架用于开启NX但没有Canary、没有PIE的简单情况from pwn import * context.binary ./vuln elf context.binary libc ELF(./libc.so.6) # 如果有libc文件 p process(./vuln) # 假设存在一个格式化字符串漏洞泄露__libc_start_main的地址 p.sendline(b%11$p) leak int(p.recvline().strip(), 16) libc.address leak - libc.sym[__libc_start_main] # 构造ROP链pop rdi; system(/bin/sh) pop_rdi libc.address 0x000000000002a3e5 # 查libc得到偏移 binsh next(libc.search(b/bin/sh\x00)) system libc.sym[system] payload bA * 64 # 填充缓冲区这个长度需要调试确认 payload p64(pop_rdi) payload p64(binsh) payload p64(system) p.sendline(payload) p.interactive()3.3 缺少gadget时的保底方案ret2csu和万能gadget64位程序传参使用的是rdi、rsi、rdx、rcx、r8、r9六个通用寄存器。在程序里不一定有现成的pop rdi; ret这类理想gadget尤其是开启Full RELRO和PIE之后能用的gadget很有限。这时候我会先找程序自身的gadget用ROPgadget --binary ./pwn或ropper搜索。如果搜不到理想的可以看看动态链接器ld.so或libc里的gadget前提是你已经知道它们的地址。在glibc的早期版本中有一个非常经典的万能gadget位于__libc_csu_init函数尾部它提供了一连串pop rbx; pop rbp; pop r12; pop r13; pop r14; pop r15; ret以及mov rdx, r15; mov rsi, r14; mov edi, r12d; call [r13rbx*8]的片段。这个gadget允许你控制rdi、rsi、rdx三个参数并调用任意地址处的函数是早期PWN题的万能钥匙。即使在新版libc中很多动态链接的二进制仍然包含__libc_csu_init虽然可能略有变化但思路一致。3.4 实际调试中的注意事项写ROP链时最容易被忽略的是栈对齐。system函数在glibc中某些版本对栈有16字节对齐要求。如果ROP链调用system时栈不对齐会直接SIGSEGV。我记得自己早期写ret2libc时同样的payload在本地可以远程不行排查半天发现就是缺了一个retgadget来调整栈对齐。解决方法很简单在调用system前先ret一次消耗掉8字节让栈指针对齐到16字节边界。这个retgadget在程序里基本都找得到。另外gadget地址要确保是实际可执行的地址。pop rdi所在段必须是可执行段不要从数据段里搜gadget否则CPU执行时同样会触发NX异常。4. 栈守卫Canary长得像随机金丝雀的报警器4.1 Canary的工作原理与插入点Canary是编译器插入到栈上的一个随机值位于局部变量区与返回地址之间。函数入口处生成一个随机值写入Canary位置函数返回前检查该值是否被修改。如果被修改说明栈缓冲区发生了溢出程序会调用__stack_chk_fail终止进程。在x86-64下Canary一般从FS段寄存器指向的TLS中取出低字节被置为\x00这是为了终止字符串读取防止Canary本身被泄露。我习惯用gdb在函数入口查看canary的生成过程你会发现它实际上是从fs:0x28取一个值放在栈上rbp-0x8的位置。溢出时如果覆盖越过Canary返回时就触发校验。4.2 绕过Canary的几条路线绕过Canary最直接的是泄露Canary。Canary在进程生命周期内保持不变如果程序有任意读漏洞比如格式化字符串或越界读就可以按索引把Canary读出来。读取后在溢出payload中保留Canary值程序就不会察觉。我经常用%p逐个尝试栈偏移直到看到某个值以00结尾且紧随栈地址之后那很可能就是Canary。第二种是fork爆破。在服务端程序中父进程接受连接后会fork出子进程处理请求。子进程的内存是从父进程复制的Canary值在fork时被继承但只要子进程崩溃不会影响父进程继续fork新的子进程。所以你可以不断重试每次都把Canary猜测的字节逐字节爆破第一字节固定是\0后面字节各256种可能只要服务端不崩溃退出用暴力猜测的方式就能在较短时间内恢复Canary。这个思路在远程题目中很常见。第三种是覆盖处理函数。__stack_chk_fail是通过PLT调用的如果程序是Partial RELRO你可以在溢出时直接覆盖GOT中的__stack_chk_fail地址为main或其他函数这样即使Canary校验失败程序也不会退出而是跳到你指定的地址。这个技巧依赖RELRO保护不足后面会细说。4.3 如何精确确定Canary在payload中的位置找Canary位置我常用两种方法。一种是看反汇编在函数入口处找类似mov rax, qword ptr fs:[0x28]; mov qword ptr [rbp-0x8], rax的指令然后从rbp-0x8算出Canary相对缓冲区的偏移。另一种是动态调试在溢出点输入一长串A观察崩溃时__stack_chk_fail打印的信息或者用gdb在返回地址处打断点看哪个区域的值被破坏了。一个容易踩的坑Canary在32位程序中的位置可能与64位不同。32位下通常放在ebp-0x464位下放在rbp-0x8。但如果编译器优化级别不同Canary的位置也会变化。不要想当然必须结合反汇编确定。我自己在写exploit时会在payload里直接用一个变量canary先发送A*偏移 canary测试程序是否正常返回。如果程序返回后还能再次运行说明Canary位置正确如果崩溃就调整偏移。这种验证方式非常有效尤其是在交互式对象的题里。4.4 Canary与PIE共存的组合场景很多新手觉得Canary泄露了还不够还要泄露程序基址。因为Canary泄露是为了绕过栈保护而程序基址泄露是为了解决PIE。这两个是独立的泄露目标。我经常在一个格式化字符串中同时完成先确定Canary所在的栈偏移再找一个返回地址的偏移同时把它们读出来。这样一次交互就把两个防护都破了省很多时间。在x64格式化字符串中栈上第几个参数是Canary一般需要调试确定。可以利用%n$p指定参数位置比如%35$p逐个尝试。记录下每个位置的地址值再与反汇编里的fs:[0x28]比较。这里我分享一个经验Canary的低字节是00所以在输出的十六进制中最后一个字节一定是00只要看到类似0x123456789abcdef00形态的值基本就是Canary。5. RELRO与GOT动态链接表是攻防焦点5.1 RELRO的两种级别与含义RELRORelocation Read-Only用于保护动态链接相关的数据结构。Partial RELRO会把.got中非懒绑定部分如数据段引用的全局变量地址设为只读但PLT相关的GOT项仍然是可写的。Full RELRO则在程序启动时完成所有动态符号解析并将整个GOT表设为只读。checksec输出中Partial RELRO和Full RELRO一目了然。为什么GOT如此重要因为当程序调用printf、system等外部函数时实际跳转地址存储在GOT表里。覆盖GOT表项相当于把某个函数的调用指向攻击者希望执行的函数。比如把printfgot改成systemplt一旦程序调用printf实际执行的就是system。这个技巧非常经典前提是GOT表可写所以Partial RELRO是攻击面。5.2 利用格式化字符串覆盖GOT的完整思路在Partial RELRO且没有Canary或可以绕过Canary的情况下格式化字符串的%n可以将已输出的字节数写入指定地址。利用这个特性就可以任意写。典型的exp会先泄露地址再计算需要多少字节然后多次以2字节为单位写入GOT表项。不过手写%n链比较繁琐现代pwntools提供了fmtstr_payload函数可以自动生成格式化字符串payloadpayload fmtstr_payload(offset, {printf_got: system_addr}) p.sendline(payload)这个函数会根据你给的覆盖字典自动安排%n的写入顺序省心很多。但它生成的payload通常比较长如果目标程序的输入缓冲不够大还是要反向工程手动精简。5.3 Full RELRO下GOT改不了怎么办Full RELRO让GOT表只读传统GOT覆盖失效。这时候需要换思路。一种常见目标是劫持hook函数。glibc提供__malloc_hook、__free_hook、__realloc_hook等钩子变量它们在调用malloc/free时会先检查是否非空如果非空则跳转到钩子函数。这些hook变量本身位于libc的写数据段Full RELRO管不到。经典的利用是把__free_hook改成system然后在堆上构造一个字符串/bin/sh\x00调用free时实际执行system(/bin/sh)。在新版本glibc中hook被移除了但在大多数CTF提供的老版本libc里仍然可用。另一个方向是FSOPFile Stream Oriented Programming通过伪造_IO_FILE结构劫持vtable中的函数指针在exit或malloc错误路径上触发。FSOP的利用复杂度较高但对于全开的现代glibc题目已经成了主流手段。还有一个思路是覆盖栈上的返回地址不管GOT和hook直接改返回地址到one_gadget。one_gadget是libc中现有的可直接执行execve(/bin/sh)的地址片段只要满足若干约束条件比如栈上某些值等于0就能成功。这是目前很多高版本PWN题的最短路因为不用拼system调用链只要能控制返回地址并知道libc基址就够。5.4 我在RELRO题目中踩过的坑我刚开始接触Full RELRO时总是下意识想改GOT结果进程报段错误查了半天才意识到GOT只读。后来学乖了拿到题目先看RELRO等级Partial和None优先考虑GOT覆盖Full则直接盯hook或one_gadget。还有一个隐蔽问题当程序使用setvbuf关闭缓冲时它会在启动阶段解析libc函数Full RELRO的动态链接在启动时全部解析完毕所以不会给攻击者留下延迟绑定的机会。另一个经验是Partial RELRO时如果程序允许一次输入很小比如只能写8字节那么利用GOT覆盖时要考虑写入顺序。GOT表项地址和写入值的高低位需要分解成两个半字用两次写入完成。每次写入后要确认写入的字节数不会过大而污染其他内存。6. 编译期防护的暗坑Fortify、符号表与strip6.1 Fortify是什么它为什么会让利用更难Fortify Source是GCC提供的一系列安全检查检测到strcpy、sprintf等危险函数时会在编译期插入额外校验并把函数调用替换为带缓冲区大小参数的__*_chk版本。例如strcpy会被替换为__strcpy_chk在运行时会检查目标缓冲区大小是否足够。如果发现溢出直接调用__fortify_fail终止进程。CTF里如果程序在编译时加了-D_FORTIFY_SOURCE2很多格式化字符串、栈溢出漏洞会因为检测提前触发而变得难利用。尤其是使用%s读取时Fortify会检查目标缓冲区大小超过预期就终止。更麻烦的是Fortify还可能导致函数原本的PLT/GOT结构变化使得某些gadget地址偏移和正常编译的不一样需要重新计算。6.2 strip掉符号表对PWN的影响strip命令删除了ELF中的符号表许多IDA分析工具在反汇编时看不到函数名只能看到sub_401000这类地址调试时也显得不友好。对PWN来说strip会改变泄露偏移的计算方式吗答案是不会改变机器码和运行逻辑只是让你更难找到关键函数。面对strip的二进制我通常用readelf -s发现没有符号后不会沮丧而是利用objdump或IDA的自动分析来识别标准函数。比如通过字符串/bin/sh反向定位调用它的函数或者通过gdb分析__libc_csu_init的调用关系来找到main。还有一个巧办法动态运行程序用info proc mappings查看libc地址再用gdb的starti在入口停一下逐步步进根据调试器显示的调用链能很快定位到main。6.3 全开防护下我通常从哪里切入当一道题是PIE开启、RELRO Full、NX开启、Canary开启、Fortify开启、strip开启拿到手感觉无从下手时不要慌。我建议按这个顺序找突破口先根据字符串和交互功能判断漏洞类型。如果程序有循环输入那肯定有信息泄露点先想办法泄露Canary和地址。找输入功能中是否存在可以被重复利用的逻辑比如注册、编辑、删除功能这类堆题往往有UAF能轻易泄露堆地址和libc地址。如果漏洞是栈溢出但只能覆盖返回地址看看是否可以利用one_gadget直接打如果约束条件不满足就找一块可控的栈空间构造完整的ROP链用leave; ret或pop rsp迁移栈。如果是堆漏洞优先考虑劫持__free_hook或利用tcache机制。libc版本不同堆利用方式差异很大这是另一篇长篇内容。全开防护并不意味着无解它只是把所有最简单的路封死了逼迫你更精确地组合多个小技巧。比如泄露Canary和PIE地址往往一次搞定利用one_gadget时可能需要提前构造约束条件堆利用时要同时绕过tcache double free检查。每一步单独看都不难难的是把所有细节放在同一条利用链里不犯错。7. 模拟实战一道综合题怎样串联所有机制7.1 题目画像与初始侦察这里我虚构一个模拟项目X来说明它是一个64位动态链接的程序checksec显示Arch: amd64-64-littleRELRO: Full RELROCanary: enabledNX: enabledPIE: enabled。程序的功能是读入用户名字存储到一个全局缓冲区然后打印欢迎信息最后再读入一段消息消息被复制到一个局部缓冲区。拿到程序我第一轮信息收集file确认是64位PIE。strings搜索发现存在/bin/sh字符串与system不一定在同个程序但看到/bin/sh往往说明有可乘之机。运行程序输入%p测试格式化字符串发现第一次打印欢迎信息时存在格式化字符串漏洞可以泄露栈上的返回地址。这一步已经确认了两个突破口格式化字符串泄露以及后续的栈溢出。7.2 利用链设计第一步先用格式化字符串泄露信息。我用一组%p或%n$p定位到Canary和返回地址。假设偏移分别为栈上第15个参数是Canary第27个参数是返回地址。计算出程序基址和libc基址。返回地址位于__libc_start_main243常见偏移于是libc.address leak - 243 - libc.sym[__libc_start_main]。程序基址则可以通过返回地址的某个栈帧中残留的main地址来计算或者通过GOT表泄露。第二步第二次输入消息时构造payload。缓冲区大小为64字节Canary位于偏移56处。payload结构为payload bA * 56 payload p64(canary) payload p64(0) # rbp无所谓 payload p64(pop_rdi_ret) # 从libc或程序里搜 payload p64(binsh_addr) payload p64(system_addr)因为Full RELROGOT改不了所以直接ret2libc。这里要注意栈对齐在system前加一个retgadget。第三步发送payload拿shell。整个过程在本地复现成功后再针对远程调整偏移和libc版本。7.3 实操中的易错点和调试心得这个流程看着简单但我在写exp时至少会犯三个低级错误。第一个错误格式化字符串泄露Canary时直接把%p的输出作为数值但输出可能带0x前缀且末尾可能换行转换不干净导致数值错误。我在代码里会写leak p.recvline().strip().split(b0x)[1][:16] value int(leak, 16)第二个错误偏移算错。格式化字符串的偏移不是固定的取决于调用约定和参数布局。我用%p查看前几十个参数时会记下每一个值与栈上已知值的关系确认Canary后不急着用先发一个不含Canary的溢出确认崩溃再发含Canary的Payload确认没有触发__stack_chk_fail。第三个错误远程libc不一致。本地用的libc和远程可能不同导致system和/bin/sh的偏移完全不同。解决方式是使用题目给的libc文件或者用libcdb.com之类的库匹配工具。在exp里最好显式加载指定的libclibc ELF(./libc-2.27.so)然后所有偏移都从libc.sym[system]和libc.search(b/bin/sh\x00)得出而不是写死。7.4 防御方视角如何手动加固一个程序写PWN题和做防御是同一枚硬币的两面。如果想要让程序更难利用我通常会开启所有编译保护gcc -fstack-protector-all -pie -Wl,-z,relro,-z,now -D_FORTIFY_SOURCE2自写一个read循环避免scanf类函数的截断问题。引入一个随机的Canary二次校验在函数内部再检查一次Canary是否被修改防止泄露Canary直接绕过。使用seccomp限制系统调用即使拿到shell也不能执行execve只能读文件这样可以迫使攻击者用orw链。但作为攻击者我也知道这些防御并非无懈可击。比如seccomp规则如果配置不够细仍然可以用openat、sendfile等替代调用读取flagCanary二次校验如果校验值泄露了也一样被绕过。防护的本质是提高攻击成本而不是彻底消除漏洞。最后分享一点个人经验写到这里我要说一句心里话防护机制并不只是让你痛苦的墙它同时也是解题的线索。Canary存在说明程序可能有栈溢出点RELRO不是Full意味着GOT可以改NX开启自然想到ROPPIE开启自然想要泄露地址。你会发现每道题的设计者都在通过防护机制的组合告诉你这道题该用什么解法。我习惯把checksec的输出当成出题人的提示而不是障碍。另一个经验是一定要学会用gdb和pwndbg配合。我见过太多人只看ida反汇编不理解栈结构直接抄exp跑不通。我强烈建议拿到题先在gdb里运行一遍输入测试数据观察栈布局找到缓冲区与Canary、返回地址的距离再动手写exp。调试花的时间永远比瞎猜时间短。如果你现在刚接触PWN不要被全开保护的题吓退。先从关闭所有保护的简单栈溢出开始再逐步开启Canary、NX、PIE每加一个保护就学对应的绕过技巧。你会发现Linux安全防护机制本身就是一本最好的PWN教材。

相关新闻

从多层因果世界到自我进化:构建通用模拟系统的架构设计与工程实践

从多层因果世界到自我进化:构建通用模拟系统的架构设计与工程实践

1. 从"模拟器"这个词说起:为什么我想做一个通用模拟系统聊到"模拟器",大多数人脑子里蹦出来的第一反应是安卓模拟器、思科模拟器、PS3模拟器这类具体工具——它们干的事情很明确:在一个环境里复刻另一个环境的行为&#…

2026/10/17 5:05:18 阅读更多 →
H3C IP上一跳保持技术:解决异地出口容灾回程路径难题

H3C IP上一跳保持技术:解决异地出口容灾回程路径难题

1. 异地出口容灾到底在防什么先把场景说清楚。所谓“职场数据中心异地出口容灾”,指的是一个企业有两个数据中心,主数据中心和灾备数据中心,各自有独立的互联网出口链路。正常情况下,所有业务流量从主数据中心的出口出去&#xff…

2026/10/17 5:05:18 阅读更多 →
Unity Shader实现航天器再入灼烧特效:温度映射、边缘发光与烧蚀残留全解析

Unity Shader实现航天器再入灼烧特效:温度映射、边缘发光与烧蚀残留全解析

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

2026/10/17 5:04:17 阅读更多 →

最新新闻

Codex+VS Code构建可审计的AI Coding工程化实践

Codex+VS Code构建可审计的AI Coding工程化实践

1. 项目概述:这不是又一个“AI写代码”的演示,而是真实开发流里的生存实验“AI Coding 实践(再续)”——这个标题里最值得咀嚼的不是“AI”,也不是“Coding”,而是那个轻描淡写的“再续”。它意味着前序实践…

2026/10/17 5:40:28 阅读更多 →
CTF实战:GET/POST混合请求与BurpSuite+HackBar全流程解析

CTF实战:GET/POST混合请求与BurpSuite+HackBar全流程解析

CTF的Web题做多了你会发现,一半以上的题目卡住的点根本不是什么高深漏洞,而是你对HTTP请求本身的理解不够扎实。这次分享的实战演练,就是用一道典型的"GET/POST混合请求"CTF挑战,把BurpSuite和HackBar这两个工具完整串一…

2026/10/17 5:40:28 阅读更多 →
S7-1214C与机器人Modbus TCP通信:从角色分配到避坑全指南

S7-1214C与机器人Modbus TCP通信:从角色分配到避坑全指南

简介:面向工业自动化与机器人集成工程师,围绕西门子S7-1214C PLC与机器人间的Modbus TCP通信,整理了从网络配置、功能码应用、寄存器地址映射到数据交换的完整设置方案。压缩包共34个文件,总大小约6.96MB,以PDF编程教程…

2026/10/17 5:40:28 阅读更多 →
西门子S7-1214C与机器人Modbus TCP通信配置与调试指南

西门子S7-1214C与机器人Modbus TCP通信配置与调试指南

简介:面向自动化工程师与PLC编程人员,系统讲解西门子S7-1214C紧凑型PLC与机器人之间基于Modbus TCP协议的通信配置方法,涵盖主从站角色选择、IP地址分配、寄存器映射、功能码使用等关键环节,并提供S7-1215C相关参考文档以便对照学…

2026/10/17 5:40:28 阅读更多 →
揭秘ice-012-audio的杀手锏Active Acoustic Adapter:声学韵律双向条件化如何实现声音控制

揭秘ice-012-audio的杀手锏Active Acoustic Adapter:声学韵律双向条件化如何实现声音控制

【免费下载链接】ice-012-audio 项目地址: https://ai.gitcode.com/hf_mirrors/darkps/ice-012-audio 点击查看 免费下载 如果你正在寻找一个支持 590 种语言、还能精准控制性别、音高、口音和语速的开源语音合成方案,那么 ice-012-audio 值得重点关注。…

2026/10/17 5:40:28 阅读更多 →
XSS常用语句、绕过思路与场景化防御实战指南

XSS常用语句、绕过思路与场景化防御实战指南

说到XSS,不少人脑子里第一反应就是弹窗alert。我以前带新人时,总有人背了一堆payload,真到测试环境全被拦下来,就开始怀疑人生。这篇就是我从日常工作中沉淀出来的XSS常用语句和绕过思路,不讨论宏大的攻击链&#xff0…

2026/10/17 5:39:28 阅读更多 →

日新闻

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

DeepSeek选型与部署:V3/R1、Ollama、API全攻略

简介:《DeepSeek使用方法.pdf》是一份面向DeepSeek入门及进阶用户的完整使用指南,重点围绕V3与R1两款模型的能力差异与适用场景展开,帮助读者根据任务类型与成本选择合适的模型。资源共1个PDF文件,压缩包大小约311KB,篇…

2026/10/17 0:00:25 阅读更多 →
200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

200+Deepseek润色指令拆解:从底层原理到场景落地的完整指南

简介:这是一份聚焦DeepSeek提示词优化的《200Deepseek润色指令》PDF文档,共收录两百余条可直接套用的润色指令,涵盖语法修正、风格统一、逻辑梳理、表达精炼等维度,并针对商务写作、学术论文、广告文案等场景给出差异化建议。文档…

2026/10/17 0:00:25 阅读更多 →
OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

OpenCV轮廓法物体计数系统:轻量、可控、可落地的工业级实现

简介:本资源是一份基于OpenCV的本科课程设计完整报告,面向计算机视觉初学者、图像处理学习者及高校通信/信息类专业学生,解决物体智能计数与检测结果结构化记录的实际问题。项目覆盖图像预处理、轮廓检测、边界框绘制、轨迹栏交互式参数调节、…

2026/10/17 0:00:25 阅读更多 →

周新闻

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

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

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

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

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

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

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

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

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

2026/10/16 15:04:24 阅读更多 →

月新闻

我发现了一个新思路:用 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/16 21:54:38 阅读更多 →
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/16 3:49:43 阅读更多 →
黑夜航拍船只数据集训练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/16 14:23:36 阅读更多 →