diff --git a/CHANGELOG.md b/CHANGELOG.md index 187d7947..f896115c 100644 --- a/CHANGELOG.md +++ b/CHANGELOG.md @@ -42,6 +42,11 @@ The format is based on [Keep a Changelog](https://keepachangelog.com/en/1.0.0/). - passive/Report non static sites.js - passive/RPO.js - passive/s3.js + - passive/Server Header Disclosure.js + - passive/SQL injection detection.js + - passive/Telerik Using Poor Crypto.js + - passive/Upload form discovery.js + - passive/X-Powered-By_header_checker.js - httpsender/Alert on Unexpected Content Types.js now checks for common content-types (`json`, `xml`, and `yaml`) more consistently. - targeted/request_to_xml.js no longer uses deprecated method to show the message in the editor dialogue. diff --git a/passive/CookieHTTPOnly.js b/passive/CookieHTTPOnly.js index 7e6f4673..a6dc1399 100644 --- a/passive/CookieHTTPOnly.js +++ b/passive/CookieHTTPOnly.js @@ -27,8 +27,17 @@ function scan(helper, msg, src) { var cookies = msg.getResponseHeader().getHeaders("Set-Cookie"); if (cookies != null) { var re_noflag = /([Hh][Tt][Tt][Pp][Oo][Nn][Ll][Yy])/g; - if (!re_noflag.test(cookies)) { - helper.newAlert().setMessage(msg).setEvidence(cookies).raise(); + if (!re_noflag.test(cookies.toString())) { + const otherInfo = + cookies.length > 1 + ? `Other values: ${cookies.slice(1).toString()}` + : ""; + helper + .newAlert() + .setMessage(msg) + .setEvidence(cookies[0]) + .setOtherInfo(otherInfo) + .raise(); } } } diff --git a/passive/Find Credit Cards.js b/passive/Find Credit Cards.js index 52b1067b..4bb5a73f 100644 --- a/passive/Find Credit Cards.js +++ b/passive/Find Credit Cards.js @@ -65,10 +65,14 @@ function scan(helper, msg, src) { } } if (foundCard.length != 0) { + const otherInfo = + foundCard.length > 1 + ? `Other instances: ${foundCard.slice(1).toString()}` + : ""; helper .newAlert() .setEvidence(foundCard[0]) - .setOtherInfo(`Other instances: ${foundCard.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } diff --git a/passive/Find Emails.js b/passive/Find Emails.js index 4829999c..d68e3a5a 100644 --- a/passive/Find Emails.js +++ b/passive/Find Emails.js @@ -63,10 +63,14 @@ function scan(helper, msg, src) { foundEmail.push(comm[0]); } // woohoo we found an email lets make an alert for it + const otherInfo = + foundEmail.length > 1 + ? `Other instances: ${foundEmail.slice(1).toString()}` + : ""; helper .newAlert() .setEvidence(foundEmail[0]) - .setOtherInfo(`Other instances: ${foundEmail.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } diff --git a/passive/Find HTML Comments.js b/passive/Find HTML Comments.js index a11a50bc..02d570ac 100644 --- a/passive/Find HTML Comments.js +++ b/passive/Find HTML Comments.js @@ -86,10 +86,14 @@ function scan(helper, msg, src) { foundComments.push(comm[0]); } if (RESULT_PER_URL == true) { + const otherInfo = + foundComments.length > 1 + ? `Other instances: ${foundComments.slice(1).toString()}` + : ""; helper .newAlert() .setEvidence(foundComments[0]) - .setOtherInfo(`Other instances: ${foundComments.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } diff --git a/passive/Find Hashes.js b/passive/Find Hashes.js index a86d9957..14a98646 100644 --- a/passive/Find Hashes.js +++ b/passive/Find Hashes.js @@ -69,12 +69,16 @@ function scan(helper, msg, src) { while ((comm = wordpress.exec(body))) { foundwordpress.push(comm[0]); } + const otherInfo = + foundwordpress.length > 1 + ? `Other instances: ${foundwordpress.slice(1).toString()}` + : ""; helper .newAlert() .setName(alertTitle[0]) .setDescription(alertDesc[0]) .setEvidence(foundwordpress[0]) - .setOtherInfo(`Other instances: ${foundwordpress.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } @@ -85,12 +89,16 @@ function scan(helper, msg, src) { while ((comm = sha512.exec(body))) { foundsha512.push(comm[0]); } + const otherInfo = + foundsha512.length > 1 + ? `Other instances: ${foundsha512.slice(1).toString()}` + : ""; helper .newAlert() .setName(alertTitle[1]) .setDescription(alertDesc[1]) .setEvidence(foundsha512[0]) - .setOtherInfo(`Other instances: ${foundsha512.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } @@ -100,12 +108,16 @@ function scan(helper, msg, src) { while ((comm = phpbb3.exec(body))) { foundphpbb3.push(comm[0]); } + const otherInfo = + foundphpbb3.length > 1 + ? `Other instances: ${foundphpbb3.slice(1).toString()}` + : ""; helper .newAlert() .setName(alertTitle[2]) .setDescription(alertDesc[2]) .setEvidence(foundphpbb3[0]) - .setOtherInfo(`Other instances: ${foundphpbb3.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } @@ -116,12 +128,16 @@ function scan(helper, msg, src) { while ((comm = mysqlold.exec(body))) { foundmysqlold.push(comm[0]); } + const otherInfo = + foundmysqlold.length > 1 + ? `Other instances: ${foundmysqlold.slice(1).toString()}` + : ""; helper .newAlert() .setName(alertTitle[3]) .setDescription(alertDesc[3]) .setEvidence(foundmysqlold[0]) - .setOtherInfo(`Other instances: ${foundmysqlold.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } @@ -132,12 +148,16 @@ function scan(helper, msg, src) { while ((comm = joomla.exec(body))) { foundjoomla.push(comm[0]); } + const otherInfo = + foundjoomla.length > 1 + ? `Other instances: ${foundjoomla.slice(1).toString()}` + : ""; helper .newAlert() .setName(alertTitle[4]) .setDescription(alertDesc[4]) .setEvidence(foundjoomla[0]) - .setOtherInfo(`Other instances: ${foundjoomla.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } @@ -147,12 +167,16 @@ function scan(helper, msg, src) { while ((comm = drupal.exec(body))) { founddrupal.push(comm[0]); } + const otherInfo = + founddrupal.length > 1 + ? `Other instances: ${founddrupal.slice(1).toString()}` + : ""; helper .newAlert() .setName(alertTitle[5]) .setDescription(alertDesc[5]) .setEvidence(founddrupal[0]) - .setOtherInfo(`Other instances: ${founddrupal.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } @@ -163,12 +187,16 @@ function scan(helper, msg, src) { while ((comm = blowfish.exec(body))) { foundblowfish.push(comm[0]); } + const otherInfo = + foundblowfish.length > 1 + ? `Other instances: ${foundblowfish.slice(1).toString()}` + : ""; helper .newAlert() .setName(alertTitle[6]) .setDescription(alertDesc[6]) .setEvidence(foundblowfish[0]) - .setOtherInfo(`Other instances: ${foundblowfish.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } @@ -179,12 +207,16 @@ function scan(helper, msg, src) { while ((comm = vbull.exec(body))) { foundvbull.push(comm[0]); } + const otherInfo = + foundvbull.length > 1 + ? `Other instances: ${foundvbull.slice(1).toString()}` + : ""; helper .newAlert() .setName(alertTitle[7]) .setDescription(alertDesc[7]) .setEvidence(foundvbull[0]) - .setOtherInfo(`Other instances: ${foundvbull.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } @@ -195,13 +227,17 @@ function scan(helper, msg, src) { while ((comm = md45.exec(body))) { foundmd45.push(comm[0]); } + const otherInfo = + foundmd45.length > 1 + ? `Other instances: ${foundmd45.slice(1).toString()}` + : ""; helper .newAlert() .setName(alertTitle[8]) .setDescription(alertDesc[8]) .setConfidence(1) .setEvidence(foundmd45[0]) - .setOtherInfo(`Other instances: ${foundmd45.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } diff --git a/passive/Find IBANs.js b/passive/Find IBANs.js index 6bddb32b..500f0f99 100644 --- a/passive/Find IBANs.js +++ b/passive/Find IBANs.js @@ -55,10 +55,14 @@ function scan(helper, msg, src) { foundIBAN.push(comm[0]); } // woohoo we found an IBAN lets make an alert for it + const otherInfo = + foundIBAN.length > 1 + ? `Other instances: ${foundIBAN.slice(1).toString()}` + : ""; helper .newAlert() .setEvidence(foundIBAN[0]) - .setOtherInfo(`Other instances: ${foundIBAN.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } diff --git a/passive/Find Internal IPs.js b/passive/Find Internal IPs.js index 53751eb8..04dca282 100644 --- a/passive/Find Internal IPs.js +++ b/passive/Find Internal IPs.js @@ -55,10 +55,14 @@ function scan(helper, msg, src) { while ((comm = re.exec(body))) { foundIP.push(comm[0]); } + const otherInfo = + foundIP.length > 1 + ? `Other instances: ${foundIP.slice(1).toString()}` + : ""; helper .newAlert() .setEvidence(foundIP[0]) - .setOtherInfo(`Other instances: ${foundIP.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } diff --git a/passive/RPO.js b/passive/RPO.js index b989ea65..a413740b 100644 --- a/passive/RPO.js +++ b/passive/RPO.js @@ -56,10 +56,14 @@ function scan(helper, msg, src) { while ((comm = re.exec(body))) { foundRPO.push(comm[0]); } + const otherInfo = + foundRPO.length > 1 + ? `Other instances: ${foundRPO.slice(1).toString()}` + : ""; helper .newAlert() .setEvidence(foundRPO[0]) - .setOtherInfo(`Other instances: ${foundRPO.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } diff --git a/passive/SQL injection detection.js b/passive/SQL injection detection.js index e017b0e2..11007c3d 100644 --- a/passive/SQL injection detection.js +++ b/passive/SQL injection detection.js @@ -1,383 +1,91 @@ // Made by kurobeats@yahoo.co.jp, regex shamelessly ripped from SQLMap project errors -function scan(ps, msg, src) { - var url = msg.getRequestHeader().getURI().toString(); - var body = msg.getResponseBody().toString(); - var alertRisk = [0, 1, 2, 3, 4]; //1=informational, 2=low, 3=medium, 4=high - var alertConfidence = [0, 1, 2, 3, 4]; //0=fp,1=low,2=medium,3=high,4=confirmed - var alertTitle = [ - "MySQL error Disclosed (script)", - "Postgresql error Disclosed (script)", - "MSSQL error Disclosed (script)", - "Microsoft Access error Disclosed (script)", - "Oracle error Disclosed (script)", - "IBM DB2 error Disclosed (script)", - "Informix error Disclosed (script)", - "Firebird error Disclosed (script)", - "SQLite error Disclosed (script)", - "SAP DB error Disclosed (script)", - "Sybase error Disclosed (script)", - "Ingress error Disclosed (script)", - "Frontbase error Disclosed (script)", - "HSQLDB error Disclosed (script)", - "", - ]; - var alertDesc = [ - "A MySQL error was discovered.", - "A Postgresql error was discovered.", - "A MSSQL error was discovered.", - "A Microsoft Access error was discovered.", - "An Oracle error was discovered.", - "An IBM DB2 error was discovered.", - "An Informix error was discovered.", - "A Firebird error was discovered.", - "An SQLite error was discovered", - "A SAP DB error was discovered", - "A Sybase error was discovered", - "An Ingress error was discovered", - "A Frontbase error was discovered", - "A HSQLDB error was discovered", - "", - ]; - var alertSolution = [ - "Ensure proper sanitisation is done on the server side, or don't. I don't care.", - "", - ]; - var cweId = [0, 1]; - var wascId = [0, 1]; +const ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); + +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100020 +name: Information Disclosure - SQL Error +description: An SQL error was found in the HTTP response body. +solution: > + Ensure proper sanitisation is done on the server side. +risk: high +confidence: medium +cweId: 209 # CWE-209: Generation of Error Message Containing Sensitive Information +wascId: 13 # WASC-13: Information Leakage +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/passive/SQL%20injection%20detection.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} - var mysql = +function scan(helper, msg, src) { + const mysql = /(SQL syntax.*MySQL|Warning.*mysql_.*|MySqlException \(0x|valid MySQL result|check the manual that corresponds to your (MySQL|MariaDB) server version|MySqlClient\.|com\.mysql\.jdbc\.exceptions)/g; - var postgresql = + const postgresql = /(PostgreSQL.*ERROR|Warning.*\Wpg_.*|valid PostgreSQL result|Npgsql\.|PG::SyntaxError:|org\.postgresql\.util\.PSQLException|ERROR:\s\ssyntax error at or near)/g; - var mssql = + const mssql = /(Driver.* SQL[\-\_\ ]*Server|OLE DB.* SQL Server|\bSQL Server.*Driver|Warning.*mssql_.*|\bSQL Server.*[0-9a-fA-F]{8}|[\s\S]Exception.*\WSystem\.Data\.SqlClient\.|[\s\S]Exception.*\WRoadhouse\.Cms\.|Microsoft SQL Native Client.*[0-9a-fA-F]{8})/g; - var msaccess = + const msaccess = /(Microsoft Access (\d+ )?Driver|JET Database Engine|Access Database Engine|ODBC Microsoft Access)/g; - var oracle = + const oracle = /(\bORA-\d{5}|Oracle error|Oracle.*Driver|Warning.*\Woci_.*|Warning.*\Wora_.*)/g; - var ibmdb2 = /(CLI Driver.*DB2|DB2 SQL error|\bdb2_\w+\(|SQLSTATE.+SQLCODE)/g; - var informix = /(Exception.*Informix)/g; - var firebird = /(Dynamic SQL Error|Warning.*ibase_.*)/g; - var sqlite = + const ibmdb2 = + /(CLI Driver.*DB2|DB2 SQL error|\bdb2_\w+\(|SQLSTATE.+SQLCODE)/g; + const informix = /(Exception.*Informix)/g; + const firebird = /(Dynamic SQL Error|Warning.*ibase_.*)/g; + const sqlite = /(SQLite\/JDBCDriver|SQLite.Exception|System.Data.SQLite.SQLiteException|Warning.*sqlite_.*|Warning.*SQLite3::|\[SQLITE_ERROR\])/g; - var sapdb = /(SQL error.*POS([0-9]+).*|Warning.*maxdb.*)/g; - var sybase = + const sapdb = /(SQL error.*POS([0-9]+).*|Warning.*maxdb.*)/g; + const sybase = /(Warning.*sybase.*|Sybase message|Sybase.*Server message.*|SybSQLException|com\.sybase\.jdbc)/g; - var ingress = /(Warning.*ingres_|Ingres SQLSTATE|Ingres\W.*Driver)/g; - var frontbase = /(Exception (condition )?\d+. Transaction rollback.)/g; - var hsqldb = + const ingress = /(Warning.*ingres_|Ingres SQLSTATE|Ingres\W.*Driver)/g; + const frontbase = /(Exception (condition )?\d+. Transaction rollback.)/g; + const hsqldb = /(org\.hsqldb\.jdbc|Unexpected end of command in statement \[|Unexpected token.*in statement \[)/g; - if (mysql.test(body)) { - mysql.lastIndex = 0; - var foundmysql = []; - var comm; - while ((comm = mysql.exec(body))) { - foundmysql.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[0], - alertDesc[0], - url, - "", - "", - foundmysql.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - - if (postgresql.test(body)) { - postgresql.lastIndex = 0; - var foundpostgresql = []; - while ((comm = postgresql.exec(body))) { - foundpostgresql.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[1], - alertDesc[1], - url, - "", - "", - foundpostgresql.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (mssql.test(body)) { - mssql.lastIndex = 0; - var foundmssql = []; - while ((comm = mssql.exec(body))) { - foundmssql.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[2], - alertDesc[2], - url, - "", - "", - foundmssql.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (msaccess.test(body)) { - msaccess.lastIndex = 0; - var foundmsaccess = []; - while ((comm = msaccess.exec(body))) { - foundmsaccess.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[3], - alertDesc[3], - url, - "", - "", - foundmsaccess.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } + const sqlImplementations = [ + { name: "MySQL", regex: mysql }, + { name: "PostgreSQL", regex: postgresql }, + { name: "MSSQL", regex: mssql }, + { name: "Microsoft Access", regex: msaccess }, + { name: "Oracle", regex: oracle }, + { name: "IBM DB2", regex: ibmdb2 }, + { name: "Informix", regex: informix }, + { name: "Firebird", regex: firebird }, + { name: "SQLite", regex: sqlite }, + { name: "SAP DB", regex: sapdb }, + { name: "Sybase", regex: sybase }, + { name: "Ingress", regex: ingress }, + { name: "Frontbase", regex: frontbase }, + { name: "HSQLDB", regex: hsqldb }, + ]; - if (oracle.test(body)) { - oracle.lastIndex = 0; - var foundoracle = []; - while ((comm = oracle.exec(body))) { - foundoracle.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[4], - alertDesc[4], - url, - "", - "", - foundoracle.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (ibmdb2.test(body)) { - ibmdb2.lastIndex = 0; - var foundibmdb2 = []; - while ((comm = ibmdb2.exec(body))) { - foundibmdb2.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[5], - alertDesc[5], - url, - "", - "", - foundibmdb2.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (informix.test(body)) { - informix.lastIndex = 0; - var foundinformix = []; - while ((comm = informix.exec(body))) { - foundinformix.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[6], - alertDesc[6], - url, - "", - "", - foundinformix.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (firebird.test(body)) { - firebird.lastIndex = 0; - var foundfirebird = []; - while ((comm = firebird.exec(body))) { - foundfirebird.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[7], - alertDesc[7], - url, - "", - "", - foundfirebird.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (sqlite.test(body)) { - sqlite.lastIndex = 0; - var foundsqlite = []; - while ((comm = sqlite.exec(body))) { - foundsqlite.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[1], - alertTitle[8], - alertDesc[8], - url, - "", - "", - foundsqlite.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (sapdb.test(body)) { - sapdb.lastIndex = 0; - var foundsapdb = []; - while ((comm = sapdb.exec(body))) { - foundsapdb.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[9], - alertDesc[9], - url, - "", - "", - foundsapdb.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (sybase.test(body)) { - sybase.lastIndex = 0; - var foundsybase = []; - while ((comm = sybase.exec(body))) { - foundsybase.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[10], - alertDesc[10], - url, - "", - "", - foundsybase.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (ingress.test(body)) { - ingress.lastIndex = 0; - var foundingress = []; - while ((comm = ingress.exec(body))) { - foundingress.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[11], - alertDesc[11], - url, - "", - "", - foundingress.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (frontbase.test(body)) { - frontbase.lastIndex = 0; - var foundfrontbase = []; - while ((comm = frontbase.exec(body))) { - foundfrontbase.push(comm[0]); - } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[12], - alertDesc[12], - url, - "", - "", - foundfrontbase.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); - } - if (hsqldb.test(body)) { - hsqldb.lastIndex = 0; - var foundhsqldb = []; - while ((comm = hsqldb.exec(body))) { - foundhsqldb.push(comm[0]); + const body = msg.getResponseBody().toString(); + for (let i = 0; i < sqlImplementations.length; i++) { + const sqlImpl = sqlImplementations[i]; + const alertTitle = `Information Disclosure - ${sqlImpl.name} error`; + const article = sqlImpl.name.match(/^(?:[IO]|MS|SQ|H)/) ? "An" : "A"; + const alertDesc = `${article} ${sqlImpl.name} error was discovered in the HTTP response body.`; + if (sqlImpl.regex.test(body)) { + sqlImpl.regex.lastIndex = 0; + const found = []; + let sqlError; + while ((sqlError = sqlImpl.regex.exec(body))) { + found.push(sqlError[0]); + } + const otherInfo = + found.length > 1 ? `Other instances: ${found.slice(1).toString()}` : ""; + helper + .newAlert() + .setName(alertTitle) + .setDescription(alertDesc) + .setEvidence(found[0]) + .setOtherInfo(otherInfo) + .setMessage(msg) + .raise(); } - ps.raiseAlert( - alertRisk[3], - alertConfidence[2], - alertTitle[13], - alertDesc[13], - url, - "", - "", - foundhsqldb.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); } } diff --git a/passive/Server Header Disclosure.js b/passive/Server Header Disclosure.js index 73559dc8..5c31053c 100644 --- a/passive/Server Header Disclosure.js +++ b/passive/Server Header Disclosure.js @@ -1,41 +1,45 @@ // Server Header Check by freakyclown@gmail.com // Server Version leaks found via header field by prateek.rana@getastra.com -var VERSION_PATTERN = new RegExp("(?:\\d+\\.)+\\d+"); +const ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); -function scan(ps, msg, src) { - var alertRisk = 1; - var alertConfidence = 2; - var alertTitle = - "Server Leaks Version Information via 'Server' HTTP Response Header Field(script)"; - var alertDesc = - "The web/application server is leaking version information via the 'Server' HTTP response header. Access to such information may facilitate attackers identifying other vulnerabilities your web/application server is subject to."; - var alertSolution = - "Ensure that your web server, application server, load balancer, etc. is configured to suppress the 'Server' header or provide generic details."; +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100019 +name: Information Disclosure - Server Header +description: > + The web/application server is leaking version information via the 'Server' HTTP response header. + Access to such information may facilitate attackers identifying other vulnerabilities your web/application server + is subject to. +solution: > + Ensure that your web server, application server, load balancer, etc. is configured to suppress the 'Server' header + or provide generic details. +risk: low +confidence: medium +cweId: 200 # CWE-200: Exposure of Sensitive Information to an Unauthorized Actor +wascId: 13 # WASC-13: Information Leakage +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/passive/Server%20Header%20Disclosure.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} - var cweId = 200; - var wascId = 13; +var VERSION_PATTERN = new RegExp("(?:\\d+\\.)+\\d+"); - var url = msg.getRequestHeader().getURI().toString(); +function scan(helper, msg, src) { var headers = msg.getResponseHeader().getHeaders("Server"); if (headers != null && containsPotentialSemver(headers)) { - var headersString = headers.toString(); - ps.raiseAlert( - alertRisk, - alertConfidence, - alertTitle, - alertDesc, - url, - "", - "", - "", - alertSolution, - headersString, - cweId, - wascId, - msg - ); + const otherInfo = + headers.length > 1 ? `Other values: ${headers.slice(1).toString()}` : ""; + helper + .newAlert() + .setEvidence(headers[0]) + .setOtherInfo(otherInfo) + .setMessage(msg) + .raise(); } } diff --git a/passive/Telerik Using Poor Crypto.js b/passive/Telerik Using Poor Crypto.js index 6df4d030..d488da77 100644 --- a/passive/Telerik Using Poor Crypto.js +++ b/passive/Telerik Using Poor Crypto.js @@ -4,17 +4,39 @@ // (c) 2017 Harrison Neal // http://www.apache.org/licenses/LICENSE-2.0 -function scan(ps, msg, src) { - var alertRisk = org.parosproxy.paros.core.scanner.Alert.RISK_HIGH; - var alertTitle = - "Telerik UI for ASP.NET AJAX CVE-2017-9248 Cryptographic Weakness"; - var alertDesc = - "A request has been made that appears to conform to poor cryptography used by Telerik UI for ASP.NET AJAX prior to v2017.2.621. An attacker could manipulate the value of the dp parameter to possibly learn the machine key and upload arbitrary files, which could then lead to the compromise of ASP.NET ViewStates and arbitrary code execution respectively. CVE-2017-9248 has a CVSSv3 score of 9.8. "; - var alertSolution = - "See http://www.telerik.com/support/kb/aspnet-ajax/details/cryptographic-weakness for update/mitigation guidance."; - var cweId = 327; - var wascId = 0; - var url = msg.getRequestHeader().getURI().toString(); +const ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); + +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100021 +name: Telerik UI for ASP.NET AJAX Cryptographic Weakness (CVE-2017-9248) +description: > + A request has been made that appears to conform to poor cryptography used by Telerik UI for ASP.NET AJAX prior to + v2017.2.621. + + An attacker could manipulate the value of the dp parameter to possibly learn the machine key and upload + arbitrary files, which could then lead to the compromise of ASP.NET ViewStates and arbitrary code execution + respectively. + + CVE-2017-9248 has a CVSSv3 score of 9.8. +solution: > + See https://docs.telerik.com/devtools/aspnet-ajax/knowledge-base/common-cryptographic-weakness for update/mitigation + guidance. +references: + - https://docs.telerik.com/devtools/aspnet-ajax/knowledge-base/common-cryptographic-weakness +risk: high +confidence: medium +cweId: 327 # CWE-327: Use of a Broken or Risky Cryptographic Algorithm +wascId: 13 # WASC-13: Information Leakage +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/passive/Telerik%20Using%20Poor%20Crypto.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} + +function scan(helper, msg, src) { var param = "dp"; var dp = null; @@ -179,35 +201,25 @@ function scan(ps, msg, src) { return; } - var alertConfidence = null; + let alertConfidence; + let otherInfo; + const url = msg.getRequestHeader().getURI().toString(); if (url.contains("DialogHandler.aspx")) { alertConfidence = org.parosproxy.paros.core.scanner.Alert.CONFIDENCE_HIGH; - alertDesc += - "The URI strongly suggests this is a Telerik.Web.UI.DialogHandler instance; confidence is HIGH."; + otherInfo = + "The URI strongly suggests this is a Telerik.Web.UI.DialogHandler instance."; } else { alertConfidence = org.parosproxy.paros.core.scanner.Alert.CONFIDENCE_MEDIUM; - alertDesc += - "The URI is not typical for a Telerik.Web.UI.DialogHandler instance, so it may have been changed (e.g., in web.config), or this may be a false positive; confidence is MEDIUM."; + otherInfo = + "The URI is not typical for a Telerik.Web.UI.DialogHandler instance, so it may have been changed (e.g., in web.config), or this may be a false positive."; } - alertDesc = alertDesc - .split(" ") - .join(java.lang.System.getProperty("line.separator")); - - ps.raiseAlert( - alertRisk, - alertConfidence, - alertTitle, - alertDesc, - url, - param, - "", - "", - alertSolution, - "", - cweId, - wascId, - msg - ); + helper + .newAlert() + .setConfidence(alertConfidence) + .setParam(param) + .setOtherInfo(otherInfo) + .setMessage(msg) + .raise(); } diff --git a/passive/Upload form discovery.js b/passive/Upload form discovery.js index 83e11134..feb7e35a 100644 --- a/passive/Upload form discovery.js +++ b/passive/Upload form discovery.js @@ -1,45 +1,50 @@ // Lazily crafted by Anthony Cozamanis - kurobeats@yahoo.co.jp -function scan(ps, msg, src) { - var url = msg.getRequestHeader().getURI().toString(); - var body = msg.getResponseBody().toString(); - var alertRisk = [0, 1, 2, 3, 4]; // risk: 0: info, 1: low, 2: medium, 3: high - var alertConfidence = [0, 1, 2, 3, 4]; // confidence: 0: falsePositive, 1: low, 2: medium, 3: high, 4: confirmed - var alertTitle = ["An upload form appeared! (script)", ""]; - var alertDesc = [ - "An upload form exists. This isn't an issue, but it could be a lot of fun! Go check it out!.", - "", - ]; - var alertSolution = [ - "This isn't an issue, but it could be a lot of fun!", - "", - ]; - var cweId = [0, 1]; - var wascId = [0, 1]; +const ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); - var uploadForm = /(type\s*=\s*['"]?file['"]?)/g; +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100022 +name: Upload Form Discovered +description: > + The presence of a file upload form can lead to various security vulnerabilities, such as uploading malicious files or + overwriting existing files, if proper validation and restrictions are not implemented. + This can result in unauthorized code execution, data breaches, or denial of service attacks. +solution: > + Implement strict validation and restrictions on uploaded files, including file type, size, and content. + Use security measures like antivirus scanning and file storage outside the web root. +risk: info +confidence: medium +cweId: 434 # CWE-434: Unrestricted Upload of File with Dangerous Type +wascId: 20 # WASC-20: Improper Input Handling +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/passive/Upload%20form%20discovery.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} + +function scan(helper, msg, src) { + const body = msg.getResponseBody().toString(); + const uploadForm = /(type\s*=\s*['"]?file['"]?)/g; if (uploadForm.test(body)) { uploadForm.lastIndex = 0; - var founduploadForm = []; - var comm; + const foundUploadForm = []; + let comm; while ((comm = uploadForm.exec(body))) { - founduploadForm.push(comm[0]); + foundUploadForm.push(comm[0]); } - ps.raiseAlert( - alertRisk[0], - alertConfidence[2], - alertTitle[0], - alertDesc[0], - url, - "", - "", - founduploadForm.toString(), - alertSolution[0], - "", - cweId[0], - wascId[0], - msg - ); + const otherInfo = + foundUploadForm.length > 1 + ? `Other instances: ${foundUploadForm.slice(1).toString()}` + : ""; + helper + .newAlert() + .setEvidence(foundUploadForm[0]) + .setOtherInfo(otherInfo) + .setMessage(msg) + .raise(); } } diff --git a/passive/X-Powered-By_header_checker.js b/passive/X-Powered-By_header_checker.js index 374f9dbc..d9a07097 100644 --- a/passive/X-Powered-By_header_checker.js +++ b/passive/X-Powered-By_header_checker.js @@ -1,36 +1,39 @@ // X-Powered-By finder by freakyclown@gmail.com -function scan(ps, msg, src) { - var alertRisk = 1; - var alertConfidence = 2; - var alertTitle = - "Server Leaks Information via 'X-Powered-By' HTTP Response Header Field(s)(script)"; - var alertDesc = - "The web/application server is leaking information via one or more 'X-Powered-By' HTTP response headers. Access to such information may facilitate attackers identifying other frameworks/components your web application is reliant upon and the vulnerabilities such components may be subject to."; - var alertSolution = - "Ensure that your web server, application server, load balancer, etc. is configured to suppress 'X-Powered-By' headers."; +const ScanRuleMetadata = Java.type( + "org.zaproxy.addon.commonlib.scanrules.ScanRuleMetadata" +); - var cweId = 200; - var wascId = 13; - - var url = msg.getRequestHeader().getURI().toString(); - var headers = msg.getResponseHeader().getHeaders("X-Powered-By"); +function getMetadata() { + return ScanRuleMetadata.fromYaml(` +id: 100023 +name: Information Disclosure - X-Powered-By Header +description: > + The web/application server is leaking information via one or more 'X-Powered-By' HTTP response headers. + Access to such information may facilitate attackers identifying other frameworks/components your web application + is reliant upon and the vulnerabilities such components may be subject to. +solution: > + Ensure that your web server, application server, load balancer, etc. is configured to suppress 'X-Powered-By' headers. +risk: low +confidence: medium +cweId: 200 # CWE-200: Exposure of Sensitive Information to an Unauthorized Actor +wascId: 13 # WASC-13: Information Leakage +status: alpha +codeLink: https://github.com/zaproxy/community-scripts/blob/main/passive/X-Powered-By_header_checker.js +helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ +`); +} +function scan(helper, msg, src) { + const headers = msg.getResponseHeader().getHeaders("X-Powered-By"); if (headers != null) { - ps.raiseAlert( - alertRisk, - alertConfidence, - alertTitle, - alertDesc, - url, - "", - "", - "", - alertSolution, - headers, - cweId, - wascId, - msg - ); + const otherInfo = + headers.length > 1 ? `Other values: ${headers.slice(1).toString()}` : ""; + helper + .newAlert() + .setEvidence(headers[0]) + .setOtherInfo(otherInfo) + .setMessage(msg) + .raise(); } } diff --git a/passive/clacks.js b/passive/clacks.js index 49791ec7..04dd6a80 100644 --- a/passive/clacks.js +++ b/passive/clacks.js @@ -27,6 +27,13 @@ helpLink: https://www.zaproxy.org/docs/desktop/addons/community-scripts/ function scan(helper, msg, src) { var headers = msg.getResponseHeader().getHeaders("X-Clacks-Overhead"); if (headers != null) { - helper.newAlert().setMessage(msg).setEvidence(headers).raise(); + const otherInfo = + headers.length > 1 ? `Other values: ${headers.slice(1).toString()}` : ""; + helper + .newAlert() + .setMessage(msg) + .setEvidence(headers[0]) + .setOtherInfo(otherInfo) + .raise(); } } diff --git a/passive/find base64 strings.js b/passive/find base64 strings.js index 02eaa73d..27736189 100644 --- a/passive/find base64 strings.js +++ b/passive/find base64 strings.js @@ -60,10 +60,14 @@ function scan(helper, msg, src) { foundstrings.push(comm[0]); } if (RESULT_PER_URL == true) { + const otherInfo = + foundstrings.length > 1 + ? `Other instances: ${foundstrings.slice(1).toString()}` + : ""; helper .newAlert() .setEvidence(foundstrings[0]) - .setOtherInfo(`Other instances: ${foundstrings.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } diff --git a/passive/find_reflected_params.py b/passive/find_reflected_params.py index bd12bc4d..757bf2e4 100644 --- a/passive/find_reflected_params.py +++ b/passive/find_reflected_params.py @@ -56,6 +56,6 @@ def scan(helper, msg, src): reflected_params.append(param.getName()) if reflected_params and not RESULT_PER_FINDING: - other_info = 'Other instances: ' + u",".join(reflected_params[1:]) + other_info = 'Other instances: ' + u",".join(reflected_params[1:]) if len(reflected_params) > 1 else '' helper.newAlert().setParam(param.getName()).setEvidence(reflected_params[0]).setOtherInfo( other_info).setMessage(msg).raise() diff --git a/passive/google_api_keys_finder.js b/passive/google_api_keys_finder.js index 30fd08bf..b2d61de4 100644 --- a/passive/google_api_keys_finder.js +++ b/passive/google_api_keys_finder.js @@ -56,11 +56,14 @@ function scan(helper, msg, src) { while ((key = re.exec(body))) { foundKeys.push(key[0]); } - + const otherInfo = + foundKeys.length > 1 + ? `Other instances: ${foundKeys.slice(1).toString()}` + : ""; helper .newAlert() .setEvidence(foundKeys[0]) - .setOtherInfo(`Other instances: ${foundKeys.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); } diff --git a/passive/s3.js b/passive/s3.js index ec3d4179..850e87dd 100644 --- a/passive/s3.js +++ b/passive/s3.js @@ -46,10 +46,14 @@ function scan(helper, msg, src) { founds3bucket.push(buckets[0]); } //raise the alert + const otherInfo = + founds3bucket.length > 1 + ? `Other instances: ${founds3bucket.slice(1).toString()}` + : ""; helper .newAlert() .setEvidence(founds3bucket[0]) - .setOtherInfo(`Other instances: ${founds3bucket.slice(1).toString()}`) + .setOtherInfo(otherInfo) .setMessage(msg) .raise(); }