Skip to content

Conversation

@spaceraccoon
Copy link

@spaceraccoon spaceraccoon commented Jun 18, 2023

Adds an experimental EmscriptenRunScriptTaint query to detect unsanitized user-input flows to Emscripten run script functions for exported WebAssembly functions.

Submitted for All for one, one for all bounty.

@spaceraccoon spaceraccoon requested a review from a team as a code owner June 18, 2023 18:24
@github-actions github-actions bot added the C++ label Jun 18, 2023
@MathiasVP MathiasVP requested a review from jketema June 19, 2023 13:44
Copy link
Contributor

@jketema jketema left a comment

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Hi @spaceraccoon,

Thanks for your contribution. Some initial comments below.

In addition:

  • This is missing tests, which should go into cpp/ql/test/experimental/query-tests/Security/
  • Currently dies goes into a directory called CVE-094 that should probably have been CWE-094

EmscriptenRunScriptTaintConfig() { this = "EmscriptenRunScriptTaintConfig" }

override predicate isSource(DataFlow::Node source) {
exists(Parameter p | source.asParameter() = p)
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

With this source, it looks to me like the following will be picked up by the query:

void foo(char *s) {
  emscripten_run_script(s);
}

int main() {
  foo("alert('FIXED CONSTANT')");
  return 0;
};

This is clearly not what we want, as the input to emscripten_run_script is clearly not user provided here.

It's better to do something like:

override predicate isSource(DataFlow::Node source) { source instanceof FlowSource }

where you'll need:

import semmle.code.cpp.security.FlowSources

override predicate isAdditionalTaintStep(DataFlow::Node fromNode, DataFlow::Node toNode) {
exists(FunctionCall fc |
fc.getTarget().hasGlobalName("TextFormat") and
fc.getArgument(1) = fromNode.asExpr() and
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is this restricted to the first argument? I would assume that dangerous input can flow into this function through any of it's arguments.

"qhelp.dtd">
<qhelp>
<overview>
<p>Finding for C++ WebAssembly function declarations that pass their parameters to Emscripten run script functions. If exported WebAssembly functions use user-supplied data in Emscripten run script commands without proper sanitization, this can make the code vulnerable to JavaScript code injection.</p>
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The query doesn't seem to check for for any sanitisation. This that intended?

fc.getTarget().hasGlobalName("emscripten_run_script_int") or
fc.getTarget().hasGlobalName("emscripten_run_script_string") or
fc.getTarget().hasGlobalName("emscripten_async_run_script") or
fc.getTarget().hasGlobalName("emscripten_async_load_script")
Copy link
Contributor

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Why is emscripten_async_load_script included here? Looking at the documentation, it seems that this only loads a script, but doesn't do anything with it. Wouldn't this only cause problems if the script is actually executed? If that's the case, it seems more appropriate to have calls to emscripten_async_load_script as an additional taint step.

@jketema
Copy link
Contributor

jketema commented Sep 24, 2024

Closing this due to inactivity.

@jketema jketema closed this Sep 24, 2024
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

Projects

None yet

Development

Successfully merging this pull request may close these issues.

2 participants