Home Zerologon Vulnerability Implementation
Post
Cancel

Zerologon Vulnerability Implementation

In this post I intend to implement the vulnerability known as Zerologon that allows an attacker to gain full control over a domain server in the Active Directory domain server.

The vulnerability acts within the scope of the Netlogon protocol, used in Windows domain servers to, among other things, facilitate access to users who want to log in to the server using an account and password.

Post index

Introduction

The attack described here1 takes advantage of flaws in a cryptographic authentication protocol that proves the authenticity and identity of a domain-joined computer to the DC. Due to incorrect use of an AES mode of operation it is possible to spoof the identity of any computer account (including that of the DC itself) and set an empty password for that account in the domain.

To carry out this project, I read the detailed indications in the report1 and through it I determine the steps I must follow to exploit the vulnerability.

In this case, I’m not going to specify how it works, but rather how to create the code to exploit the vulnerability. If you want to understand why it works, read the report1 where it is detailed.

What must be implemented to exploit the vulnerability is what you see in the following image (Figure 1).

Figure 1 Figure 1 - When encrypting a message consisting only of zeroes, with an all-zero IV, there is a 1 in 256 chance that the output will only contain zeroes as well.

How to reproduce it ?

To ensure that the cryptogram is made up exclusively of zeros, the following must be taken into account:

  • IV → All zeros.
  • Message → All zeros.
  • Key → Since the length of the key is 8 bits, there are 2^8 = 256 combinations of keys. Some one of the 256 combinations will generate as output a zero. This will cause the cryptogram to be zeros.

We are going to comment the most important parts (All the code is attached at the end of the post to be able to see it complete and execute it):

  1. Initialization: We start with the requirements stipulated in Figure 1 and the CFB8 block cipher mode. Figure 2 Figure 2 - Initialization proccess.

  2. Encryption and decryption functions: they receive the message or cryptogram, followed by the key and the IV. Figure 3 Figure 3 - Encryption and decryption functions.

  3. Brute force to obtain the cryptogram we want: for each attempt, a key is randomly generated. We check if the cryptogram generated with the generated cryptogram is all zeros. Finally, a series of statistics are printed to see if any cryptogram is equal to zero and the position of the attempt. equal to zero and the position of the attempt. Figure 4 Figure 4 - Brute force to obtain the cryptogram we want:.

  4. Random string generation function. Figure 5 Figure 5 - Random string generation function.

Results

One of the executions generates a match among 256 attempts made, see it in Figure 6. Figure 6 Figure 6 - Results 1

If we go to position 77, as shown in the previous image, we can see that the cryptogram is all zeros. Figure 7 Figure 7 - Results 2

But that will not always be the case. There will be times when the number of matches is zero. This is because the random string generation function is not as random as it seems and does not generate all possible combinations. Figure 8 Figure 8 - Results 3

On the other hand, there will be occasions when the opposite is true. In this case there are two coincidences. Figure 9 Figure 9 - Results 4

Conclusions

By simply sending a number of Netlogon messages in which various fields are filled with zeroes, an attacker can change the computer password of the domain controller that is stored in the AD. This can then be used to obtain domain admin credentials and then restore the original DC password.

Code developed

Here you have the code developed to exploit CVE-2020-1472 vulnerability.

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
146
147
148
149
150
151
152
153
154
155
import java.io.UnsupportedEncodingException;
import java.nio.charset.Charset;
import java.security.InvalidAlgorithmParameterException;
import java.security.InvalidKeyException;
import java.security.NoSuchAlgorithmException;
import java.security.SecureRandom;
import java.util.ArrayList;
import java.util.List;

import javax.crypto.BadPaddingException;
import javax.crypto.Cipher;
import javax.crypto.IllegalBlockSizeException;
import javax.crypto.KeyGenerator;
import javax.crypto.NoSuchPaddingException;
import javax.crypto.SecretKey;
import javax.crypto.spec.IvParameterSpec;
import javax.crypto.spec.SecretKeySpec;

import jakarta.xml.bind.DatatypeConverter;

public class Cryptor {

    private Cipher cipher;
    static int numIntentos = 256;
    static byte[] iv = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    
    static byte[] text = {0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00,
            0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00};
    
    private static Charset CHARSET = Charset.forName("ISO-8859-1");

    public Cryptor() throws CryptingException {
        try {
            cipher = Cipher.getInstance("AES/CFB8/NoPadding");
        } catch (NoSuchAlgorithmException e) {
            throw new SecurityException(e);
        } catch (NoSuchPaddingException e) {
            throw new SecurityException(e);
        }
    }

    public byte[] decrypt(byte[] input, SecretKey keySpec, IvParameterSpec ivSpec) throws CryptingException {
        try {
            cipher.init(Cipher.DECRYPT_MODE, keySpec, ivSpec);
            return cipher.doFinal(input);
        } catch (IllegalBlockSizeException e) {
            throw new SecurityException(e);
        } catch (BadPaddingException e) {
            throw new SecurityException(e);
        } catch (InvalidKeyException e) {
            throw new SecurityException(e);
        } catch (InvalidAlgorithmParameterException e) {
            throw new SecurityException(e);
        }
    }

    public byte[] encrypt(byte[] input, SecretKey keySpec, IvParameterSpec ivSpec) throws CryptingException {
        try {
            cipher.init(Cipher.ENCRYPT_MODE, keySpec, ivSpec);
            return cipher.doFinal(input);
        } catch (InvalidKeyException e) {
            throw new SecurityException(e);
        } catch (InvalidAlgorithmParameterException e) {
            throw new SecurityException(e);
        } catch (IllegalBlockSizeException e) {
            throw new SecurityException(e);
        } catch (BadPaddingException e) {
            throw new SecurityException(e);
        }
    }

    public static void main(String Args[]) throws UnsupportedEncodingException {

        try {
            Cryptor c = new Cryptor();

        	int cont = 0;
        	List<String> posiciones = new ArrayList<String>();
            for (int i = 0; i < Cryptor.numIntentos; i++) {
                System.out.println("Posicion: " + i);
                System.out.println("TEXT    -> 0x" + DatatypeConverter.printHexBinary(text));
                
            	// KEY
            	String secretKey = generateRandomString(16);
            	System.out.println("KEY     -> " + secretKey);
            	SecretKey keySpec = new SecretKeySpec(secretKey.getBytes(CHARSET), "AES");
            	
            	// IV
                System.out.println("IV      -> 0x" + DatatypeConverter.printHexBinary(Cryptor.iv));
                IvParameterSpec ivSpec = new IvParameterSpec(Cryptor.iv);
            	
                // ENCRYPT
                byte[] encrypted = c.encrypt(Cryptor.text, keySpec, ivSpec);
                if (DatatypeConverter.printHexBinary(encrypted).contains("00000000000000000000000000000000")) {	
                	System.out.println("Encoded -> 0x" + DatatypeConverter.printHexBinary(encrypted) + " <--------------- ####### UN RESULTADO #######");
                	cont++;
                	posiciones.add(Integer.toString(i));
                }
                else 
                	System.out.println("Encoded -> 0x" + DatatypeConverter.printHexBinary(encrypted));
                
                //DECRYPT
                byte[] decrypted = c.decrypt(encrypted, keySpec, ivSpec);
                System.out.println("Decoded -> 0x" + DatatypeConverter.printHexBinary(decrypted));
                
                System.out.println("------------------------------------");
			}

            System.out.println("####### RESULTADOS #######");
            System.out.println("Numero intentos   -> " + Cryptor.numIntentos);
            System.out.println("Coincidencias     -> " + cont);
            System.out.println("En las posiciones -> " + posiciones);
            

        } catch (CryptingException e) {
            e.printStackTrace();
        }
    }
    
    public static String generateRandomString(int length) {
        String CHAR_LOWER = "abcdefghijklmnopqrstuvwxyz";
        String CHAR_UPPER = CHAR_LOWER.toUpperCase();
        String NUMBER = "0123456789";

        String DATA_FOR_RANDOM_STRING = CHAR_LOWER + CHAR_UPPER + NUMBER;
        SecureRandom random = new SecureRandom();

        if (length < 1) throw new IllegalArgumentException();

        StringBuilder sb = new StringBuilder(length);
        
        for (int i = 0; i < length; i++) {
            int rndCharAt = random.nextInt(DATA_FOR_RANDOM_STRING.length());
            char rndChar = DATA_FOR_RANDOM_STRING.charAt(rndCharAt);

            sb.append(rndChar);
        }

        return sb.toString();
    }

    class CryptingException extends RuntimeException {

        private static final long serialVersionUID = 7123322995084333687L;

        public CryptingException() {
            super();
        }

        public CryptingException(String message) {
            super(message);
        }
    }
}

Reverse Footnote

This post is licensed under CC BY 4.0 by the author.