Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
5 changes: 4 additions & 1 deletion README.md
Original file line number Diff line number Diff line change
Expand Up @@ -54,6 +54,8 @@ codeql database analyze database.db --format=sarif-latest --output=./tob.sarif -
|[Invalid string size passed to string manipulation function](./cpp/src/docs/security/CStrnFinder/CStrnFinder.md)|Finds calls to functions that take as input a string and its size as separate arguments (e.g., `strncmp`, `strncat`, ...) and the size argument is wrong|error|low|
|[Iterator invalidation](./cpp/src/docs/security/IteratorInvalidation/IteratorInvalidation.md)|Modifying a container while iterating over it can invalidate iterators, leading to undefined behavior.|warning|medium|
|[Missing null terminator](./cpp/src/docs/security/NoNullTerminator/NoNullTerminator.md)|This query finds incorrectly initialized strings that are passed to functions expecting null-byte-terminated strings|error|high|
|[Potentially unguarded protocol handler invocation](./cpp/src/docs/security/PotentiallyUnguardedProtocolHandler/PotentiallyUnguardedProtocolHandler.md)|Detects calls to URL protocol handlers with untrusted input that may not be properly validated for dangerous protocols|warning|medium|
|[Unsafe implicit integer conversion](./cpp/src/docs/security/UnsafeImplicitConversions/UnsafeImplicitConversions.md)|Finds implicit integer casts that may overflow or be truncated, with false positive reduction via Value Range Analysis|warning|low|

### Go

Expand All @@ -77,7 +79,8 @@ codeql database analyze database.db --format=sarif-latest --output=./tob.sarif -

| Name | Description | Severity | Precision |
| --- | ----------- | :----: | :--------: |
|[Recursive functions](./java-kotlin/src/docs/security/Recursion/Recursion.md)|Detects possibly unbounded recursive calls|warning|low|
|[Potentially unguarded protocol handler invocation](./java/src/docs/security/PotentiallyUnguardedProtocolHandler/PotentiallyUnguardedProtocolHandler.md)|Detects calls to URL protocol handlers with untrusted input that may not be properly validated for dangerous protocols|warning|medium|
|[Recursive functions](./java-kotlin/src/docs/security/Recursion/Recursion.md)|Detects recursive calls|warning|low|

## Query suites

Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,87 @@
# Potentially unguarded protocol handler invocation

This query detects calls to URL protocol handlers with untrusted input that may not be properly validated for dangerous protocols. This vulnerability is related to CWE-939 (Improper Authorization in Handler for Custom URL Scheme) and aligns with CVE-2022-43550.

When applications invoke protocol handlers (like `rundll32 url.dll,FileProtocolHandler` on Windows, `xdg-open` on Linux, `open` on macOS, or Qt's `QDesktopServices::openUrl()`), untrusted URLs can potentially trigger dangerous protocols such as `file://`, `smb://`, or other custom handlers that may lead to unauthorized file access, command execution, or other security issues.

## Detected patterns

The query identifies several common protocol handler invocation patterns:

- **Windows**: `rundll32 url.dll,FileProtocolHandler` via system calls
- **Linux**: `xdg-open` via system calls
- **macOS**: `open` command via system calls
- **Qt applications**: `QDesktopServices::openUrl()`

## Recommendation

Always validate URL schemes before passing them to protocol handlers. Only allow safe protocols like `http://` and `https://`. Reject or sanitize URLs containing potentially dangerous protocols.

## Example

The following vulnerable code passes untrusted input directly to a protocol handler:

```cpp
#include <QDesktopServices>
#include <QUrl>

void openUserUrl(const QString& userInput) {
// VULNERABLE: No validation of the URL scheme
QDesktopServices::openUrl(QUrl(userInput));
}
```

An attacker could provide a URL like `file:///etc/passwd` or `smb://attacker-server/share` to access unauthorized resources.

The corrected version validates the URL scheme before opening:

```cpp
#include <QDesktopServices>
#include <QUrl>

void openUserUrl(const QString& userInput) {
QUrl url(userInput);
QString scheme = url.scheme().toLower();

// Only allow safe protocols
if (scheme == "http" || scheme == "https") {
QDesktopServices::openUrl(url);
} else {
// Log error or show warning to user
qWarning() << "Rejected unsafe URL scheme:" << scheme;
}
}
```

For system command invocations:

```cpp
void openUserUrlViaShell(const char* userInput) {
// VULNERABLE: Untrusted input passed to xdg-open
char cmd[512];
snprintf(cmd, sizeof(cmd), "xdg-open '%s'", userInput);
system(cmd);
}
```

Should be corrected to validate the scheme:

```cpp
bool isValidScheme(const char* url) {
return (strncasecmp(url, "http://", 7) == 0 ||
strncasecmp(url, "https://", 8) == 0);
}

void openUserUrlViaShell(const char* userInput) {
if (isValidScheme(userInput)) {
char cmd[512];
snprintf(cmd, sizeof(cmd), "xdg-open '%s'", userInput);
system(cmd);
}
}
```

## References

- [CWE-939: Improper Authorization in Handler for Custom URL Scheme](https://cwe.mitre.org/data/definitions/939.html)
- [CVE-2022-43550: USB Creator has insufficiently protected credentials](https://ubuntu.com/security/CVE-2022-43550)
Original file line number Diff line number Diff line change
@@ -0,0 +1,124 @@
/**
* @name Potentially unguarded protocol handler invocation
* @id tob/cpp/unguarded-protocol-handler
* @description Detects calls to URL protocol handlers with untrusted input that may not be properly validated for dangerous protocols
* @kind path-problem
* @tags security
* external/cwe/cwe-939
* @precision medium
* @problem.severity warning
* @security-severity 6.5
* @group security
*/

import cpp
private import semmle.code.cpp.ir.dataflow.TaintTracking
private import semmle.code.cpp.security.FlowSources
private import semmle.code.cpp.security.CommandExecution
private import semmle.code.cpp.controlflow.IRGuards

/**
* Holds when `call` invokes a system function (system, popen, exec*) with a command string
* that contains a URL protocol handler, `sink` is the argument carrying the URL, and
* `handlerType` describes which handler is invoked.
*/
predicate isShellProtocolHandlerSink(FunctionCall call, Expr sink, string handlerType) {
call.getTarget() instanceof SystemFunction and
exists(StringLiteral sl |
sl.getParent*() = call.getArgument(0) and
(
sl.getValue().regexpMatch("(?i).*rundll32.*url\\.dll.*FileProtocolHandler.*") and
handlerType = "rundll32 url.dll,FileProtocolHandler"
or
sl.getValue().regexpMatch("(?i).*xdg-open.*") and
handlerType = "xdg-open"
or
sl.getValue().regexpMatch("(?i).*\\bopen\\b.*") and
handlerType = "open"
)
) and
sink = call.getArgument(0)
}

/**
* Holds when `call` invokes QDesktopServices::openUrl and `sink` is the URL argument.
*/
predicate isQtProtocolHandlerSink(FunctionCall call, Expr sink) {
call.getTarget().hasQualifiedName("", "QDesktopServices", "openUrl") and
sink = call.getArgument(0)
}

/**
* Gets the sink expression for a given DataFlow::Node matching either Qt or shell handler sinks.
*/
Expr getSinkExpr(DataFlow::Node sink) {
result = sink.asExpr() or
result = sink.asIndirectExpr()
}

/**
* Holds when `g` is a guard condition that validates the URL scheme of expression `e`.
* Flow is considered safe on the `branch` edge.
*/
predicate urlSchemeGuardChecks(IRGuardCondition g, Expr e, boolean branch) {
branch = [true, false] and
exists(FunctionCall fc | g.getUnconvertedResultExpression().getAChild*() = fc |
// C string comparison on the URL (strcmp, strncmp, etc.)
fc.getTarget().getName() =
[
"strcmp", "strncmp", "strcasecmp", "strncasecmp", "strstr", "strcasestr", "_stricmp",
"_strnicmp"
] and
e = fc.getAnArgument()
or
// Qt QString::startsWith check
fc.getTarget().hasQualifiedName("", "QString", "startsWith") and
e = fc.getQualifier()
or
// Qt QUrl::scheme() comparison
exists(FunctionCall schemeCall |
schemeCall.getTarget().hasQualifiedName("", "QUrl", "scheme") and
fc.getAnArgument() = schemeCall and
e = schemeCall.getQualifier()
)
)
}

/**
* Configuration for tracking untrusted data to protocol handler invocations.
*/
module PotentiallyUnguardedProtocolHandlerConfig implements DataFlow::ConfigSig {
predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }

predicate isSink(DataFlow::Node sink) {
isQtProtocolHandlerSink(_, getSinkExpr(sink))
or
isShellProtocolHandlerSink(_, getSinkExpr(sink), _)
}

predicate isBarrier(DataFlow::Node node) {
node = DataFlow::BarrierGuard<urlSchemeGuardChecks/3>::getABarrierNode()
or
node = DataFlow::BarrierGuard<urlSchemeGuardChecks/3>::getAnIndirectBarrierNode()
}
}

module PotentiallyUnguardedProtocolHandlerFlow =
TaintTracking::Global<PotentiallyUnguardedProtocolHandlerConfig>;

import PotentiallyUnguardedProtocolHandlerFlow::PathGraph

from
PotentiallyUnguardedProtocolHandlerFlow::PathNode source,
PotentiallyUnguardedProtocolHandlerFlow::PathNode sink, FunctionCall call, string callType
where
PotentiallyUnguardedProtocolHandlerFlow::flowPath(source, sink) and
(
isQtProtocolHandlerSink(call, getSinkExpr(sink.getNode())) and
callType = "QDesktopServices::openUrl()"
or
isShellProtocolHandlerSink(call, getSinkExpr(sink.getNode()), callType)
)
select call, source, sink,
callType + " is called with untrusted input from $@ without proper URL scheme validation.",
source.getNode(), "this source"
2 changes: 2 additions & 0 deletions cpp/test/include/libc/stdint.h
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,8 @@
typedef unsigned char uint8_t;
typedef unsigned short uint16_t;
typedef unsigned int uint32_t;
typedef unsigned long size_t;
typedef long ssize_t;

#endif
#else // --- else USE_HEADERS
Expand Down
2 changes: 2 additions & 0 deletions cpp/test/include/libc/stdlib.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
extern "C" {
#endif

extern int system(const char *);

extern void *reallocf(void *, unsigned long);

int rand(void) {
Expand Down
2 changes: 2 additions & 0 deletions cpp/test/include/libc/string_stubs.h
Original file line number Diff line number Diff line change
Expand Up @@ -28,6 +28,8 @@ extern int wprintf(const wchar_t * format, ...);
extern wchar_t* wcscpy(wchar_t * s1, const wchar_t * s2);
extern void perror(const char *s);
extern int puts(const char *s);
extern int strcmp(const char *, const char *);
extern int strncmp(const char *, const char *, size_t);

extern void openlog(const char*, int, int);
extern void syslog(int, const char*, ...);
Expand Down
2 changes: 2 additions & 0 deletions cpp/test/include/libc/unistd.h
Original file line number Diff line number Diff line change
Expand Up @@ -7,6 +7,8 @@
extern "C" {
#endif

ssize_t read(int, void *, size_t);

void _exit(int);

#ifdef __cplusplus
Expand Down
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
edges
| PotentiallyUnguardedProtocolHandler.cpp:27:11:27:13 | read output argument | PotentiallyUnguardedProtocolHandler.cpp:28:29:28:31 | *buf | provenance | |
| PotentiallyUnguardedProtocolHandler.cpp:39:11:39:13 | read output argument | PotentiallyUnguardedProtocolHandler.cpp:42:31:42:33 | *buf | provenance | |
nodes
| PotentiallyUnguardedProtocolHandler.cpp:27:11:27:13 | read output argument | semmle.label | read output argument |
| PotentiallyUnguardedProtocolHandler.cpp:28:29:28:31 | *buf | semmle.label | *buf |
| PotentiallyUnguardedProtocolHandler.cpp:39:11:39:13 | read output argument | semmle.label | read output argument |
| PotentiallyUnguardedProtocolHandler.cpp:42:31:42:33 | *buf | semmle.label | *buf |
subpaths
#select
| PotentiallyUnguardedProtocolHandler.cpp:28:3:28:27 | call to openUrl | PotentiallyUnguardedProtocolHandler.cpp:27:11:27:13 | read output argument | PotentiallyUnguardedProtocolHandler.cpp:28:29:28:31 | *buf | QDesktopServices::openUrl() is called with untrusted input from $@ without proper URL scheme validation. | PotentiallyUnguardedProtocolHandler.cpp:27:11:27:13 | read output argument | this source |
| PotentiallyUnguardedProtocolHandler.cpp:42:5:42:29 | call to openUrl | PotentiallyUnguardedProtocolHandler.cpp:39:11:39:13 | read output argument | PotentiallyUnguardedProtocolHandler.cpp:42:31:42:33 | *buf | QDesktopServices::openUrl() is called with untrusted input from $@ without proper URL scheme validation. | PotentiallyUnguardedProtocolHandler.cpp:39:11:39:13 | read output argument | this source |
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
security/PotentiallyUnguardedProtocolHandler/PotentiallyUnguardedProtocolHandler.ql
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Potentially unguarded protocol handler invocation

This query detects calls to URL protocol handlers with untrusted input that may not be properly validated for dangerous protocols. This vulnerability is related to CWE-939 (Improper Authorization in Handler for Custom URL Scheme) and aligns with CVE-2022-43550.

When Java applications invoke protocol handlers (like `Desktop.browse()`, `Runtime.exec()` with `xdg-open`/`open`, or `rundll32 url.dll,FileProtocolHandler` on Windows), untrusted URLs can potentially trigger dangerous protocols such as `file://`, `smb://`, or other custom handlers that may lead to unauthorized file access, command execution, or other security issues.

## Detected patterns

The query identifies several common protocol handler invocation patterns:

- **Java AWT**: `Desktop.browse(URI)` - the platform-agnostic standard
- **Windows**: `Runtime.exec()` with `rundll32 url.dll,FileProtocolHandler`
- **Linux**: `Runtime.exec()` with `xdg-open`
- **macOS**: `Runtime.exec()` with `open` command

## Recommendation

Always validate URL schemes before passing them to protocol handlers. Only allow safe protocols like `http://` and `https://`. Reject or sanitize URLs containing potentially dangerous protocols.

## Example

The following vulnerable code passes untrusted input directly to a protocol handler:

```java
import java.awt.Desktop;
import java.net.URI;

public class UrlOpener {
public void openUserUrl(String userInput) throws Exception {
// VULNERABLE: No validation of the URL scheme
Desktop.getDesktop().browse(new URI(userInput));
}
}
```

An attacker could provide a URL like `file:///etc/passwd` or `smb://attacker-server/share` to access unauthorized resources.

The corrected version validates the URL scheme before opening:

```java
import java.awt.Desktop;
import java.net.URI;

public class UrlOpener {
public void openUserUrl(String userInput) throws Exception {
URI uri = new URI(userInput);
String scheme = uri.getScheme();

// Only allow safe protocols
if ("http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme)) {
Desktop.getDesktop().browse(uri);
} else {
throw new SecurityException("Rejected unsafe URL scheme: " + scheme);
}
}
}
```

For system command invocations:

```java
public class UrlOpener {
public void openUserUrlViaShell(String userInput) throws Exception {
// VULNERABLE: Untrusted input passed to xdg-open
Runtime.getRuntime().exec(new String[]{"xdg-open", userInput});
}
}
```

Should be corrected to validate the scheme:

```java
import java.net.URI;

public class UrlOpener {
private boolean isValidScheme(String url) {
try {
URI uri = new URI(url);
String scheme = uri.getScheme();
return "http".equalsIgnoreCase(scheme) || "https".equalsIgnoreCase(scheme);
} catch (Exception e) {
return false;
}
}

public void openUserUrlViaShell(String userInput) throws Exception {
if (isValidScheme(userInput)) {
Runtime.getRuntime().exec(new String[]{"xdg-open", userInput});
} else {
throw new SecurityException("Invalid or unsafe URL scheme");
}
}
}
```

## References

- [CWE-939: Improper Authorization in Handler for Custom URL Scheme](https://cwe.mitre.org/data/definitions/939.html)
- [CVE-2022-43550: USB Creator has insufficiently protected credentials](https://ubuntu.com/security/CVE-2022-43550)
Loading
Loading