Application security

Attacking web services Pt 1 – SOAP

July 15, 2011 by Ken Johnson

I often receive testing related questions from AppSec folks new to web services about the techniques used to discover and attack them. Often, web services are seen as difficult to enumerate, interpret, and exploit as well as an arena with only a small arsenal of tools available.

We’d like to bridge that gap a bit by introducing some techniques and offer code, which can be found here. Let’s jump right into it. In this article we will cover SOAP specifically. Other popular standards exist such as JSON and REST but for the purpose of this tutorial we will cover SOAP.

Simple Object Access Protocol or “SOAP” leverages an XML structure for messages and typically communicates over HTTP protocol. Web service protocols are a lightweight communication mechanism useful for API driven connectivity and often seen in use with mobile applications.

To follow along with certain portions of the tutorial you will need to install JRuby, Buby, Savon, and Nokogiri gems and have downloaded or purchased a copy of PortSwigger’s Burp Suite. The idea is we plan on extending some of Burp’s capabilities to make attacking SOAP easier.

As mentioned previously, SOAP communicates messages in an XML structure. This XML structure can be defined in a WSDL or “Web Services Description Language” document. Enumerating this information provides a wealth of data used in formulating attacks and forming requests.

SOAP actions and the parameters they are very useful bits of information and can be extracted from the WSDL. An action might be something like getEmailAddress which is passed a parameter and value such as “profileid=1000”. Forming that request and sending it may allow you to discover other user’s e-mail addresses simply by changing the profileid value.

Now we could do the enumeration of a WSDL manually by reviewing the response and making sense of the (sometimes) large amounts of data within XML tags but why not create re-usable code to do this quickly and seamlessly?

Install JRuby

$ sudo apt-get install jruby

Install the Savon and Buby gems

$ sudo jruby –S gem install buby
$ sudo jruby –S gem install savon

Now it’s time to write some code. The Savon gem is a Ruby/JRuby library that allows us to build a SOAP client and interact with the web service. Create a file named attack_soap.rb (click here to get the code) and enter the following code:

Line by Line walk-through

Lines 1-2:

This is a directive to our script that basically says, “Use these libraries”.

Line 5:

Next we need to enter code that allows us to hook into Burp Suite and provide a medium to which the tool and our code can meet. We defined a class CustomMenuItem. Line 33 gives a good idea of where this comes into play.

Lines 8-9:

We create a method called enum_wsdl. This method is called on line 27 and passed a URL (rhost object). On line 9 you see that we instantiate the Savon client and pass it the value associated with rhost.

Lines 11- 12:

Both of these are optional values. Line 10 specifies the use of basic authorization which a username and password of “guest”. Line 11 sends traffic to our Burp proxy instance.

Lines 13- 18:

Line 13 ensures that client.wsdl and client.wsdl.soap_actions exist prior to being called. Line 14 gives us a pretty purple –{*}- icon and states “List of available action(s):”. Lines 15-17 iterate through the array of soap_actions, “puts” each action to the console, and on line 17 we end the loop statement.

Lines 19-21:

Rescue clause in case things go belly-up and if so, we will gracefully handle the error on line 19. On line 20 we will print a red –{-}- marker along with the error ($!) to the screen. Line 21 ends the enum_wsdl method.

Lines 23 -24:

Line 23 is where we define a method called menu_item_clicked. This is extremely important because Burp wants to invoke this method, naming convention specific, on the class we passed it on line 33 when the menu item is clicked (makes sense).

All this means is the class CustomMenuItem must have a method named menu_item_clicked. Take care in noticing we pass it *params. This means multiple objects can be passed to this method.

On line 24 we break up the couple of objects that are passed into two separate objects called menu_item_caption and message_info.

Lines 26 – 31:

The object message_info is an array of messages. Each messages has certain values it is associated with. When we iterate through this array, line 26, we can access these values. On line 27 we do just that by taking the itm object (which is really a message) and access the url value associated with it. We call the enum_wsdl method and pass it this url value. Finally, lines 28, 29, and 31 close off the loop, method, anc class.

Line 33:

Last but not least, we need to create the menu item. We do this by calling Burp’s ($burp) registerMenuItem method. We pass the name of the menu item which will be visible to us “enumerate wsdl” and then the class which it can search for the menu_item_clicked method.

The overall workflow will be:

  • Click on enumerate wsdl menu item in Burp.
  • CustomMenuItem.menu_item_clicked is called and passed two objects.
  • From within menu_item_clicked, the enum_wsdl method is called.
  • We parse the WSDL for available actions and print them to the console.

Example of running the script:

$ jruby –S buby –i –B ~/Desktop/burp/burp.jar –r ~/Desktop/attack_soap.rb

The next step would be to request the WSDL and intercept the request in Burp. For example:

Intercepted request to http://192.168.1.149/WebGoat/services/WSDLScanning?WSDL

Clicking on “enumerate wsdl” produces:

Now we have an easy to read list of available actions!

We shouldn’t stop here. We need to take this a bit farther. Let’s add some code here to render available parameters and values. This way we can use these values to form a request.

Now it is important to note that the following code will not parse every WSDL under the sun and “automagically” create the request correctly. This is more of a demonstration on a smaller scale of what is possible with the right code in addition to providing a visual representation of how SOAP requests look.

Now we need to revisit the code we’ve already written and make some additions. The following photo is the first half of the code base.

Line 3:

We will utilize the Nokogiri library. Hence we “require” it. Ensure you’ve performed the following prior to running this script:

$ sudo jruby –S gem install nokogiri

Lines 24-27:

On line 24 we create a parse_wsdl method. The method takes in a wsdl XML. Line 25 instantiates and empty hash called wsdl_element_hash. Line 26 creates a doc object that is a Nokogiri parsed wsdl and can be searched easily. Lastly, line 27 searches the XML for the XML tag <wsdl:message> </wsdl:message> .

Lines 28-29:

test_m1 and test_m2 are instantiated so that they can be used outside of the loop (important in the next few lines of code). The ultimate goal of this method is to return a hash with a SOAP Action as a key and the parameter as the value.

As you can see there is an action Request which has a parameter called “id”. We need to retrieve the getFirstName, getLoginCount, etc. actions and the parameters it takes to form the SOAP request.

Conceptually, the hash we are creating will look like:

{

'getFirstName => 'id',
'getLoginCount => 'id',
'getCreditCard' => 'id',
'getLastName' =>  'id'

}

Lines 30-34:

Portions of the XML/WSDL document that contained the <wsdl:message> tags were previously parsed (on line 27) out into an array type structure. The object was then labeled msg. On line 30, we begin iterating through the msg array. Lines 31 & 32 match the “request” SOAP operations (since we are forming a request) and the parameter/type combinations. Lines 33 & 34 strips out the match data (data which matched our regex on lines 31 & 32) unless there was no match. In this case test_m1 and test_m2 are set to “nil”.

Lines 36-38:

Provides a sanity check, if test_m1 and test_m2 do NOT return nil, meaning we’ve successfully matched the XML content against our regular expressions check, we put the SOAP action and its parameter into the wsdl_element_hash.

Lines 39 – 41:

Shown in the picture below, we are ending the loop statement, returning the wsdl_hash_element hash object and closing the parse_wsdl method.

Lines 43-46:

The method called form_request is instantiated and the rhost value (which is a full URL) is accepted as input. Lines 44-46 stand up some of the client settings we will need to correctly build our SOAP client.

Lines 47-48:

Line 47 creates an object called wsdl, which is the WSDL in pure XML representation form. Line 48 passes that XML object to the parse_wsdl method.

Lines 49-53:

On line 49 we iterate through each available soap action discovered in the WSDL. On line 50 we create a conditional setting. If our hash, created by the parse_wsdl method, has the soap_action (itm) action then we need to create a request. On lines 51 to 56 we do this. Lines 52 and 53 form the necessary input. So getCreditCard becomes :get_credit_card and id becomes :id. This is important because this is the format the Savon gem requires requests to be made in.

Lines 54-56:

We create a request on the fly. Every action is passed into the client request along with every parameter.

Lines 57-61:

Closes the loops and the form_request method.

Lines 67-70:

We make a conditional statement depending on which menu item was clicked. If “enumerate wsdl” was clicked, we got to the enum_wsdl method. If “form SOAP request”, we divert to the form_request method.

Line 78:

We add another custom menu item called “form SOAP request” which once clicked will not only parse the WSDL but automatically create the SOAP request and send it through to our intercepting proxy for further attack scenarios.

So how does it all look when we are done?

We click on form SOAP request

Four SOAP requests were made (since we had four available actions).

Upon review of the “getCreditCardRequest” wsdl:message we see that we’ve successfully obtained some poor fellow’s credit card number.

So from here we can perform active scans on the formed SOAP requests, send to intruder and fuzz or send to repeater to tweak. The sky is the limit but for now we’ve coded up an auto parse-to-SOAP-request tool and hacked a credit card number FTW!

Part 2 is now published for the above demonstration…and more.

Posted: July 15, 2011
Author
Ken Johnson
View Profile

Ken Johnson is a Senior Application Security Consultant performing source code analysis and web application penetration testing. Ken is the primary developer of the Web Exploitation Framework (wXf) and contributes to various open source application security projects. He has spoken at AppSec DC, OWASP NoVA, Northern Virginia Hackers Association.

18 responses to “Attacking web services Pt 1 – SOAP”

  1. david says:

    Can you confirm if this works with the latest Savon gem? (july 25)

  2. Scott says:

    Very nice coding. Another way to do this is to use SoapUI, have it build your SOAP request and setup SoapUI to use a proxy (Burp) and from there run it through Intruder and Burps’s scanner.

  3. Ken Johnson says:

    ‘@David – I will go ahead and review the latest Savon gem. Unfortunately the gem does change quite a bit and I understand there are some significant changes as of late.

    @Scott – Thank you and I agree that SoapUI can be handy. Also, you can always create requests directly within burp by copying/pasting (if you had an operation description page as you see commonly with asmx resources) the SOAP request into repeater by creating a new repeater tab within burp and filling in the relevant data.

    Cheers,

    Ken

  4. Ken Johnson says:

    ‘@David – On line 47 where it says:

    wsdl = client.wsdl.to_xml

    modify this to:

    wsdl = client.wsdl.xml

    I’ve uploaded the script with the VERY slight modification to :

    https://github.com/WebExploitationFramework/Buby-Scripts/blob/master/examples/new_attack_soap.rb

    Thanks

  5. david says:

    Hi Ken,

    Cool good fix, I am still finding soap ui easier, and for MS stuff you can often browse the asmx file to get the syntax for each specific request which helps.

  6. Ken Johnson says:

    ‘@David – Understood (re: soapUI). I’m hoping to get wXf’s SOAP capabilities to that point. We will see. Laundry list of things to do first.

    Regarding the MS stuff……..I love that about asmx :-). I guess it is time to check out the security release of soapUI because, as I mentioned to scott above, I’ve been loading into intruder/repeater and fuzzing that way (copying/pasting the formed request, ie – endpoint.asmx?op=example) and substituting int/string/boolean for my payloads. It might very well be the case that soapUI (security edition) has better capabilities.

    Thank you for your suggestions!

  7. Jared says:

    Hi Ken,

    I have tried both scripts, old and new, and result is as follows:


    c:burpsuite_v1.4.01>jruby -S buby -i -B burpsuite_v1.4.01.jar -r attack_soap.rb

    Loading: "attack_soap.rb"
    JRuby limited openssl loaded. http://jruby.org/openssl
    gem install jruby-openssl for full support.
    Global $burp is set to #<Buby:0x442b95 @burp_callbacks=#<#:0x1b
    b3e9a>, @burp_extender=#>
    Important Note: exit with $burp.close
    buby(main):001:0>
    ←[1m←[35m-{*}-←[0m List of available action(s):
    D, [2012-01-04T12:44:33.507000 #3588] DEBUG -- : HTTPI executes HTTP GET using t
    he net_http adapter
    Exception in thread "Thread-25" org.jruby.exceptions.RaiseException: (Error) exe
    cution expired

    Can you please suggest what is wrong?

  8. InfosecGuy says:

    Kevin loving the plugin, however im getting the following

    /usr/lib/jruby//lib/ruby/1.8/irb/input-method.rb:68:in `initialize’: No such file or directory – File not found – âr (Errno::ENOENT)
    from /usr/lib/jruby//lib/ruby/1.8/irb/input-method.rb:68:in `open’
    from /usr/lib/jruby//lib/ruby/1.8/irb/input-method.rb:68:in `initialize’
    from /usr/lib/jruby//lib/ruby/1.8/irb/context.rb:80:in `new’
    from /usr/lib/jruby//lib/ruby/1.8/irb/context.rb:80:in `initialize’
    from /usr/lib/jruby//lib/ruby/1.8/irb.rb:92:in `new’
    from /usr/lib/jruby//lib/ruby/1.8/irb.rb:92:in `initialize’
    from /usr/lib/jruby//lib/ruby/1.8/irb.rb:57:in `new’
    from /usr/lib/jruby//lib/ruby/1.8/irb.rb:57:in `start’
    from /usr/lib/jruby/lib/ruby/gems/1.8/gems/buby-1.3.1-java/bin/buby:106
    from /usr/lib/jruby/lib/ruby/gems/1.8/gems/buby-1.3.1-java/bin/buby:19:in `load’
    from /usr/lib/jruby/bin/buby:19

    Any help would be great ^_^

  9. InfosecGuy says:

    lol apologies Kevin (Ken 😉 )

  10. Henrik says:

    Hi Ken.

    Might be a basic quetions, but how do you find the url? In this case http://192.168.1.149/WebGoat/services/WSDLScanning?WSDL

    A spider will not find it if it is not linked and if you can’t sniff the traffic or if you don’t already know where to go all of this you describe can’t be used. Or am I wrong?

  11. rick says:

    Can we also use this attack on a non-wsdl mode?

  12. Ken Johnson says:

    ‘@Henrik – Sorry this took so long, I don’t seem to get a notification that any comments were posted so I didn’t realize that you had asked this question.

    Anyways, you might try using the Google search query –

    site:something.com filetype:wsdl

    HTH

  13. Ken Johnson says:

    ‘@Rick – I’m not sure what you mean

  14. Ken Johnson says:

    ‘@Jared – Make sure you use the following libs:

    JRuby-1.6.7.2

    HTTPI 0.9.0 (or greater, current is 1.1.0 I think)

    Make sure you install the gems Savon && Buby.

    For RVM users, you can use my personal buby-script repository located here –

    https://github.com/WebExploitationFramework/Buby-Scripts

    Once you have done a

    $ git clone git://github.com/WebExploitationFramework/Buby-Scripts.git

    change into the directory and accept the .rvmrc file. (if it complains that jruby isn’t installed $rvm install jruby-1.6.7.2)

    After doing so, type:

    $ bundle install (if it errors because bundler doesn’t exist ….
    $ gem install bundler)

    After that you should be good to go and up-to-date.

    ALSO, for anyone who posts a comment and does not hear back from me, reach out on Twitter please @cktricky)

    Thanks

  15. savonTester says:

    Hi,

    Very nive article here, thanks !

    I’m having trouble to install the savon gem through jruby :

    jruby –1.9 -S gem install savon
    JRuby limited openssl loaded. gem install jruby-openssl for full support.
    http://jruby.kenai.com/pages/JRuby_Builtin_OpenSSL
    System.java:-2:in `arraycopy’: java.lang.ArrayIndexOutOfBoundsException
    from DefaultResolver.java:111:in `makeTime’
    from DefaultResolver.java:277:in `create’
    from DefaultResolver.java:317:in `handleScalar’
    from DefaultResolver.java:435:in `orgHandler’
    from DefaultResolver.java:455:in `node_import’
    from org/yecht/ruby/DefaultResolver$s_method_1_0$RUBYINVOKER$node_import.gen:-1:in `call’
    from CachingCallSite.java:147:in `call’
    from RubyLoadHandler.java:39:in `handle’
    from Parser.java:300:in `addNode’
    from DefaultYAMLParser.java:676:in `yyparse’
    from Parser.java:290:in `yechtparse’
    from Parser.java:284:in `parse’
    from YParser.java:152:in `load’
    from org/yecht/ruby/YParser$s_method_0_1$RUBYINVOKER$load.gen:-1:in `call’

    The installation works well with only #gem install savon, but burp doesn’t launch after :

    gem list savon
    *** LOCAL GEMS ***
    savon (1.2.0)

    jruby –1.9 -S buby -i -B /pentest/web/burpsuite/burpsuite_v1.4.01.jar -r /home/fred/soapAtk2.rb
    /usr/lib/jruby//lib/ruby/site_ruby/shared/builtin/javasupport/core_ext/object.rb:75 warning: already initialized constant StartBurp
    Your JRE appears to be OpenJDK.
    Burp has not been fully tested on this platform and you may experience problems.
    Loading: “/home/fred/soapAtk2.rb”
    /usr/lib/jruby//lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’: no such file to load — savon (LoadError)
    from /usr/lib/jruby//lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’
    from /home/fred/soapAtk2.rb:3
    from /home/fred/soapAtk2.rb:31:in `require’
    from /usr/lib/jruby//lib/ruby/site_ruby/1.8/rubygems/custom_require.rb:31:in `require’

    Do you know how to solve the ArrayIndexOutOfBoundsExcept error please?

    regards

  16. theguy says:

    Using burp 1.5rc3, everything launched fine with jruby but there’s no hook in burp. The “enumerate WDSL” option never appears in the proxy intercept. No errors, nothing.

  17. Andrew King says:

    ‘@savonTester – I experienced that too. You can install the gems properly using a version of jruby newer than what is supported in Backtrack. Just install RVM and jruby 1.6.x

  18. Ken Johnson says:

    ‘@theguy – Did you see the following under Burp’s alert tab (where you can see the registered piece) –

    1354145673454 Suite [BurpExtender] registering JRuby handler callbacks
    1354145673520 Suite [JRuby::Buby] registered callback
    1354145695021 Suite Handler # registered for “enumerate wsdl”
    1354145695028 Suite Handler # registered for “form SOAP request”

    Thanks

Leave a Reply

Your email address will not be published.