Hackerman's Hacking Tutorials

The knowledge of anything, since all things have causes, is not acquired or complete unless it is known by its causes. - Avicenna

Sep 19, 2024 - 5 minute read - Comments - Attack Surface Analysis

Steam's 'Open in Desktop' Button

This is not a bug, but some notes about the new Steam "Open in Desktop" button. I am going to show how to look for bugs in these kinds of browser-to-desktop interactions.

When you go to a game's Steam page in the browser, you get this button.

Game page

Clicking on it, will open the game page in the Steam desktop app.

Every time you see a web to app transition without any user notification, a security control has been circumvented. Whether this is good or bad is not the objective here.

Summary: How does it Work?

  1. WebSocket connection from the web page to the Steam desktop app at localhost:27060.
  2. Web page passes a message like this to the desktop app.
     {
       "message": "OpenSteamURL",
       "url": "steam://openurl/https://store.steampowered.com/app/1517290/Battlefield_2042/?utm_bid=3546095213808494257",
       // removed
     }
    
  3. Steam desktop opens the page URL.

How Can I Also See It?

There are only a few ways to bypass those browser security controls and it's almost always a WebSocket.

  1. Go to the BF 2042 page at https://store.steampowered.com/app/1517290/Battlefield_2042/.
  2. F12 to open Developer Tools (I assume you're using a Chromium based browser).
    1. Edge annoyingly asks if you actually want to open DevTools. Check the box so it doesn't ask again.
  3. Switch to the Network tab.
  4. ctrl + F5 to refresh the page.
  5. Click on Open in Desktop. Switch back to Dev Tools.
  6. There will be a bunch of junk here that we have to sift through.
    1. Optionally, you could use Burp and filter these in HTTP Proxy.
  7. Click on the Status column to sort by the response status code.
  8. See this 101 on top? That's what we want.

WebSocket handshake

It's even conveniently named openindesktopclient.js. Click on it to see the header, request, response, and messages.

WebSocket handshake

101 is the response code for switching protocols. While the Protocol upgrade mechanism is technically protocol agnostic, I have only seen it in WebSocket connections. Upon further searching, it looks like it can also be used to upgrade an HTTP/1.1 connection to HTTP/2.

I Wanna See the WebSocket Messages

You can click on the Message tab in the previous image or go the Network tab of DevTools Click WS. You can even filter messages by connection (which is supposedly useful if you have multiple ones in the same page which I've never seen).

WebSocket Messages

The 3rd message is the one that opens the page.

{
  "message": "OpenSteamURL",
  "url": "steam://openurl/https://store.steampowered.com/app/1517290/Battlefield_2042/?utm_bid=3546095213808494257",
  "universe": 1,
  "accountid": 0,
  "sequenceid": 2
}

If you want to see which process is doing this, run netstat -anb in an admin prompt and look for who is listening on 127.0.0.1:27060. It's steam.exe.

Protocol Handlers

This is actually the Steam protocol handler. And that can also lead to a bunch of RCEs.

  1. Close Steam. As in right-click on the taskbar icon and select Exit Steam.
  2. Run Process Monitor.
  3. Press F12 to open DevTools for this page and select the Console tab.
  4. Click on this link. Hover you mouse over it to see the actual link matches the caption.
    1. steam://openurl/https://store.steampowered.com/app/1517290/Battlefield_2042/.
  5. See the browser pop-up that asks if you want to open Steam.
  6. If you click on Open Steam, Steam desktop will open and navigate the BF 2042 page.

You can see the protocol handler in the Console tab of DevTools.

Protocol handler in the Console tab

Steam is actually executed with this protocol handler as the parameter. Switch to Procmon and press ctrl + t or Tools (menu) > Process Tree. Procmon is cutting off the complete parameter in the screenshot.

Steam launched in Procmon

This blog is just trying to show where to look for these things. If you want to learn more please start with the following links:

  1. Eric Lawrence's (he also wrote Fiddler) excellent blog: Web-to-App Communication: App Protocols.
  2. a Attack Surface Analysis - Part 2 - Custom Protocol Handlers.

Why Use a WebSocket?

A WebSocket is the most common way to bypass the annoying protocol handler dialog because it's not bound by the Same Origin Policy.

It's not always a WebSocket server. Here's a bug by Jonathan Leitschuh where it turns out Zoom was using a local web server (that even remained on the machine after removing Zoom) to do "seamless transition."

So How do I Find Bugs Here?

The moment you see a local web server or WebSocket server, you need to open Burp and change the Origin header.

  1. Go to the website in Burp.
  2. Select the WebSocket handshake request (the one with the 101 response header).
  3. Send to Repeater. Hint: ctrl + r thanks to Agarri's Burp course.
  4. Switch to Repeater. Hint: ctrl + shift + r.
  5. Change the Origin header to something else like https://example.net.
  6. ???
  7. Click send.

If this goes through then you have a bug. You can connect to the local WebSocket server from any website and send requests.

But in this case, we cannot. Fiddle with the Origin header and see what is accepted. It's only https://store.steampowered.com and not even other subdomains.

Fiddling with the Origin header

This means it's not vulnerable (at least from this attack surface). This issue is so common that is even has its own specific CWE: CWE-1385: Missing Origin Validation in WebSockets.

Note the browser sets the Origin header and it cannot be modified by JavaScript because it's a Forbidden Header.

But what if I have a malicious local app? If you're running a local app that wants to spoof the Origin header then you have bigger problems.

But muh persistence tradecraft! lol, shut up!

There are other way to do web-to-app communication apart from protocol handlers and WebSockets. See Browser Architecture: Web-to-App Communication Overview

How about You Show Us Some Actual Bugs!

Here are a bunch of references:

  1. Websites Can Run Arbitrary Code on Machines Running the ‘PlayStation Now’ Application.
    1. The images were not disclosed the report, but the H1 report starts from scratch and shows how to find an RCE in a local WebSocket server.
  2. CVE-2021-43907 - Remote Code Execution in Visual Studio Code's Remote WSL Extension
    1. This is basically the same bug as before.
    2. See me and a bunch of people from HackerNews complain about MSRC.
  3. localghost: Escaping the Browser Sandbox Without 0-Days
  4. Tavis Ormandy found a similar issue in Logitech Options
  5. Full Steam Ahead: Remotely Executing Code in Modern Desktop Application Architectures by Thomas Shadwell in Infiltrate 2019.

Obviously, there's more, but I am le tired. Thanks for reading and you know where to find me.

Knee Deep in tree-sitter CST

comments powered by Disqus