Disassembly showed the binary simply making a connection on port 1234
and printing whatever it received. When trying to actually run the
binary, it didn't appear to work. It acted like the connection had been
refused.
On more careful examination of network traffic, we discovered that the
TCP connection was being reset from our side. Wireshark
had colorful things to say about the packet from the remote server:
# tshark -Vn port 1234
Frame 1 (54 bytes on wire, 54 bytes captured)
...
Internet Protocol, Src: 1.2.3.4 (1.2.3.4), Dst: 209.195.0.124 (209.195.0.124)
...
Transmission Control Protocol, Src Port: 31337 (31337), Dst Port: 1234 (1234), Seq: 0, Len: 0
...
Flags: 0x02 (SYN)
...
Frame 2 (60 bytes on wire, 60 bytes captured)
...
Internet Protocol, Src: 209.195.0.124 (209.195.0.124), Dst: 1.2.3.4 (1.2.3.4)
...
Transmission Control Protocol, Src Port: 1234 (1234), Dst Port: 31337 (31337), Seq: 558752047, Ack: 16777216, Len: 0
...
Acknowledgment number: Broken TCP.
...
Flags: 0x12 (SYN ACK)
...
Frame 3 (54 bytes on wire, 54 bytes captured)
...
Internet Protocol, Src: 1.2.3.4 (1.2.3.4), Dst: 209.195.0.124 (209.195.0.124)
...
Transmission Control Protocol, Src Port: 31337 (31337), Dst Port: 1234 (1234), Seq: 16777216, Ack: 558752048, Len: 0
...
Flags: 0x04 (RST)
...
In a normal TCP session, you send SYN, get back SYN ACK, and send ACK. After
that, data starts flowing. In this case, the SYN went out, the SYN ACK came
back with a broken sequence number, and our side drops the connection with
RST. Each side of the TCP connection is supposed to acknowledge the other
side's sequence number, plus 1. If we send SYN(seq:5), we expect to get SYN-ACK(ack:6,seq:1000), and then we send ACK(ack:1001,seq:6). In the captured traffic
the acknowledged sequence number was totally wrong.
After playing with various initial sequence numbers, a pattern started to
be discovered. If we sent a seq of 0, we'd get back an ack of 16777216
(where we expected 1). A seq of 1, ack of 33554432 (where we expected 2).
Looking at the numbers in hex, it was immediately obvious that the acks
were being endian flopped: 0x00000001 into 0x01000000 == 16777216, etc.
The solution was to first stop our RST packet:
# iptables -I OUTPUT -p tcp --tcp-flags RST RST -j DROP
Then to generate traffic with carefully controlled sequence and
acknowledgement values, matching the endianness breakage:
#!/usr/bin/env python
from scapy import *
import struct
def flop(x):
return struct.unpack("<L", struct.pack(">L", x))[0]
p = IP(dst="209.195.0.124")/TCP(dport=1234, flags="S", seq=0, sport=31337)
send(p)
ps = sniff(timeout=5, filter="port 1234")
for r in ps:
if TCP in r and r.sport == 1234:
a = IP(dst="209.195.0.124")/TCP(dport=1234, flags="A", sport=31337, seq=flop(r.ack), ack=flop(r.seq)+1)
send(a)
ans = sniff(timeout=5, filter="port 1234")
for r in ans:
if TCP in r and r.sport == 1234:
print r.load
Finally we could transmit the traffic through the drunk Kenshoto router, fetching
the first data-carrying packet after the TCP handshake:
# ./mangle-seq
Am I blacking out?
After quals, Kenshoto gave us a copy of the FreeBSD kernel module that they used
to
confuse the TCP session. This is handy for
reproducing the challenge.