Recently, I had a chance to play with Cloudflare Workers. Workers provide a similar capability to other Function-as-a-Service (FaaS) offerings, like AWS’s Lambda or Azure’s Functions. Described by Cloudflare, in terms of what you can do with these Workers:
Anything and everything. You’re writing code, so the possibilities are infinite. Your Service Worker will intercept all HTTP requests destined for your domain, and can return any valid HTTP response. Your worker can make outgoing HTTP requests to any server on the public internet.
Reading Adam Chester’s Lambda Redirector blog, there were obvious applications for Cloudflare Workers to achieve a similar thing. Taking inbound requests from an implant and relaying this back to our infrastructure.
At the time, I wasn’t aware of this blog written by @MYZXCG (thanks to @ZephyFish for pointing it out). @MYZXCG’s blog covers the steps needed to setup Cobalt Strike to use an Nginx redirector, and to subsequently send traffic to this redirector using Cloudflare Workers. So as to not reinvent the wheel, this blog will focus on an alternative approach to the same task, with additional OpSec aspects which might be of interest.
Setup
A significant appeal of the Workers service is the ability to deploy our code under Cloudflare’s workers.dev
domain. There is the option to deploy Workers under a user-controlled domain, but this workers.dev
domain allows to effectively ‘domain front’ using the categorisation of Cloudflare’s catch-all domain.
While it would have been preferable to use the Serverless Framework to handle deployment and teardown of Workers, it seems that there is an outstanding issue with supporting deployment to the workers.dev
domain.
As a result, we’ll use wrangler
to handle our deployment. This can be installed using npm
:
npm install -g @cloudflare/wrangler
Once installed, we need to configure the comand-line tool to use our Cloudflare credentials. You can use wrangler login
or take a premade API token and run a wrangler config
and paste it in.
To generate an API token, having created a Cloudflare account, navigate to the API tokens portal. For the purposes of this, we can use the Edit Cloudflare Workers
permissions template.
Hello Worker
To get started, we’ll create and deploy a simple “Hello Worker” templated project (as documented here). To generate the template, run the following command:
wrangler generate helloworld
Before we can deploy this Worker, we’ll need to add our account ID to the generated wrangler.toml
file. You can easily retrieve your account ID by running a wrangler whoami
.
We can then run a wrangler publish
command to deploy our “Hello Worker” function. If everything goes well, we should be returned a URL of the format https://helloworld.[your_worker_domain].workers.dev
.
If we visit or curl this URL, we should get our “Hello worker!” output back - Success!
Creating a Redirector
To repurpose our simple project to redirect traffic, we need to edit the wrangler.toml
settings file and our Javascript index.js
file. Firstly, we can set some environment variables to use in our main code.
- TS - The destination URL for our traffic, e.g. our Cobalt Strike team server or some other redirecting infrastructure (e.g. https://bad.stuff/).
- WORKER_ENDPOINT - The deployed endpoint for our Worker, we’ll use this to strip the URI path of received requests and send it on.
The completed wrangler.toml
file should look something like the below. Note, [WORKER_NAME]
is based on the name
field, so would just be "helloworld"
in this case:
The index.js
file can then be modified as below:
If we run a wrangler publish
now, we should have an endpoint that will take our requests and forward them onto a destination host. As we’re taking the request URI and appending that to the destination URL, we can customise our traffic profile as we see fit (i.e. using a malleable profile).
With our Cobalt Strike listener setup, we can launch a Beacon and get a callback to our Team Server. Great stuff.
Improving OpSec
Looking at the Javascript code (and the Worker example docs), it should be relatively clear that we can manipulate most aspects of our requests and responses to fit our use case, e.g. adding or stripping headers as needed.
One application of this could be to restrict redirected traffic based on a custom header (as outlined here). Any traffic not supplying this header would then be responded to with some suitably-benign response.
For this, we’ll add an additional environment variable, HEADER_KEY
to our variables block in wrangler.toml
.
We can then modify our index.js
file as below. Note here we’re using a static X-Custom-PSK
header name, but you could pull that out as an environment variable too depending on your use case.
Browsing to this URL without providing our header value, we should receive our benign response.
We then need to ensure that our callback traffic includes the custom header in all our GET and POST requests. For Cobalt Strike, this can be achieved by adding the header to the http-get
and http-post
client
blocks, as below (adapting @xpn’s profile from the Lambda redirector blog for simplicity).
Detection Opportunities
As with most command-and-control (C2) channels, detection is a significant challenge. This is exacerbated by the fact that, in this configuration, we can adapt the Beacon malleable profile to fit whichever scenario we may choose.
In this blog specifically, we’re using Cloudflare’s workers.dev
domain, rather than a custom domain name. A valuable exercise may be to review an organisation’s existing traffic to this top-level domain, alerting or preventing access as appropriate.
Conclusion
Cloudflare Workers, much like other Function-as-a-Service offerings, present an alternative means of fronting command-and-control traffic. Deploying one (or many) Worker redirectors is trivial using deployment tools, such as wrangler
.