We have a HTTPS server and client talking to each other with you right in the middle! The client essentially executes
curl –cacert server.crt https://nsa.gov
with some magic to redirect the transmitted data to your socket, to which the server responds with a lovely German-language poem.
NOTE: There is nothing else hosted on the server; no need to brute-force filenames. Moreover, it may behave untypically due to hackiness.
Your task is to make the client receive a CTF-themed adaption of another German poem instead; to be precise, the HTTP response must consist of the following bytes:
5761 6c6c 6521 2057 616c 6c65 0a4d 616e |Walle! Walle.Man| 6368 6520 5374 7265 636b 652c 0a44 6173 |che Strecke,.Das| 7320 7a75 6d20 5a77 6563 6b65 0a46 6c61 |s zum Zwecke.Fla| 6767 656e 2066 6c69 65c3 9f65 6e2c 0a55 |ggen flie..en,.U| 6e64 206d 6974 2072 6569 6368 656d 2c20 |nd mit reichem, | 766f 6c6c 656d 2053 6368 7761 6c6c 650a |vollem Schwalle.| 5a75 2064 656e 2050 756e 6b74 656e 2073 |Zu den Punkten s| 6963 6820 6572 6769 65c3 9f65 6e2e 0a |ich ergie..en..|Upon receiving this response from the server, the client sends the flag to you through the same connection used to intercept the HTTPS traffic, so make sure not to overlook it!
Server: https://130.211.200.153:4433
Client: nc 130.211.200.153 9955(If you just forward everything from one of those ports to the other, the connection succeeds and everything works fine. Then hack.)
NOTE: The setup for this challenge is not entirely trivial, so if you’re confused about unexpected things happening, please contact yyyyyyy on IRC. There is also a good chance something’s broken.
EPIC HINT published six hours before the end: The server’s ciphersuites have been carefully chosen to allow this attack. (Plus the server was patched a little bit.)
Summary: attacking nonce-repeating TLS server using AES-GCM cipher.
Here we have to implement a man-in-the-middle attack against custom TLS server. The ciphersuite used is ECDHE-ECDSA-AES128-GCM-SHA256 which is normally secure and all other ciphersuites are disabled. After the hint was given we concentrated on this ciphersuite and looked for possible attacks. AES-GCM is known to be very weak if the tag length is small. So googling for “tls aes gcm tag length” yielded the recent paper Nonce-Disrespecting Adversaries: Practical Forgery Attacks on GCM in TLS. It also matches the task name “ndis”.
So the idea of the attack is that when nonces are repeated (due to weak random, for example), then it is possible to recover authentication key from the GCM and make forgeries.
There is a proof-of-concept tool by the paper authors: https://github.com/nonce-disrespect/nonce-disrespect. In order to compile it, we need the latest NTL library.
The main tool there is gcmproxy. This proxy captures TLS packets and waits until nonces are repeated. Then the key is reconstructed and we can modify packets. Note that we can’t decrypt packets, only modify by xor! So one of the problems is to figure out which fragments contain which data to know what to xor and where. The hard part is that each time we debug a forgery, we have to wait until nonces collide on the server. Nonces were 1-byte values and they must to collide in the first ~5 packets.
After debugging it a lot, we arrived at the following modification to the forgery function:
func forgeRecord(rec1 *TLSRecord, key *GCMAuthKey) (rec2 *TLSRecord) { fragment := make([]byte, rec1.Header.FragmentLength) copy(fragment, rec1.Fragment) fmt.Println(rec1) # modify content-length: 256 -> 127 if len(fragment) == 45 { payload, err := hex.DecodeString("030701") if err != nil {panic(err)} xor(fragment[16+8:], payload) } # modify poem if len(fragment) == 280 { payload, err := hex.DecodeString("3815180316014d38111f665837705c535e55581d6e7e780a171f0a5f2a290e0300000e07025420036f0c1f11657c4c070815114e4d091c1a45a5f0171a2665211a0b534d041b500145010c1816190c46191d18660a19543c5948040e1f036f003501540b45064f3c014e001b0e1d2a1c1d1707000d1d0b1d45acfd161a246574746f20686f726368740a6f74746f3a206d6f7073206d6f70730a6f74746f20686f6666740a0a6f74746f73206d6f7073206b6c6f7066740a6f74746f3a206b6f6d6d206d6f7073206b6f6d6d0a6f74746f73206d6f7073206b6f6d6d740a6f74746f73206d6f7073206b6f747a740a6f74746f3a206f676f74746f676f74740a") if err != nil {panic(err)} xor(fragment[8:], payload) } rec2 = &TLSRecord{rec1.Header, rec1.SeqNo, fragment} ... |
Then we ran it as follows (a few times):
# run proxy $ ./gcmproxy -l=127.0.0.1:5001 -r=130.211.200.153:4433 -w=127.0.0.1:5002 # connect client to proxy $ socat -v tcp:130.211.200.153:9955 tcp:127.0.0.1:5001 2>&1 ... ..............g..4Dp...:&f...[.> 2016/10/02 15:49:28.750616 length=28 from=529 to=556 hxp{NTw1C3:_n0t_ev3n_0nce.} |
The flag: hxp{NTw1C3:_n0t_ev3n_0nce.}
PS: the challenge is quite obviously relying on using the PoC tool, because coding the full attack from scratch would take way more time (and is a bit boring).