**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
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
!(https://images.seebug.org/1599209771537-w331s)Figure 4: Root Password
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
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
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
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.
* 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
function fails as a result of the malformed shadow entry. It defaults to
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
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