原创 haidragon 安全狗的自我修养
“面向安全专业人员的 Linux Shellcoding”
今天我们将学习 Linux Shellcoding 并学习实践知识。
shell代码
编写 shellcode 是了解有关汇编语言以及程序如何与操作系统交互的更多信息的好方法。
为什么红队成员和渗透测试人员编写 shellcode?因为在实际情况下,shellcode 可以注入到正在运行的程序中,使其执行一些它本来没有设计的作用,例如缓冲区溢出攻击。
因此,shellcode 通常用作漏洞利用中的 “payload”。
为什么叫 “shellcode”?从历史上看,shellcode 是机器代码,执行时会打开 shell。
Shellcode 在渗透测试和红队中发挥着至关重要的作用,原因如下:
了解低级操作:编写 shellcode 需要深入了解汇编语言以及程序如何在低级与操作系统交互。漏洞利用的有效载荷 : Shellcode 通常用作漏洞利用中的有效负载。当缓冲区溢出等漏洞被利用时,shellcode 可以注入到正在运行的进程中以执行任意命令。规避和混淆:制作有效的 shellcode 涉及逃避防病毒和入侵检测系统检测的技术。高级后开发:除了简单地生成 shell 之外,现代 shellcode 还可以设计为执行复杂的开发后任务。多功能性:现代 shellcode 不仅限于生成 shell。它可以被设计成执行广泛的操作,例如下载额外的恶意软件、创建后门。
Shellcode 测试
在测试 shellcode 时,只需将其插入到程序中并执行它就很方便了。
以下 C 程序 (run.c
) 将用于测试我们的所有代码:
强烈建议对 C 和 Assembly 有深入的理解。此外,了解堆栈的运行方式也是一个显著的优势。
【linux安全】禁用 ASLR
集会
让我们再次回顾一些更多的介绍性信息。
x86 Intel 寄存器集。
EAX、EBX、ECX 和 EDX 都是 32 位通用寄存器。
AH、BH、CH 和 DH 访问这些通用寄存器的高 16 位,而 AL、BL、CL 和 DL 访问低 8 位。
EAX、AX、AH 和 AL 被称为“累加器”寄存器,可用于 I/O 端口访问、算术运算、中断调用等。这些 registers 对于实现 system 调用也很有用。
EBX、BX、BH 和 BL 被称为 “Base” 寄存器,用作内存访问的 base 指针。此 register 通常用于存储 system call 参数的指针,有时用于存储中断的返回值。
ECX、CX、CH 和 CL 称为 “Counter” 寄存器。
EDX、DX、DH 和 DL 被称为“数据”寄存器,可用于 I/O 端口访问、算术运算和某些中断调用。
组装说明。在汇编编程中,有一些指令很重要:
mov eax, 32 ; assign: eax = 32
xor eax, eax ; exclusive OR
push eax ; push something onto the stack
pop ebx ; pop something from the stack
; (what was on the stack in a register/variable)
call mysuperfunc ; call a function
int 0x80 ; interrupt, kernel command
Linux 系统调用充当桥接用户空间和内核空间的 API。要在汇编程序中使用 Linux 系统调用,请执行以下步骤:
- 将系统调用号加载到 EAX 寄存器中。
- 根据需要将 system call 的参数放入 EBX、ECX 和其他 registers。
- 调用适当的中断 (80h)。
- 结果通常在 EAX 寄存器中返回。
可以在 '/usr/include/asm/unistd_32.h' 中找到 x86 系统调用的完整列表。
libc 如何包装 syscall 的示例:
让我们编译和反汇编:
gcc -masm=intel -static -m32 -o exit0 exit0.c
gdb -q ./exit0
0xfc = exit_group()
和 0x1 = exit()
空字节数
我们来研究一下简单的程序:
编译并运行:
gcc -m32 -w -o woow woow.c
./woow
当为针对 C 代码的漏洞提供 shellcode 时,必须避免使用空字节 (\x00),因为它们会终止指令链。这一点至关重要,因为 shellcode 通常包含在以 NUL 结尾的字符串中。如果 shellcode 包含 null 字节,则被利用的 C 代码可能会忽略并丢弃从第一个 null 字节开始的任何后续代码。
这个挑战特别与机器代码有关。例如,要调用数字为 0xb 的系统调用,您需要将 EAX 寄存器设置为 0xb,而不使用包含空字节的计算机代码。
避免在 shellcode 中使用空字节 (\x00) 以利用 C 代码,因为它们会过早终止代码,导致其余代码被忽略。
让我们去编译并运行两个等效的代码。
第一个 exit1.asm
:
编译并调查 exit1.asm:
nasm -f elf32 -o exit1.o exit1.asm
ld -m elf_i386 -o exit1 exit1.o
./exit1
objdump -M intel -d exit1
正如你所看到的,我们在机器代码中的字节为零。
下一个 exit2.asm:
编译并调查 exit2.asm:
nasm -f elf32 -o exit2.o exit2.asm
ld -m elf_i386 -o exit2 exit2.o
./exit2
objdump -M intel -d exit2
如您所见,其中没有嵌入的零字节。
计算机 CPU 中的 EAX 寄存器可以分成更小的部分:AX、AH 和 AL。AX 是 EAX 的下半部分,AL 是下半部分,AH 是下半部分的上四分之一。
这在编写 shellcode (小型、特殊用途的代码) 时很重要,因为我们需要避免 “null bytes” (0x00),这可能会弄乱代码。
使用寄存器的较小部分可以帮助我们做到这一点。例如,使用 'mov al, 0x1' 只会更改 EAX 的一小部分,并避免创建 null 字节,这与 'mov eax, 0x1' 不同,它可能会在我们的代码中创建不需要的 null 字节。
EAX 是一个 32 位寄存器。AX 是 EAX 的低 16 位。AL 是 AX 的低 8 位(这意味着它也是 EAX 的一部分)。AH 是 AX 的高 8 位。
这两个程序在功能上是等效的。
正常退出
让我们从最简单的示例开始。让我们使用 exit.asm 代码作为 shellcoding 的第一个示例 (example1.asm
):
提取字节码:
nasm -f elf32 -o example1.o example1.asm
ld -m elf_i386 -o example1 example1.o
objdump -M intel -d example1
这是十六进制的样子。
所以,我们需要的字节是 31 c0 b0 01 cd 80。将顶部的代码 (run.c
) 替换为:
现在,编译并运行:
gcc -z execstack -m32 -o run run.c
./run
echo $?
-z execstack
关闭 NX 保护以使堆栈可执行
我们的程序返回 0 而不是 1,因此我们的 shellcode 可以正常工作。
生成 linux shell
让我们编写一个简单的 shellcode 来生成一个 shell (example2.asm):
要编译它,请使用以下命令:
nasm -f elf32 -o example2.o example2.asm
ld -m elf_i386 -o example2 example2.o
./example2
使用将是一种简单的方法,但它有一个缺点:会丢弃用户的权限。system("/bin/sh")system
相反,我们可以使用 ,它稍微复杂一些,但没有这个问题。需要三条信息:execve execve
要运行的程序(进入 EBX 寄存器),程序的参数(这进入 ECX 寄存器,如果没有参数,则可以是),null环境变量(这进入 EDX 寄存器,如果不需要,也可以进入)。null
在示例代码 (example3.asm) 中,我们避免了 null 字节,并使用堆栈直接保存这些值。
现在,让我们组装它并检查它是否正常工作并且不包含任何 null 字节:
nasm -f elf32 -o example3.o example3.asm
ld -m elf_i386 -o example3 example3.o
./example3
objdump -M intel -d example3
然后,通过一些 bash hacking 和 objdump 提取字节码:
objdump -d ./example3|grep '[0-9a-f]:'|grep -v 'file'|cut -f2 -d:|cut -f1-6 -d' '|tr -s ' '|tr '\t' ' '|sed 's/ $//g'|sed 's/ /\\x/g'|paste -d '' -s |sed 's/^/"/'|sed 's/$/"/g'
所以,我们的 shellcode 是:
然后,将顶部的代码 (run.c) 替换为:
编译并运行:
gcc -z execstack -m32 -o run run.c
./run
结论:
在恶意软件开发中,shellcoding 是指创建小型汇编程序来执行诸如打开命令 shell 之类的任务。这需要知道如何编写和管理直接与操作系统交互的代码,同时避免可能破坏代码的 null 字节等问题。