Since we create the packets ourselves, it is trivial to fill in the sender/source fields as we like. We can spoof a DNS host name, an IP address, a MAC address, etc.
I found that SNMP walking my cable modem only worked if I claimed to be someone else.
Given a cable modem, watch the traffic, say with ethereal
.
I see three MAC addresses: my own (M1), that of the cable modem (M2)
and that of the server (M3). Almost all traffic involves M1 and/or M3,
but once every few hours there is an ARP request of the cable modem
for the address 10.43.8.1. Interesting.
An nmap
of the cable modem shows:
# nmap 192.168.100.1 (The 1643 ports scanned but not shown below are in state: closed) Port State Service 80/tcp open http # nmap -sU 192.168.100.1 (The 1469 ports scanned but not shown below are in state: closed) Port State Service 68/udp open dhcpclient 161/udp open snmp
But if I say that I am 10.43.8.1, then the result is
# nmap -S 10.43.8.1 -e ethkabel 192.168.100.1 sendto in send_tcp_raw: sendto(3, packet, 40, 0, 192.168.100.1, 16) => Operation not permitted (The 1642 ports scanned but not shown below are in state: closed) Port State Service 20/tcp filtered ftp-data 80/tcp filtered http # nmap -sU -S 10.43.8.1 -e ethkabel 192.168.100.1 sendto in send_udp_raw: sendto(3, packet, 28, 0, 192.168.100.1, 16) => Operation not permitted (The 1466 ports scanned but not shown below are in state: closed) Port State Service 53/udp open domain 68/udp open dhcpclient 161/udp open snmp 162/udp open snmptrap 514/udp open syslog
Interesting. So showing the right identity can be important.
Continuing this example, if I try to access the SNMP port, I get
% snmpwalk 192.168.100.1 public 1.3 Timeout: No Response from 192.168.100.1If I change my IP sender address to 10.43.8.1, then again the same timeout happens, but
ethereal
shows that there is SNMP traffic:
the modem sends replies to 212.142.xxx.yyy. Aha. So that is also an
interesting identity. Changing my identity to 212.142.xxx.yyy shows
% snmpwalk 192.168.100.1 public 1.3 system.sysUpTime.0 = Timeticks: (24973200) 2 days, 6:39:12.00 % snmpwalk 192.168.100.1 public system.sysDescr.0 = <<HW_REV: 3; VENDOR: Motorola Corporation; BOOTR: 2150; SW_REV: SB5100E-2.3.1.3-SCM01-NOSH; MODEL: SB5100E>> system.sysObjectID.0 = OID: enterprises.1166.1.450.12.2 system.sysUpTime.0 = Timeticks: (19688900) 2 days, 6:41:29.00 system.sysContact.0 = system.sysName.0 = SB5100E system.sysLocation.0 = system.sysServices.0 = 79 system.sysORLastChange.0 = Timeticks: (0) 0:00:00.00 system.sysORTable.sysOREntry.sysORID.1 = OID: enterprises.1166.1.20 system.sysORTable.sysOREntry.sysORDescr.1 = Motorola Cable Modem SNMP Agent Capabilities Statement ...
A complete snmpwalk gives a lot of output, with interesting items like the upload and download speeds, and all IP addresses and ports used.
How does one change identity? In the above, two techniques were used.
One is the ARP spoof:
# while true; do sleep 20; nemesis arp -d ethkabel -S 10.43.8.1 -D 10.43.11.178; done(Ask the cable modem every twenty seconds: "We are 10.43.8.1, and our MAC address is M1, can you tell us the MAC address corresponding to 10.43.11.178?" The cable modem will oblige and answer with M2, and as a side effect will note down that 10.43.8.1 has MAC address M1, that is our address. Now all traffic for 10.43.8.1 goes to us.)
The other is a simple change of routing tables.
# ip addr add dev ethkabel local 212.142.xxx.yyy # ip addr del dev ethkabel local $myIP
When sending an IP packet to some host, the hardware address of the host is needed.
The Address Resolution Protocol (ARP) is used to associate hardware addresses to protocol addresses. It can be used whenever the data link supports broadcast. The most common use is on ethernet. See RFC 826 (and 903, 1293, 1868, ...).
hw type | prot type | hw addr len | IP addr len | |
op | source hw addr | source IP addr | dest hw addr | dest IP addr |
An ARP packet is either a request (op = 1) or a reply (op = 2). A request is broadcast ("Who has dest IP? Please tell me") with source hw addr and source IP addr pointing at the submitter of the request, dest IP addr being the topic of the question, and dest hw addr all zero. A reply ("source IP is at source hw addr") is sent to the requester only, and has as dest hw addr and dest IP addr the addresses of the requester, and as source hw addr and source IP addr the addresses that answer the question.
An ARP packet will be sent in an ethernet frame, but there need not be any relation between the hardware (MAC) addresses given in the ethernet frame header, and those given in the ARP packet.
Machines have an ARP cache with recently used addresses.
Among the attributes that an IP address in the ARP cache can have are:
published: we are willing to reply on behalf of the target
("proxy ARP"); incomplete: we have an IP address but do not
know the corresponding MAC address - an ARP request was sent, but no
reply has arrived yet; permanent (static): the address was
given "by hand" (% arp -s 192.168.1.77 0:1:2:3:4:5
) and is
not changed by netword traffic.
The following action is expected when an ARP request is received: If I am the target (or proxy for the target): reply, and also add the sender info to my cache. (That is reasonable: the request comes because someone wants to connect to us, and soon we'll want to reply and need that info.)
The following action is expected when an ARP reply is received: If there is no outstanding request and no cache entry: possibly ignore the reply. Otherwise: update the cache.
Suppose A wants B to believe that C is at MAC address M. There are two cases.
Case 1 B already has an ARP entry for C. Send an ARP reply to B telling that C is at M.
Case 2 B does not have an ARP entry for C. Maybe B would ignore an unsolicited reply. Let us make sure he has an ARP entry by sending a fake ping (with spoofed sender C).
Various utilities exist to do packet injection. For example,
arpoison
is especially for sending ARP replies.
A more generally useful tool is nemesis
. It will send
all kinds of hand-crafted packets.
For example (on Linux):
# nemesis arp -d eth3 -r -S 192.168.1.77 -h 0:1:2:3:4:5 -D 192.168.1.14 -m 00:76:12:a2:44:70(send an ARP packet, to the
eth3
interface, a reply, saying
"192.168.1.77 is at 0:1:2:3:4:5", sending it to 192.168.1.14 who is at
00:76:12:a2:44:70).
If we were in Case 2, then first
# nemesis icmp -qE -S 192.168.1.77 -D 192.168.1.14(send an ICMP ECHO "ping" packet to 192.168.1.14 with spoofed source 192.168.1.77).
A (with MAC address M) tells B that C is at M, and tells C that B is at M. Now both B and C will send the packets meant for the other via A.
A can read, possibly change, the conversation.
This is better than sniffing: an ssh
connection may be
intercepted this way, and even though ssh
will warn
@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ @ WARNING: HOST IDENTIFICATION HAS CHANGED! @ @@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@@ IT IS POSSIBLE THAT SOMEONE IS DOING SOMETHING NASTY! Someone could be eavesdropping on you right now (man-in-the-middle attack)! It is also possible that the host-key has just been changed. Please contact your system administrator. Are you sure you want to continue connecting (yes/no)?when it finds that B and C suddenly have different public keys, most naive users will just answer "yes" as one always does when some question is asked. This is a practical way to defeat
ssh
. This man-in-the-middle attack is automated by the
sshmitm
utility from the dsniff
package.
A quote:
The easy availability of dsniff/sshmitm drives home the point that MITM is a real threat, not just a theoretical one. With the dsniff tools, it took this author only a few minutes to spoof DNS replies to a victim host, redirect an SSH connection to an intermediate system, and conduct a successful MITM against it. The sshmitm program obligingly printed out my username and password, and then echoed the contents of the trapped SSH session as I typed. Scary stuff. It should serve to raise user and sysadmin awareness of their responsibility to maintain the SSH known-hosts lists, configure client behavior in accordance with a considered security policy, and think carefully before overriding security warnings about changed or missing host-keys.
No satisfactory solutions have been proposed. One can be a little bit cautious. Or one can be slow but rigorously secure.
One approach is to always reject unsolicited ARP replies, and to reject all frames with different "outside" and "inside" MAC addresses. But this also rejects some legitimate uses:
Proxy ARP is used to connect two ethernet segments. The gateway in-between will answer ARP requests for machines on one side by machines on the other side.
Gratuitous ARP is an unsolicited broadcast ARP packet.
It comes in two flavours. The packet sent by arping -U
is the request "Who has my-IP. Please tell me." that has both as
source and as dest IP address the IP address of the sender, and
the sender's MAC as source MAC address, and ff:ff:ff:ff:ff:ff
as dest MAC address. The intention of such a request is to detect
possible duplicate IP addresses on the network.
One also uses this with 0.0.0.0 as source IP address, in order to
avoid that listeners update their ARP cache.
The packet sent by arping -A
is the reply "my-IP is at my-MAC"
with the same ethernet header (with broadcast dest address), and the same
fields as the previous, except (i) that this is a reply, and
(ii) the dest MAC address equals my-MAC.
Do not confuse these two variations on Gratuitous ARP with the Reverse ARP broadcast used at boot time to ask for one's own IP address given the ARP address. (RARP uses op = 3 for the reverse request, and op = 4 for the reverse reply.)
One can use digital signatures to authenticate ARP replies. This S-ARP is secure, but inconvenient, and much slower than the usual ARP.
A TCP/IP connection is started by sending SYN, the other side replying SYN ACK, and then sending ACK. After this handshake a connection exists, and data can be sent. What would prevent someone from faking the sender to be some trustred host?
Each TCP packet has two serial numbers: the seqence number and the acknowledgement number. Each packet with the ACK flag acknowledges receipt of all packets preceding the number given as the ack number. So, the conversation start is e.g.: (i) SYN, Seq 4189934627, Ack 0, (ii) SYN ACK, Seq 1370322447, Ack 4189934628, (iii) ACK, Seq 4189934628, Ack 1370322448.
Example
1 mymachine -> webserver TCP myport > http [SYN] Seq=1861201800 Ack=0 2 webserver -> mymachine TCP http > myport [SYN, ACK] Seq=1736749861 Ack=1861201801 3 mymachine -> webserver TCP myport > http [ACK] Seq=1861201801 Ack=1736749862 4 mymachine -> webserver HTTP GET /foo/index.html HTTP/1.1 (Seq=1861201801 Ack=1736749862 Len=420) 5 webserver -> mymachine TCP http > myport [ACK] Seq=1736749862 Ack=1861202221 6 webserver -> mymachine HTTP HTTP/1.1 200 OK (text/html) (Seq=1736749862 1861202221 Len=1408) 7 mymachine -> webserver TCP myport > http [ACK] Seq=1861202221 Ack=1736751270 8 webserver -> mymachine HTTP HTTP Continuation (Seq=1736751270 Ack=1861202221 Len=1398) 9 mymachine -> webserver TCP myport > http [ACK] Seq=1861202221 Ack=1736752668 10 webserver -> mymachine TCP http > myport [FIN, ACK] Seq=1736752668 Ack=1861202221 11 mymachine -> webserver TCP myport > http [ACK] Seq=1861202221 Ack=1736752669One sees that the sequence number is incremented by 1 for a SYN and FIN, by 0 for an ACK, by
Len
(the data length) for data.
Normally, packets with wrong ack number are ignored and discarded: a low number is probably some old retransmitted packet, and a high number is garbage or perhaps some very old packet from a previous conversation between the same two endpoints.
If the source of the conversation is spoofed, then the SYN ACK of Step (ii) will be sent to the wrong machine (the faked source), and the spoofer does not know what Ack number to use in Step (iii). But this number is the only piece of information preventing a successful impersonation.
(There is a small detail: the faked source will receive an unexpected SYN ACK and reply with RST, resetting the connection. So, for this to work, one must make sure that the faked source is down, or is DoSsed. See, e.g., TCP SYN flood below.)
People have known for a long time that guessing a sequence number sufficed for a remote attack, but the world awoke when the attack was first performed in reality.
On 1994-12-25, Kevin Mitnick broke into the system in the home of Tsutomu Shimomura in order to steal some security related software. Shimomura had left a tcpdump running, so details have been preserved. Mitnick first made some test connections from a different machine to the machine that was going to be spoofed, and found that the initial sequence numbers were 2021824000, 2021952000, 2022080000, 2022208000, ..., 2024256000, each 128000 more than the previous one. So, he knew that the next initial sequence number was going to be 2024384000 so that the Ack had to be 2024384001. That was all.
(A spectacular chase followed. Books have been written and a movie was made. Mitnick was sentenced to 46 months in prison, and 3 years without touching a computer.)
Of course people wondered afterwards how one can avoid such attacks. Basically one can't, but one can pick the initial sequence numbers in a way that is much more difficult to guess. The numbers cannot be chosen at random, since that would leave the possibility of an old and a new conversation between the same two hosts interfering because they used overlapping intervals of sequence numbers. But one can pick the number using a cryptographic hash involving the source and destination IP addresses and ports and a secret on the local system, and adding to that value something that increases over time. Basically, this is what rfc1948 advises.
Linux is even more safe and uses a new secret every five minutes. Here is the code:
In tcp_ipv4.c
:
static inline __u32 tcp_v4_init_sequence(...) { return secure_tcp_sequence_number(destip, srcip, destport, srcport); }
In random.c
:
#define COUNT_BITS 8 #define COUNT_MASK ((1<<8)-1) #define HASH_BITS 24 #define HASH_MASK ((1<<24)-1) static struct keydata { time_t rekey_time; __u32 count; // already shifted to the final position __u32 secret[12]; } ____cacheline_aligned ip_keydata[2]; static spinlock_t ip_lock = SPIN_LOCK_UNLOCKED; static unsigned int ip_cnt; static struct keydata *__check_and_rekey(time_t time) { struct keydata *keyptr; spin_lock_bh(&ip_lock); keyptr = &ip_keydata[ip_cnt & 1]; if (!keyptr->rekey_time || (time - keyptr->rekey_time) > REKEY_INTERVAL) { keyptr = &ip_keydata[1^(ip_cnt & 1)]; keyptr->rekey_time = time; get_random_bytes(keyptr->secret, sizeof(keyptr->secret)); keyptr->count = (ip_cnt & COUNT_MASK) << HASH_BITS; mb(); ip_cnt++; } spin_unlock_bh(&ip_lock); return keyptr; } static inline struct keydata *check_and_rekey(time_t time) { struct keydata *keyptr = &ip_keydata[ip_cnt & 1]; rmb(); if (!keyptr->rekey_time || (time - keyptr->rekey_time) > REKEY_INTERVAL) keyptr = __check_and_rekey(time); return keyptr; } __u32 secure_tcp_sequence_number(__u32 saddr, __u32 daddr, __u16 sport, __u16 dport) { struct timeval tv; __u32 seq; __u32 hash[4]; struct keydata *keyptr; do_gettimeofday(&tv); keyptr = check_and_rekey(tv.tv_sec); hash[0] = saddr; hash[1] = daddr; hash[2] = (sport << 16) + dport; hash[3] = keyptr->secret[11]; seq = halfMD4Transform(hash, keyptr->secret) & HASH_MASK; seq += keyptr->count; seq += tv.tv_usec + tv.tv_sec*1000000; return seq; }
Comments The pair of structs ip_keydata
holds the current and
the next keydata, so that one CPU gets consistent values while another
CPU is making a change. The pair is protected by the spinlock ip_lock
,
preventing two CPUs making a change at the same time. The parity of ip_cnt
determines the current struct. Once in a REKEY_INTERVAL (300 seconds) the routine
get_random_bytes()
is called. It gets some bytes from the entropy pool
that is fed with timings of keystrokes and mouse moves and network interrupts.
The secret value and source and destination IP address and port are hashed
by a partial MD4 to give a 24-bit random value. In the leading 8 bits
an 8-bit counter (incremented every REKEY_INTERVAL) is stored.
Finally, the time (in microseconds) is added.
So, every 5 minutes the leading 8 bits of this value are increased by about 18,
and values may start repeating after slightly over an hour.
Guessing, or cryptanalysis, seems unfeasible.
Some other operating systems use weaker mechanisms. Just calling a random generator is not good enough. The design criterion of a random generator is that it must produce arbitrary values, evenly distributed over all possibilities, and a linear congruential generator is usually good enough. Here one needs more: a generator that withstands cryptanalysis.
Even when sequence numbers cannot be guessed, it may be possible to determine them. In Phrack 64#14 a possible approach is sketched.
If machines B and C are having a TCP/IP conversation, can A take over the session with C, or just inject some stuff? As we saw above this is easy to do provided A can get at the sequence numbers. This is trivial when the packets pass A's local ethernet. Sometimes ARP spoofing or DNS spoofing makes a man-in-the-middle attack possible. Maybe etherleak can provide information on sequence numbers.
Various utilities exist to automate this process.
The most popular ones seem to be
hunt
(with description in
hunt.txt
),
and
ettercap
.
For example, play man-in-the-middle between hosts 192.168.1.8 and
192.168.1.14:
# ettercap -a -i eth3 192.168.1.8 192.168.1.14Press
[h]
for help.
Commands or text can be injected with [i] pwd\r\n
or so.
(That is, the Enter key finishes the text to enter, and carriage return
and linefeed are represented by \r
and \n
.)
There are lots of features, see for example this
article.
DNS is used to convert a domain name into an IP address or back ("reverse DNS"). A client sends a query to a server, and the server generally does one of the following: (i) return the answer, in case the server is authoritative for the domain or has the information cached from an earlier query, (ii) go out on the net and ask other servers ("recursive lookup"), (iii) do not go out but just tell the client where he should go himself ("referral").
Typically the name server of an ISP will do all the work for its clients,
while otherwise servers just refer. A query for foo.bar.com.
would go out to a root server, and that root server will return
a list of addresses of com.
name servers.
A query to a com.
server then yields addresses for
bar.com.
name servers. Etc.
A reply may contain more information than what was asked.
For example, if the reply is a referral to ns.bar.com.
it will generally also contain as "glue" the IP address of that host.
The additional data will be accepted as trustworthy when it is
"in bailiwick".
bai.li.wick ('b{a-}-li-.wik) Etymology: ME i[baillifwik], fr. i[baillif] + i[wik] dwelling place, village, fr. OE i[w{i-}c]; akin to OHG i[w{i-}ch] dwelling place, town 1) n, the office or jurisdiction of a bailiff 2) n, one's special domain
This means the following: if the com.
name server
when asked for foo.bar.com.
refers to a bar.com.
name server, then that latter server is regarded as authoritative
for that domain. Before the "in bailiwick" requirement was added,
it was trivial to poison the DNS cache: force the resolver to
query a server under your control and add all the records you want.
Poisoning is still possible. After the resolver has sent out its query, an imposter can send a fake reply, and hope it arrives before the proper reply. The reply of the server is matched to the query by the client using a 16-bit transaction ID (TID) and is discarded if there is no match. If an imposter wants to inject his forged data, he has to guess this TID correctly, and of course the reply must come in on the correct (UDP or TCP) port, and he will not get a chance at all if the information was already present in cache.
Old resolver libraries just increment TID by one for each query, and guessing is easy. Good resolvers use a random value. Old resolver libraries use a fixed port for the queries. Good resolvers use a random port number. (The servers of course use the well-known port 53 for DNS.)
All of this was well-known, but in 2008 Dan Kaminsky came with an
attack that was much more effective than earlier attacks.
If the resolver is asked to resolve 16382644.foo.bar.com.
,
where the number is chosen at random, then the answer will not be in cache,
so that we pass the first hurdle. Keep generating such queries, each
followed by a few hundred fake replies. If the port is known then
we may hit the right TID within 10 seconds. The fake reply will be a referral,
naming a host under the attackers control as name server for the
bar.com.
domain and now this domain has been hijacked.
This process may be started for example via a URL on some web page
that refers to the attacker's machine.
If the port is also random then this attack takes more effort -
success after ten hours was reported for a fast network.
Exploit code is found in the
Metasploit framework.
The current conclusion is that DNS is not safe.
The Network File System NFS (say, v2) is a security disaster. (NFS v4 can be configured to be more secure.)
NFS keeps user state and credentials at the server. With each request the user sends a cookie, the filehandle. Snooping the filehandle suffices to get access.
Even worse, authentication is done on the client. Injecting suitable packets will convince the server about one's credentials.
Usually NFS runs over UDP, and UDP is trivial to spoof. No sequence numbers to worry about.
Most people are lazy, install the operating system in the default way, or get their machine preinstalled that way, and never upgrade or patch. As soon as some exploit is discovered, one can break in.
On the other hand, there are the very active people, who scan for viruses, net attacks etc. The funny part is that it is often possible to use attacks against these active people that the lazy people are not vulnerable to.
There have been found buffer overflows in virus scanners (both for
incoming and outgoing email), and spam scanners like SpamAssassin
,
ethernet watchers like tcpdump
, ethereal
and
Snort
, etc. Thus, local or remote root is sometimes possible
by exploiting such vulnerabilities.
A TCP connection is setup by the handshake (i) SYN, (ii) SYN-ACK, (iii) ACK. If the attacker sends a SYN, but never sends the ACK, then the receiver has a half-open connection and has to wait for a timeout before the corresponding data can be freed. A flood of such requests, with varying (spoofed) source addresses, is an effective Denial of Service attack. In rfc2267 it is suggested that ISPs should filter spoofed source addresses to combat this and similar attacks. See also CA-96.21.
See above under ping.