IP-ADDR: 10.10.10.225 sink.htb
nmap scan:
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.1 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_ 256 18:cd:9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
3000/tcp open ppp?
| fingerprint-strings:
| GenericLines, Help:
| HTTP/1.0 200 OK
| Content-Type: text/html; charset=UTF-8
| Set-Cookie: lang=en-US; Path=/; Max-Age=2147483647
| Set-Cookie: i_like_gitea=b06d007546b891b1; Path=/; HttpOnly
| Set-Cookie: _csrf=g8iWus6Yg0Gy4q5O_dzZHqGWHTM6MTYyODczODc4MTA2ODg3ODgzMQ; Path=/; Expires=Fri, 13 Aug 2021 03:26:21 GMT; HttpOnly
| X-Frame-Options: SAMEORIGIN
| Date: Thu, 12 Aug 2021 03:26:21 GMT
| <!DOCTYPE html>
| <html lang="en-US" class="theme-">
| <head data-suburl="">
| <meta charset="utf-8">
| <meta name="viewport" content="width=device-width, initial-scale=1">
| <meta http-equiv="x-ua-compatible" content="ie=edge">
| <title> Gitea: Git with a cup of tea </title>
| <link rel="manifest" href="/manifest.json" crossorigin="use-credentials">
| <meta name="theme-color" content="#6cc644">
| <meta name="author" content="Gitea - Git with a cup of tea" />
| <meta name="description" content="Gitea (Git with a cup of tea) is a painless
5000/tcp open http Gunicorn 20.0.0
|_http-server-header: gunicorn/20.0.0
|_http-title: Sink Devops
-
Port 3000 is running "gitea", Identified from fingerprint-strings.
-
Port 5000 is running "gunicorn/20.0.0" server.
-
Gitea running Version: 1.12.6
-
There is no signup option in gitea.
-
Only interesting thing found is, user "root" contributions but can not see them
Final thing before diving is that "gunicorn" is a python http server and it cannot handle brute forcing or fuzzing.
Port 5000 gunicorn is server running a static webapp and have login and signup option
Found haproxy
is running from response header when doing sign. Also get a session cookie which is a flash auth token.
we can verify cookie with flask-unsign
❯ flask-unsign --decode --cookie 'eyJlbWFpbCI6Int7Nyo3fX1AdGVzdC5jb20ifQ.YSyjmQ.1l4PRcj8FZl60I98PFQkHpyaF-E'
{'email': '{{7*7}}@test.com'}
From google search for "haproxy" for something that already known and found
There is HTTP Request Smuggling Vulnerability in haproxy package, versions <1.8.19-1+deb10u3; Source snyk Vulnerability DB
Vulnerability resolve in Debian:10 haproxy to version 1.8.19-1+deb10u3 or higher.
The way i verify that this Vulnerability present in this box is,
From Port 22 version header "OpenSSH 8.2p1 Ubuntu 4ubuntu0.1", Running host version is possibly "Focal" and update date 2020-06-08; Source launchpad
And Vulnerability resolve on 2020-09-26 update. Source launchpad
Or, Just send a bad request to server and it will return the version number
Server is running version 1.9.10 and http smuggling Vulnerability found in HAProxy before 2.0.6.
"When malformed or abnormal HTTP requests are interpreted by one or more entities in the data flow between the user and the web server, such as a proxy or firewall, they can be interpreted inconsistently, allowing the attacker to "smuggle" a request to one device without the other device being aware of it."
Important terms
transfer-encoding
: The Transfer-Encoding header specifies the form of encoding used to safely transfer the payload body to the user; Source developer.mozilla.org- "chunked" value specifications.
- With
chunked
value Enable, Data is sent in a series of chunks. - The Content-Length header is omitted.
- At the beginning of each chunk(data) you need to add the length of the current chunk in hexadecimal format, followed by '
\r\n
' and then the chunk itself, followed by another '\r\n
'. - The terminating chunk is a regular chunk, with the exception that its length is zero and formatted same as regular data chunks.
- With
- RFC2616
Resources for learning/understanding
- Video: Hiding Wookiees in HTTP: HTTP smuggling by @regilero
- Video: Practical Attacks Using HTTP Request Smuggling by @defparam
- Video: HTTP Desync Attacks: Smashing into the Cell Next Door by @albinowax
- Research: request-smuggling from portswigger
- Blog: Protocol Layer Attack - HTTP Request Smuggling
Tools
CL.TE: the front-end server uses the Content-Length
header and the back-end server uses the Transfer-Encoding
header.
hacktricks source that found from google search.
CVE identifier: CVE-2019-18277
Vulnerability Description: "A flaw was found in HAProxy before 2.0.6. In legacy mode, messages featuring a transfer-encoding header missing the "chunked" value were not being correctly rejected. The impact was limited but if combined with the "http-reuse always" setting, it could be used to help construct an HTTP request smuggling attack against a vulnerable component employing a lenient parser that would ignore the content-length header as soon as it saw a transfer-encoding one (even if not entirely valid according to the specification)."
And here is the haproxy Vulnerability PoC by ndavison
When Transfer-Encoding
and Content-Length
is provided together, The request HAProxy sends to the backend has correctly prioritized Transfer-Encoding
, and has stripped out the content-length
and cut off everything, which went outside the boundary of the Transfer-Encoding
request (Anything after 0
).
However, if we have a \x0b
(vertical tab) before the "chunked" string (note: \x0c
aka form feed also works).
In this case, the Transfer-Encoding
is not detected by HAProxy, and so the Content-Length
is used. However, because the Transfer-Encoding
remains in the request sent to the backend, it means that if a backend server manages to parse the Transfer-Encoding
header and proceeds to treat the request as a TE encoded request, a desync could occur and the backend TCP socket could be poisoned. This could then lead to HTTP request smuggling.
So if the backend server allowed Transfer-Encoding
then backend server give priority to Transfer-Encoding
header and terminate request at terminating chunk 0
and anything after that treated as second request.
- If not worked then use connection as
keep-alive
And you can add 0b
byte before chunked
string from hex tab in repeater window and with \n
switch in repeater window you can see non-printable chars
If everything goes well, our comment contains admin session cookie
Got admin cookie because while backend server waiting to complete 300 bytes of data of our request while frontend server forward another user's request to backend and this request concatenate to our request to complete 300 bytes.
And replacing session cookie with admin cookie ge to the admin panel
From admin's /notes
found 3 notes with creds and subdomains
Chef Login : http://chef.sink.htb Username : chefadm Password : /6'fEGC&zEx{4]zz
Dev Node URL : http://code.sink.htb Username : root Password : FaH@3L>Z3})zzfQ3
Nagios URL : https://nagios.sink.htb Username : nagios_adm Password : g8<H6GK\{*L.fB3C
First i tried to login to gitea server because there is a user named "root" who has some contributions
With root:FaH@3L>Z3})zzfQ3
creds logged in successfully and found some repositories
Some interesting Information collected from these repositories:
-
Got ssh key for user "marcus" from http://10.10.10.225:3000/root/Key_Management/commit/b01a6b7ed372d154ed0bc43a342a5e1203d07b1e commit in root's "Key_Management" repository.
-
Found some aws secret from http://10.10.10.225:3000/root/Log_Management/commit/e8d68917f2570f3695030d0ded25dc95738fb1ba commit in root's "Log_Management" repository.
- Get shell as user marcus and there is a another use name "david".
There are multiple containerd container (total 16) running same web server application.
This application is same, where we exploit http request smuggling in foothold.
-
Every container of this application is running some services that doing there parts(guessing)
- Running
/usr/local/bin/gunicorn
server. - Running
/home/haproxy/haproxy
server. - Executing
/home/bot/bot.py
which give us admin token from http smuggling attack. - Running
/usr/bin/fail2ban-server
to prevent brute-force on web server.
- Running
There are some local services running
Port 4566
is by defaut used by localstack
localstack running from a container on 172.18.0.2
.
- localstack provides an easy-to-use test/mocking framework for developing Cloud applications. It spins up a testing environment on your local machine that provides the same functionality and APIs as the real AWS cloud environment.
awscli
already installed in the box.
There is a local aws instance is running and awscli already installed in the box and we found aws secret from gitea.
Configure awscli to access localstack instance by running aws configure
Get access to aws secretsmanager, aws secrets manager stores credentials.
aws --endpoint-url="http://127.0.0.1:4566/" secretsmanager list-secrets
There are 3 secret IDs(ARN), we can retrieve there secret string with; Source aws docs
aws --endpoint-url="http://127.0.0.1:4566/" secretsmanager get-secret-value --secret-id <ARN>
and get david password from "arn:aws:secretsmanager:us-east-1:1234567890:secret:Jira Support-LtCKQ"
There is a encrypted file in david home folder
From aws kms docs found some kms command that are useful.
decrypt
: Decrypts ciphertext that was encrypted by a KMS key.
enable-key
: Sets the key state of a KMS key to enabled. This allows you to use the KMS key for cryptographic operations.
list-keys
: Gets a list of all KMS keys in the caller's Amazon Web Services account and Region.
describe-key
: Provides detailed information about a KMS key.
Get kms key from aws server
aws --endpoint-url="http://127.0.0.1:4566/" kms list-keys
Now to can decrypt that file with decrypt command
aws --endpoint-url="http://127.0.0.1:4566/" kms decrypt \
--ciphertext-blob fileb:///home/david/Projects/Prod_Deployment/servers.enc \
--key-id <KeyId> \
--output text \
--query Plaintext | base64
but when run this command, get key is disabled error.
First we need to enable all key with
aws --endpoint-url="http://127.0.0.1:4566/" kms enable-key --key-id <KeyId>
Second, This file is not encrypted with default encryption algorithm. We can check this by describing 'KeyArn"
aws --endpoint-url="http://127.0.0.1:4566/" kms describe-key --key-id "<KeyArn>"
Final decryption script
keys=$(aws --endpoint-url="http://127.0.0.1:4566/" kms list-keys | grep KeyId | cut -d'"' -f4)
for key in $keys;
do
aws --endpoint-url="http://127.0.0.1:4566/" kms enable-key --key-id "$key"
aws --endpoint-url="http://127.0.0.1:4566/" kms decrypt \
--ciphertext-blob fileb:///home/david/Projects/Prod_Deployment/servers.enc \
--encryption-algorithm "RSAES_OAEP_SHA_256" \
--key-id "$key" \
--output text \
--query Plaintext | base64
done
And file decrypted with 804125db-bdf1-465a-a058-07fc87c0fad0
aws --endpoint-url="http://127.0.0.1:4566/" kms decrypt \
--ciphertext-blob fileb:///home/david/Projects/Prod_Deployment/servers.enc \
--encryption-algorithm "RSAES_OAEP_SHA_256" \
--key-id 804125db-bdf1-465a-a058-07fc87c0fad0 \
--output text \
--query Plaintext | base64 \
--decode > decrypted-file
Got an admin password which worked for root user.