Talos Vulnerability Report

TALOS-2019-0960

Moxa AWK-3131A iw_webs hostname Authentication Bypass Vulnerability

February 24, 2020
CVE Number

CVE-2019-5165

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 sub419f64 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 sub419f64, 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

Credit

Discovered by Jared Rittle and Carl Hurd of Cisco Talos.