Unlocking IAM's Nokia G-240W-A router (Part 1)

Context

As we recently upgraded our home internet to try out Maroc Telecom’s 100mbps fiber offer, I’ve noticed that the Nokia router they installed had horrible Wi-Fi speed - the max I could to get standing next to it was around 60mbps down, while in the opposite room, the speed was always under 10mbps.

What’s interesting is the fact that it had decent range - I get full bars most of the time, and my Wi-Fi adapter reports a 300mbps data rate.

This got me intrigued, so I tried to take a look at what’s running inside this thing.

First look

Doing a little online research, you will find out that the firmware on this router had quite a bad history.

There’s various blog posts showing that routers running on similar firmware were found to be vulnerable to unauthenticated RCE vulnerabilities, and had hardcoded Telnet backdoor accounts.

The first thing I did, of course, is log into the web administration portal - there’s a sticker in the back of the router with the credentials, which seem device specific, which is good… Or so I thought - as it turns out there’s a known ‘superuser’ account for the web panel, which has a default password: AdminGPON / ALC#FGU

After fiddling with the web interface, trying to force the WiFi to use 802.11n and 40MHz bandwidth, I’ve noticed no difference. It was time to take a deeper dive.

According to someone at lafibre.info forum, Telnet access to this router was possible via the ONTUSER / SUGAR2A041 ‘backdoor’ account, before an update was deployed sometime in June 2018.

I’ve tried the web account credentials as well as any backdoor credentials I was able to find, to no avail. The fact that Telnet access locks for 5 minutes every 3 failed attempts didn’t help as well.

That’s when I’ve decided to take a look at the configuration backup and restore functionnality.

Analysing the backup file

As the ‘AdminGPON’ user, you’re able to import and export the router configuration.

As the exported config.cfg file wasn’t plain-text, I’ve tried looking for magic values, and binwalk was able to find something right away:

$ binwalk config.cfg

DECIMAL     HEXADECIMAL   DESCRIPTION
--------------------------------------------------------------------
20          0x14          Zlib compressed data, default compression

Being the lazy guy that I am, I’ve opened the file in a hex editor, stripped out the first 20 bytes, then pasted the hex dump in CyberChef, and a huge xml file showed up. Bingo!

Exploring the xml a bit, I was able to spot the Telnet credentials (or so I thought):

<TelnetEnable rw="RW" t="boolean" v="True"></TelnetEnable>
<TelnetUserName ml="256" rw="RW" t="string" v="admin"></TelnetUserName>
<TelnetPassword ml="256" rw="RW" t="string" v="OYdLWUVDdKQTPaCIeTqniA==" ealgo="ab"></TelnetPassword>

Alrighty, now how do I crack/decrypt this admin password?

Since this was 128-bit base64 encoded data, I tried MD5’ing known passwords, then I tried every other known hashing algorithm, but none of them gave me anything - so it has to be either salted or encryption.

Exploring a little further, I’ve found another Telnet credential thingy:

<TelnetSshAccount. n="TelnetSshAccount" t="staticObject">
<Enable rw="RW" t="boolean" v="False"></Enable>
<UserName ml="64" rw="RW" t="string" v=""></UserName>
<Password ml="64" rw="RW" t="string" v="" ealgo="ab"></Password>
</TelnetSshAccount.>

But it’s unused… Damn.

Now that modifying the configuration file seemed more necessary, I tried to take a second look at the exported config.cfg.

Modifying the backup file

I kept exporting backup files while changing the router settings to regenerate different files for me to look at.

After a few comparisons with the not so naked eye (I wear glasses), I was able to tell that a single 32-bit value in the header was always changing radically, so this must be a checksum of sorts.

Now before guessing what’s being checksumed and how, I first need to know where the zlib data ends and if there’s something else after it.

So I tried recreating the compressed chunk, which was pretty trivial, again thanks to CyberChef.

It turns out there’s about 300 bytes after it that are unknown, but still, CRC32 on the deflated data gave the exact value at offset 8, which was in big endian. Yay!

Now the null bytes made more sense, and it was clear to me that the other values next to the checksum must be positive integers.

Soon after, I was able to figure out that the first one (+4) was the length of the deflated data, while the second one (+C) was the size of the xml file.

Since in all the config.cfg files that I’ve exported, the rest of the bytes were static, this seemed enough for me to generate my own backup file, and as expected, uploading my crafted config.cfg worked!

Here’s a simple python script I’ve made for this purpose, for those interested.

Unlocking Telnet and SSH access

So I took the password that I’ve set for the the ‘AdminGPON’ web account from:

<WebAccount. n="WebAccount" t="staticObject">
<Enable rw="RW" t="boolean" v="True"></Enable>
<Priority max="10" min="1" rw="RW" t="unsignedInt" v="1"></Priority>
<UserName ml="64" rw="RW" t="string" v="AdminGPON"></UserName>
<Password ml="64" rw="RW" t="string" v="[REDACTED]" ealgo="ab"></Password>
<PresetPassword ml="64" rw="R" t="string" v="xEKUBYh1yT50dQFNwAr/5A==" ealgo="ab"></PresetPassword>
</WebAccount.>

And used it for TelnetPassword config node, but logging in as admin still didn’t work.

Since I can now generate these password hashes (or whatever they are) also, I tried changing my ‘AdminGPON’ password and comparing the resulting value with the TelnetPassword one (OYdLWUVDdKQTPaCIeTqniA==), and after a couple of tries, I’ve realized that the password was simply: admin.

Since we all know that admin:admin is the first login combo that I, and everyone else, use, when trying to login on any panel that’s not ours, it seems like they now ignore these credentials.

But don’t worry, TelnetSshAccount comes to the rescue:

<TelnetSshAccount. n="TelnetSshAccount" t="staticObject">
<Enable rw="RW" t="boolean" v="True"></Enable>
<UserName ml="64" rw="RW" t="string" v="hexdd"></UserName>
<Password ml="64" rw="RW" t="string" v="[REDACTED]" ealgo="ab"></Password>
</TelnetSshAccount.>

I’ve set the ‘Enable’ flag to true, chose a username, and copied my password, and lo and behold, after a reboot, we have root!

Stay tuned for more. :)

1-click RCE with Skype Web Plugin and Qt apps

Context

Earlier this year, I’ve heard that you could send links with custom URI schemes through Discord, that can trigger without the user’s confirmation when using desktop clients.

I’ve been experimenting with URI schemes ever since, I wanted to see how far I can push it in terms of exploitability, and I’ve found that you could do all sort of fun things in windows - stuff like opening the action center or the task switcher, etc. But I have quickly lost interest however, since I’ve realized there was a bunch of filters that Discord has in place, likely due to various reports from researchers, that prevent a lot of the known ‘malicious’ scheme links from triggering (though I still don’t understand Discord’s decision of not simply using a whitelist for schemes instead of adding regex filters ¯\_(ツ)_/¯).

A couple of months later, it hit the headlines that Electronic Arts’ digital distribution platform, Origin, was found to be vulnerable to an RCE through a URI scheme. This was due to an AngularJS template injection, that was met with some bad implementation that exposed Qt’s common desktop services to JavaScript - more on this here.

A second blog post from the same researcher emerged, a couple of days later, where he found another ‘flaw’ in Origin that can be leveraged for RCE, but unlike the first one, this one wasn’t exactly new - it was merely Qt’s plugin feature which can be abused with the right conditions to load arbitrary .dll files through SMB shares …

Following that blog post, I’ve re-enumerated the URL protocols on my machine (using this), and went back to fiddling with URI schemes once more, and soon enough, I was able to find an interesting attack vector …

Qt plugin injection

According to Wikipedia, Qt is a free and open-source widget toolkit for creating GUIs and cross-platform applications, and it seems to be used by quite a lot of apps - a simple explorer search for the Qt core module revealed at least 9 64-bit apps currently installed on my machine:

Most of the modern Qt apps you will find will support a -platformpluginpath commandline option, which specifies a path to start with while loading plugins. What’s interesting here is the fact that SMB shares are also supported, which means one can load plugins remotely.

So the question here would be, how can we start a Qt app with this -platformpluginpath argument so we can have it load our malicious plugin?

Yes, you guessed that right, URI schemes. In order for us to be able to launch an app with our custom input in the commandline, it has to have a registered custom URI handler so we can launch it via a webpage and have the URI be sent as an argument, here’s what that looks like on the registry:

However, there’s a slight problem with this approach (as explained in the second blog post I’ve mentionned earlier), and that’s the fact that modern browsers tend to apply URL encoding to the URI, before the app gets executed, which reduces our chance of pulling off the argument injection, since in most cases we would need to inject a double-quote to break out of the URI argument.

This is where the Skype Web Plugin comes to play …

Skype Web Plugin

When Skype for Web first launched, you could use Skype for instant messaging and share multimedia files, but not as a VoIP tool. To make voice and video calls in most supported browsers, people had to install a plugin.

Doing a quick search online revealed the last available version of the plugin was 7.32.6.278, which was released somewhere in 2017, but is still obtainable via their official CDN: https://swx.cdn.skype.com/plugin/7.32.6.278/SkypeWebPlugin.msi

After Microsoft introduced plugin-free Skype for Web for their supported browsers, they ditched the plugin and dropped support for Internet Explorer. However, the plugin remains available in systems where previously installed, with the only way to get rid of it being a manual uninstall.

The ‘Skype Web Plugin’ registers a custom URI handler: swx, which launches SkypeShell.exe, which is what looks like to be some sort of Internet Explorer shell, which means that it uses IE’s rendering engine (mshtml.dll) to open webpages.

To my surpise, this thing will open any HTTP URL, all you need to do is replace http(s) with swx, and you have a clickable link that forcefully renders your website using mshtml.dll, but here’s the real problem …

First of all, protected mode is off by default, so this may potentially allow for more attack vectors that I’m currently not aware of, since I’m not familiar with what protected mode exactly even does, but this certainly doesn’t sound good.

Secondly (which is what’s more relevant to us), I can load any link with a custom URI from within the shell without having the user to confirm or know anything.

For reference, here’s what trying to exploit a Qt app using plugin injection would prompt for, when running IE (11.116.18362.0) normally:

So not only can we successfully inject arguments regardless of what browser the victim uses, we can also spawn a dosen of apps through URIs simultaneously.

The Proof of Concept

Now that we’ve got a clear idea of the attack vector we will be using, we’ll start on the implementation.

The first thing we would need to do is find target Qt apps to exploit, which are essentially apps that allow loading plugins (nearly all of them do), and that have a registered URI handler.

I’ve found 2 of them already installed on my machine:

I’ve tried to find more apps that meet the 2 conditions to verify my claims, and sure enough with little to no effort I was able to find the Transmission torrent client, which I’m also adding to the list.

Like I said earlier, this is not exactly a new thing, 2 months ago someone wrote this article explaining how Malwarebytes and Cisco Webex Teams (both of which are programs I’ve used before) were found to be vulnerable to this attack, but what’s interesting here, is the stealthiness and reliability that we can accomplish with the help of the Skype Web Plugin …

So I started by simply spamming an .html file with iframes that have calculator:// as a src attribute:

<iframe src='calculator://' height="0" frameborder="0"></iframe>
<iframe src='calculator://' height="0" frameborder="0"></iframe>
...

And as expected, I soon started to hear my CPU fan …

So at the very least this can be used for DoS by spam launching resource heavy apps, so far so good.

Now on to the code execution…

I’ve fired up Visual Studio and made a quick DLL that shows a MessageBox on attach with the process file name as a message.

To make our .dll loadable by Qt, we have to:

  • Add a .qtmetad section with valid plugin metadata: We make VS generate an empty one that we’ll fill out manually with a hex editor using content from the original plugin.
  • Find a plugin we can override: There’s this one called the Windows Integration Plugin (qwindows.dll) which all apps seem to require in order to run on Windows.

To make this stealthy, I’ve added an ExitProcess() right after the DLL is done executing to prevent the target app from showing up, so as of this stage there’s no visible interruption (besides the Skype shell) and the user won’t notice anything.

Now all that’s left for us to have is an SMB share that will host our malicous plugins.

Here’s a detailed video of the PoC in action, from crafting the .dll files to the MessageBox on the 3 apps:

DoS Bonus

I’ve come across this URI ms-cxh-full:// that seems to be the fullscreen version of ms-cxh:// which is something used in Microsoft account setup, but what’s interesting is that this one can cause a black screen that can’t be dismissed, forcing the user to sign out/reboot. ⚠️ Try at your own risk ⚠️

Update

Microsoft released the KB4503293 cumulative update for Windows 10 version 1903 on June 11, which updated Internet Explorer from .116 to .175, which includes sanitization for URI scheme links to prevent command-line argument injection:

Internet Explorer 11.175.18362.0

As a result, this attack is no longer feasible on an up-to-date Windows system.

(edited)