AWVS-SQL injection partial source code analysis
**PS:**In the article, I will indent some sentence patterns, the text is fixed by default, and the input indentation corresponds to the code indentation, so please pay attention to my paragraph indentation.
classErrorBasedSQLInjection.inc
classSQLErrorMessages()
this.plainArray
Store common sql injection error messages, such as
'Microsoft OLE DB Provider for ODBC Drivers',
'Error Executing Database Query',
'Microsoft OLE DB Provider for SQL Server',
'ODBC Microsoft Access Driver',
this.regexArray
Store the regular matching information of common sql injection, such as
/(Incorrect\ssyntax\snear\s'[^']*')/,
/(Syntax error: Missing operand after '[^']*' operator)/,
/Syntax error near\s.*?\sin the full-text search condition\s/,
/column "\w{5}" does not exist/,
/near\s[^:]+?:\ssyntax\serror/,
/(pg_query\(\)[:]*\squery\sfailed:\serror:\s)/,
/('[^']*'\sis\snull\sor\snot\san\sobject)/,
/(ORA-\d{4,5}:\s)/,
**PS:** Here is an explanation of the difference between this.plainArray and this.regexArray, both of which match strings, but this.plainArray directly matches the contents of the array with the strings, requiring a complete match Variables meet the matching requirements, and this.regexArray emphasizes a regular matching rule. As long as there is content in the string that meets the matching rules, it is considered to meet the matching requirements.
this.FalsePositivesPlainArray
Store the obvious characteristics of sql injection error reporting, such as
"Connection Timeout",
"(0x80131904)",
"org.apache.commons.dbcp.SQLNestedException: Cannot get a connection, pool error Timeout waiting for idle object"
this.FalsePositivesRegexArray
Temporarily empty, judging from the array name, it is used to store the expression characteristics when SQL injection errors are reported.
classSQLErrorMessages.prototype.isFalsePositive = function (text)
Function
A function used to judge whether there are obvious features of sql error message.
content
Match the obtained data with the data in this.FalsePositivesPlainArray in turn, and return true if it exists.
Match the obtained data with the data in this.FalsePositivesRegexArray in turn, and return true if it exists.
Return false if none.
classSQLErrorMessages.prototype.searchOnText = function (text)
Function
By means of string matching, it is judged which kind of sql injection characteristics the string belongs to.
content
Declare a variable first _in
, the default value is body
Match the obtained data, if "HTTP/1." or "HTTP/0." is recognized, the obtained information is recognized as a response packet, and the value is assigned _in
as response .
Match the obtained data with the data in this.plainArray in turn. If there is, return highlightFromTextSearch(this.plainArray[i], text, _in) . The function of this function is to match the value of this.plainArray[i] in text Return to _in .
Match the obtained data with the data in this.regexArray in turn, if there is return highlightFromRegexMatch(m, _in) , this function returns the regular matching result in m to **_in**, where m=this.regexArray[ i].exec(text)
Return false if none.
InjectionResult(data, adItem)
Function
Assign values to the class properties this.data and this.adItem
content
this.data = data;
this.adItem = adItem;
classErrorBasedSQLInjection(targetUrl, errorMessages, scheme, inputIndex, variationIndex, reflectionPoint)
Function
Assign values to class attributes, especially save the input content (change value) this.variations , and initialize some class attributes.
content
First assign values to some class variables
this.scheme = scheme;
this.targetUrl = targetUrl;
this.errorMessages = errorMessages;
this.inputIndex = inputIndex;
this.reflectionPoint = reflectionPoint;
this.foundVulnOnVariation = false;
this.lastJob = null;
this.lastJobConfirm = null;
this.disableSensorBased = false;
If scheme is not empty, initialize this.currentVariation and this.origValue = this.getOrigValue();
If scheme is not empty and inputIndex is not empty, create array this.variations and assign inputIndex to this.variations .
If scheme is not empty but inputIndex is empty, create an array this.variations and assign this.scheme.selectVariationsForInput(inputIndex) to this.variations .
classErrorBasedSQLInjection.prototype.isSQLInjection = function (sensorData, start, end)
Function
Determine the database type based on the fingerprint feature, and store the result of the attempted injection.
content
Initialize items first , items = sensorData.getItems(“SQL_Query”) , sensorData.getItems here can be understood as a function to capture and analyze http request packets.
If items is empty null .
The value item is taken out sequentially from items .
If item is not empty, initialize dbType = "mysql" addData = item.additional
If the length of addData is greater than or equal to 2, db = addData[1]
If matched in db database=
, db = db.substr(9) dbType = db.toLowerCase()
Take the value entry from item.dataList in turn
If the variable start string is matched in the entry
If dbType == 'mysql' and ax.util.testForInjection(entry, ax.util.InjectionType.MySQL, start, end) is not equal to empty (the return value of the attempted injection function is not empty), return InjectionResult(entry, item )
If dbType == 'mssql' or dbType == 'mssql_or_access' and ax.util.testForInjection(entry, ax.util.InjectionType.MySQL, start, end) is not equal to empty, return InjectionResult(entry, item)
If dbType == 'pg' and ax.util.testForInjection(entry, ax.util.InjectionType.MySQL, start, end) is not equal to empty, return InjectionResult(entry, item)
If dbType == 'sqlite' and ax.util.testForInjection(entry, ax.util.InjectionType.MySQL, start, end) is not equal to empty, return InjectionResult(entry, item)
If dbType == 'oracle' and ax.util.testForInjection(entry, ax.util.InjectionType.MySQL, start, end) is not equal to empty, return InjectionResult(entry, item)
If dbType == 'sybase' and ax.util.testForInjection(entry, ax.util.InjectionType.MySQL, start, end) is not equal to empty, return InjectionResult(entry, item)
If dbType does not meet the above conditions, and ax.util.testForInjection(entry, ax.util.InjectionType.MySQL, start, end) is not equal to empty, return InjectionResult(entry, item)
Otherwise return false
classErrorBasedSQLInjection.prototype.getOrigValue = function ()
Function
Matches whether the input data (this.inputIndex) has already been entered.
content
Initialize the variable value , the initial value is empty.
Take values sequentially from this.variations (this array is the input content stored in classErrorBasedSQLInjection), match the input data (this.inputIndex) with the previously stored data (this.variations), and the matching result is varValue, if it contains relation (variations contains the input data this.inputIndex). If value is empty and varValue is not empty, assign value to varValue , exit the loop, and return value .
classErrorBasedSQLInjection.prototype.request = function (value)
Function
Appends a value value to the current variation and sends an HTTP request.
content
If there is a file upload ( this.scheme.hasFileInput ) and the input data has a key flag ( this.scheme.getInputFlags(this.inputIndex) ) and the uploaded data is a file ( INPUT_FLAG_IS_FILE ), then set the upload file name (setInputFileName) to value , set the upload file type (setInputContentType) to image/png , and set the upload file value (setInputValue) to value . Otherwise, set the upload file value to value .
Initialize lastJob ( this.lastJob = new THTTPJob() ), assign this.lastJob.url to this.targetUrl
If the value in targetHasAcuSensor ( this.scheme.targetHasAcuSensor ) is not empty, call this.lastJob.addAspectHeaders()
Call populateRequest , passing lastjob ( this.scheme.populateRequest(this.lastJob) )
PS: The function of calling populateRequest is to fill in the perfect request header.
If the string Referer does not exist in the request header , call the function addHeader to add the request header Referer
Then send a package to lastJob ( this.lastJob.execute() ).
Initialize variable tmp = false
If the request result does not report an error ( wasError )) and the response packet can be received, send a packet to reflectionPoint , copy the response packet data to the lastJob response packet, and assign tmp to the value of wasError .
Finally return !this.lastJob.wasError && !tmp
classFileInclusion.prototype.requestProof = function (value, dontEncode)
Function
Extract the proof, and judge whether the extraction proof ( Proof ) exists.
content
If there is a file upload ( this.scheme.hasFileInput ) and the input data has a key flag ( this.scheme.getInputFlags(this.inputIndex) ) and the uploaded data is a file ( INPUT_FLAG_IS_FILE ), then set the upload file name (setInputFileName) to value , set the upload file type ( setInputContentType ) to image/png .
If dontEncode == TRUE , call setEncodedInputValue , set the encoded input value ( this.inputIndex ) to value ( this.scheme.setEncodedInputValue(this.inputIndex, value) ), otherwise set the input value ( this.inputIndex ) to value .
otherwise
If dontEncode == TRUE , call setEncodedInputValue , set the encoded input value ( this.inputIndex ) to value ( this.scheme.setEncodedInputValue(this.inputIndex, value) ), otherwise set the input value ( this.inputIndex ) to value .
Initialize this.lastJobProof = new THTTPJob()
Initialize this.lastJobProof.url = this.targetUrl
If the value in targetHasAcuSensor ( this.scheme.targetHasAcuSensor ) is not empty, call this.lastJob.addAspectHeaders()
Call populateRequest , passing lastjob ( this.scheme.populateRequest(this.lastJob) )
If the string Referer does not exist in the request header , call the function addHeader to add the request header Referer
Then send a package to lastJob ( this.lastJob.execute() ).
return !this.lastJobProof.wasError
classFileInclusion.prototype.extractProofOfExploit = function (testValue)
Function
Attempt to extract proof of exploit
content
Initialize proof = false
Initialize proof_title = false
Initialize proof_contents = false
init- regex = false
If testValue starts with **http://**, then
testValue = "http://bxss.me/t/fit.txt?";
regex = /(63c19a6da79816b21429e5bb262daed863c19a6da79816b21429e5bb262daed8)/;
proof_title = "URL - http://bxss.me/t/fit.txt";
otherwise
testValue = "../../../../../../../../proc/version";
regex = /(Linux\sversion\s\d.*?\s\(.*?\)\s\(gcc\sversion\s\d.*?\(.*?\)\s*\)\s\#.*?[A-Z]{3}\s\d{4})/;
proof_title = "File - /proc/version";
if regex is not empty
If the testValue extraction proves that the result is flase , return false .
Assign the variable match to this.lastJobProof.response.toString().match(regex) , here is the matching result returned after matching regex (regular matching rules) to the information in the request package in testValue .
If match is not empty and the length of match (array) is greater than 1, assign proof_contents to match[1] .
If proof_contents is not empty, return [proof_title, proof_contents].
return proof.
classFileInclusion.prototype.alert = function (testValue, matchedText, sourceFile, sourceLine, additionalInfo, acuSensor)
Function
Generate report items for scanners.
content
Initialize this.foundVulnOnVariation = true
Initialize- flags=[]
If acuSensor == TRUE , write the values verified and acusensor to flags .
If this.reflectionPoint is not empty, write the value stored into flags .
Initialize proof_title = false
Initialize proof_contents = false
Initialize the constant proof = this.extractProofOfExploit(testValue)
If proof exists and proof (array) length is 2, then proof_title = proof[0] , proof_contents = proof[1] .
If both proof_title and proof_contents are not empty, write the value verified into flags .
Initialize newVuln
var newVuln = {
typeId: "File_inclusion.xml",
path: this.scheme.path,
tags: flags,
highlights: [matchedText],
details: {
input_type: this.scheme.getInputTypeStr(this.inputIndex),
input_name: this.scheme.getInputName(this.inputIndex),
proof_title: proof_title ? proof_title : false,
proof_contents: proof_contents ? proof_contents : false,
test_value: testValue,
matched_text: matchedText ? matchedText : false,
reflection_point: this.reflectionPoint ? this.reflectionPoint.url.url : false
},
http: this.lastJob.getNativeObject(),
ssl: scriptArg.target.url.protocol == 'https',
parameter: this.scheme.getInputName(this.inputIndex),
attackVector: testValue
};
If sourceFile is not empty or additionalInfo is not empty, add value to newVuln
newVuln.sensor = {
file: sourceFile,
line: sourceLine,
additional: additionalInfo
};
Add newVuln to scanState ( scanState.addVuln(newVuln) )
classFileInclusion.prototype.testInjection = function (value, dontEncode)
Function
Try to perform sql injection, and generate a report item if the attempt fails.
content
Initialize sensorPayload = value.includes(this.injectionValidator.startMark)
Returns false if **!this.request(value, dontEncode) is true** .
Initialize job = this.lastJob
Create data = this.disableSensorBased , if empty then call getSensorData(this.lastJob) .
If data and sensorPayload are not empty, initialize the variable injRes = this.isFileInclusion(value, data)
If injRes and injRes.adItem are not empty, assign a value to the variable additional, additional = "File: " + injRes.data + "\r\n" + injRes.adItem.additional[0] , generate a report item this.alert (value, "", injRes.adItem.fileName, injRes.adItem.fileNo, additional, 1) and return false .
Otherwise, initialize this.disableSensorBased = true .
If this.reflectionPoint is empty, assign a value to the variable matchedText , matchedText = this.injectionPatterns.searchOnText(job.response.toString()) , where the information returned by the response packet of the job is matched with the information in injectionPatterns , and the matched The result is assigned to matchedText .
If matchedText is not empty, generate a report item this.alert(value, matchedText) and return false .
returns true .
classFileInclusion.prototype.testInjectionSelfInclude = function (value)
Function
Try to perform sql injection, and generate a report item if the attempt fails.
content
Returns false if sending http request failed ( !this.request(value, 0) ) .
Initialize job = this.lastJob .
Assign a value to matchmatch = job.response.toString().match(/(<%@[^%]+?%>)/)
, , where the regular matching method is matched <%@[^%]+?%>
with the response packet of the job , and if there is a string in job.response that meets the matching rules, it is assigned to match .
If match and match[1] are not empty, generate a report item this.alert(value, match[1]) and return false .
returns true .
classFileInclusion.prototype.startTesting = function ()
Function
The main function, which tests all input changes.
content
Initialize inputType = this.scheme.getInputTypeStr(this.inputIndex)
Initialize inputName = this.scheme.getInputName(this.inputIndex)
Take the values in order from this.variations
If this.foundVulnOnVariation == false then exit the loop.
Assign variable this.currentVariation = i .
If targetHasAcuSensor or reflectionPoint is not empty
If reflectionPoint is empty, then this.injectionValidator.startMark = rnd.randStrDigits(6) , this.injectionValidator.endMark = rnd.randStrDigits(6)
If the result of the attempt to inject is false , continue the loop.
!this.testInjection(`1${this.injectionValidator.startMark}/../../xxx\\..\\..\\${this.injectionValidator.endMark}`)
否则,this.injectionValidator.startMark = rnd.randStrDigits(6),this.injectionValidator.endMark = rnd.randStrDigits(6)
If the result of the attempt to inject is false , continue the loop.
!this.testInjection(`1${this.injectionValidator.startMark}/../../xxx\\..\\..\\${this.injectionValidator.endMark}`)
If this.injectionPatterns is not empty, assign origValue = this.getOrigValue() and initialize extension = "jpg" .
If origValue is not empty and origValue contains words 符点"."
, take the string after the last dot (.) and assign it to extension .
Initialize schemeExtension = ""
Initialize schemePath = scheme.path
If schemePath is not empty and schemePath contains words , take the string after符点"."
the last dot (.) and assign it to schemeExtension ; take the string after the last backslash (/) and assign it to schemeFileName .
If the result of the attempt to inject is false , continue the loop.
!this.testInjection("http://some-inexistent-website.acu/some_inexistent_file_with_long_name%3F." + extension, 1)
If the result of the attempt to inject is false , continue the loop.
!this.testInjection("1some_inexistent_file_with_long_name%00." + extension, 1)
If the result of the attempt to inject is false , continue the loop.
!this.testInjection("Http://" + AcuMonitor_AMServer + "/t/fit.txt", 1)
If the result of the attempt to inject is false , continue the loop.
!this.testInjection("http://" + AcuMonitor_AMServer + "/t/fit.txt%3F." + extension, 1)
If the result of the attempt to inject is false , continue the loop.
!this.testInjection(AcuMonitor_AMServer, 0)
If schemeExtension == 'jsp' and origValue contains the string backslash (/) .
If the result of the attempt to inject is false , continue the loop.
!this.testInjectionSelfInclude(schemePath)
If the result of the attempt to inject is false , continue the loop.
!this.testInjectionSelfInclude(schemePath.replace(/^\/[^\/]+/, ""))
If the result of the attempt to inject is false , continue the loop.
!this.testInjectionSelfInclude(schemePath.replace(/^\/[^\/]+/, "").replace(/^\/[^\/]+/, ""))
If the result of the attempt to inject is false , continue the loop.
!this.testInjectionSelfInclude(schemePath.replace(/^\/[^\/]+/, "").replace(/^\/[^\/]+/, "").replace(/^\/[^\/]+/, ""))
If schemeExtension == 'jsp' .
If the result of the attempt to inject is false , continue the loop.
!this.testInjectionSelfInclude(schemeFileName)
If the result of the attempt to inject is false , continue the loop.
!this.testInjectionSelfInclude("./" + schemeFileName)
If the result of the attempt to inject is false , continue the loop.
!this.testInjectionSelfInclude("/" + schemeFileName)
SQL_Injection_In_Basic_Auth.script
Function
First determine whether the visited website meets the requirements of SQL injection, handle the URL, and call the testInjection function up to four times to determine whether there is SQL injection.
try username
try password
Username in single quotes, password in double quotes
Username in double quotes, password in single quotes
Include
#include constants.inc;
#include classErrorBasedSQLInjection.inc;
custom function
function alert(path, value, job, matchedText, sourceFile, sourceLine, additionalInfo, acuSensor)
AWVS is a function used to report vulnerabilities. When a vulnerability is judged, this function is called to report the vulnerability, and the corresponding vulnerability information is echoed. This only needs to know the general effect, and careful analysis will involve a lot of calling functions, so I won’t go into details.
function request(dir, withAop, path, value)
Initialize variables
lastJob = new THTTPJob();
lastJob.url = dir.url;
lastJob.uri = path;
lastJob.autoAuthenticate = false;
Add the header name Authorization to the lastJob request header , and the value is value . ( lastJob. request. addHeader(“Authorization”, value, true) )
If withAop == True , call the function lastJob.addAspectHeaders() to add the request header
send package lastJob.execute()
returns **!lastJob.wasError**
function testInjection(dir, withAop, path, value, tester)
Assign a value to the variable sensorPayload
sensorPayload = value.includes("MUFDVVNUQVJUJ1wiKi9cclxuIFx0QUNVRU5EOmFhYWE=") || value.includes("YWFhYToxQUNVU1RBUlQnXCIqL1xyXG4gXHRBQ1VFTkQ=")
MUFDVVNUQVJUJ1wiKi9cclxuIFx0QUNVRU5EOmFhYWE=
BASE64解密结果
1ACUSTART'\"*/\r\n \tACUEND:aaaa
YWFhYToxQUNVU1RBUlQnXCIqL1xyXG4gXHRBQ1VFTkQ=
BASE64解密结果
aaaa:1ACUSTART'\"*/\r\n \tACUEND
Returns false if the request result is false (request(dir, withAop, path, value)) .
init job = lastJob
data = getSensorData(job)
If the data is not empty and the result of the sensorPayload judgment is not empty, assign the variable **injRes = tester.isSQLInjection(data, ACUINJSTART, ACUINJEND)** to determine whether there is SQL injection.
If injRes and injRes.adItem are not empty, assign the variable additional = "SQL query: " + injRes.data + "\r\n" + injRes.adItem.additional[0] and call the alert function ( alert(path, value, job, "", injRes.adItem.fileName, injRes.adItem.fileNo, additional, 1) ) and returns false .
Otherwise initialize the variable matchedText = errorMessages.searchOnText(job.response.body)
If matchedText is not empty, call the alert function ( alert(path, value, job, matchedText) ) and return false .
returns true .
main function
init dir = getCurrentDirectory()
If the status code after the request is 401 and the variable value of the variable name WWW-Authenticate in the request header contains Basic , then initialize the variable
var lastJob = null;
var errorMessages = new classSQLErrorMessages();
var dirName = dir.fullPath;
If the end of the URL is not a backslash (/) , add a backslash (/) to the end of the URL ( if (dirName.charAt(dirName.length - 1) != '/') dirName = dirName + '/' )
Assign variable **tester = new classErrorBasedSQLInjection(scanURL, errorMessages, null, 0)** The function is to assign values to class attributes ( this.variations ).
If dir.hasAcuSensor is not empty
If testInjection(dir, true, dirName, "Basic MUFDVVNUQVJUJ1wiKi9cclxuIFx0QUNVRU5EOmFhYWE=", tester) returns true [Try Username], call the function testInjection ( testInjection(dir, true, dirName, "Basic YWFhYToxQUNVU1RBUlQn XCIqL1xyXG4gXHRBQ1VFTkQ=", tester) ) 【Try password】.
otherwise
If testInjection(dir, false, dirName, "Basic Jzoi") returns true [single quote user, double quote password], then call the function testInjection ( testInjection(dir, false, dirName, 'Basic Ijon') ) [double quote user, single quote password].
SQL_Injection_In_URI.script
Function
First determine whether the visited website meets the requirements of SQL injection, handle the URL, and call the testInjection function up to six times to determine whether there is SQL injection.
Include
#include constants.inc;
#include classErrorBasedSQLInjection.inc;
#include dir_listing_helpers.inc;
custom function
function alert(path, value, job, matchedText, sourceFile, sourceLine, additionalInfo, acuSensor)
AWVS is a function used to report vulnerabilities. When a vulnerability is judged, this function is called to report the vulnerability, and the corresponding vulnerability information is echoed.
function request(dir, withAop, path)
Initialize variables
lastJob = new THTTPJob();
lastJob.url = dir.url;
lastJob.uri = path;
If withAop == True , call the function lastJob.addAspectHeaders() to add the request header
Add request header
lastJob.request.addHeader('User-Agent', '1\'"2000', true);
lastJob.request.addHeader('referer', '1\'"3000', true);
lastJob.request.addHeader('client-ip', '1\'"4000', true);
lastJob.request.addHeader('x-forwarded-for', '1\'"5000', true);
lastJob.request.addHeader('accept-language', '1\'"6000', true);
lastJob.request.addHeader('via', '1\'"7000', true);
send package lastJob.execute()
returns **!lastJob.wasError**
function testInjection(dir, withAop, path, value, tester)
Assign variable sensorPayload = value.includes(ACUINJSTART)
Returns false if the request result is false ( request(dir, withAop, path, value) ) .
init job = lastJob
data = getSensorData(job)
If the data is not empty and the result of the sensorPayload judgment is not empty, assign the variable **injRes = tester.isSQLInjection(data, ACUINJSTART, ACUINJEND)** to determine whether there is SQL injection.
If both injRes and injResinjRes are not empty, assign the variable additional = "SQL query: " + injRes.data + "\r\n" + injRes.adItem.additional[0] and call the alert function ( alert(path, value , job, "", injRes.adItem.fileName, injRes.adItem.fileNo, additional, 1) ) and returns false .
Otherwise initialize the variable matchedText = errorMessages.searchOnText(job.response.body)
If matchedText is not empty, call the alert function ( alert(path, value, job, matchedText) ) and return false .
returns true .
main function
init dir = getCurrentDirectory()
If the status code is 200 after the request , initialize the variable matches = new classDirListingMatches()
If **matches.searchOnText(dir.response.body)** is empty, initialize the variable
var lastJob = null;
var errorMessages = new classSQLErrorMessages();
var dirName = dir.fullPath;
If the end of the URL is not a backslash (/) , add a backslash (/) to the end of the URL ( if (dirName.charAt(dirName.length - 1) != '/') dirName = dirName + '/' )
Assign variable **tester = new classErrorBasedSQLInjection(scanURL, errorMessages, null, 0)** The function is to assign values to class attributes ( this.variations ).
If dir.hasAcuSensor is not empty
如果testInjection(dir, true, dirName, dirName + `1 A C U I N J S T A R T ′ " {ACUINJSTART}'" A C U I N J S T A R T′ "{ACUINJEND}`, tester, dirName)returnsfalse, then returnstrue. [Simple construction]
If testInjection(dir, true, dirName, dirName + `index/1 KaTeX parse error: Can't use function '\'' in math mode at position 14: {ACUINJSTART}\̲'̲" {ACUINJEND}`, tester , dirName) returns false , then returns true . [index construction]
If testInjection(dir, true, dirName, dirName + `?id=1 KaTeX parse error: Can't use function '\'' in math mode at position 14: {ACUINJSTART}\̲'̲" {ACUINJEND}`, tester, dirName) returns false , then returns true . [Construction with parameters]
If matchedText is empty
If the return value of testInjection(dir, false, dirName, dirName + "1'\“1000") is false , return true . [Simple construction]
If the return value of testInjection(dir, false, dirName, dirName + "index/1'\“1000") is false , return true . [index construction]
If the return value of testInjection(dir, false, dirName, dirName + "?id=1'\“1000") is false , return true . [Build with parameters]