SQL injection via unescaped device_id in SRQL query construction #680
Labels
No labels
1week
2weeks
Failed compliance check
IP cameras
NATS
Possible security concern
Review effort 1/5
Review effort 2/5
Review effort 3/5
Review effort 4/5
Review effort 5/5
UI
aardvark
accessibility
amd64
api
arm64
auth
back-end
bgp
blog
bug
build
checkers
ci-cd
cleanup
cnpg
codex
core
dependencies
device-management
documentation
duplicate
dusk
ebpf
enhancement
eta 1d
eta 1hr
eta 3d
eta 3hr
feature
fieldsurvey
github_actions
go
good first issue
help wanted
invalid
javascript
k8s
log-collector
mapper
mtr
needs-triage
netflow
network-sweep
observability
oracle
otel
plug-in
proton
python
question
reddit
redhat
research
rperf
rperf-checker
rust
sdk
security
serviceradar-agent
serviceradar-agent-gateway
serviceradar-web
serviceradar-web-ng
siem
snmp
sysmon
topology
ubiquiti
wasm
wontfix
zen-engine
No milestone
No project
No assignees
1 participant
Notifications
Due date
No due date set.
Dependencies
No dependencies set.
Reference
carverauto/serviceradar#680
Loading…
Add table
Add a link
Reference in a new issue
No description provided.
Delete branch "%!s()"
Deleting a branch is permanent. Although the deleted branch may continue to exist for a short time before it actually gets removed, it CANNOT be undone in most cases. Continue?
Imported from GitHub.
Original GitHub issue: #2142
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/issues/2142
Original created: 2025-12-16T05:15:19Z
Summary
devices.getDevicetool inpkg/mcp/tools_devices.goaccepts a device ID parameter and builds an SRQL query to retrieve device details from the database.Code with bug
In
pkg/mcp/tools_devices.go:In
pkg/mcp/server.go:Evidence
Example
Step 1: Normal device ID query
{"device_id": "device-123"}SHOW devices WHERE device_id = 'device-123' LIMIT 1Step 2: Malicious device ID with OR injection
{"device_id": "device' OR '1'='1"}SHOW devices WHERE device_id = 'device' OR '1'='1' LIMIT 1Step 3: Malicious device ID with comment injection
{"device_id": "device' -- "}SHOW devices WHERE device_id = 'device' -- ' LIMIT 1--removes the rest of the query, eliminating the LIMIT clause and potentially other security checksStep 4: Malicious device ID with UNION injection
{"device_id": "device' UNION SELECT * FROM sensitive_table WHERE '1'='1"}SHOW devices WHERE device_id = 'device' UNION SELECT * FROM sensitive_table WHERE '1'='1' LIMIT 1Failing test
Test script
Test output
The test passes because it successfully demonstrates that malicious input is directly incorporated into the query without sanitization. Each test case shows a different SQL injection attack vector that would work against this code.
Inconsistency within the codebase
Reference code
pkg/mcp/tools_graph.go(lines 64-66)Current code
pkg/mcp/tools_devices.go(line 84)Contradiction
The codebase shows awareness of input sanitization in
tools_graph.gowherestrconv.Quote()is used to safely quote the device ID before interpolation. However, the same safety measure is not applied intools_devices.go, creating an inconsistency in security practices across similar functionality.Full context
The MCP (Model Context Protocol) server is a component that provides an HTTP API for AI assistants and external tools to query ServiceRadar's monitoring data. The server exposes various tools including device querying capabilities.
The vulnerable code path is:
/mcp/tools/callendpointdevices.getDevicewith arguments includingdevice_idhandleToolCallfunction inserver.goroutes the request to the appropriate tool handlerdevices.getDevicetool handler intools_devices.goreceives the device_id parameterexecuteSRQLQueryThe MCP server also has a parallel code path through JSON-RPC compatibility where
executeGetDeviceinserver.gohas the same vulnerability.According to the SRQL service documentation (
docs/docs/srql-service.md), the SRQL service supports API key authentication via theX-API-Keyheader and rate limiting, but these controls do not prevent SQL injection attacks since the malicious SQL is embedded within what appears to be a legitimate SRQL query.External documentation
Exploit scenario
Attack Prerequisites:
Attack Steps:
Step 1: Attacker sends a reconnaissance request to identify the vulnerability
Step 2: If the response contains multiple devices or unexpected data, the vulnerability is confirmed. The attacker proceeds with data extraction.
Step 3: Extract database schema information
Step 4: Extract sensitive data from discovered tables (e.g., credentials, API keys, user information)
Step 5: For persistent access, the attacker could potentially:
Impact:
Why has this bug gone undetected?
This SQL injection vulnerability has likely gone undetected for several reasons:
Trust in SRQL abstraction layer: Developers may have assumed that the SRQL query language provides some level of protection or sanitization, treating it as a safer abstraction over raw SQL. However, SRQL is simply translated to PostgreSQL SQL, so SQL injection in SRQL queries is still possible.
Limited production testing with malicious inputs: The MCP server is relatively new code (introduced in commit
6049244f"new mcp server -- untested"). Security testing with injection payloads may not have been performed yet, especially if the server is still in development or early deployment.Authentication provides false sense of security: The SRQL service requires API key authentication and has rate limiting. This may have created a false sense that the system is secure, causing developers to focus less on input validation. However, authenticated users can still exploit SQL injection.
Device IDs typically don't contain special characters: In normal operation, device IDs are likely alphanumeric identifiers without special characters. This means that in typical usage scenarios, the vulnerability would never be triggered, allowing it to remain hidden during functional testing.
No automated security scanning: The codebase may not have automated SQL injection detection tools (SAST/DAST) integrated into the CI/CD pipeline that would flag string concatenation of user input into SQL queries.
Pattern established early: The vulnerable pattern was introduced in the initial commit of the MCP server and then replicated across multiple locations. Once a pattern is established, it tends to be copied without scrutiny.
Inconsistent security practices: While
tools_graph.gousesstrconv.Quote()for input sanitization, this practice wasn't consistently applied across all tools, suggesting no formal security review or coding standards enforcement for input handling.Recommended fix
The recommended fix is to implement proper input validation and parameterized queries. Here are the specific changes:
Option 1: Input sanitization with quote escaping (minimal change)
In
pkg/mcp/tools_devices.go:In
pkg/mcp/server.go:Option 2: Use strconv.Quote for consistency (better approach)
This would make the code consistent with
tools_graph.go:Option 3: Extend SRQL to support parameterized queries (best long-term solution)
Modify the
QueryExecutorinterface to support parameterized queries:Then use it in the code:
Additional recommendations:
fmt.Sprintfwith user input across the MCP packageRelated bugs
The same SQL injection vulnerability pattern exists in multiple locations in the MCP package:
pkg/mcp/server.go:705-executeGetDevicefunction has identical vulnerabilitypkg/mcp/query_utils.go:98-buildRecentLogsQueryfunction:fmt.Sprintf(" WHERE poller_id = '%s'", params.PollerID)pkg/mcp/query_utils.go:148-buildDevicesQueryfunction:fmt.Sprintf("device_type = '%s'", params.Type)pkg/mcp/query_utils.go:166-buildDevicesQueryfunction:fmt.Sprintf("status = '%s'", params.Status)pkg/mcp/tools_logs.go:105-registerLogToolsfunction:fmt.Sprintf("poller_id = '%s'", recentArgs.PollerID)pkg/mcp/tools_sweeps.go:98-registerSweepToolsfunction:fmt.Sprintf("poller_id = '%s'", recentArgs.PollerID)pkg/mcp/tools_sweeps.go:155-registerSweepToolsfunction:fmt.Sprintf("poller_id = '%s'", summaryArgs.PollerID)pkg/mcp/tools_events.go:101-registerEventToolsfunction:fmt.Sprintf("poller_id = '%s'", alertArgs.PollerID)pkg/mcp/builder.go:169-BuildFiltersfunction:fmt.Sprintf("%s = '%s'", sqlField, strValue)All of these instances should be reviewed and fixed using one of the recommended approaches above. The entire MCP package requires a security audit for SQL injection vulnerabilities.
Imported GitHub comment.
Original author: @mfreeman451
Original URL: https://github.com/carverauto/serviceradar/issues/2142#issuecomment-3663453159
Original created: 2025-12-17T03:19:17Z
closing as completed