**CVE-2020-13859 Authentication Bypass** A format error in **/etc/shadow** coupled with a logic bug in the LuCI - OpenWrt Configuration Interface framework allows the undocumented system account "mofidev" to login to the management interface without a password. The MoFi firmware has a built-in feature to assist a device owner with regaining access to the device if the root password is unknown. To accomplish this, visiting the "About" page generates a one-time password for the mofidev system account. The mofidev Linux account is defined in /etc/passwd. The one- time password displayed on this page is not the actual password. This value is used as part of the calculation which results in a 6-digit numeric password. A device owner would need to contact Mofi Network's support for further instructions on how to gain access to the "Setup Wizard". ![](https://images.seebug.org/1599209769243-w331s)Figure 2: About page I then discovered that you do not need to contact support for the...
**CVE-2020-13859 Authentication Bypass** A format error in **/etc/shadow** coupled with a logic bug in the LuCI - OpenWrt Configuration Interface framework allows the undocumented system account "mofidev" to login to the management interface without a password. The MoFi firmware has a built-in feature to assist a device owner with regaining access to the device if the root password is unknown. To accomplish this, visiting the "About" page generates a one-time password for the mofidev system account. The mofidev Linux account is defined in /etc/passwd. The one- time password displayed on this page is not the actual password. This value is used as part of the calculation which results in a 6-digit numeric password. A device owner would need to contact Mofi Network's support for further instructions on how to gain access to the "Setup Wizard". ![](https://images.seebug.org/1599209769243-w331s)Figure 2: About page I then discovered that you do not need to contact support for the one-time password. Clicking the "Run Setup Wizard" link will load the _http://IP/cgi- bin/luci/quick/wizard_ page and display a login prompt. Entering _mofidev_ in the username field while leaving the password field blank or set to any string will allow anyone to access the "Quick Wizard". ![](https://images.seebug.org/1599209770267-w331s)Figure 3: Login page for Quick Wizard At this stage, the root password can be changed along with the network parameters. At the end of the wizard, the changes will be saved, and the router will be rebooted resulting in a complete unauthenticated device takeover. ![](https://images.seebug.org/1599209771537-w331s)Figure 4: Root Password Reset To understand the root cause of this vulnerability, we must first understand the Predictable One-Time Password vulnerability. **** **MOFI4500-4GXeLTE - Predictable One Time Password** During static analysis, a script named **/usr/bin/otp.sh** looked very interesting. This custom script is called when visiting the **About** page and generates the one-time password code for Mofi support. It also sets a six- digit password for the "mofidev" account. The code is generated using a static secret resulting in a predictable six-digit password. ![](https://images.seebug.org/1599209772593-w331s)Figure 5: otp.sh The logic shows the following: 1. A random PIN (OTP Code) is generated and stored in /etc/config/otppin. 2. The PIN is concatenated with the static SECRET removing all spaces to create a TOKEN 3. The TOKEN is hashed with MD5 and all letters are removed. 4. The mofidev password is reset to the first six digits of the TOKEN With knowledge of the static SECRET, we can simply generate a TOKEN to reliably determine the password for the mofidev account. The goal was to generate an OTP password and login. When attempting this attack, I generated a six-digit OTP password and it worked on the first try! I was excited but wanted to dig deeper. Then I attempted to SSH as the mofidev user but was unsuccessful, which was puzzling since I was able to login to the web interface using the code. I tried to verify by using **su** from the root account and this also failed, so I downloaded the shadow file and ran it through a numeric wordlist. The password was successfully cracked but was not the same value that I used for the web interface. I found that the About page automatically refreshes every few seconds which kept resetting the OTP password to a different value. By the time I attempted to SSH as mofidev, the password was already changed to a new random password. I fixed this issue by turning Burp Intercept on and sending the request through Repeater. This prevented the automatic refresh from changing the password to a new value. ![](https://images.seebug.org/1599209773879-w331s)Figure 6: Automatic Refresh After verifying that only a single request was sent to the About page, I tried to login via SSH but it failed. To verify the mofidev password, I copied the shadow file and attempted to crack the hash for mofidev which showed the OTP password was set to the same value used for the web authentication. ![](https://images.seebug.org/1599209776323-w331s)Figure 7: Cracking the OTP code Through additional manual testing, I realized I could log in to the web interface as mofidev without a password using the About page. Now I was even more puzzled. After digging deeper, I found the answer in the check password logic defined in **/usr/lib/lua/luci/sys.lua**. This logic is not customized by the vendor and is part of the OpenWrt LuCi framework. The vulnerable logic is highlighted below and results in a "fail open" condition if the password hash is null. ![](https://images.seebug.org/1599209778085-w331s)Figure 8: sys.lua We just demonstrated that the hash was successfully cracked so how could the password hash be null? It turns out the number of fields defined in **/etc/shadow** is important. Notice that the number of fields defined for mofidev is less than the root. ![](https://images.seebug.org/1599209780574-w331s)Figure 9: Malformed /etc/shadow entry Each entry in the [shadow(5)](https://man7.org/linux/man- pages/man5/shadow.5.html) file contains nine fields separated by colons. The mofidev account was missing the last three fields. * Username * Password hash * Date of the last password changed * Minimum password age * Maximum password age * Password warning period * **Password inactivity period** * **Account expiration date** * **Reserved field** Tracing through the function calls shows the reason pwh is nil is because of the call to the [nixio.getsp()](https://neopallium.github.io/nixio/modules/nixio.html#nixio.getsp) function fails as a result of the malformed shadow entry. It defaults to [nixio.getpw()](https://neopallium.github.io/nixio/modules/nixio.html#nixio.getpw) which returns the second field for the mofidev entry in **/etc/passwd** which is ' **x** '. The result of pwh is checked and the function returns **nil, pwe** because pwh is ' **x** '. ![](https://images.seebug.org/1599209781588-w331s)Figure 10: user.getpassword logic Running a simple test with the **user.checkpasswd** logic snippet confirms this issue. If pwh is null, the hash is never checked against the nixio.crypt() function. Not calling nixo.crypt() makes sense because the hash is null; however an authentication decision is based on this result, which means the function _fails open_ resulting in any password value being allowed for the mofidev account. ![](https://images.seebug.org/1599209783183-w331s)Figure 11: Logic test I discovered this issue had already been [reported](https://github.com/openwrt/luci/issues/1700) to the OpenWrt LuCi project in 2018. The developer states this is expected behavior when no password is set for the user. A patch was issued, but I reviewed the changeset and this vulnerability was not fixed. ![](https://images.seebug.org/1599209784715-w331s)Figure 12: LuCi Issue #1700 I created pull request [#4030](https://github.com/openwrt/luci/pull/4030) on 5/7/20 to fix this issue; it has not been accepted by the project yet. My patch prevents the fail-open condition by ensuring that pwh is not null and verifies the hash against the is.crypt() function. ![](https://images.seebug.org/1599209785962-w331s)Figure 13: LuCi patch Both vulnerabilities have been fixed by the vendor and the solution was to delete the undocumented mofidev account. Generating an OTP password is no longer possible on MoFi4500 devices with the latest firmware installed. A similar issue appears to exist in the previous generation MoFi3500 firmware v3.9.2std, however it is unknown if a fix has been publicly released. The MoFi3500 **otp.sh** script does not use a static secret and the format of **/etc/shadow** for the mofidev account is correct. It is unknown when either of these vulnerabilities were introduced, but it appears to have existed since 2015.