If you’re managing a Matrix <-> IRC bridge and rely on the popular matrix-appservice-irc package, you’ll want to pay attention to CVE-2022-39203. This vulnerability was discovered in September 2022 and patched in version .35.. In this exclusive explainer, I’ll break down what happened, how the exploit works, and what you can do to protect your users right now, with code snippets and actionable advice throughout.

What is matrix-appservice-irc?

matrix-appservice-irc is an open-source Node.js bridge that allows chat between Matrix and IRC networks. It acts as a middleman, letting users on either side communicate seamlessly. It’s widely used by communities looking to integrate IRC and Matrix rooms.

What is CVE-2022-39203?

CVE-2022-39203 is a security flaw introduced by how the bridge identifies and maps channels/rooms between Matrix and IRC. By sending specially crafted strings, an attacker could “confuse” the bridge into thinking two separate channels are actually the same. This lets them merge an attacker-controlled channel with an existing one on the IRC side, escalating their permissions – for example, granting themselves operator (“ops”) status.

Vulnerability summary:  
> Attackers can specify a specific string of characters, which would confuse the bridge into combining an attacker-owned channel and an existing channel, allowing them to grant themselves permissions in the channel.

Patched in: matrix-appservice-irc version .35.  
Potential workaround: Disable dynamic channel joining:  

dynamicChannels:
  enabled: false

How Did the Exploit Work?

Here’s a simplified description. The bridge keeps a mapping of Matrix rooms to IRC channels. It relies on string matching when working out which channel a Matrix room is linked to. If an attacker creates a Matrix room with a carefully chosen name (like one using weird character encoding, or hidden Unicode “looks similar” tricks), the bridge can be fooled into thinking this room matches an existing channel, or that two rooms should be mapped to the same channel in IRC.

Let’s look at a rough example from a pre-patch scenario

// vuln-snippet.js (used only for demonstration)
function getIrcChannel(matrixRoomId) {
  // (oversimplified)
  // Assumes the matrixRoomId after some conversions matches an IRC channel
  return "#" + matrixRoomId.toLowerCase();
}

// Attacker chooses matrixRoomId: "ExampleRoom "
const channel1 = getIrcChannel("ExampleRoom");   // "#exampleroom"
const channel2 = getIrcChannel("ExampleRoom ");  // "#exampleroom "

// IRC channel comparison fails to check for subtle distinctions

if (channel1 === channel2) {
   // Would (incorrectly) treat as same channel
}

Now, suppose the bridge actually does some normalization or Unicode folding, it becomes easy to introduce edge cases where "ExampleRoom" and "ExаmplеRoom" (with some Cyrillic/Unicode letters) *look identical* but are internally different, or vice versa.

The attacker could

1. Create a fake Matrix room that, due to normalization tricks, collides with the identifier of an existing bridged channel.

Trick the bridge into treating both as the same IRC channel.

4. Modify things like channel modes or permissions through the Matrix side, gaining unauthorized IRC privileges.

(Simplified) PoC Exploit

*Note: For safety, never run exploits on production systems or without consent. This is for educational awareness only.*

# Attacker creates a new Matrix room with crafted name:
# Exploits character encoding mishandling
curl -X POST "https://your.matrix.server/_matrix/client/r/createRoom"; \
     -d '{"room_alias_name":"targetedroom_specialchar"}' \
     -H "Authorization: Bearer <ACCESS_TOKEN>"


*Replace specialchar with a non-standard Unicode—say, a non-breaking space or lookalike letter. This could cause the bridge, if unpatched, to combine this new room with the legitimate target.*

Then, by joining this room and interacting through the bridge, the attacker could end up with permissions they shouldn’t have in the IRC channel.

What Was The Fix?

SOURCE: matrix-org/matrix-appservice-irc#1657

The developers fixed the flaw by hardening how channel names are compared and sanitized, ensuring no accidental overlap from weird encoding or spaces.

In short:  
- More robust normalization of room/channel names.

1. Upgrade Immediately

Upgrade your matrix-appservice-irc bridge to .35. or newer.

With npm

npm install matrix-appservice-irc@latest

Check your version

npx matrix-appservice-irc -V
# Should show .35. or later

If you can’t upgrade, use the following configuration in your config.yaml

dynamicChannels:
  enabled: false


This stops the bridge from connecting Matrix rooms to *new* IRC channels not specifically mentioned in your config file. Existing mappings remain, but attackers can’t create new links on the fly.

3. Audit Current Bridges

Double-check any Matrix rooms mapped to IRC channels. Remove any suspicious or unauthorized Matrix rooms. Review your logs for unusual channel join events, especially ones with odd Unicode or whitespace in room IDs.

References and More Details

- matrix-org/matrix-appservice-irc Security Advisory
- CVE details page
- Upgrade release notes (v.35.)


Summary:  
CVE-2022-39203 let attackers potentially take over IRC channels by crafting special Matrix room names, exploiting loose mapping logic in matrix-appservice-irc. It’s fixed in .35.. Operators should upgrade ASAP, or disable new dynamic channel joining as a temporary safeguard.

*Stay safe, keep your bridges secure, and keep an eye on those changelogs!*

Timeline

Published on: 09/13/2022 19:15:00 UTC
Last modified on: 09/16/2022 02:38:00 UTC