In June 2022, the security community discovered CVE-2022-3095, a nuanced yet dangerous bug in Dart’s URI parsing logic. This vulnerability, which also affects Flutter, stems from a critical mismatch between Dart’s RFC 3986-based URI parsing and the WhatWG URL standard that most browsers use. The difference centers around the handling of the backslash \ character in URIs. If left unpatched, this quirk can be abused to bypass authorization checks in Dart and Flutter-based web applications.

In this article, we’ll break down the vulnerability in simple terms, provide code snippets to illustrate the issue, offer guidance on mitigation, and conclude with proof-of-concept details.

The Fundamentals: RFC 3986 vs. WhatWG URL Parsing

Dart (prior to version 2.18) and Flutter (prior to 3.30) strictly follow RFC 3986 for URI parsing. This standard views the backslash \ as a valid path character.

Browsers, however, use the WhatWG URL Standard, which treats the backslash as a path delimiter—equivalent to the forward slash /.

This mismatch can be problematic. If a web app uses Dart’s URI parser for security checks, but browsers interpret URLs differently, bad actors could slip malicious URLs past your checks.

Official References

- Dart Security Advisory
- CVE Details for CVE-2022-3095
- RFC 3986 URI Spec
- WhatWG URL Spec

Here’s a simple Dart server-side code snippet

import 'dart:io';

void handleRequest(HttpRequest request) {
  final uri = request.uri;
  // Check for protected area
  if (uri.path.startsWith('/admin')) {
    if (!isAuthenticated(request)) {
      request.response.statusCode = HttpStatus.unauthorized;
      request.response.write('Unauthorized');
      request.response.close();
      return;
    }
  }
  // Serve page
  request.response.write('Welcome to: ${uri.path}');
  request.response.close();
}

The Intended Protection

- Only authenticated users can access /admin.

The Bypass

Because Dart’s Uri treats \ as a path character, /admin\dashboard is NOT equal to /admin/dashboard.

Exploit Example

- Attacker sends: http://vulnerable.app/admin\dashboard
- Dart sees: Path is /admin\dashboard. No match for /admin → No auth check.
- Browser sees: /admin/dashboard. Renders admin dashboard!

This bypasses the authentication check, exposing sensitive routes.

Dart Code (Vulnerable)

void main() {
  final url = Uri.parse('/admin\dashboard');
  print(url.path); // Output: /admin\dashboard

  if (url.path.startsWith('/admin')) {
    print('Protected area!');
  } else {
    print('Public area!');
  }
}

Output

Protected area!

Yet, if you secure your app only for /admin/, but not /admin\, you’re wide open.

A real-life example would be

curl -v http://localhost:808/admin\dashboard

If your app only matches /admin/dashboard, auth can be skipped using a backslash in the URL.

Upgrade Dart to >=2.18 or Flutter to >=3.30.

Recent versions now treat \ the same as / when parsing URIs.

Temporary Workaround:  
Manually normalize incoming URIs before applying security logic.

String safeNormalizePath(String path) =>
  path.replaceAll(r'\', '/');

void handleRequest(HttpRequest request) {
  final uri = request.uri;
  final normalizedPath = safeNormalizePath(uri.path);
  if (normalizedPath.startsWith('/admin')) {
    // ... secure logic ...
  }
  // ... rest of handler ...
}

Conclusion

CVE-2022-3095 is a subtle, yet dangerous gap between two parsing standards. If your Dart or Flutter app protects access based on path matching or whitelisting, and you haven’t updated past Dart 2.18 or Flutter 3.30, you’re at risk for auth bypass.

What To Do

- Update Dart/Flutter immediately

For more technical details, consult

- Dart Security Advisory GHSA-w9x6-4h53-c3c3
- CVE-2022-3095 at NVD

Timeline

Published on: 10/27/2022 16:15:00 UTC
Last modified on: 10/31/2022 16:20:00 UTC