1
+ /**
2
+ * TLS-Scanner - A TLS configuration and analysis tool based on TLS-Attacker.
3
+ *
4
+ * Copyright 2017-2019 Ruhr University Bochum / Hackmanit GmbH
5
+ *
6
+ * Licensed under Apache License 2.0
7
+ * http://www.apache.org/licenses/LICENSE-2.0
8
+ */
9
+ package de .rub .nds .tlsscanner .probe ;
10
+
11
+ import java .util .ArrayList ;
12
+ import java .util .Arrays ;
13
+ import java .util .Collections ;
14
+ import java .util .LinkedList ;
15
+ import java .util .List ;
16
+
17
+ import javax .crypto .Cipher ;
18
+ import javax .crypto .SecretKey ;
19
+ import javax .crypto .spec .IvParameterSpec ;
20
+ import javax .crypto .spec .SecretKeySpec ;
21
+
22
+ import org .apache .commons .lang3 .ArrayUtils ;
23
+
24
+ import de .rub .nds .modifiablevariable .util .ArrayConverter ;
25
+ import de .rub .nds .tlsattacker .core .config .Config ;
26
+ import de .rub .nds .tlsattacker .core .constants .CipherSuite ;
27
+ import de .rub .nds .tlsattacker .core .constants .HandshakeMessageType ;
28
+ import de .rub .nds .tlsattacker .core .constants .NamedGroup ;
29
+ import de .rub .nds .tlsattacker .core .constants .ProtocolVersion ;
30
+ import de .rub .nds .tlsattacker .core .protocol .message .NewSessionTicketMessage ;
31
+ import de .rub .nds .tlsattacker .core .protocol .message .ProtocolMessage ;
32
+ import de .rub .nds .tlsattacker .core .state .State ;
33
+ import de .rub .nds .tlsattacker .core .state .TlsContext ;
34
+ import de .rub .nds .tlsattacker .core .workflow .ParallelExecutor ;
35
+ import de .rub .nds .tlsattacker .core .workflow .WorkflowTraceUtil ;
36
+ import de .rub .nds .tlsattacker .core .workflow .factory .WorkflowTraceType ;
37
+ import de .rub .nds .tlsscanner .config .ScannerConfig ;
38
+ import de .rub .nds .tlsscanner .constants .ProbeType ;
39
+ import de .rub .nds .tlsscanner .rating .TestResult ;
40
+ import de .rub .nds .tlsscanner .report .SiteReport ;
41
+ import de .rub .nds .tlsscanner .report .result .ProbeResult ;
42
+ import de .rub .nds .tlsscanner .report .result .SessionTicketZeroKeyResult ;
43
+
44
+ /**
45
+ *
46
+ * The Probe checks for CVE-2020-13777.
47
+ *
48
+ * Quote: "GnuTLS 3.6.x before 3.6.14 uses incorrect cryptography for encrypting
49
+ * a session ticket (a loss of confidentiality in TLS 1.2, and an authentication
50
+ * bypass in TLS 1.3). The earliest affected version is 3.6.4 (2018-09-24)
51
+ * because of an error in a 2018-09-18 commit. Until the first key rotation, the
52
+ * TLS server always uses wrong data in place of an encryption key derived from
53
+ * an application."[1]
54
+ *
55
+ * Reference [1]: http://cve.mitre.org/cgi-bin/cvename.cgi?name=CVE-2020-13777
56
+ * Reference [2]: https://www.gnutls.org/security-new.html
57
+ *
58
+ */
59
+ public class SessionTicketZeroKeyProbe extends TlsProbe {
60
+
61
+ /**
62
+ * Magic Bytes the plaintext state in GnuTls starts with
63
+ */
64
+ public static final byte [] GNU_TLS_MAGIC_BYTES = ArrayConverter .hexStringToByteArray ("FAE1C0EA" );
65
+
66
+ /**
67
+ * Offset of the IV according to the ticket struct in rfc5077
68
+ */
69
+ public static final int IV_OFFSET = 16 ;
70
+
71
+ /**
72
+ * Length of the IV according to the ticket struct in rfc5077
73
+ */
74
+ public static final int IV_LEN = 16 ;
75
+
76
+ /**
77
+ * Offset of the length field for the in the encrypted state according to
78
+ * the ticket struct in rfc5077
79
+ */
80
+ public static final int SESSION_STATE_LENFIELD_OFFSET = 32 ;
81
+
82
+ /**
83
+ * Length of the length field for the in the encrypted state according to
84
+ * the ticket struct in rfc5077
85
+ */
86
+ public static final int SESSION_STATE_LENFIELD_LEN = 2 ;
87
+
88
+ /**
89
+ * Offset of the encrypted state according to the ticket struct in rfc5077
90
+ */
91
+ public static final int SESSION_STATE_OFFSET = 34 ;
92
+
93
+ private List <CipherSuite > supportedSuites ;
94
+
95
+ public SessionTicketZeroKeyProbe (ScannerConfig scannerConfig , ParallelExecutor parallelExecutor ) {
96
+ super (parallelExecutor , ProbeType .SESSION_TICKET_ZERO_KEY , scannerConfig , 0 );
97
+ }
98
+
99
+ public SessionTicketZeroKeyProbe (ParallelExecutor parallelExecutor , ProbeType type , ScannerConfig scannerConfig ,
100
+ int danger ) {
101
+ super (parallelExecutor , type , scannerConfig , danger );
102
+ }
103
+
104
+ @ Override
105
+ public ProbeResult executeTest () {
106
+ State state ;
107
+ try {
108
+ Config tlsConfig = getScannerConfig ().createConfig ();
109
+ tlsConfig .setQuickReceive (true );
110
+ List <CipherSuite > ciphersuites = new LinkedList <>();
111
+ ciphersuites .addAll (supportedSuites );
112
+ tlsConfig .setDefaultClientNamedGroups (NamedGroup .getImplemented ());
113
+ tlsConfig .setWorkflowTraceType (WorkflowTraceType .HANDSHAKE );
114
+ tlsConfig .setHighestProtocolVersion (ProtocolVersion .TLS12 );
115
+ tlsConfig .setDefaultClientSupportedCiphersuites (ciphersuites .get (0 ));
116
+ tlsConfig .setDefaultSelectedCipherSuite (tlsConfig .getDefaultClientSupportedCiphersuites ().get (0 ));
117
+ tlsConfig .setAddECPointFormatExtension (true );
118
+ tlsConfig .setAddEllipticCurveExtension (true );
119
+ tlsConfig .setAddSessionTicketTLSExtension (true );
120
+ tlsConfig .setAddServerNameIndicationExtension (true );
121
+ tlsConfig .setAddRenegotiationInfoExtension (false );
122
+ state = new State (tlsConfig );
123
+ executeState (state );
124
+ } catch (Exception E ) {
125
+ LOGGER .error ("Could not scan for " + getProbeName (), E );
126
+ return new SessionTicketZeroKeyResult (TestResult .ERROR_DURING_TEST , TestResult .ERROR_DURING_TEST );
127
+ }
128
+
129
+ if (!WorkflowTraceUtil .didReceiveMessage (HandshakeMessageType .NEW_SESSION_TICKET , state .getWorkflowTrace ())) {
130
+ return new SessionTicketZeroKeyResult (TestResult .UNSUPPORTED , TestResult .UNSUPPORTED );
131
+ }
132
+
133
+ byte [] ticket = null ;
134
+ for (ProtocolMessage msg : WorkflowTraceUtil .getAllReceivedMessages (state .getWorkflowTrace ())) {
135
+ if (msg instanceof NewSessionTicketMessage ) {
136
+ NewSessionTicketMessage newSessionTicketMessage = (NewSessionTicketMessage ) msg ;
137
+ ticket = newSessionTicketMessage .getTicket ().getIdentity ().getValue ();
138
+ }
139
+ }
140
+
141
+ byte [] key = new byte [32 ];
142
+ byte [] iv , encryptedSessionState ;
143
+ byte [] decryptedSessionState = null ;
144
+
145
+ try {
146
+ iv = Arrays .copyOfRange (ticket , IV_OFFSET , IV_OFFSET + IV_LEN );
147
+ byte [] sessionStateLen = Arrays .copyOfRange (ticket , SESSION_STATE_LENFIELD_OFFSET ,
148
+ SESSION_STATE_LENFIELD_OFFSET + SESSION_STATE_LENFIELD_LEN );
149
+ int sessionStateLenInt = ArrayConverter .bytesToInt (sessionStateLen );
150
+ encryptedSessionState = Arrays .copyOfRange (ticket , SESSION_STATE_OFFSET , SESSION_STATE_OFFSET
151
+ + sessionStateLenInt );
152
+ Cipher cipher = Cipher .getInstance ("AES/CBC/NOPADDING" );
153
+ SecretKey aesKey = new SecretKeySpec (key , "AES" );
154
+ cipher .init (Cipher .DECRYPT_MODE , aesKey , new IvParameterSpec (iv ));
155
+ decryptedSessionState = cipher .doFinal (encryptedSessionState );
156
+ LOGGER .debug ("decryptedSsessionState" + ArrayConverter .bytesToHexString (decryptedSessionState ));
157
+ } catch (Exception e ) {
158
+ return new SessionTicketZeroKeyResult (TestResult .FALSE , TestResult .FALSE );
159
+ }
160
+ TestResult hasDecryptableMasterSecret ;
161
+ TestResult hasGnuTlsMagicBytes ;
162
+
163
+ if (checkForMasterSecret (decryptedSessionState , state .getTlsContext ())) {
164
+ hasDecryptableMasterSecret = TestResult .TRUE ;
165
+ } else {
166
+ hasDecryptableMasterSecret = TestResult .FALSE ;
167
+ }
168
+
169
+ if (checkForGnuTlsMagicBytes (decryptedSessionState )) {
170
+ hasGnuTlsMagicBytes = TestResult .TRUE ;
171
+
172
+ } else {
173
+ hasGnuTlsMagicBytes = TestResult .FALSE ;
174
+ }
175
+
176
+ return new SessionTicketZeroKeyResult (hasDecryptableMasterSecret , hasGnuTlsMagicBytes );
177
+ }
178
+
179
+ @ Override
180
+ public boolean canBeExecuted (SiteReport report ) {
181
+ return report .getCipherSuites () != null && (report .getCipherSuites ().size () > 0 );
182
+ }
183
+
184
+ private boolean checkForMasterSecret (byte [] decState , TlsContext context ) {
185
+ List <Byte > target = Arrays .asList (ArrayUtils .toObject (context .getMasterSecret ()));
186
+ List <Byte > source = Arrays .asList (ArrayUtils .toObject (decState ));
187
+ if (Collections .indexOfSubList (source , target ) == -1 ) {
188
+ return false ;
189
+ }
190
+ return true ;
191
+ }
192
+
193
+ private boolean checkForGnuTlsMagicBytes (byte [] decState ) {
194
+ try {
195
+ for (int i = 0 ; i < GNU_TLS_MAGIC_BYTES .length ; i ++)
196
+ if (decState [i ] != GNU_TLS_MAGIC_BYTES [i ])
197
+ return false ;
198
+ } catch (Exception e ) {
199
+ return false ;
200
+ }
201
+ return true ;
202
+ }
203
+
204
+ @ Override
205
+ public ProbeResult getCouldNotExecuteResult () {
206
+ return new SessionTicketZeroKeyResult (TestResult .COULD_NOT_TEST , TestResult .COULD_NOT_TEST );
207
+ }
208
+
209
+ @ Override
210
+ public void adjustConfig (SiteReport report ) {
211
+ supportedSuites = new ArrayList <>(report .getCipherSuites ());
212
+ }
213
+
214
+ }
0 commit comments