@@ -15,10 +15,11 @@ import java
1515import semmle.code.java.dataflow.TaintTracking
1616import semmle.code.java.dataflow.FlowSources
1717import semmle.code.java.controlflow.Guards
18+ import semmle.code.java.security.ExternalProcess
1819
1920/**
20- * A call to Desktop.browse() method for handling
21- * TODO (alan): also support legacy/generic cases like invoking "rundll32 url.dll,FileProtocolHandler"
21+ * A call to Desktop.browse() method. This is the platform-agnostic standard now.
22+ * NOTE (alan): the URLRedirect query will likely also trigger due to a little imprecision there
2223 */
2324class DesktopBrowseCall extends MethodCall {
2425 DesktopBrowseCall ( ) {
@@ -27,8 +28,44 @@ class DesktopBrowseCall extends MethodCall {
2728 }
2829
2930 Expr getUrlArgument ( ) { result = this .getArgument ( 0 ) }
31+ }
32+
33+ /**
34+ * Legacy Windows-specific command execution for protocol handling, based on https://imagej.net/ij/source/ij/plugin/BrowserLauncher.java and
35+ * seen in CVE-2022-43550. We match on the Windows shell command, since it more distinct and pervasive.
36+ */
37+ class LegacyWindowsProtocolHandlerCall extends ArgumentToExec {
38+ LegacyWindowsProtocolHandlerCall ( ) {
39+ // Single string: "rundll32 url.dll,FileProtocolHandler " + url
40+ this instanceof StringArgumentToExec and
41+ exists ( StringLiteral sl |
42+ sl .getParent * ( ) = this and
43+ sl .getValue ( ) .regexpMatch ( "(?i).*rundll32.*url\\.dll.*FileProtocolHandler.*" )
44+ )
45+ or
46+ // Array: {"rundll32", "url.dll,FileProtocolHandler", url}
47+ this .getType ( ) .( Array ) .getElementType ( ) instanceof TypeString and
48+ exists ( ArrayInit init , StringLiteral rundll , StringLiteral urldll |
49+ init = this .( ArrayCreationExpr ) .getInit ( ) and
50+ rundll = init .getAnInit ( ) and
51+ urldll = init .getAnInit ( ) and
52+ rundll .getValue ( ) .regexpMatch ( "(?i)rundll32(\\.exe)?" ) and
53+ urldll .getValue ( ) .regexpMatch ( "(?i)url\\.dll.*FileProtocolHandler" )
54+ )
55+ }
3056
31- string getDescription ( ) { result = "Desktop.browse()" }
57+ Expr getUrlArgument ( ) {
58+ // For single string exec, the URL is tainted into the concatenated string
59+ result = this and this instanceof StringArgumentToExec
60+ or
61+ // For arrays, find elements after "url.dll,FileProtocolHandler"
62+ exists ( ArrayInit init , int urlIdx , int dllIdx |
63+ init = this .( ArrayCreationExpr ) .getInit ( ) and
64+ result = init .getInit ( urlIdx ) and
65+ init .getInit ( dllIdx ) .( StringLiteral ) .getValue ( ) .regexpMatch ( "(?i)url\\.dll.*FileProtocolHandler" ) and
66+ urlIdx > dllIdx
67+ )
68+ }
3269}
3370
3471/**
@@ -66,10 +103,11 @@ module PotentiallyUnguardedProtocolHandlerConfig implements DataFlow::ConfigSig
66103
67104 predicate isSink ( DataFlow:: Node sink ) {
68105 exists ( DesktopBrowseCall call | sink .asExpr ( ) = call .getUrlArgument ( ) )
106+ or
107+ exists ( LegacyWindowsProtocolHandlerCall call | sink .asExpr ( ) = call .getUrlArgument ( ) )
69108 }
70109
71110 predicate isBarrier ( DataFlow:: Node node ) {
72- // Consider sanitized if there's a guard checking the scheme
73111 node = DataFlow:: BarrierGuard< isUrlSchemeCheck / 3 > :: getABarrierNode ( )
74112 }
75113}
@@ -81,11 +119,22 @@ import PotentiallyUnguardedProtocolHandlerFlow::PathGraph
81119
82120from
83121 PotentiallyUnguardedProtocolHandlerFlow:: PathNode source ,
84- PotentiallyUnguardedProtocolHandlerFlow:: PathNode sink , DesktopBrowseCall call
122+ PotentiallyUnguardedProtocolHandlerFlow:: PathNode sink , Expr call , string callType
85123where
86124 PotentiallyUnguardedProtocolHandlerFlow:: flowPath ( source , sink ) and
87- sink .getNode ( ) .asExpr ( ) = call .getUrlArgument ( )
125+ (
126+ exists ( DesktopBrowseCall dbc |
127+ call = dbc and
128+ sink .getNode ( ) .asExpr ( ) = dbc .getUrlArgument ( ) and
129+ callType = "Desktop.browse()"
130+ )
131+ or
132+ exists ( LegacyWindowsProtocolHandlerCall whc |
133+ call = whc and
134+ sink .getNode ( ) .asExpr ( ) = whc .getUrlArgument ( ) and
135+ callType = "rundll32 url.dll,FileProtocolHandler"
136+ )
137+ )
88138select call , source , sink ,
89- call .getDescription ( ) +
90- " is called with untrusted input from $@ without proper URL scheme validation." ,
139+ callType + " is called with untrusted input from $@ without proper URL scheme validation." ,
91140 source .getNode ( ) , "this source"
0 commit comments