CVE-2019-5153
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.
Moxa AWK-3131A Firmware version 1.13
http://www.moxa.com/product/AWK-3131A.htm
9.9 - CVSS:3.0/AV:N/AC:L/PR:L/UI:N/S:C/C:H/I:H/A:H
CWE-121: Stack-based Buffer Overflow
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
(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)
2019-10-31 - Vendor Disclosure
2020-02-20 - Public Release
Discovered by Jared Rittle of Cisco Talos.