C++ : NULL application name with an unquoted path in call to CreateProcess#319
Conversation
…ocess Calling a function of the CreatePorcess* family of functions, which may result in a security vulnerability if the path contains spaces.
geoffw0
left a comment
There was a problem hiding this comment.
Thanks for this contribution @raulgarciams. QL, tests and help all look excellent! I haven't found any results in the wild, but I imagine this query will make a valuable regression check.
I've made a few comments - mostly not much more than typos.
| <qhelp> | ||
|
|
||
| <overview> | ||
| <p>This query indicates that there is a call to a function of the <code>CreatePorcess*</code> family of functions, which may result in a security vulnerability if the path contains spaces.</p> |
There was a problem hiding this comment.
Typo CreatePorcess -> CreateProcess.
|
|
||
| <example> | ||
| <p>In the following example, <code>CreateProcessW</code> is called with a NULL value for <code>lpApplicationName</code>, | ||
| and the value for <code>lpCommandLine</code> that represent the application path is not quoted and has spaces int.</p> |
| { | ||
| HANDLE h = 0; | ||
|
|
||
| CreateProcessWithTokenW( // Depends on the caller. In this case the caller sends an ApplicatioName |
There was a problem hiding this comment.
ApplicatioName -> ApplicationName.
| this.getTarget().hasGlobalName("CreateProcessAsUserA") or | ||
| this.getTarget().hasGlobalName("CreateProcessAsUserW") | ||
| ) then ( result = 1 ) | ||
| else (result = -1 ) |
There was a problem hiding this comment.
The -1 cases in this predicate and the one below could be omitted. It's perfectly fine for a predicate to have no result, and either way a use of FunctionCall.getArgument(thisPredicate()) would have no result.
There was a problem hiding this comment.
Got it. I will fix it & submit the change
There was a problem hiding this comment.
When I remove the "else" I get the following compilation error: Unexpected input ‘}’ expecting one of: ‘and’, ‘implies’, ‘in’, instanceof’, ‘or’, …
There was a problem hiding this comment.
The best thing to do is to rewrite those predicates using and and or:
(
this.getTarget().hasGlobalName("CreateProcessA") or
this.getTarget().hasGlobalName("CreateProcessW")
) and
result = 0
or
...
There was a problem hiding this comment.
Never mind, with Dave's recommendation below, I don't need the IF anymore. Thanks
dave-bartolomeo
left a comment
There was a problem hiding this comment.
Overall looks good. Most of my feedback is just pointing out a few typos, plus one significant simplification to the actual query.
| # Visual studio temporaries, except a file used by QL4VS | ||
| .vs/* | ||
| !.vs/VSWorkspaceSettings.json | ||
|
|
There was a problem hiding this comment.
Unnecessary change?
| <qhelp> | ||
|
|
||
| <overview> | ||
| <p>This query indicates that there is a call to a function of the <code>CreatePorcess*</code> family of functions, which may result in a security vulnerability if the path contains spaces.</p> |
There was a problem hiding this comment.
Typo - s/CreatePorcess/CreateProcess/
|
|
||
| <example> | ||
| <p>In the following example, <code>CreateProcessW</code> is called with a NULL value for <code>lpApplicationName</code>, | ||
| and the value for <code>lpCommandLine</code> that represent the application path is not quoted and has spaces int.</p> |
There was a problem hiding this comment.
Typo - s/int/in it/
There was a problem hiding this comment.
Typo - s/represent/represents/
| @@ -0,0 +1,135 @@ | |||
| /** | |||
| * @name NULL application name with an unquoted path in call to CreateProcess | |||
| * @description Calling a function of the CreatePorcess* family of functions, which may result in a security vulnerability if the path contains spaces. | |||
There was a problem hiding this comment.
Typo - s/CreatePorcess/CreateProcess/
| #define far | ||
| #define LOGON_WITH_PROFILE 0x00000001 | ||
|
|
||
| typedef char CHAR; |
There was a problem hiding this comment.
Would it be possible to remove most of these typedefs, and leave the various struct definitions with empty bodies (e.g. struct _STARTUPINFOW { };? This shouldn't affect the query, and all of these cut-and-paste definitions from the Windows headers kind of clutter up the real test cases.
There was a problem hiding this comment.
No problem. I have been trying to make the test function calls as close to the real ones, but I will change it.
|
BTW. After talking with Robert & Dave offline. I will try to make some major changes. Thanks |
Improved the query to avoid multiple calls to hasGlobalName Fixed typos Simplified the test case file
|
Changes LGTM. I wasn't involved in the offline conversation - are the changes finished or is there more to come? |
semmledocs-ac
left a comment
There was a problem hiding this comment.
Technical writer editorial review.
Documentation looks great. Just a few comments/suggestions.
| QuotedCommandInCreateProcessFunctionConfiguration quotedConfig | | ||
| cmd = call.getArgument(call.getCommandLineArgumentId()) | ||
| and quotedConfig.hasFlow(DataFlow2::exprNode(source), DataFlow2::exprNode(cmd)) | ||
| and msg2 = " and with an unquoted lpCommandLine (" + cmd + ") may result in a security vulnerability if the path contains spaces." |
There was a problem hiding this comment.
As per comment above, consider changing
may result in a security vulnerability
to
introduces a security vulnerability
|
I should have pasted the offline conversation summary here, sorry about that: We were trying to discuss if there was a way to detect this scenario when a developer uses a writable buffer (as the API intends) instead of doing an unsafe cast from a static read-only string to a writable string, but we cannot easily check if the unquoted string is copied ( i.e. lpComamndLine = new wchar_t[256]; wcscpy_s(lpComamndLine, 256, L"c:\program files\foo"); ) This version of the Semmle rule (with the changes recommended on the PR) can detect most of the cases with a static string, improving over the original PreFAST rule that misses anything that is not hardcoded directly on the CreateProcess* function call, and that adding the check for the lpCommandLine argument to have a space/tab helps to avoid a false positive. Historically the check for C6277 made sense for a time when the majority of the Windows apps were not using Unicode, and the CreateProcess*A versions of the API would work with a string literal (circa Windows 9x), so we are not expecting a lot of hits on this one. |
(MSFT feedback) Adding a new tag in the header @msrc.severity important
|
@raulgarciamsft - thanks for making those changes |
|
Great. Support for string buffers sounds like a desirable improvement but I'm very happy with this as a solid initial version of the query. I'll merge this in a few hours unless anybody speaks up (@dave-bartolomeo ?). |
dave-bartolomeo
left a comment
There was a problem hiding this comment.
Fixes look great. LGTM
Add some more vulnerable ActiveRecord methods
Calling a function of the CreatePorcess* family of functions, which may result in a security vulnerability if the path contains spaces.