0x01. ALGO
1- Lire le script source
2- Obfusquer le script
2-a- Générer un nom de fonction aléatoire qui se chargera de désobfusquer la chaine
2- Générer une clé, de la taille du script source divisé par deux
3- Chiffrer avec xor le script avec la clé
4- Ecrire le fichier C avec le script chiffré et la clé
4-a- Le fichier C, déchiffrera le script
4-b- Le script déchiffré sera passé directement dans la fonction system()
Ainsi, le fichier final, n'aura aucune chaine, et ne laissera aucune trace de fichie (contrairement à shc)
En revanche, le résultat ne sais pas prendre en compte les arguments du script.
Cela peut être utile pour des scripts qui ne devrait pas être lu par un tiers, par exemple un script de maintenance avec des mots de passe intégrés.
De plus, la faiblesse de system() fait, que lors de l'exécution du script, la commande apparait dans les process (avec shc également)...
0x02. OBFUSCATION BASH
...pour protéger la chaine d'exécution, nous allons masquer le process, par de l'obfuscation Bash. L'idée est la commande qui sera préalablement compressée, encodée en base 64 puis en rot13. Pour l'évaluer (faire exécuter par le langage), le process sera inversé.
L'encodage en Rot13 depuis Bash, se fait avec la commande tr :
tr 'A-Za-z' 'N-ZA-Mn-za-m'
encode() { gzip -c|base64 -w0|tr 'A-Za-z' 'N-ZA-Mn-za-m'; } decode() { tr 'A-Za-z' 'N-ZA-Mn-za-m'|base64 -d|gunzip -c; } script_shell="ifconfig|grep -iE 'ether|HW|mac'|awk '{ print $2 }'" obfuscated_shell_script=$(echo "$script_shell"|encode)
echo "$obfuscated_shell_script"|decode
ifconfig|grep -iE 'ether|HW|mac'|awk '{ print $2 }'
Du coup :
eval "$(echo "$obfuscated_shell_script"|decode)"
a1:c8:3b:76:4a:02 9e:33:7c:27:af:47 12:d7:b3:b9:48:97 b2:24:b9:27:81:7a
0x04. YASHC.SH
#!/bin/bash APP="$(basename "$0")" VERSION="1.0" ####################################################################################################################### # # Functions # ### #--- Banner ----------------------------------------------------------------------------------------------------------- function banner() { echo ' +---------------------+------------------+ |░█░█░█▀█░█▀▀░█░█░█▀▀░| | |░░█░░█▀█░▀▀█░█▀█░█░░░| YASHC '$VERSION' | |░░▀░░▀░▀░▀▀▀░▀░▀░▀▀▀░| | +---------------------+------------------+ + Yet Another Shell (script) compiler | +----------------------------------------+ ' } function usage() { e=0 ; [ $# -eq 1 ] && e=$1 case $e in 2) echo "Error: unable to read input bash script file" ;; 3) echo "Error: unable to write output binary file" ;; esac banner echo "Usage: $APP [-U] [-H] [-O] [-v] [-k] <-f script> <-o outfile>" echo echo " -H : use shc's hardening system" echo " -U : use shc's anti debugging system" echo " -O : obfuscate initial bash code via rot13 & base64" echo " -v : be verbose" echo " -k : keep C source files" echo echo "Create binary with script to execute inside itself." echo echo "NOTE : Arguments are not supported. Only for sh/bash scripts." echo exit $e } #--- Encryption ------------------------------------------------------------------------------------------------------- function _xor() { src="$1" key="$2" dst="$3" python -c " i = 0 out = '' key = '$key' for byte in open('$src','rb').read(): res = ord(byte) ^ ord(key[i%len(key)]) out += chr(res) i = i + 1 open('$dst','w').write(out) " } #--- Random string ---------------------------------------------------------------------------------------------------- function _strrand() { local size="$1" dd if=/dev/urandom | tr -dc "a-zA-Z0-9" | head -c "$size" 2>/dev/null } #--- Printf ----------------------------------------------------------------------------------------------------------- function _display() { [ $VERBOSE -eq 1 ] && printf -- "$*\n" } ####################################################################################################################### # # Bash # ### #--- Args ------------------------------------------------------------------------------------------------------------- KEEP_FILES=0 VERBOSE=0 OBFUSCATE=0 HARDEN=0 UNTRACEABLE=0 while [ $# -ge 1 ] ; do if [ "$1" = "-k" ]; then KEEP_FILES=1 fi if [ "$1" = "-v" ]; then VERBOSE=1 fi if [ "$1" = "-f" ]; then shift script="$1" fi if [ "$1" = "-o" ]; then shift exefile="$1" fi if [ "$1" = "-O" ]; then OBFUSCATE=1 fi if [ "$1" = "-U" ]; then UNTRACEABLE=1 fi if [ "$1" = "-H" ]; then HARDEN=1 fi if [ "$1" = "-h" ]; then usage 0 break fi shift done [ ! -r "$script" ] && usage 2 touch "$exefile" || usage 3 #--- Temp filename ---------------------------------------------------------------------------------------------------- tsh="$script.b64.sh" tmp="$script.xor.tmp" log="$script.gcc.log" outfile="$script.c" C_SCRIPT_TMPFILE="$tmp.xor.c.tmp" C_XOR_KEY_TMPFILE="$tmp.xor-key.c.tmp" #--- Obfuscation ------------------------------------------------------------------------------------------------------ if [ $OBFUSCATE -eq 1 ]; then ## Function that encode (compress+base64+rot13) encode() { gzip -c|base64 -w0|tr 'A-Za-z' 'N-ZA-Mn-za-m'; } ## Function that decode (compress+base64+rot13) with random name (upto 63 chars), must not begin with a digit func_name_decode=f$(_strrand $((RANDOM%48+15))) decode_func="$(echo "$func_name_decode() { tr 'A-Za-z' 'N-ZA-Mn-za-m'|base64 -d|gunzip -c; }"|base64 -w0)" ## Original script is encoded with 'encode' function b64_src="$(cat "$script"|encode)" ## New script will be compiled echo 'eval "$(echo "'$decode_func'"|base64 -d)" ; eval "$(echo "'$b64_src'"|'$func_name_decode');#"' > "$tsh" script="$tsh" fi #--- Infos ------------------------------------------------------------------------------------------------------------ script_size=$(stat -c "%s" "$script") _display "[-] Script size : $script_size" key_size=$(($script_size/2)) _display "[-] Key size : $key_size" password=$(_strrand "$key_size") ####################################################################################################################### # # C code source # ### #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - C_HEADER_1=" // // ------------------------------------------------------------- // yashc Version 1.0, Yet Another (simple) Shell Script Compiler // WTFPL SecureInfo42 <contact@secureinfo.eu> // ------------------------------------------------------------- // #include <stdio.h> #include <unistd.h> #include <string.h> #include <stdlib.h> #include <sys/types.h> #include <sys/ptrace.h> #include <sys/wait.h> #include <sys/errno.h> #include <errno.h> #include <fcntl.h> #include <signal.h> int HARDEN=0; int UNTRACEABLE=0; const char key[]={" #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - C_SCRIPT_BEGIN="}; const char src[]={" #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - C_SCRIPT_END="}; " #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - C_PROTECT=" " # TODO: add some features to protect executable from reverse engineering #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - C_BUFFER_DST=" char dst[$script_size]; " #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - C_XOR=" // Encryption //----------------------------------------------------- void xor(int size) { int i=0; for(i=0;i<size-1;i++) { dst[i]=src[i]^key[i%sizeof(key)]; } dst[i]='\\0'; } " #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - C_HARDEN=" // Protection : hardening //----------------------------------------- // Source: code from shc #define HARDEN 1 static void gets_process_name(const pid_t pid, char * name) { char procfile[BUFSIZ]; sprintf(procfile, \"/proc/%d/cmdline\", pid); FILE* f = fopen(procfile, \"r\"); if (f) { size_t size; size = fread(name, sizeof (char), sizeof (procfile), f); if (size > 0) { if ('\\n' == name[size - 1]) name[size - 1] = '\\0'; } fclose(f); } } void harden() { if (ptrace(PT_TRACE_ME, 0, 0, 0) < 0) { printf(\"Operation not permitted\\n\"); kill(getpid(), SIGKILL); exit(1); } } " C_UNTRACEABLE=" // Protection : untraceable //--------------------------------------- // Source: code from shc #define UNTRACEABLE 1 void untraceable(char * argv0) { char proc[80]; int pid, mine; switch(pid = fork()) { case 0: pid = getppid(); #if defined(__FreeBSD__) sprintf(proc, \"/proc/%d/mem\", (int)pid); #else sprintf(proc, \"/proc/%d/as\", (int)pid); #endif close(0); mine = !open(proc, O_RDWR|O_EXCL); if (!mine && errno != EBUSY) mine = !ptrace(PT_ATTACHEXC, pid, NULL, 0); if (mine) { kill(pid, SIGCONT); } else { perror(argv0); kill(pid, SIGKILL); } _exit(mine); case -1: break; default: if (pid == waitpid(pid, 0, 0)) return; } perror(argv0); _exit(1); } " C_MAIN=" // Main //----------------------------------------------------------- extern char **environ; int main(int ac, char **av, char **ev) { #if defined(HARDEN) harden(); #endif #if defined(UNTRACEABLE) untraceable(av[0]); #endif memset(dst, ' ', $script_size); xor("$script_size"); system(dst); } " #- - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - ####################################################################################################################### # # Main # ### _display "[+] Encrypting script with key..." _xor "$script" "$password" "$tmp" _display "[+] Dumping encrypted script..." xxd -c 16 -i "$tmp" |grep ',' > $C_SCRIPT_TMPFILE _display "[+] Dumping key..." printf "%s" "$password"|xxd -c 16 -i > $C_XOR_KEY_TMPFILE #--- C source --------------------------------------------------------------------------------------------------------- _display "[+] Building output file '$outfile'..." echo "$C_HEADER_1" > "$outfile" cat $C_XOR_KEY_TMPFILE >> "$outfile" echo "$C_SCRIPT_BEGIN" >> "$outfile" cat $C_SCRIPT_TMPFILE >> "$outfile" echo "$C_SCRIPT_END" >> "$outfile" echo "$C_PROTECT" >> "$outfile" echo "$C_BUFFER_DST" >> "$outfile" echo "$C_XOR" >> "$outfile" [ $HARDEN -eq 1 ] && echo "$C_HARDEN" >> "$outfile" [ $UNTRACEABLE -eq 1 ] && echo "$C_UNTRACEABLE" >> "$outfile" echo "$C_MAIN" >> "$outfile" #--- Compilation ------------------------------------------------------------------------------------------------------ _display "[+] Compiling to '$exefile' ..." E=1 ; gcc "$outfile" -Wimplicit-function-declaration -o "$exefile" 2>"$log" && E=0 #--- Cleaning --------------------------------------------------------------------------------------------------------- _display "[+] Cleaning temporary files ..." rm -f $C_XOR_KEY_TMPFILE rm -f $C_SCRIPT_TMPFILE rm -f "$tmp" if [ $E -eq 0 ]; then if [ $KEEP_FILES -eq 0 ]; then rm -f "$outfile" rm -f "$tsh" else _display "\n[+] C-source file :" ls -l "$outfile" fi else echo "[x] Error, see files: $(ls "$log")" exit $E fi #--- Output ----------------------------------------------------------------------------------------------------------- _display "\n[+] Outfile : " [ $VERBOSE -eq 1 ] && ( ls -l "$exefile" ; file "$exefile" ) exit $E
0x04. DEMO
Le script source :
secureinfo.eu$ cat cow-fortune.sh #!/bin/bash r=$(((RANDOM%51)+1)) i=1 for c in $(cowsay -l) do i=$((i+1)) if [ $i -eq $r ] then fortune | cowsay -f $c exit fi done
Devient alors :
// // ------------------------------------------------------------- // yashc Version 1.0, Yet Another (simple) Shell Script Compiler // WTFPL SecureInfo42 <contact@secureinfo.eu> // ------------------------------------------------------------- // #include <stdio.h> #include <stdlib.h> #include <string.h> #include <unistd.h> // NOTE : key et src changé pour compatibilité avec GeShi (coloration syntaxique en PHP) const char key[]={ '\x32', '\x2b', '\x6f', '\x6f', '\x53', '\x72', '\x6f', '\x39', '\x56', '\x6a', '\x6a', '\x74', '\x62', '\x74', '\x42', '\x53', '\x4a', '\x4c', '\x4d', '\x2f', '\x46', '\x30', '\x7a', '\x7a', '\x6a', '\x61', '\x56', '\x51', '\x6d', '\x39', '\x73', '\x69', '\x55', '\x7a', '\x4f', '\x76', '\x39', '\x31', '\x39', '\x52', '\x57', '\x32', '\x67', '\x64', '\x4f', '\x4a', '\x36', '\x37', '\x70', '\x4b', '\x5a', '\x45', '\x4e', '\x42', '\x6e', '\x57', '\x73', '\x34', '\x43', '\x69', '\x74', '\x66', '\x48', '\x37', '\x61', '\x7a', '\x68', '\x67', '\x74', '\x69', '\x46', '\x7a', '\x51', '\x30', '\x46', '\x4a', '\x70', '\x4d', '\x51', '\x4c', '\x41', '\x66', '\x63', '\x59', '\x6b', '\x66', '\x64', '\x6a', '\x55', '\x43', '\x38', '\x58', '\x39', '\x58', '\x2f', '\x38', '\x5a', '\x55', '\x57', '\x51', '\x61', '\x63', '\x57', '\x32', '\x66', '\x4e', '\x72', '\x68', '\x65', '\x4f', '\x62', '\x32', '\x51', '\x59', '\x6c', '\x33' }; const char src[]={ '\x57', '\x5d', '\x0e', '\x03', '\x73', '\x50', '\x4b', '\x11', '\x33', '\x09', '\x02', '\x1b', '\x42', '\x56', '\x0b', '\x2a', '\x0f', '\x3a', '\x14', '\x42', '\x2a', '\x45', '\x36', '\x48', '\x20', '\x09', '\x35', '\x63', '\x0a', '\x72', '\x30', '\x0e', '\x25', '\x03', '\x1f', '\x25', '\x68', '\x5e', '\x72', '\x11', '\x3f', '\x61', '\x36', '\x31', '\x7a', '\x0f', '\x62', '\x07', '\x40', '\x27', '\x14', '\x11', '\x0b', '\x32', '\x25', '\x2d', '\x36', '\x44', '\x08', '\x38', '\x04', '\x16', '\x18', '\x63', '\x24', '\x31', '\x32', '\x0a', '\x4d', '\x10', '\x0f', '\x3d', '\x1c', '\x57', '\x27', '\x1d', '\x44', '\x2a', '\x1b', '\x0f', '\x29', '\x0c', '\x01', '\x6a', '\x0f', '\x1c', '\x3d', '\x32', '\x3e', '\x24', '\x74', '\x0f', '\x4e', '\x28', '\x6c', '\x55', '\x08', '\x23', '\x14', '\x38', '\x20', '\x04', '\x36', '\x66', '\x56', '\x25', '\x39', '\x2b', '\x0d', '\x3f', '\x29', '\x48', '\x14', '\x29', '\x27', '\x62', '\x5d', '\x4c', '\x26', '\x28', '\x3f', '\x1f', '\x26', '\x7f', '\x25', '\x0d', '\x20', '\x33', '\x09', '\x13', '\x0e', '\x04', '\x1c', '\x34', '\x04', '\x6c', '\x14', '\x49', '\x33', '\x3c', '\x5a', '\x2a', '\x1f', '\x12', '\x2f', '\x09', '\x12', '\x2e', '\x03', '\x0f', '\x0c', '\x1f', '\x78', '\x56', '\x70', '\x11', '\x15', '\x5f', '\x05', '\x57', '\x05', '\x7a', '\x52', '\x60', '\x45', '\x27', '\x13', '\x0d', '\x39', '\x25', '\x37', '\x65', '\x4a', '\x07', '\x20', '\x5b', '\x32', '\x53', '\x01', '\x74', '\x50', '\x17', '\x21', '\x24', '\x26', '\x03', '\x0f', '\x3b', '\x3e', '\x57', '\x0f', '\x09', '\x31', '\x2a', '\x0b', '\x14', '\x29', '\x16', '\x07', '\x18', '\x04', '\x01', '\x2d', '\x2d', '\x0f', '\x33', '\x7b', '\x35', '\x6b', '\x2e', '\x4d', '\x55', '\x0f', '\x1e', '\x14', '\x36', '\x5c', '\x5e', '\x75', '\x4e', '\x04', '\x2f', '\x01', '\x0d', '\x53', '\x7b', '\x42', '\x1f', '\x35', '\x70', '\x4e', '\x39' }; char dst[232+1]; void xor(int size) { int i=0; for(i=0;i<size;i++) { dst[i]=src[i]^key[i%sizeof(key)]; } dst[i]='\x00'; } extern char **environ; int main(int ac, char **av, char **ev) { memset(dst, ' ', 232); xor(232); system(dst); }
En exécution :
secureinfo.eu$ yashc -f cow-fortune.sh -o cow-fortune -v [-] Script size : 380 [-] Key size : 190 [+] Encrypting script with key... [+] Dumping encrypted script... [+] Dumping key... [+] Building output file 'cow-fortune.sh.c'... [+] Compiling to 'cow-fortune' ... [+] Cleaning temporary files ... [+] Outfile : -rwx------ 1 nexus nexus 8608 Nov 20 21:07 cow-fortune cow-fortune: Mach-O 64-bit x86_64 executable, flags:<NOUNDEFS|DYLDLINK|TWOLEVEL|PIE>
Le rendu, dans iTerm :

La démo :

Yashc peut être utilisé pour protéger des mots de passe dans un fichier de configuration :
=> Écrit par : Nicolas, le 13 mai 2019