xmitm: xml man in the middle

This post is a result of ideas and tools developed during the review of client-side applications that use the XMPP protocol to communicate with a server (opening a raw socket, not using HTTP as a transport).

The only way we could think of getting our hands on the communication was to write a small set of scripts to trick the client and encapsulate the communication inside HTTP requests that we could then manipulate using standard proxy tools such as burp.

Although the information and scripts described in this post are focussed on intercepting a XML communication, the same principles apply to man in the middle any ASCII protocol such as smtp, ftp or pop.

update: slides available here

The first step is to trick the client to connect to our local box instead of connecting to the remote server, this is done by adjusting the hosts file.

Diagram showing the XML dialog between client and server

A ruby script will sit in the middle of the communication and will be able to intercept and modify messages sent and received by the client:-

Diagram that shows how the ruby script stands between client and server for intercepting the XML dialog

Once this is done, our attack will need three elements:

  • the xmitm script.
  • an external web proxy tool.
  • a dummy web server.

The script will intercept the connection and send the data to the proxy. We need the dummy server (the body of the response will be the body of the request) to close the loop with the proxy (I will add some nice graphs to clarify this soon).

The original XML message is encapsulated in an HTTP request and passed through the proxy. The user can inspect and modify the message using a standard web proxy tool. The request is then forwared to a dummy *echo* web server that replies with the same payload that was requested. The script can extract the modified payload and forward it to the server.

The same process is applied to incoming messages.

Below is the main body of the script (you can also grab the code):-

# create a server that accepts connections from the client
server = TCPServer.new($local_host, $local_port)

while(local = server.accept ) do
  # everytime we accept a connection for the client, we open a connection
  # with the server to stablish the dialog.
  remote = TCPSocket.new($remote_host, $remote_port)

  # if one of the ends of the communication closes the socket, we
  # toggle this flag
  alive = true

  while alive do
    # see the explanation below
    result = select([local, remote], nil, nil)

    if result != nil then
      for socket in result[0]

        # detect if one end of the connection is closed and
        # close the other end
        if (socket.eof?)
          local.close
          remote.close
          alive = false
          break
        end

        # read the information that one peer wants to send to the other
        data = socket.gets($eom)

        # encapsulate the data into an HTTP proxy request
        res = Net::HTTP.new($proxy_host, $proxy_port).start do |http|
          req = Net::HTTP::Post.new("http://#{$dummyhttp_host}:#{$dummyhttp_port}/")
          req.body= data
          http.request(req)
        end

        modified_data = res.body.chomp

        # send the modified data to the other end of the connection
        if (socket == local)
          remote.puts(modified_data)
        else
          local.puts(modified_data)
        end
        socket.flush
      end
    end
  end
end

What the script does can be summarized in the following steps:

  1. Create a TCP server, listening on the port the client is expecting.
  2. For each connection accepted:
    • Open a connection with the remote server.
    • Wait until one end of the communication (first the client, then the server, then the client, etc.) has something to transmit.
    • Grab the XML message.
    • Put that message as a payload of a new Net::HTTP::Post request.
    • Send the request to the external web proxy.
    • Grab the body of the response given by the proxy (already modified by the user using the external proxy).
    • Send the modified request to the other end of the line.

The most interesting piece of the code is the one regarding Kernel#select function that waits for data to become available from input/output devices.

A note regarding the specifics of the protocol we were dealing with, each peer ends its messages using a special character (a NULL byte), that caracter is defined in the $eom variable and the script keeps reading the socket until that end of message character is read.

The last piece of the puzzle is the dummy HTTP server. I coded two flavours: a ruby version and a java version (not yet available for download based on the SingleFileHTTPServer example). You can pick your choice. Here is the ruby one:-

require 'webrick'

include WEBrick

# create the server, no output, disable logging
s = HTTPServer.new(
  :Port => 2000,
  :Logger => Log.new(nil, BasicLog::FATAL),
  :AccessLog => []  )

# the *echo* functionality
s.mount_proc("/") do |req, res|
  res.body = req.body
  res['Content-Type'] = req['Content-Type']
end

# clean tear down
trap('INT') { s.shutdown }

s.start

And this completes the XML protocol man-in-the-middle DIY kit. Hope you find it useful. ;)

Share and Enjoy: These icons link to social bookmarking sites where readers can share and discover new web pages.
  • Digg
  • del.icio.us
  • Slashdot
  • Technorati

2 Responses to “xmitm: xml man in the middle”

  1. Patriarca Says:

    Great tool! I’m looking forward to try it in a test! :)
    Keep posting cool staff!

  2. Wagner Elias - Think Security First | BCP, BIA, DRP, Security Assessment, Risk Assessment, Security Developer » links for 2007-12-21 Says:

    [...] etd’s linux Dos and Dont’s » Blog Archive » xmitm: xml man in the middle Ataque de MITM em XML (tags: xml ataques pentest) [...]