paint-brush
How we kindly asked our users to update their app via an XSS attackby@devbook
248 reads

How we kindly asked our users to update their app via an XSS attack

by DevbookMarch 25th, 2021
Read on Terminal Reader
Read this story w/o Javascript
tldt arrow

Too Long; Didn't Read

Devbook is a desktop app that allows developers to search Stack Overflow, official documentation, and code on GitHub. It's the first step in building a search engine for developers. We forgot to ship the auto-update functionality in the first version of Devbook. We injected our custom script to the onerror event listener on the <img/tag tag on the tag to get the update prompt. This means the HTML code isn't sanitized and we can use Electron's API to find out the version of the app.
featured image - How we kindly asked our users to update their app via an XSS attack
Devbook HackerNoon profile picture

Devbook is a desktop app that allows developers to search Stack Overflow, official documentation, and code on GitHub. It's the first step in building a search engine for developers.

We shared a crude first version of Devbook on Hacker News in December. HN folks seemed to like it. Devbook was on the front page and eventually got to the #5 spot. We were excited! People might actually like what we built!

Feedback and suggestions started coming in. It was time to work on an update. With this momentum, working on the next version was easy. We shipped a new update in the next few days and were ready to hear what people think.

Here came the horrible realization though - we forgot to ship the auto-update functionality in the first version. How will people already using Devbook update to the new version? This isn't a website or a mobile app. Devbook is a desktop app distributed outside of any app store. People don't just receive notifications about a new version.

About 500 users from HN were stuck on the first version, unable to update and we had no way to reach out to them. In the grand scheme of things, 500 users are not that many. For us though, it was crucial to get them to update to the new version and learn what they think. Those 500 users were everything. We had to come up with some way to inform them about a new version.

Time to get creative. We started thinking. The app is communicating with our server every time a user searches. So there might be a way to get our message across. Maybe every time when a user searches in Devbook we could change the content of the first result so it tells the user to update?

Then it hit us. What we were actually sending as a search response from our server was the actual Stack Overflow HTML. In Devbook, we just (dangerously and with great ignorance) injected the HTML into the app's frontend. That's great (if you ignore the security implications of course)! It means we can change the HTML to whatever we want!

Yeah, but how do we know which users should actually receive the custom HTML? We had people using two versions of the app - one without the auto-updater and one with the auto-updater. Well, here comes the ugliness of all this. As I said above, we were injecting the HTML code directly into the app's frontend. This means the HTML code isn't sanitized. With this, we can theoretically run any code we want. We could use Electron's API to find out the version of the app and show the update prompt based on that.

That's exactly what we ended up doing. We injected our custom script to the onerror event listener on the <img/> tag

<img style="display:none" onerror="update_code" src="#"/>

Here's the actual code prompting users to update

Vaclav Mlejnsky profile image
Vaclav Mlejnsky
Edit profile
JOINED
Jul 12, 2019
More from Vaclav Mlejnsky
Search Stack Overflow, docs, and code on GitHub from a single app
#productivity #programming #showdev
What productivity tools are you using?
#discuss #programming #productivity #devtools
// Cleanup the old download reminder
clearTimeout(window.devbookUpdateHandle);
if (!window.isDevbookNewVersionCheckDisabled) {
  const remote = window.require("electron").remote;
  const appVersion = remote.app.getVersion();
  if (appVersion === "0.0.1") {
    function askForNewVersionDownload() {
      window.isDevbookNewUpdateSilenced = true;
      const shouldDownload = confirm("New Devbook version is available \\n\\n Click OK to download. \\n\\n You must install the new version manually!");
      if (shouldDownload) {
        remote.shell.openExternal("<new_version_url>");
      } else {
        clearTimeout(window.devbookNewUpdateHandle);
        const updateHandle = setTimeout(() => {
          askForNewVersionDownload();
        }, 8 * 60 * 60 * 1000);
        window.devbookNewUpdateHandle = updateHandle;
      }
    }
    if (!window.isDevbookNewUpdateSilenced) {
      setTimeout(() => {
        askForNewVersionDownload();
      }, 4000);
    }
  }
}
window.isDevbookNewVersionCheckDisabled = true;

This is the actual model that a user sees

Eventually, about 150 users updated through this hack to a Devbook version that has an auto-updater.