Talos Vulnerability Report

TALOS-2019-0944

Moxa AWK-3131A iw_webs User Configuration Remote Code Execution Vulnerability

February 24, 2020
CVE Number

CVE-2019-5153

Summary

An exploitable remote code execution vulnerability exists in the iw_webs configuration parsing functionality of the Moxa AWK-3131A firmware version 1.13. A specially crafted user name entry can cause an overflow of an error message buffer, resulting in remote code execution. An attacker can send commands while authenticated as a low privilege user 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

9.9 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H

CWE

CWE-121: Stack-based Buffer Overflow

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 web-based interface is the ability to import and export device configuration files. These files contain a variety of options that control the device's settings and are one of the ways that a user can make configuration changes. Among the available options is the ability to configure additional users for the device, similar to the functionality exposed in "Main Menu -> Maintenance -> Account Settings." When an attempt is made to add a user via a configuration file, a check is made to verify that the username conforms to the requirements. In the case that the username does not conform, an error message is generated using a sprintf call similar to the following:

sprintf(<stack_buffer>, "Failed to set (%s: %s) to '%s'.\n", "account2", "username", <user_controlled_input>)

When a username of 0x110 bytes is entered, the second to last word will overwrite the return address value after execution has returned from the iw_websRedirect function.

Disassembly for the affected binaries can be found below:

# iw_webs
# sub_454fe4
...
00455248  93c30024   lbu     $v1, 0x24($fp) {var_13c}  {0x0}
0045524c  27c20030   addiu   $v0, $fp, 0x30 {var_130}
00455250  afa30010   sw      $v1, 0x10($sp) {var_150}  {0x0}
00455254  3c030047   lui     $v1, 0x47
00455258  246410dc   addiu   $a0, $v1, 0x10dc  {0x4710dc, "/var/web_config.ini"}
0045525c  24050006   addiu   $a1, $zero, 6
00455260  00403021   move    $a2, $v0 {var_130}
00455264  24070100   addiu   $a3, $zero, 0x100
00455268  8f828888   lw      $v0, -0x7778($gp)  {iw_configbyINI}
0045526c  0040c821   move    $t9, $v0 
00455270  0320f809   jalr    $t9                                                             # iw_configByINI gets called, parsing the passed config file and overflowing
00455274  00000000   nop     
...
004554b4  27c20030   addiu   $v0, $fp, 0x30 {var_130}
004554b8  8fc40160   lw      $a0, 0x160($fp) {arg_0}                                         # $a0 gets set into the user-controlled buffer
004554bc  3c030047   lui     $v1, 0x47
004554c0  24651108   addiu   $a1, $v1, 0x1108  {data_471108, "ConfirmConfImp.asp"}
004554c4  00003021   move    $a2, $zero  {0x0}
004554c8  00403821   move    $a3, $v0 {var_130}
004554cc  8f828208   lw      $v0, -0x7df8($gp)  {iw_websRedirect}  {0x4c3af8}
004554d0  0040c821   move    $t9, $v0  {iw_websRedirect}
004554d4  0411af97   bal     iw_websRedirect
004554d8  00000000   nop     
...

# libiwUtil.so
# iw_configbyINI
...
0006f11c  24020800   addiu   $v0, $zero, 0x800
0006f120  afc20034   sw      $v0, 0x34($fp) {var_5c4_2}  {0x800}
0006f124  8fc20028   lw      $v0, 0x28($fp) {var_5d0_1}
0006f128  afa20010   sw      $v0, 0x10($sp) {var_5e8_2}
0006f12c  8fc40600   lw      $a0, 0x600($fp) {arg_8}
0006f130  8f828040   lw      $v0, -0x7fc0($gp)  {data_ce070}
0006f134  24452aa0   addiu   $a1, $v0, 0x2aa0  {0x82aa0, "Failed to set (%s: %s) to '%s'.\n"}
0006f138  8fc60020   lw      $a2, 0x20($fp) {var_5d8}
0006f13c  8fc70024   lw      $a3, 0x24($fp) {var_5d4_1}
0006f140  8f8286e0   lw      $v0, -0x7920($gp)  {sprintf}
0006f144  0040c821   move    $t9, $v0
0006f148  0320f809   jalr    $t9                                                             # sprintf is writing to an offset from $fp but not checking bounds
0006f14c  00000000   nop     
...

# iw_webs
# iw_websRedirect
00441334  27bdf7d8   addiu   $sp, $sp, -0x828
00441338  afbf0824   sw      $ra, 0x824($sp) {__saved_$ra}
0044133c  afbe0820   sw      $fp, 0x820($sp) {__saved_$fp}
00441340  03a0f021   move    $fp, $sp {var_828}
00441344  3c1c004d   li      $gp, 0x4cb8f0
0044134c  afbc0010   sw      $gp, 0x10($sp) {var_818}  {_gp}
00441350  afc40828   sw      $a0, 0x828($fp) {arg_0}                                         # arg_0 gets set into the user-controlled buffer
00441354  afc5082c   sw      $a1, 0x82c($fp) {arg_4}
00441358  afc60830   sw      $a2, 0x830($fp) {arg_8}
0044135c  afc70834   sw      $a3, 0x834($fp) {arg_c}
00441360  27c20838   addiu   $v0, $fp, 0x838 {arg_10}
00441364  afc2001c   sw      $v0 {arg_10}, 0x1c($fp) {var_80c}
00441368  8fc2001c   lw      $v0, 0x1c($fp) {var_80c}
0044136c  27c30020   addiu   $v1, $fp, 0x20 {var_808}
00441370  00602021   move    $a0, $v1 {var_808}
00441374  8fc50834   lw      $a1, 0x834($fp) {arg_c}
00441378  00403021   move    $a2, $v0
0044137c  8f828884   lw      $v0, -0x777c($gp)  {vsprintf}
00441380  0040c821   move    $t9, $v0
00441384  0320f809   jalr    $t9
00441388  00000000   nop     
0044138c  8fdc0010   lw      $gp, 0x10($fp) {var_818}  {_gp}
00441390  afc20018   sw      $v0, 0x18($fp) {var_810}
00441394  8fc20018   lw      $v0, 0x18($fp) {var_810}
00441398  28420800   slti    $v0, $v0, 0x800
0044139c  14400004   bne     $v0, $zero, 0x4413b0
004413a0  00000000   nop     
...
004413b0  27c20020   addiu   $v0, $fp, 0x20 {var_808}
004413b4  00402021   move    $a0, $v0 {var_808}
004413b8  8fc50830   lw      $a1, 0x830($fp) {arg_8}
004413bc  0c10ffdd   jal     iw_websSetErrorString
004413c0  00000000   nop     
004413c4  8fdc0010   lw      $gp, 0x10($fp) {var_818}
004413c8  8fc40828   lw      $a0, 0x828($fp) {arg_0}                                         # $a0 gets set from arg_0
004413cc  8fc5082c   lw      $a1, 0x82c($fp) {arg_4}
004413d0  8f8280c8   lw      $v0, -0x7f38($gp)  {websRedirect}  {0x4c39b8}
004413d4  0040c821   move    $t9, $v0  {websRedirect}
004413d8  0411a3ed   bal     websRedirect
004413dc  00000000   nop     
...

# iw_webs
# websRedirect
0042a390  27bdffc8   addiu   $sp, $sp, -0x38
0042a394  afbf0034   sw      $ra, 0x34($sp) {__saved_$ra}
0042a398  afbe0030   sw      $fp, 0x30($sp) {__saved_$fp}
0042a39c  03a0f021   move    $fp, $sp {var_38}
0042a3a0  3c1c004d   li      $gp, 0x4cb8f0
0042a3a8  afbc0018   sw      $gp, 0x18($sp) {var_20}  {_gp}
0042a3ac  afc40038   sw      $a0, 0x38($fp) {arg_0}                                           # arg_0 gets set into the user-controlled buffer
...
0042a504  3c02004    li      $v0, 0x467bb0  {"http://%s/%s"}
0042a50c  afc20020   sw      $v0, 0x20($fp) {var_18_1}  {data_467bb0, "http://%s/%s"}
0042a510  8fc20038   lw      $v0, 0x38($fp) {arg_0}                                           # $v0 gets set from arg_0
0042a514  8c4200d8   lw      $v0, 0xd8($v0)                                                   # $v0 gets dereferenced and must contain a valid address
0042a518  30428000   andi    $v0, $v0, 0x8000
0042a51c  10400004   beqz    $v0, 0x42a530
0042a520  00000000   nop     
...

# iw_webs
# sub_454fe4
...
004554b4  27c20030   addiu   $v0, $fp, 0x30 {var_130}
004554b8  8fc40160   lw      $a0, 0x160($fp) {arg_0}
004554bc  3c030047   lui     $v1, 0x47
004554c0  24651108   addiu   $a1, $v1, 0x1108  {data_471108, "ConfirmConfImp.asp"}
004554c4  00003021   move    $a2, $zero  {0x0}
004554c8  00403821   move    $a3, $v0 {var_130}
004554cc  8f828208   lw      $v0, -0x7df8($gp)  {iw_websRedirect}  {0x4c3af8}
004554d0  0040c821   move    $t9, $v0  {iw_websRedirect}
004554d4  0411af97   bal     iw_websRedirect
004554d8  00000000   nop     
004554dc  8fdc0018   lw      $gp, 0x18($fp) {var_148}  {_gp}                                 # the call to iw_websRedirect -> websRedirect returns
...
004554e0  03c0e821   move    $sp, $fp
004554e4  8fbf015c   lw      $ra, 0x15c($sp) {__saved_$ra}                                   # $ra is loaded with a value from the user-controlled buffer
004554e8  8fbe0158   lw      $fp, 0x158($sp) {__saved_$fp}
004554ec  27bd0160   addiu   $sp, $sp, 0x160
004554f0  03e00008   jr      $ra                                                             # execution flow is controlled
004554f4  00000000   nop     

Crash Information

(gdb) c
Continuing.

Program received signal SIGBUS, Bus error.
0x42424242 in ?? ()
(gdb) i r
          zero       at       v0       v1       a0       a1       a2       a3
 R0   00000000 00000001 00000000 00000001 2b0113e8 00000000 00000000 00000000 
            t0       t1       t2       t3       t4       t5       t6       t7
 R8   00000000 80000008 80089458 fffffff0 6e2e0d0a 09093c2f 626f6479 3e3c2f68 
            s0       s1       s2       s3       s4       s5       s6       s7
 R16  00000000 00000000 00000000 004b3775 2aaca818 00410000 00000002 00000000 
            t8       t9       k0       k1       gp       sp       s8       ra
 R24  00000008 2af247c0 00000001 00000000 004cb8f0 7fe71610 41414141 42424242 
        status       lo       hi badvaddr    cause       pc
      0100ff13 9999999a 00000001 42424242 00800010 42424242 
          fcsr      fir      hi1      lo1      hi2      lo2      hi3      lo3
      00000000 00000000 00000000 00000000 00000000 00000000 00000000 00000000 
        dspctl  restart
      00000000 00000000 
(gdb) 

Timeline

2019-10-31 - Vendor Disclosure
2020-02-20 - Public Release

Credit

Discovered by Jared Rittle of Cisco Talos.