RSS Feed

Entries in WCF (3)


Abusing WCF to Perform Remote Port Scans

Last weekend at Shmoocon, I demonstrated how an attacker can trick certain WCF web services into performing an unauthorized port scan of machines behind a firewall.  For those that were not able to attend the talk, the slides are posted here. The part that covers the port scanning technique may not be clear in isolation, so I’ll try and explain it in detail. The problem is related to the WSDualHttpBinding, so in order to understand how the scanning technique works you must first understand some WSDualHttpBinding basics. 

The WSDualHttpBinding

The WSDualHttpBinding is one of several “Duplex” WCF bindings.  The term Duplex refers to the bi-directional nature of the communication channel, meaning that both the client and the service can directly send messages to each other.   This is ideal for scenarios where a service needs to “push” data down to a client, rather than the alternative of constantly polling the server for a callback.   In order to do this over HTTP, which is by nature a one-way protocol, WCF sets up a dedicated HTTP listener port on the client that accepts incoming HTTP requests from the service (known as the callback channel).   If you are like me, you probably just raised an eyebrow when I said that WCF sets up an inbound HTTP listener on the client machine.  This scenario sounds odd from a security perspective, which is what initially caught my eye.

The first step in establishing a session with WSDualHttpBinding requires the client and server to negotiate the duplex connection.  This negotiation is a required part of the connection sequence, and is the mechanism that can be abused to perform remote port scanning.  The negotiation starts with the client sending a “CreateSequence” SOAP request to the web service endpoint.  A typical CreateSequence request is shown below.

As you can see, the CreateSequence request includes a “ReplyTo” address.  This address is the URL of the callback channel at which the client expects to receive callback requests from the service.  When the service receives this request, it reacts by initiating a “CreateSequenceResponse” to the ReplyTo address, and then responding to the original request with a “202 Accepted”.  Conceptually this is represented by the diagram below.  Note that the circled numbers represent the order in which each request and response occurs. 

The scenario above represents the intended chain of events for a CreateSequence negotiation.  There are a few important things to note:

  • There are two separate HTTP conversations occurring.  One is between the client and the service over port 80, and the other is between the service and the client on port 8000.
  • When the service receives a CreateSequence request, it will immediately attempt to issue the CreateSequenceResponse request to the address that is passed within the ReplyTo value.  This does NOT have to be the same address (or port) where the CreateSequence request originated from. 

Next, let’s introduce another slightly more complex example.  In this scenario, we have 4 machines:

  • The client, which in this case will end up being the bad guy
  • The WCF service that uses WSDualHttpBinding
  • Two unrelated hosts that will serve as targets

The client in this case will send two CreateSequence requests to the service.  The first request will include a ReplyTo address of Target1, and the second request will include a ReplyTo address of Target2.  Again, the circled numbers represent the order in which each request and response occurs. 

This diagram is much more interesting as it depicts what is certainly NOT an intended use case.  As illustrated above, the first CreateSequence request (1) causes the service to initiate a connection to Target1 on port 8000, just as the second CreateSequence request  (4) does to Target2.   Even more interesting is that the “Accepted” HTTP response (7) to the second CreateSequence request (4) does not occur until AFTER the connection to Target1 times out (5).  This means that the delay between the second CreateSequence (4) and the subsequent “Accepted” response (7) was directly related to the response time of the first CreateSequenceResponse attempt (5). It appears that a WCF service will not respond to a new CreateService request until all previous CreateSequenceResponse requests have either been acknowledged or timed out. 

What Does this Mean?

Based on the behavior described above, the CreateSequence HTTP response delay is an effective mechanism to determine the state of a prior connection request.  By issuing multiple requests to different hosts and ports, we can use this behavior to probe remote hosts from the server hosting the WCF service.  Depending on the connectivity available from the host, we can even probe systems that would not otherwise be available to us (such as on an internal network or DMZ). 

In order to prove this theory, I wrote a utility to issue successive CreateSequence requests to a WCF service that each have a different ReplyTo address and/or port.  It measures the time between a CreateSequence request and the “202 Accepted” response in an attempt to determine whether a previous request was successful.  The utility is fairly simple and operates as follows (assume that Service is the WCF service we want to mis-use, and that the Target is the machine we want to port scan): 

  • Request #1:  Issue a CreateSequence request to Service which will ReplyTo Target on Port 1.  The delay (if any) on this first request is not associated with a connection we initiated so the timing of this first response is ignored. 
  • Request #2: Issue another CreateSequence request to Service which will ReplyTo Target on Port2.  A timer is used to measure the time between this request the “202 Accepted” response from Service.   This response will not occur until the previous CreateSequenceResponse has been acknowledged or timed out.  As such, this delay will be used to infer the outcome of the probe caused by Request #1.
  • Request #3:  Issue a CreateSequence request to Service which will ReplyTo Target on Port3.  A timer is used to measure the time between this request the “202 Accepted” response from Service.   This response will not occur until the previous CreateSequenceResponse has been acknowledged or timed out.  As such, this delay will be used to infer the outcome of the probe caused by Request #2.
  • and on and on and on…

Proof of Concept

As a proof of concept, I deployed an instance of the MSDN CalculatorDuplex sample service to a virtual machine in the Microsoft Azure cloud to use as a test case.  This service is a simple calculator web service that uses the WCF WSDualHttpBinding. As it turns out, the Azure environment was a great place to test this concept since Azure VMs actually reside on an internal private 10.x.x.x network behind a firewall.  Conceptually, this is represented in the diagram below (note, this is an over simplified diagram based on what I have seen in my limited testing with Azure).

Based on an analysis of the VM running the sample service, it also appeared that the VMs within the Azure environment typically run IIS on port 20000.  I used the utility to remotely scan other VMs within the 10.x.x.x address space on this port through requests to the Calculator service.  The results from the initial test are shown in the screenshot below.

As you can see, the result of each probe is inferred based on the average response time of the other requests.  The scan above shows that four of the probes returned very quickly (around 114 ms) while the others appear to have timed out.  The probes that do not time out in this case are the other internal VMs that are up and running IIS on port 20000.  As a second test, I used the utility to probe ports on the localhost of the machine running the Calculator service.  As you can see below, the probe to port 3389 times out while the others return after about 1 second.  So in this case, the Remote Desktop service is running on the localhost.

So to summarize, this appears to be a potential design flaw within the WCF create sequence negotiation process.  As a result, any service that uses this binding can be abused by a remote user to scan other hosts (even those behind a firewall that they may not otherwise have access to).  Certain web-based attacks can also be proxied through these services since the remote attacker has the ability to control not only the target address and port, but also the complete URI that will be requested. The source code for the scanner utility is posted here for reference.


WCF Binary Soap Plug-In for Burp

Update 2010:  With the official release of Burp Suite v1.3, both plug-ins discussed in this post can be used with either Pro or Free versions of Burp.

If you run into a Silverlight application that consumes WCF, there’s a good chance it will use Binary XML Message Encoding to send data between the Silverlight client and the WCF endpoint. These messages usually include a Content-Type: application/soap+msbin1 header to indicate that they are using Microsoft’s .NET Binary Format for SOAP (NBFS). From an attack perspective, the main problem with this encoding format is that you can’t simply edit requests or responses on-the-fly like you would with text-based SOAP messages, since the recipient of the message expects the data to be properly encoded (otherwise it will throw an exception) and, as such, will throw an exception if it’s not.

My initial research into what security tools support NBFS didn’t turn up much. The only option I found were two WCF Binary Inspectors for Fiddler (one here written by Richard Berg, and another here written by Samuel Jack). Both of these inspectors are essentially plug-ins for Fiddler that add support to view NBFS encoded data. Originally these both looked like the solution I was after, however upon further analysis I realized that while those plug-ins let you VIEW encoded messages, they don’t let you EDIT them. I decided it would be a worthwhile effort to try and leverage the plug-in architecture of Burp Suite (through use of the BurpExtender interface) to write a NBFS plug-in for Burp.

The Solution (sort of)

Not wanting to re-invent the wheel, I figured I would leverage the work that had already been done with Fiddler by calling into one of the existing Fiddler libraries from Burp. I chose to use Richard Berg’s code since it looks like it can be ported entirely to Java down the road if needed (it doesn’t rely on WCF’s built-in decoder). Luckily for me, his code also had all of the methods needed to both encode and decode message data.

The way the plug-in works is pretty simple…when a request comes in, the processProxyMessage method of BurpExtender is used to check whether the requests should be decoded and, if so, passes the request data to the C# library. The C# library decodes the message and returns the plain-text version back to Burp. As requests exit Burp, the processHttpMessage method of BurpExtender is used to determine whether the request needs to be re-encoded and, if so, calls into the C# library again.

There are a couple of interesting points to note here:

  • The processHttpMessage of BurpExtender is currently only supported in the Professional version of Burp Suite. It is my understanding that this method will be supported in the Free version starting with the next release (v1.3) but for now only licensed users of Burp pro have access to this extender method.
  • Both the processProxyMessage AND processHttpMessage methods of BurpExtender alway fire BEFORE a response can be edited by the user. Unfortunately this precludes the Plug-in from being able to re-encode RESPONSE messages should the user want to edit one.

What this means is that you’ll need to resort to the proxy chaining as a workaround for this if you use the Burp Free Edition (explained in more detail below). Additionally, even if you use Burp Professional Edition, you’ll need to use this workaround if you want to edit RESPONSE data (REQUEST data can be edited on the fly with a single instance of Burp Professional).

Plug-In Versions

There are two version of the Burp plug-in available:

Burp Professional Edition Plug-in: Allows binary requests to be edited on the fly. This version does not support editing of response data. Pro users can use the Free Edition Plug-in with Burp Professional for editing response data.

Burp Universal Plug-in: The Universal Plug-in works with both Free and Professional Editions of Burp and supports editing of binary REQUESTS and RESPONSES. The caveat to using this version of the plug-in is that you’ll need to chain two burp instances together as outlined in the diagram below for the plugin to work properly.

The purpose of chaining two proxies together is as follows:

  • The first instance handles decoding requests, intercepting (and editing) requests, and re-encoding edited responses. Set this instance to intercept REQUESTS only (not responses) and to use the 2nd proxy as the next hop.
  • The second instance handles re-encoding edited requests, decoding responses, and intercepting (and editing) responses. Set this instance to intercept RESPONSES only (not requests).

Each proxy will add or remove a custom header (X-WCF-Proxy: must-encode) to edited requests/responses which they use to notify each other of whether re-encoding of a message is necessary. This custom header is removed when read by the plug-in, so it shouldn’t ever get disclosed to the target system.

Albeit it slightly crude, I didn’t see much in the way of a better work around (I am certainly open to suggestions if anyone has any). It should be noted that this workaround is ONLY necessary if you are using the Free Edition (1.2.x) of Burp Suite OR if you want to want to edit WCF binary response content using Burp Professional Edition. Editing WCF binary request data is supported with a single instance of the Burp Professional Plug-In.

Next Steps

These plug-ins were created as a proof of concept for the talk at OWASP AppSec DC 2009. Looking forward, the C# decoding library should easily port to pure Java since it doesn’t make use of the native WCF decoding classes. This would not only eliminate cross-language calls but would also make the plug-in platform independent (since the implementation would be in pure Java). The drawback to this approach, of course, is that we would be using a home grown decoder for a proprietary Microsoft protocol that could change down the road.

In any case, hopefully the plug-ins will be useful in the short term until more security tools include native support for NBFS messages. You can find both versions of the plug-in available for free on our tools page.


Slides from AppSec DC Posted

Slides from the “Attacking WCF Web Services” talk I presented last week at OWASP AppSec DC 2009 are now available for download.  We’ve also released the WCF Binary Soap Plug-In for Burp that was demonstrated during the presentation.  There will be a separate blog post dedicated to this plug-in published later today, so I definitely recommend reading it to get the full scoop on what it does and how to use it.

Overall, the conference was great.  Aside from the presentations, the highlight for me was when we ran into Jason Alexander (aka George Costanza) at dinner on Thursday night.  These pretzels are making me thirsty!!