The SSH client supports either publickey or password authentication for regular users.
2.2 TCP PORT 3000 (HTTP)
A Gitea page opens up when you access the service via a web browser:
There are no public repositories in the Explore > Repositories section but a list of accounts were revealed under the Explore > Users as well as the group, Sink_Solutions, under Explore > Organizations.
The users -- david, marcus, and root, are all members of Sink_Solutions:
There is also a login option but there are no user credentials gotten yet nor is there a register option for new users. Finding valid user credentials might be one of the goals in the box during exploitation.
2.3 TCP PORT 5000 (HTTP)
Opening the service via a web browser brings you to the following page:
An option to sign up is available where I registered as the user, jebidiah:
And upon successful registration, you will be redirected to the following page:
On the same page, posting comments on posts are possible:
There is also an option to save personal notes within your account on /notes:
Posting notes will create a table on the same page (/notes):
And when you view the notes listed on the table, you are redirected to a page, /notes/<integer> and in this case, /notes/17436:
How the integer in the link and the numerical ID of the posted notes are hard to correlate and fuzzing seems to be not viable as well:
$ wfuzz -z range,1-2000 -H "Cookie: session=eyJlbWFpbCI6InRlc3RAbWFpbC5jb20ifQ.YIUHSQ.E0jQ74qMjbmow86LsvEoC6zLK30" --hc=500 http://10.10.10.225:5000/notes/FUZZ
Target: http://10.10.10.225:5000/notes/FUZZ
Total requests: 2000
=====================================================================
ID Response Lines Word Chars Payload
=====================================================================
000000003: 302 3 L 24 W 219 Ch "3"
000000001: 302 3 L 24 W 219 Ch "1"
000000002: 302 3 L 24 W 219 Ch "2"
Total time: 0
Processed Requests: 140
Filtered Requests: 137
Requests/sec.: 0
/usr/lib/python3/dist-packages/wfuzz/wfuzz.py:78: UserWarning:Fatal exception: Pycurl error 7: Failed to connect to 10.10.10.225 port 5000: Connection refused
The server times out when flooded by a large volume of requests. But, the usual response code for the other requests are 500 (Internal Server Error) and for notes -- 1, 2, and 3, the response was 302 (Redirect). Trying to access the previously mentioned notes only redirects you back to /notes.
PART 3 : EXPLOITATION
3.2 HTTP REQUEST SMUGGLING
Checking for the response headers of the service running in port 5000:
While searching for relevant exploits for gunicorn with haproxy, I came across this article by Nathan Davidson and a practical application in a CTF challenge write up. It highlights a HTTP Request Smuggling using CL.TE (Content-Length -> Transfer-Encoding) vulnerability that leads to HTTP Desynchronization.
How request smuggling works is that specifying a Transfer-Encoding header with a value chunked along with Content-Length can force the server to interpret the request as multiple requests. The server will interpret the “0\r\n\r\n” as the end of the initial request and will then begin to parse the succeeding lines as a new request. The terminator for the smuggled request will be supplied by the server once it parses the second request.
In the following example, when the request passes through haproxy, the request will be considered as chunked as well as into the server itself leveraging HTTP Request Smuggling using TE.TE (Transfer-Encoding -> Transfer-Encoding):
And after the request was sent, the server responded as well to the smuggled request:
3.2 HTTP DESYNCHRONIZATION
The goal of the exploit is to force haproxy to interpret the Content-Length instead of the Transfer-Encoding header in the initial request to leverage a CL.TE instead of TE.TE in order to trigger an HTTP Desynchronization wherein the server will hang while waiting for a request with a “0\r\n\r\n” terminator; therefore, making the intercepted request part of the smuggle request.
To do this, according to the article by Nathan Davidson, write the Transfer-Encoding header with [\x0b] or [\x0c] before the value, chunked. These characters are short spaces that haproxy will not be able to parse. This will enable the attacker to intercept other requests as part of the smuggled request’s body.
After the sending the request above then checking the created note, an HTTP request was intercepted and was saved along with the note:
Setting the right Content-Length (in this case, 300) for the smuggled request will let you intercept the succeeding request properly. Setting it too long and the request terminator might be interpreted as part of the note.
The entire request will look like this once interpreted by the server:
Using the session cookie to access the service reveals that the cookie belongs to admin@sink.htb and the notes previously found while fuzzing are now readable.
The exploit for triggering the HTTP Desync was automated using this python script:
There is a root user for the Gitea service, maybe this is the credentials for the user given that it is for code.sink.htb.
PART 4 : USER SHELL (marcus)
4.1 EC2 INFORMATION
Logging in using the credentials, root:FaH@3L>Z3})zzfQ3, the following repositories become available:
The Key_Management repo has been archived and upon viewing, there has been 8/9 commits by marcus:
The commit history highly suggests deployments in AWS. The commit with the message, “Adding EC2 Key Management Structure”, seems promising. And in there, an SSH private key is added to the repository as well as various configurations for an EC2 Client.
marcus@sink:~$ id
uid=1001(marcus) gid=1001(marcus) groups=1001(marcus)
marcus@sink:~$ ls -l
total 4
-r-------- 1 marcus marcus 33 Apr 25 05:21 user.txt
marcus@sink:~$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
gitlab-psql:x:994:995::/var/opt/gitlab/postgresql:/bin/sh
gitlab-prometheus:x:993:994::/var/opt/gitlab/prometheus:/bin/sh
marcus:x:1001:1001:,,,:/home/marcus:/bin/bash
david:x:1000:1000:,,,:/home/david:/bin/bash
git:x:115:123:Git Version Control,,,:/home/git:/bin/bash
marcus@sink:~$ ls -l /home/david
total 4
drwxr-x--- 3 david david 4096 Dec 2 12:28 Projects
There seems to be another user, david, inside the machine. And his home directory seems to be readable. There is a directory, Projects, however that is protected. This might be interesting.
PART 5 : PIVOT (marcus -> david)
5.1 AWS CONFIGURE
Checking to see if port 4566 is listening inside the machine:
There are three services running - kms, logs, and secretsmanager. In order to access these services via the aws-cli, we need an AWS Access Key ID and an AWS Secret Access Key. Looking back into the repositories in Gitea, there was a Log_Management repo and given that a “logs” service is running, there might be something interesting there.
In the last commit, “Preparing for prod”, the AWS Access Key ID and an AWS Secret Access Key were deleted from the create_logs.php file:
We can now run aws configure in order to access the running services:
marcus@sink:~$ aws configure
AWS Access Key ID [None]: AKIAIUEN3QWCPSTEITJQ
AWS Secret Access Key [None]: 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
Default region name [None]: eu
Default output format [None]:
5.2 SECRETSMANAGER
Enumerating the secretsmanager service to see if there are anything interesting:
marcus@sink:~$ aws secretsmanager list-secrets --endpoint-url http://127.0.0.1:4566
{
"SecretList": [
{
"ARN": "arn:aws:secretsmanager:us-east-1:1234567890:secret:Jenkins Login-ePSxN",
"Name": "Jenkins Login",
"Description": "Master Server to manage release cycle 1",
[..omitted..]
}
},
{
"ARN": "arn:aws:secretsmanager:us-east-1:1234567890:secret:Sink Panel-inxLk",
"Name": "Sink Panel",
"Description": "A panel to manage the resources in the devnode",
[..omitted..]
}
},
{
"ARN": "arn:aws:secretsmanager:us-east-1:1234567890:secret:Jira Support-dgnlR",
"Name": "Jira Support",
"Description": "Manage customer issues",
[..omitted..]
}
}
]
}
There were three secrets available in the secretsmanager service. Extracting their respective values:
Credentials from three users were leaked from the secretsmanager service. More importantly, one of them was for the user, david (david:EALB=bcC=`a7f2#k).
marcus@sink:~$ su david
Password: EALB=bcC=`a7f2#k
david@sink:/home/marcus$ id
uid=1000(david) gid=1000(david) groups=1000(david)
PART 6 : PRIVILEGE ESCALATION (david -> root)
6.1 PORT FORWARD -- 4566
Now that we have a shell as the user, david, the Projects directory should now be accessible wherein an encrypted file, servers.enc, is stored:
david@sink:~$ ls -l Projects
total 4
drwxrwx--- 2 david david 4096 Feb 1 09:43 Prod_Deployment
david@sink:~$ ls -l Projects/Prod_Deployment/
total 4
-rw-r----- 1 david david 512 Feb 1 09:43 servers.enc
david@sink:~$ file Projects/Prod_Deployment/servers.enc
Projects/Prod_Deployment/servers.enc: data
Looking back again into the running services accessible via http://127.0.0.1:4566, one of them was kms which stands for Key Management Service for AWS. It might have been used to encrypt the file. To make things easier, I forwarded the service running on port 4566 to my local machine:
$ ssh -i id_rsa -l marcus -L 4566:127.0.0.1:4566 10.10.10.225
marcus@sink:~$ su david
Password: EALB=bcC=`a7f2#k
david@sink:/home/marcus$
6.2 KMS ENUMERATION
Enumerating this time the said service after running aws configure again:
I took all the KeyId values from the output of list-keys which I will then iterate through to get their individual descriptions:
$ for i in $(cat kms_ids); do aws kms describe-key --key-id $i --endpoint-url http://127.0.0.1:4566 | jq -c '.KeyMetadata | {"KeyId": .KeyId, "Description": .Description, "KeyState": .KeyState, "EncryptionAlgorithms": .EncryptionAlgorithms}'; done > key_descriptions
There were multiple keys but only a handful were enabled and only one could be used for encryption or decryption -- 804125db-bdf1-465a-a058-07fc87c0fad0:
The contents are still gibberish but when you check what kind of output was generated, the file turned to be a gzip compressed data. Using zcat to view the contents: