MitM Using Golang, Meet Timmy
This post is an introduction to Timmy (Tiny evil man in the middle). There are a lot of MiTM tools used to assess software that communicates via TCP/IP. They all have a few basic ingredients, but often differ stylistically or in their intended use cases. Burp can do invisible HTTP/HTTPS proxying and excels at HTTP and Websockets. Mallory is good at transparent plain TCP+SSL. mitmproxy
is command line HTTP and offers a great user experience once you learn it. These tools each serve a valuable niche or scratched an itch for their author (or both).
Why MiTM tools? These tools allow their operator to peek inside of a (potentially, and hopefully) SSL protected TCP stream. They may then provide specialized decoding of one or more protocols. Many of these tools that provide transparent proxying expect you to be fluent with IP tables to bounce traffic around on a MiTM gateway.
So, why implement yet another MiTM tool? Mostly, to scratch an itch and play around with Golang, but to make it fun I have set out a few design goals:
- Fast
- Minimal size
- Clean code base
- Easy to modify bytes in flight using a script pipeline
- Transparent proxying support
- SSL/TLS
What I learned writing Mallory was that, while a GUI is nice, it takes a lot of effort and sucks away valuable time from other more important features. With Timmy, the goal is to provide an easy and streamlined way to view TCP streams, and to inject one or more scripts into a pipeline that modifies these streams. The pipeline should work just like Unix pipes, allowing any number of data modifying scripts to be chained together simply. If these goals are all met, Timmy will support nearly any MiTM workflow for times when the more specialized tools don’t grok your protocol.
Zooming in, each MiTM has a few basic ingredients. First it must have a TCP daemon that accepts victim connections. For hard coded redirects, it must listen on each port to be redirected. For transparent MiTM it can listen on one single port port and then figure out where the connection is destined using iptables
. A victim connection comes in and the server then makes a connection to a specific IP:port
in the case of a statically assigned destination port or it looks up the destination IP:port
using something like iptables
. Next the server sets up two data pumps: one reads input from the victim and sends it to the proper destination and the other reads data from the proper destination and sends it back to the victim. Many MiTM tools provide a DNS server to capture the hard-wired redirects. Timmy will not have a DNS server, instead, relying on dnsmasq
or whatever DNS server you prefer.
Let’s check out how Timmy sets up its data pumps, as this involves some very specific Go constructs:
func connMitmer(c chan net.Conn, conf Config) {
for {
conn := <-c
m := Mitmer{
InConn: conn.(*net.TCPConn),
Conf: conf,
}
go m.MitmConn()
}
}
This is the heart of Timmy. One or more listening sockets are configured to accept connections. They pass each connection to the connMitmer
function via a special Go feature known as a channel. In Go, channels are used for multiple concurrent functions to communicate; the paradigm is to communicate instead of sharing memory. connMitmer
takes connections from the accepted connections channel. The <-
operator is a blocking operator that reads from the channel c
. Channel c
receives TCP connections. With the victim connection in hand Timmy then builds a struct
to hold everything it needs to MiTM this connection. It then spawns up a Goroutine using go m.MitmConn()
. This routine is where the data pump lives. Every MiTM tool that works with TCP has to maintain victim connections and shuttle data between victim sockets and real destination sockets. Goroutines, i.e. go someFunc()
, are cheap, so Timmy uses one for each victim socket/real destination pair. Tune back in to next time for the details of the data pump.
This concludes the first post in a series of blog posts that will be made as Timmy is developed. Future articles will cover the following topics:
- The data pump
- How to MiTM SSL
- How Timmy configures itself
- The data modification pipeline
Thanks for reading!