Script permettant de convertir un source en Shell (bash, sh) en un fichier binaire, en lui appliquant un chiffrement par XOR. Le programme C associé au source Shell permettra d'avoir un binaire plutôt qu'un script ouvert à tous. Ce script n'a pas pour vocation de remplacer *shc*, mais d'offrir une alternative plus light avec un algorithme relativement simple.





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&eacute; pour compatibilit&eacute; 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 :


./files/shell-to-exe/banner.jpg

La démo :


./files/shell-to-exe/demo.jpg

Yashc peut être utilisé pour protéger des mots de passe dans un fichier de configuration :




   =>   Écrit par : Nicolas, le 13 mai 2019


 
Mots clés :  
  macos 
  
  linux 
  
  unix 
  
  system 
    >   Articles connexes :

Cheat SHeet OpenSSL



Sécurisation du Macbook Air



Comment gagner du temps sur Internet



/tmp et /var/log en noexec sur macOS



7953072