Headline
CVE-2019-5165: TALOS-2019-0960 || Cisco Talos Intelligence Group
An exploitable authentication bypass vulnerability exists in the hostname processing of the Moxa AWK-3131A firmware version 1.13. A specially configured device hostname can cause the device to interpret select remote traffic as local traffic, resulting in a bypass of web authentication. An attacker can send authenticated SNMP requests to trigger this vulnerability.
Summary
An exploitable authentication bypass vulnerability exists in the hostname processing of the Moxa AWK-3131A firmware version 1.13. A specially configured device hostname can cause the device to interpret select remote traffic as local traffic, resulting in a bypass of web authentication. An attacker can send authenticated SNMP requests to trigger this vulnerability.
Tested Versions
Moxa AWK-3131A Firmware version 1.13
Product URLs
http://www.moxa.com/product/AWK-3131A.htm
CVSSv3 Score
8.0 - CVSS:3.0/AV:N/AC:H/PR:H/UI:N/S:C/C:H/I:H/A:H
CWE
CWE-288: Authentication Bypass Using an Alternate Path or Channel
Details
The Moxa AWK-3131A Industrial IEEE 802.11a/b/g/n wireless AP/bridge/client is a wireless networking appliance intended for use in industrial environments. It is designed to provide wireless communication capabilities to the environments in which it is deployed. Communication with the device is possible using HTTP, Telnet, and SSH.
Included with the AWK-3131A is a web management portal, through which various configuration changes can be made. During normal operation, access to the sensitive parts of this portal is restricted by a login portal. Only two pages - “/webNonce” and “/Login.asp” - are supposed to be accessible without successfully authenticating. If the traffic originates from the AWK itself, however, the authentication check is not enforced, allowing for access to any valid page without credentials.
Three cases exist that make the AWK interpret a request as local traffic: a request from 127.0.0.1, a request from its own IP address, or a request from its own hostname. By setting the device hostname to the IP address of an attacker machine it is possible to bypass this check as no further verification is performed.
When any request is received, the connecting IP address is extracted from the socket and converted into its numbers-and-dots form before being stored into a buffer of webs variables at offset 0x30. The check to determine whether or not the traffic is local is then performed. If it is determined that the traffic is local, another webs variable (offset 0xd8) is set to the value 0x40 and execution continues.
This can be seen in the following code:
sub_427220: # function used to determine if the connection is local or remote
...
00427318 8fdc0010 lw $gp, 0x10($fp) {var_20}
0042731c 8fc2001c lw $v0, 0x1c($fp) {var_14_1} # load $v0 with the webs address base
00427320 24420030 addiu $v0, $v0, 0x30 # move $v0 to the ip address buffer offset
00427324 00402021 move $a0, $v0 # $a0 now contains a pointer to the IP address
00427328 3c020046 lui $v0, 0x46
0042732c 2445770c addiu $a1, $v0, 0x770c {0x46770c, "127.0.0.1"}
00427330 8f828504 lw $v0, -0x7afc($gp) {strcmp}
00427334 0040c821 move $t9, $v0
00427338 0320f809 jalr $t9 # strcmp(IP_ADDR, "127.0.0.1")
0042733c 00000000 nop
00427340 8fdc0010 lw $gp, 0x10($fp) {var_20}
00427344 10400019 beqz $v0, 0x4273ac # branch to 0x4273ac if IP_ADDR == "127.0.0.1"
00427348 00000000 nop
0042734c 8fc2001c lw $v0, 0x1c($fp) {var_14_1} # load $v0 with the webs address base
00427350 24420030 addiu $v0, $v0, 0x30 # move $v0 to the IP address buffer offset
00427354 00402021 move $a0, $v0 # $a0 now contains a pointer to the IP address
00427358 3c02004c lui $v0, 0x4c
0042735c 24454450 addiu $a1, $v0, 0x4450 {websIpaddr}
00427360 8f828504 lw $v0, -0x7afc($gp) {strcmp}
00427364 0040c821 move $t9, $v0
00427368 0320f809 jalr $t9 # strcmp(IP_ADDR, DEVICE_IP_ADDR)
0042736c 00000000 nop
00427370 8fdc0010 lw $gp, 0x10($fp) {var_20}
00427374 1040000d beqz $v0, 0x4273ac # branch to 0x4273ac if IP_ADDR == DEVICE_IP_ADDR
00427378 00000000 nop
0042737c 8fc2001c lw $v0, 0x1c($fp) {var_14_1} # load $v0 with the webs address base
00427380 24420030 addiu $v0, $v0, 0x30 # move $v0 to the IP address buffer offset
00427384 00402021 move $a0, $v0 # $a0 now contains a pointer to the IP address
00427388 3c02004c lui $v0, 0x4c
0042738c 24454410 addiu $a1, $v0, 0x4410 {websHost}
00427390 8f828504 lw $v0, -0x7afc($gp) {strcmp}
00427394 0040c821 move $t9, $v0
00427398 0320f809 jalr $t9 # strcmp(IP_ADDR, DEVICE_HOSTNAME)
0042739c 00000000 nop
004273a0 8fdc0010 lw $gp, 0x10($fp) {var_20} {_gp}
004273a4 14400006 bne $v0, $zero, 0x4273c0 # continue to 0x4273ac if IP_ADDR == DEVICE_HOSTNAME
004273a8 00000000 nop
004273ac 8fc2001c lw $v0, 0x1c($fp) {var_14_1} # branch location when a "local" connection is detected
004273b0 8c4200d8 lw $v0, 0xd8($v0) # load $v0 with the value at the webs_address_base+0xd8
004273b4 34430040 ori $v1, $v0, 0x40 # $v0 is null at this point, so $v1 is effectively set to 0x40
004273b8 8fc2001c lw $v0, 0x1c($fp) {var_14_1} # load $v0 with the webs_address_base (var_14_1)
004273bc ac4300d8 sw $v1, 0xd8($v0) # storing $v1 (0x40) into the pointer to the local/remote webs variable
004273c0 8fc40030 lw $a0, 0x30($fp) {arg_0} # branch location when a "non-local" connection is detected
004273c4 24050002 addiu $a1, $zero, 2
004273c8 3c020042 lui $v0, 0x42
004273cc 24467438 addiu $a2, $v0, 0x7438 {sub_427438}
004273d0 8fc7001c lw $a3, 0x1c($fp) {var_14_1}
004273d4 8f8281ec lw $v0, -0x7e14($gp) {socketCreateHandler} {0x4c3adc}
004273d8 0040c821 move $t9, $v0 {socketCreateHandler}
004273dc 0411bbce bal socketCreateHandler # continues setup of the connection
004273e0 00000000 nop
Execution continues until a decision needs to be made on whether or not to apply password authentication. This decision is made based on the result of a few checks, performed in sub_419f64 below. First a check is made to see if either of the pages that do not require authentication (/webNonce or /Login.asp) have been requested. If the request is for one of these pages, the function returns 0x0 indicating that no authentication is required, otherwise it continues to the next check. At address 0x00419fec a check is performed to see if the webs local/remote webs variable is set to 0x40, indicating that the traffic is local. When this is true the AWK further checks that the ‘Cookie’ HTTP header is set. If both of these checks pass, the function will again return 0x0 indicating that no authentication is required. When 0x0 is returned to websSecurityHandler from sub_419f64, authentication will not be enforced and the page will be served.
This can be seen in the code below:
websSecurityHandler:
...
0041aba8 8fc40030 lw $a0, 0x30($fp) {arg_0}
0041abac 8fc50040 lw $a1, 0x40($fp) {arg2}
0041abb0 0c1067d9 jal sub_419f64 # call sub_419f64 to determine if authentication needs to be enforced
0041abb4 00000000 nop # when this returns $v0 will contain 0x1 if auth needs enforced and 0x0 if it does not
0041abb8 8fdc0018 lw $gp, 0x18($fp) {var_18}
0041abbc 14400012 bne $v0, $zero, 0x41ac08 # when $v0 is 0x0, the path where authentication is NOT enforced is taken
0041abc0 00000000 nop
...
0041abc4 8f82821c lw $v0, -0x7de4($gp) {0x4c3b0c} {iw_webDebugFlags} # path when authentication is NOT enforced
0041abc8 8c420000 lw $v0, ($v0) {iw_webDebugFlags}
0041abcc 1040000b beqz $v0, 0x41abfc
0041abd0 00000000 nop
...
0041ac08 8fc40030 lw $a0, 0x30($fp) {arg_0} # path when authentication is enforced
0041ac0c 8fc50040 lw $a1, 0x40($fp) {arg2}
0041ac10 0c106829 jal sub_41a0a4
0041ac14 00000000 nop
...
sub_419f64: # function to determine if authentication should be enforced
00419f64 27bdffe0 addiu $sp, $sp, -0x20
00419f68 afbf001c sw $ra, 0x1c($sp) {__saved_$ra}
00419f6c afbe0018 sw $fp, 0x18($sp) {__saved_$fp}
00419f70 03a0f021 move $fp, $sp {var_20}
00419f74 3c1c004d li $gp, 0x4cb8f0
00419f7c afbc0010 sw $gp, 0x10($sp) {var_10} {_gp}
00419f80 afc40020 sw $a0, 0x20($fp) {arg_0}
00419f84 afc50024 sw $a1, 0x24($fp) {arg_4}
00419f88 3c020046 lui $v0, 0x46 # start of block checking for /webNonce
00419f8c 2444665c addiu $a0, $v0, 0x665c {0x46665c, "/webNonce"}
00419f90 8fc50024 lw $a1, 0x24($fp) {arg_4}
00419f94 24060009 addiu $a2, $zero, 9
00419f98 8f828568 lw $v0, -0x7a98($gp) {strncmp}
00419f9c 0040c821 move $t9, $v0
00419fa0 0320f809 jalr $t9
00419fa4 00000000 nop
00419fa8 8fdc0010 lw $gp, 0x10($fp) {var_10}
00419fac 1040000c beqz $v0, 0x419fe0 # if the requested page is /webNonce, jump to the do not enforce authentication block
00419fb0 00000000 nop
00419fb4 3c020046 lui $v0, 0x46 # start of block checking for /Login.asp
00419fb8 2444656c addiu $a0, $v0, 0x656c {data_46656c, "/Login.asp"}
00419fbc 8fc50024 lw $a1, 0x24($fp) {arg_4}
00419fc0 2406000a addiu $a2, $zero, 0xa
00419fc4 8f828568 lw $v0, -0x7a98($gp) {strncmp}
00419fc8 0040c821 move $t9, $v0
00419fcc 0320f809 jalr $t9
00419fd0 00000000 nop
00419fd4 8fdc0010 lw $gp, 0x10($fp) {var_10}
00419fd8 14400004 bne $v0, $zero, 0x419fec # if the requested page is NOT /Login.asp, jump to the next check
00419fdc 00000000 nop
...
00419fec 8fc20020 lw $v0, 0x20($fp) {arg_0} # start of block checking for a request from a local ip or hostname
00419ff0 8c4200d8 lw $v0, 0xd8($v0) # if the request is coming from 127.0.0.1, the device IP, or the device hostname, $v0 will equal 0x40
00419ff4 30420040 andi $v0, $v0, 0x40
00419ff8 10400008 beqz $v0, 0x41a01c # if the request is from a local ip or hostname, do not make the jump
00419ffc 00000000 nop
0041a000 8fc20020 lw $v0, 0x20($fp) {arg_0} # start of block checking to see if the 'Cookie' HTTP header is set
0041a004 8c4200b8 lw $v0, 0xb8($v0) # loading $v0 with a pointer to the 'Cookie' HTTP header
0041a008 10400004 beqz $v0, 0x41a01c # continue to not enforce authentication if the header is set
0041a00c 00000000 nop
0041a010 00001021 move $v0, $zero {0x0} # start of block indicating to not enforce authentication
0041a014 08106823 j 0x41a08c # jump to function epilogue
0041a018 00000000 nop
...
Timeline
2019-11-27 - Vendor Disclosure
2020-02-24 - Public Release
Discovered by Jared Rittle and Carl Hurd of Cisco Talos.