@@ -19,9 +19,9 @@ import semmle.code.cpp.security.Security
1919import semmle.code.cpp.valuenumbering.GlobalValueNumbering
2020import semmle.code.cpp.ir.IR
2121import semmle.code.cpp.ir.dataflow.TaintTracking
22- import semmle.code.cpp.ir.dataflow.TaintTracking2
2322import semmle.code.cpp.security.FlowSources
2423import semmle.code.cpp.models.implementations.Strcat
24+ import DataFlow:: PathGraph
2525
2626Expr sinkAsArgumentIndirection ( DataFlow:: Node sink ) {
2727 result =
@@ -66,154 +66,70 @@ predicate interestingConcatenation(DataFlow::Node fst, DataFlow::Node snd) {
6666 )
6767}
6868
69- class TaintToConcatenationConfiguration extends TaintTracking:: Configuration {
70- TaintToConcatenationConfiguration ( ) { this = "TaintToConcatenationConfiguration" }
71-
72- override predicate isSource ( DataFlow:: Node source ) { source instanceof FlowSource }
73-
74- override predicate isSink ( DataFlow:: Node sink ) { interestingConcatenation ( sink , _) }
75-
76- override predicate isSanitizer ( DataFlow:: Node node ) {
77- node .asInstruction ( ) .getResultType ( ) instanceof IntegralType
78- or
79- node .asInstruction ( ) .getResultType ( ) instanceof FloatingPointType
80- }
69+ class ConcatState extends DataFlow:: FlowState {
70+ ConcatState ( ) { this = "ConcatState" }
8171}
8272
83- class ExecTaintConfiguration extends TaintTracking2:: Configuration {
84- ExecTaintConfiguration ( ) { this = "ExecTaintConfiguration" }
73+ class ExecState extends DataFlow:: FlowState {
74+ DataFlow:: Node fst ;
75+ DataFlow:: Node snd ;
8576
86- override predicate isSource ( DataFlow:: Node source ) {
87- exists ( DataFlow:: Node prevSink , TaintToConcatenationConfiguration conf |
88- conf .hasFlow ( _, prevSink ) and
89- interestingConcatenation ( prevSink , source )
90- )
77+ ExecState ( ) {
78+ this =
79+ "ExecState (" + fst .getLocation ( ) + " | " + fst + ", " + snd .getLocation ( ) + " | " + snd + ")" and
80+ interestingConcatenation ( fst , snd )
9181 }
9282
93- override predicate isSink ( DataFlow:: Node sink ) {
94- shellCommand ( sinkAsArgumentIndirection ( sink ) , _)
95- }
83+ DataFlow:: Node getFstNode ( ) { result = fst }
9684
97- override predicate isSanitizerOut ( DataFlow:: Node node ) {
98- isSink ( node ) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
99- }
85+ DataFlow:: Node getSndNode ( ) { result = snd }
10086}
10187
102- module StitchedPathGraph {
103- // There's a different PathNode class for each DataFlowImplN.qll, so we can't simply combine the
104- // PathGraph predicates directly. Instead, we use a newtype so there's a single type that
105- // contains both sets of PathNodes.
106- newtype TMergedPathNode =
107- TPathNode1 ( DataFlow:: PathNode node ) or
108- TPathNode2 ( DataFlow2:: PathNode node )
109-
110- // this wraps the toString and location predicates so we can use the merged node type in a
111- // selection
112- class MergedPathNode extends TMergedPathNode {
113- string toString ( ) {
114- exists ( DataFlow:: PathNode n |
115- this = TPathNode1 ( n ) and
116- result = n .toString ( )
117- )
118- or
119- exists ( DataFlow2:: PathNode n |
120- this = TPathNode2 ( n ) and
121- result = n .toString ( )
122- )
123- }
124-
125- DataFlow:: Node getNode ( ) {
126- exists ( DataFlow:: PathNode n |
127- this = TPathNode1 ( n ) and
128- result = n .getNode ( )
129- )
130- or
131- exists ( DataFlow2:: PathNode n |
132- this = TPathNode2 ( n ) and
133- result = n .getNode ( )
134- )
135- }
136-
137- DataFlow:: PathNode getPathNode1 ( ) { this = TPathNode1 ( result ) }
88+ class ExecTaintConfiguration extends TaintTracking:: Configuration {
89+ ExecTaintConfiguration ( ) { this = "ExecTaintConfiguration" }
13890
139- DataFlow2:: PathNode getPathNode2 ( ) { this = TPathNode2 ( result ) }
91+ override predicate isSource ( DataFlow:: Node source , DataFlow:: FlowState state ) {
92+ source instanceof FlowSource and
93+ state instanceof ConcatState
94+ }
14095
141- predicate hasLocationInfo (
142- string filepath , int startline , int startcolumn , int endline , int endcolumn
143- ) {
144- exists ( DataFlow:: PathNode n |
145- this = TPathNode1 ( n ) and
146- n .hasLocationInfo ( filepath , startline , startcolumn , endline , endcolumn )
147- )
148- or
149- exists ( DataFlow2:: PathNode n |
150- this = TPathNode2 ( n ) and
151- n .hasLocationInfo ( filepath , startline , startcolumn , endline , endcolumn )
152- )
153- }
96+ override predicate isSink ( DataFlow:: Node sink , DataFlow:: FlowState state ) {
97+ shellCommand ( sinkAsArgumentIndirection ( sink ) , _) and
98+ state instanceof ExecState
15499 }
155100
156- query predicate edges ( MergedPathNode a , MergedPathNode b ) {
157- exists ( DataFlow:: PathNode an , DataFlow:: PathNode bn |
158- a = TPathNode1 ( an ) and
159- b = TPathNode1 ( bn ) and
160- DataFlow:: PathGraph:: edges ( an , bn )
161- )
162- or
163- exists ( DataFlow2:: PathNode an , DataFlow2:: PathNode bn |
164- a = TPathNode2 ( an ) and
165- b = TPathNode2 ( bn ) and
166- DataFlow2:: PathGraph:: edges ( an , bn )
167- )
168- or
169- // This is where paths from the two configurations are connected. `interestingConcatenation`
170- // is the only thing in this module that's actually specific to the query - everything else is
171- // just using types and predicates from the DataFlow library.
172- interestingConcatenation ( a .getNode ( ) , b .getNode ( ) ) and
173- a instanceof TPathNode1 and
174- b instanceof TPathNode2
101+ override predicate isAdditionalTaintStep (
102+ DataFlow:: Node node1 , DataFlow:: FlowState state1 , DataFlow:: Node node2 ,
103+ DataFlow:: FlowState state2
104+ ) {
105+ state1 instanceof ConcatState and
106+ state2 .( ExecState ) .getFstNode ( ) = node1 and
107+ state2 .( ExecState ) .getSndNode ( ) = node2
175108 }
176109
177- query predicate nodes ( MergedPathNode mpn , string key , string val ) {
178- // here we just need the union of the underlying `nodes` predicates
179- exists ( DataFlow:: PathNode n |
180- mpn = TPathNode1 ( n ) and
181- DataFlow:: PathGraph:: nodes ( n , key , val )
182- )
183- or
184- exists ( DataFlow2:: PathNode n |
185- mpn = TPathNode2 ( n ) and
186- DataFlow2:: PathGraph:: nodes ( n , key , val )
187- )
110+ override predicate isSanitizer ( DataFlow:: Node node , DataFlow:: FlowState state ) {
111+ (
112+ node .asInstruction ( ) .getResultType ( ) instanceof IntegralType
113+ or
114+ node .asInstruction ( ) .getResultType ( ) instanceof FloatingPointType
115+ ) and
116+ state instanceof ConcatState
188117 }
189118
190- query predicate subpaths (
191- MergedPathNode arg , MergedPathNode par , MergedPathNode ret , MergedPathNode out
192- ) {
193- // just forward subpaths from the underlying libraries. This might be slightly awkward when
194- // the concatenation is deep in a call chain.
195- DataFlow:: PathGraph:: subpaths ( arg .getPathNode1 ( ) , par .getPathNode1 ( ) , ret .getPathNode1 ( ) ,
196- out .getPathNode1 ( ) )
197- or
198- DataFlow2:: PathGraph:: subpaths ( arg .getPathNode2 ( ) , par .getPathNode2 ( ) , ret .getPathNode2 ( ) ,
199- out .getPathNode2 ( ) )
119+ override predicate isSanitizerOut ( DataFlow:: Node node , DataFlow:: FlowState state ) {
120+ isSink ( node , state ) // Prevent duplicates along a call chain, since `shellCommand` will include wrappers
200121 }
201122}
202123
203- import StitchedPathGraph
204-
205124from
206- DataFlow:: PathNode sourceNode , DataFlow:: PathNode concatSink , DataFlow2:: PathNode concatSource ,
207- DataFlow2:: PathNode sinkNode , string taintCause , string callChain ,
208- TaintToConcatenationConfiguration conf1 , ExecTaintConfiguration conf2
125+ ExecTaintConfiguration conf , DataFlow:: PathNode sourceNode , DataFlow:: PathNode sinkNode ,
126+ string taintCause , string callChain , DataFlow:: Node concatResult
209127where
128+ conf .hasFlowPath ( sourceNode , sinkNode ) and
210129 taintCause = sourceNode .getNode ( ) .( FlowSource ) .getSourceType ( ) and
211- conf1 .hasFlowPath ( sourceNode , concatSink ) and
212- interestingConcatenation ( concatSink .getNode ( ) , concatSource .getNode ( ) ) and // this loses call context
213- conf2 .hasFlowPath ( concatSource , sinkNode ) and
214- shellCommand ( sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , callChain )
215- select sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , TPathNode1 ( sourceNode ) .( MergedPathNode ) ,
216- TPathNode2 ( sinkNode ) .( MergedPathNode ) ,
130+ shellCommand ( sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , callChain ) and
131+ concatResult = sinkNode .getState ( ) .( ExecState ) .getSndNode ( )
132+ select sinkAsArgumentIndirection ( sinkNode .getNode ( ) ) , sourceNode , sinkNode ,
217133 "This argument to an OS command is derived from $@, dangerously concatenated into $@, and then passed to "
218- + callChain , sourceNode , "user input (" + taintCause + ")" , concatSource ,
219- concatSource .toString ( )
134+ + callChain , sourceNode , "user input (" + taintCause + ")" , concatResult ,
135+ concatResult .toString ( )
0 commit comments