我如何重新编程我的 shellcode 片段以避免空字节?
How can I reprogram my shellcode snippet to avoid null bytes?
我编写了一段 x64 linux 程序集。它所做的只是打印一行“Hello world”,仅此而已。但是我想做的是通过 objdump 从它的目标文件中复制字节,这样我就可以为我的缓冲区溢出攻击制作我自己的 shellcode。
我面临的问题是 shellcode 包含大量空字节,这将终止我的 shellcode 的执行。
root@kali:~/C scripts/shellcode/Assembly Based Shellcode# cat print.asm
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, 12
syscall
mov rax, 60
xor rdi, rdi
syscall
message:
db "Hello world", 10
root@kali:~/C scripts/shellcode/Assembly Based Shellcode# nasm -f elf64 print.asm && ld print.o -o print && ./print
Hello world
root@kali:~/C scripts/shellcode/Assembly Based Shellcode# objdump -D print.o
print.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_start>:
0: b8 01 00 00 00 mov [=10=]x1,%eax
5: bf 01 00 00 00 mov [=10=]x1,%edi
a: 48 be 00 00 00 00 00 movabs [=10=]x0,%rsi
11: 00 00 00
14: ba 0c 00 00 00 mov [=10=]xc,%edx
19: 0f 05 syscall
1b: b8 3c 00 00 00 mov [=10=]x3c,%eax
20: 48 31 ff xor %rdi,%rdi
23: 0f 05 syscall
0000000000000025 <message>:
25: 48 rex.W
26: 65 6c gs insb (%dx),%es:(%rdi)
28: 6c insb (%dx),%es:(%rdi)
29: 6f outsl %ds:(%rsi),(%dx)
2a: 20 77 6f and %dh,0x6f(%rdi)
2d: 72 6c jb 9b <message+0x76>
2f: 64 fs
30: 0a .byte 0xa
root@kali:~/C scripts/shellcode/Assembly Based Shellcode#
我希望 shellcode 没有空字节。然而事实并非如此。
有人可以帮我更正我的代码吗?
您似乎对汇编和缓冲区溢出感到困惑。
我像这样重新编写了汇编文件:
section .text
GLOBAL _start
_start:
xor rax, rax ; Clear the RAX register
push rax ; Push the NULL byte [ string terminator ]
add al, 0x1 ; RAX = 1, to put the system in sys_write mode
mov rdi, rax ; RDI = 1, to setup the fist parameter for write ( file descriptor to write to ). The integral value for 'stdout' is 1.
lea rsi, [rel msg+0x41414141] ; Move the relative RIP address of msg to RSI to prepare the string buffer for writing to the stdout. Also add a large 4-byte offset to evade NULL bytes.
sub rsi, 0x41414141 ; Subtract that large offset to make the RSI point correctly.
xor rdx, rdx ; Empty the 3rd argument for write
mov dl, 0xc ; RDX = 12, 12 ==> string length of msg
syscall ; system call
msg db "Hello world", 0xa
编辑:根据评论部分的讨论,我删除了终止 NULL 字节
section .text
GLOBAL _start
_start:
push 0x1
pop rax
mov rdi, rax
mov rbx, 'AAAAArld'
shr rbx, 0x28
push rbx
mov rbx, 'Hello wo'
push rbx
mov rsi, rsp
push 0xc
pop rdx
syscall
然后我像这样编译程序:
root@kali:~/Desktop/assembly# nasm -f elf64 main.asm; ld main.o -o main.elf; ./main.elf
Hello world
Segmentation fault
段错误真的无关紧要,因为您想将其用作缓冲区溢出攻击的 shellcode,所以那里无论如何都存在段错误。
现在,从目标代码中提取字节:
root@kali:~/Desktop/assembly# for i in $(objdump -D main.o | grep "^ " | cut -f2); do echo -n "\x$i"; done; echo
\x48\x31\xc0\x50\x04\x01\x48\x89\xc7\x48\x8d\x35\x4f\x41\x41\x41\x48\x81\xee\x41\x41\x41\x41\x48\x31\xd2\xb2\x0c\x0f\x05\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a
[选项]:您可以在执行内存损坏漏洞之前测试您的 shellcode
为此,只需复制上述命令中的字节并创建一个新的 C 文件[看来您对 C 脚本也感到困惑]
#include <stdio.h>
int main(void) {
char shellcode[] = "\x48\x31\xc0\x50\x04\x01\x48\x89\xc7\x48\x8d\x35\x4f\x41\x41\x41\x48\x81\xee\x41\x41\x41\x41\x48\x31\xd2\xb2\x0c\x0f\x05\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a";
int (*ret)() = (int (*)())shellcode;
// The above line will create an integer pointer ret make it point to a function which doesn't require parameter [ indicated by the () ]. Then it will type casting to cast the shellcode to a function pointer of the same type.
// So this will essentially cast your shellcode array address to a function pointer which you can later use to call it as a function and execute the code.
ret(); // Execute the shellcode
}
然后编译程序并确保堆栈可执行,否则你最终会在这里遇到段错误并且 shellcode 将不会执行。
root@kali:~/Desktop/assembly# gcc -z execstack test.c; ./a.out
Hello world
Segmentation fault
从上面的代码来看,shellcode 似乎工作得很好!
我已经在一个基本的应用程序上尝试过这个并且它有效,所以你的问题应该得到解决。
另一点要提到的是,只有当您的可执行应用程序 [您将要利用的应用程序] 使用停止在 NULL 字节(如 strcpy()
上的输入法时,您才需要删除 NULL字节。
如果您的可执行文件使用 gets()
和 fgets()
这样的输入函数,那么您无需担心 NULL 字节 [除非您也期待格式字符串漏洞]
这来自 fgets 的手册页:
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is
read, it is stored into the buffer. A terminating null byte ('[=19=]') is stored after the last character in the buffer.
这显然意味着 NULL 字节不应影响您的利用。
希望你的疑惑得到解开!
我编写了一段 x64 linux 程序集。它所做的只是打印一行“Hello world”,仅此而已。但是我想做的是通过 objdump 从它的目标文件中复制字节,这样我就可以为我的缓冲区溢出攻击制作我自己的 shellcode。
我面临的问题是 shellcode 包含大量空字节,这将终止我的 shellcode 的执行。
root@kali:~/C scripts/shellcode/Assembly Based Shellcode# cat print.asm
section .text
global _start
_start:
mov rax, 1
mov rdi, 1
mov rsi, message
mov rdx, 12
syscall
mov rax, 60
xor rdi, rdi
syscall
message:
db "Hello world", 10
root@kali:~/C scripts/shellcode/Assembly Based Shellcode# nasm -f elf64 print.asm && ld print.o -o print && ./print
Hello world
root@kali:~/C scripts/shellcode/Assembly Based Shellcode# objdump -D print.o
print.o: file format elf64-x86-64
Disassembly of section .text:
0000000000000000 <_start>:
0: b8 01 00 00 00 mov [=10=]x1,%eax
5: bf 01 00 00 00 mov [=10=]x1,%edi
a: 48 be 00 00 00 00 00 movabs [=10=]x0,%rsi
11: 00 00 00
14: ba 0c 00 00 00 mov [=10=]xc,%edx
19: 0f 05 syscall
1b: b8 3c 00 00 00 mov [=10=]x3c,%eax
20: 48 31 ff xor %rdi,%rdi
23: 0f 05 syscall
0000000000000025 <message>:
25: 48 rex.W
26: 65 6c gs insb (%dx),%es:(%rdi)
28: 6c insb (%dx),%es:(%rdi)
29: 6f outsl %ds:(%rsi),(%dx)
2a: 20 77 6f and %dh,0x6f(%rdi)
2d: 72 6c jb 9b <message+0x76>
2f: 64 fs
30: 0a .byte 0xa
root@kali:~/C scripts/shellcode/Assembly Based Shellcode#
我希望 shellcode 没有空字节。然而事实并非如此。 有人可以帮我更正我的代码吗?
您似乎对汇编和缓冲区溢出感到困惑。
我像这样重新编写了汇编文件:
section .text
GLOBAL _start
_start:
xor rax, rax ; Clear the RAX register
push rax ; Push the NULL byte [ string terminator ]
add al, 0x1 ; RAX = 1, to put the system in sys_write mode
mov rdi, rax ; RDI = 1, to setup the fist parameter for write ( file descriptor to write to ). The integral value for 'stdout' is 1.
lea rsi, [rel msg+0x41414141] ; Move the relative RIP address of msg to RSI to prepare the string buffer for writing to the stdout. Also add a large 4-byte offset to evade NULL bytes.
sub rsi, 0x41414141 ; Subtract that large offset to make the RSI point correctly.
xor rdx, rdx ; Empty the 3rd argument for write
mov dl, 0xc ; RDX = 12, 12 ==> string length of msg
syscall ; system call
msg db "Hello world", 0xa
编辑:根据评论部分的讨论,我删除了终止 NULL 字节
section .text
GLOBAL _start
_start:
push 0x1
pop rax
mov rdi, rax
mov rbx, 'AAAAArld'
shr rbx, 0x28
push rbx
mov rbx, 'Hello wo'
push rbx
mov rsi, rsp
push 0xc
pop rdx
syscall
然后我像这样编译程序:
root@kali:~/Desktop/assembly# nasm -f elf64 main.asm; ld main.o -o main.elf; ./main.elf
Hello world
Segmentation fault
段错误真的无关紧要,因为您想将其用作缓冲区溢出攻击的 shellcode,所以那里无论如何都存在段错误。
现在,从目标代码中提取字节:
root@kali:~/Desktop/assembly# for i in $(objdump -D main.o | grep "^ " | cut -f2); do echo -n "\x$i"; done; echo
\x48\x31\xc0\x50\x04\x01\x48\x89\xc7\x48\x8d\x35\x4f\x41\x41\x41\x48\x81\xee\x41\x41\x41\x41\x48\x31\xd2\xb2\x0c\x0f\x05\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a
[选项]:您可以在执行内存损坏漏洞之前测试您的 shellcode
为此,只需复制上述命令中的字节并创建一个新的 C 文件[看来您对 C 脚本也感到困惑]
#include <stdio.h>
int main(void) {
char shellcode[] = "\x48\x31\xc0\x50\x04\x01\x48\x89\xc7\x48\x8d\x35\x4f\x41\x41\x41\x48\x81\xee\x41\x41\x41\x41\x48\x31\xd2\xb2\x0c\x0f\x05\x48\x65\x6c\x6c\x6f\x20\x77\x6f\x72\x6c\x64\x0a";
int (*ret)() = (int (*)())shellcode;
// The above line will create an integer pointer ret make it point to a function which doesn't require parameter [ indicated by the () ]. Then it will type casting to cast the shellcode to a function pointer of the same type.
// So this will essentially cast your shellcode array address to a function pointer which you can later use to call it as a function and execute the code.
ret(); // Execute the shellcode
}
然后编译程序并确保堆栈可执行,否则你最终会在这里遇到段错误并且 shellcode 将不会执行。
root@kali:~/Desktop/assembly# gcc -z execstack test.c; ./a.out
Hello world
Segmentation fault
从上面的代码来看,shellcode 似乎工作得很好!
我已经在一个基本的应用程序上尝试过这个并且它有效,所以你的问题应该得到解决。
另一点要提到的是,只有当您的可执行应用程序 [您将要利用的应用程序] 使用停止在 NULL 字节(如 strcpy()
上的输入法时,您才需要删除 NULL字节。
如果您的可执行文件使用 gets()
和 fgets()
这样的输入函数,那么您无需担心 NULL 字节 [除非您也期待格式字符串漏洞]
这来自 fgets 的手册页:
fgets() reads in at most one less than size characters from stream and stores them into the buffer pointed to by s. Reading stops after an EOF or a newline. If a newline is read, it is stored into the buffer. A terminating null byte ('[=19=]') is stored after the last character in the buffer.
这显然意味着 NULL 字节不应影响您的利用。
希望你的疑惑得到解开!