Blog:

Breaking (Bad) Cross-Site Request Forgery Protection – The Netgear Nighthawk M1

G Richter 10 Aug 2019

What is CSRF?

Cross-site Request Forgery (CSRF) is a descriptive term, but pretty oblique if you don’t know exactly what it means. Broken down, it’s pretty simple:

  • A malicious web page running in your browser can send requests to other sites.
  • When it sends those requests, it’ll use the current session cookie stored in your browser.
  • So if you’re logged into something (or even if you’re not), the malicious page can send requests from your browser context, with the privileges of your current session on that site.

Basically, the malicious web page can try to “forge” “requests” from its own site to another (“cross-site”). So it’s “Cross-Site Request Forgery”. Simple!

If you’re running a web application, and you haven’t implemented some kind of CSRF protection, then you’re not checking whether requests originated from pages you control. Baddies might rely on this to make requests to a site with the current session permissions of a logged-in user. They might try to change that user’s password, for instance, and steal their account.

However, a malicious page can’t usually read data back from another site. There’s a policy in all browsers called the Same Origin Policy. By default, if one page asks to read the contents of another site, the answer is a resounding NO.

There are exceptions to this, but they can be discussed in depth another time. Regardless, the SOP stops one page from reading the contents of another.

So, usually, a CSRF-protection mechanism looks like this:

  • A token is issued by a website.
  • The token is checked when a request is parsed, and rejected if the token’s wrong.

The assumption is that since a malicious site can’t read back responses from another site, it can’t know the token, so it can’t send valid requests.

There are (obvious?) exceptions

Let’s say you want to embed an image off another server. Unless the server application implements an extremely heavy cross-origin protection logic, or some other kind of authentication, then you’ll probably be allowed to do this. This would fall into the category of cross-origin embedding.

Mozilla have a handy breakdown of what’s allowed to be embedded:

Cross-origin embedding is fine. You can embed scripts, images, CSS, media, fonts – all kinds of web trash.

What does this mean for routers?

CSRF is particularly interesting in the case of devices running on your LAN which might have web interfaces exposed – e.g. routers. While your router web interface might not be directly accessible over the “Internet”, it is accessible from your browser. And, as we know, since malicious websites can try to make requests anywhere, they can even try to make requests to your router.

So, it makes sense that routers would include CSRF protection mechanisms. That’s exactly what the Netgear Nighthawk M1 has.

Netgear Nighthawk M1 CSRF Protection

The Netgear Nighthawk M1 web interface has a CSRF protection token mechanism. All requests to the API endpoints have to have a token parameter, and look a bit like this:

POST /Forms/config HTTP/1.1

Host: 192.168.1.1

User-Agent: Mozilla/5.0 (X11; Linux x86_64; rv:60.0) Gecko/20100101 Firefox/60.0

Accept: */*

Accept-Language: en-GB,en;q=0.5

Accept-Encoding: gzip, deflate

Referer: http://192.168.1.1/index.html

Content-type: application/x-www-form-urlencoded

Content-Length: 113

Cookie: sessionId=0000000d-bW1XS27NZzc39egmHaPFXRP9vuZl3wp

Connection: close

general.shutdown=restart&err_redirect=/error.json&ok_redirect=/success.json&token=k8BKKSfEUxxMlrrFaCyQgGuTMGRSbnl

This token is retrieved under normal circumstances by reading the contents of a file at /api/model.json, which is a dynamically-generated file containing a JSON object of various configuration and session values, including the session.secToken.

[...snip...]

"session": {

"userRole": "Guest",

"lang": "en",

"hintDisplayPassword": "",

"secToken": "oEO6wjnuQWBQJmtAcrVCj8forV1MtnW",

"clientIP": "192.168.1.44",

"supportedLangList": [

{

"id": "en",

"isCurrent": true,

"label": "English"

},

[...snip...]

Now, we can’t simply embed this JSON object in JavaScript on a 3rd party page and read it back. Pure JSON’s not allowed. If it were JSONP, that would be different. JSONP wraps a JSON object in a function call, so you can embed it and access it like a normal script. Browsers allow that.

You know what browsers also allow? Plain old JavaScript

Turns out the file at /js/NetgearStrings.js is also dynamically-generated. And contains this nice netgearLoadData variable at the top.

Which happens to contain the session.secToken value as well.

Conclusion

It’s not big, it’s not clever. But it’s definitely the easiest CSRF bypass I’ve seen. A bit like leaving the key under the mat. Or, just totally misunderstanding the reasoning for having a key for the house, and not locking the door.

I’m also not entirely sure how TODO code gets left on production devices by major US manufacturers. grep is a very easy tool to use.

This issue has been assigned CVE-2019-14526. A post-authentication command injection issue we found has also been assigned CVE-2019-14527.