How to write an async backdoor using public API (part 1)

jychp
6 min readJan 9, 2024

--

In this series of articles, I will detail how I developed a backdoor using various public APIs for communication.

This series of articles is intended solely for educational purposes. The goal is to understand the entire cycle of a cyber-attack, including the design and development of the tools. It is also worth noting that the use of this tool may violate the Terms of Service (ToS) of different providers, and it is your responsibility to check and control it before any usage.

Why?

In cybersecurity, intrusion is often portrayed as the pinnacle of skill, and a pentester is hailed as an idol. However, in reality, it is almost the easiest part …

During a real attack, which is only touched upon in a red team mission, discretion, information gathering, and OPSEC are the real challenges.

In factn anyone can hack into companies after reading three tutorials (this is sad, but this is the reality). It takes much more knowledge to remain discreet in a network for the long term.

A robust network filtering policy should always default to “deny all,” including outbound traffic. With such a policy, you should already be restricted, but okay, you can still use your favorite backdoor to send out TCP on port 443 that is not usualy filtered. Now, let’s add a layer security with protocol inspection; it becomes more complicated. You’ll have to go through an HTTP backdoor, and if there’s SSL inspection, all your traffic will be analyzed. And if you’re dealing with a serious admin, you’ll be limited to a few trusted sites for outbound connections.

Alright, but this level of filtering is rarely implemented, so why bother? Well, for discretion. Beaconing to a server every minutes and maintaining a TCP connection can be spoted, but now imagine that your calls are hidden among legit calls, let’s say calls to the logging stack’s API? Or within Microsoft synchronization? Much more discreet, right?

For each phase of the attack and for each purpose, a specific backdoor is more suitable than another. During the initial phase, a very generic tool that includes various lateralization features, scans, etc., is practical. But then, having a tool dedicated to exfiltration and another for maintaining your access, or even a backup access, is better.

This small tutorial, divided into several parts, aims to get into the mindset of an attacker and develop an original backdoor to address the need for discretion in the case of a maintenance/backup access in a persistent attack.

The Concept

Alright, so we’re going to develop a backdoor, but what do we need? Nothing too fancy; we want to stay under the radar. For me, the absolute minimum is: a shell, a socks proxy, port forwarding, and file download (yes, just like SSH/SCP — after all, it’s still the best backdoor, isn’t it?)

So far, nothing complicated — we want a backdoor capable of sending an arbitrary bytes stream and receiving a response. But to spice things up a bit, we want to do all of this in a serverless fashion using public APIs… Huh?

Well, yes, we’re going to attempt to create a backdoor that leverages public APIs — often whitelisted, frequently excluded from SSL inspection, and whose traffic will blend in with the masses (Microsoft, Grafana, Sentry, etc.).

Choice of Languages

Alright, let’s be honest; the choice here is entirely arbitrary. I’m not a developer; I believe I am pretty decent inPython, and that’s all. So, our client will be in Python.

For the backdoor, I wanted a compiled language for which I could easily do cross-compilation. Therefore, I went with Go, where I have the amazing experience of… 1 project. Yes, I know Go binaries can be large; we could do this in C, but… I’m a bit lazy. After all, we’re here to explain concepts, not to provide a ready-to-use backdoor.

Our Constraints

Alright, if we simplify, the problem is that we want to be able to simulate a TCP connection through asynchronous API calls. Well, the neat and orderly OSI model with its well-defined layers takes a hit…

When working with APIs and bending their usage, we encounter a few challenges:

  • Payload size: Depending on the APIs, you may not be able to send as much data per call as you’d like.
  • Rate limit: Calls are often limited, and it can quickly become troublesome. Even if an endpoint is limited to 120 calls per minute, it means the backdoor can hit it every second, and the client can do the same. This implies a latency > 1 second (yes, forget about watching Netflix on the socks; it’s going to be an eye-watering experience).
  • Endpoint URLs: On typical backdoors, we use crafted URIs with the ID in the GET or header. Here, you’ll have to work with the parameters at your disposal.

And finally, because we enjoy a challenge, we want a modular backdoor. This means we want to generate a backdoor that can communicate via Microsoft, Grafana, or any other platform, all from the same base.

PMV (Minimum Viable Product)

Well, since I’m not entirely stupid, I’ve already proof-of-concepted all of this. Spoiler: it’s possible to create this backdoor. Now, let’s do it properly and intelligently.

For this Minimum Viable Product (PMV), we’ll start with a Fake API with an “/in” and an “out/” endpoint. Backdoor will read in and write on out, client will write on in and read on out. It’s not fanct, but it will allow us to test our backdoor skeleton.

Now, it’s quite simple. Here’s the workflow I’ve implemented:

  • The client launches a module and requests a tunnel with the backdoor using a unique ID to identify the tunnel (e.g. 93ef4369:sh)
  • The backdoor responds and stores the tunnel ID (93ef4369:OK)
  • The client send data (e.g., a command) encoded in base64, and prefixes it with the unique tunnel ID (e.g. 93ef4369:dW5hbWUgLWEK)
  • The backdoor retrieves this payload and forwards it to corresponding module (in this case, a shell), and returns the result (e.g. 93ef4369:V2VsbERvbmVZb3VLbm93bkhvd1RvQmFzZTY0RGVjb2RlIQo=)
  • The client retrieves the data and forwards it to the module (in our case display the command result)

Note 1: The unique ID allows us to have multiple “tunnels” on a single endpoint, enabling us to have a shell, a socks proxy, and port forwarding simultaneously. This will be useful later.

Note 2: The base64 encoding will be useful later; we can put it in a Word document, a Notion page, a log line, etc. It generates more volume (33%), but for the simplicity of our backdoor, it’s acceptable.

Result

For this first article, I’ll spare you the code section, which doesn’t add much. Once the concepts are laid out, writing a few lines of code will give you a functional backdoor and a rudimentary client.

Please note, the goal here is just to validate the tunnel aspect. Many things still need improvement (no encryption, no tunnel cleanup leading to memory saturation, poor error handling, etc.). But we have the satisfaction of having a backdoor — okay, just a shell going through a makeshift API. Now, we need to build upon it.

Feel free to implement it yourself or find my source code on https://github.com/jychp/shameleon

And the little bonus when you do it yourself is that your backdoor slips under the radar of antivirus software :)

https://www.virustotal.com/gui/file/3637f986b08f20517e4cbedfa7c7ffdf55783f7ef546259241519dd3ee1c5129/detection

https://www.virustotal.com/gui/file/3637f986b08f20517e4cbedfa7c7ffdf55783f7ef546259241519dd3ee1c5129/detection

--

--

No responses yet