Skip to content

Commit a7fe37a

Browse files
authored
Merge pull request #9047 from geoffw0/xxe6
C++: Add support for SAX2XMLReader in the CWE-611 XXE query.
2 parents 1d10f14 + 85cc9b8 commit a7fe37a

File tree

4 files changed

+115
-8
lines changed

4 files changed

+115
-8
lines changed

cpp/ql/src/Security/CWE/CWE-611/XXE.ql

Lines changed: 77 additions & 8 deletions
Original file line numberDiff line numberDiff line change
@@ -60,8 +60,15 @@ class XercesDOMParserClass extends Class {
6060
/**
6161
* The `SAXParser` class.
6262
*/
63-
class SAXParserClass extends Class {
64-
SAXParserClass() { this.hasName("SAXParser") }
63+
class SaxParserClass extends Class {
64+
SaxParserClass() { this.hasName("SAXParser") }
65+
}
66+
67+
/**
68+
* The `SAX2XMLReader` class.
69+
*/
70+
class Sax2XmlReader extends Class {
71+
Sax2XmlReader() { this.hasName("SAX2XMLReader") }
6572
}
6673

6774
/**
@@ -113,7 +120,7 @@ class DisableDefaultEntityResolutionTranformer extends XXEFlowStateTranformer {
113120
call.getTarget() = f and
114121
(
115122
f.getDeclaringType() instanceof AbstractDOMParserClass or
116-
f.getDeclaringType() instanceof SAXParserClass
123+
f.getDeclaringType() instanceof SaxParserClass
117124
) and
118125
f.hasName("setDisableDefaultEntityResolution") and
119126
this = call.getQualifier() and
@@ -146,8 +153,7 @@ class CreateEntityReferenceNodesTranformer extends XXEFlowStateTranformer {
146153
CreateEntityReferenceNodesTranformer() {
147154
exists(Call call, Function f |
148155
call.getTarget() = f and
149-
f.getDeclaringType() instanceof AbstractDOMParserClass and
150-
f.hasName("setCreateEntityReferenceNodes") and
156+
f.getClassAndName("setCreateEntityReferenceNodes") instanceof AbstractDOMParserClass and
151157
this = call.getQualifier() and
152158
newValue = call.getArgument(0)
153159
)
@@ -168,12 +174,57 @@ class CreateEntityReferenceNodesTranformer extends XXEFlowStateTranformer {
168174
}
169175

170176
/**
171-
* The `AbstractDOMParser.parse` or `SAXParser.parse` method.
177+
* The `XMLUni.fgXercesDisableDefaultEntityResolution` constant.
178+
*/
179+
class FeatureDisableDefaultEntityResolution extends Variable {
180+
FeatureDisableDefaultEntityResolution() {
181+
this.getName() = "fgXercesDisableDefaultEntityResolution" and
182+
this.getDeclaringType().getName() = "XMLUni"
183+
}
184+
}
185+
186+
/**
187+
* A flow state transformer for a call to `SAX2XMLReader.setFeature`
188+
* specifying the feature `XMLUni::fgXercesDisableDefaultEntityResolution`.
189+
* Transforms the flow state through the qualifier according to this setting.
190+
*/
191+
class SetFeatureTranformer extends XXEFlowStateTranformer {
192+
Expr newValue;
193+
194+
SetFeatureTranformer() {
195+
exists(Call call, Function f |
196+
call.getTarget() = f and
197+
f.getClassAndName("setFeature") instanceof Sax2XmlReader and
198+
this = call.getQualifier() and
199+
globalValueNumber(call.getArgument(0)).getAnExpr().(VariableAccess).getTarget() instanceof
200+
FeatureDisableDefaultEntityResolution and
201+
newValue = call.getArgument(1)
202+
)
203+
}
204+
205+
final override XXEFlowState transform(XXEFlowState flowstate) {
206+
exists(int createEntityReferenceNodes |
207+
encodeXercesFlowState(flowstate, _, createEntityReferenceNodes) and
208+
(
209+
globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // true
210+
encodeXercesFlowState(result, 1, createEntityReferenceNodes)
211+
or
212+
not globalValueNumber(newValue).getAnExpr().getValue().toInt() = 1 and // false or unknown
213+
encodeXercesFlowState(result, 0, createEntityReferenceNodes)
214+
)
215+
)
216+
}
217+
}
218+
219+
/**
220+
* The `AbstractDOMParser.parse`, `SAXParser.parse` or `SAX2XMLReader.parse`
221+
* method.
172222
*/
173223
class ParseFunction extends Function {
174224
ParseFunction() {
175225
this.getClassAndName("parse") instanceof AbstractDOMParserClass or
176-
this.getClassAndName("parse") instanceof SAXParserClass
226+
this.getClassAndName("parse") instanceof SaxParserClass or
227+
this.getClassAndName("parse") instanceof Sax2XmlReader
177228
}
178229
}
179230

@@ -188,6 +239,17 @@ class CreateLSParser extends Function {
188239
}
189240
}
190241

242+
/**
243+
* The `createXMLReader` function that returns a newly created `SAX2XMLReader`
244+
* object.
245+
*/
246+
class CreateXmlReader extends Function {
247+
CreateXmlReader() {
248+
this.hasName("createXMLReader") and
249+
this.getUnspecifiedType().(PointerType).getBaseType() instanceof Sax2XmlReader // returns a `SAX2XMLReader *`.
250+
}
251+
}
252+
191253
/**
192254
* A call to a `libxml2` function that parses XML.
193255
*/
@@ -250,12 +312,19 @@ class XXEConfiguration extends DataFlow::Configuration {
250312
// source is the write on `this` of a call to the `SAXParser`
251313
// constructor.
252314
exists(CallInstruction call |
253-
call.getStaticCallTarget() = any(SAXParserClass c).getAConstructor() and
315+
call.getStaticCallTarget() = any(SaxParserClass c).getAConstructor() and
254316
node.asInstruction().(WriteSideEffectInstruction).getDestinationAddress() =
255317
call.getThisArgument() and
256318
encodeXercesFlowState(flowstate, 0, 1) // default configuration
257319
)
258320
or
321+
// source is the result of a call to `createXMLReader`.
322+
exists(Call call |
323+
call.getTarget() instanceof CreateXmlReader and
324+
call = node.asExpr() and
325+
encodeXercesFlowState(flowstate, 0, 1) // default configuration
326+
)
327+
or
259328
// source is an `options` argument on a `libxml2` parse call that specifies
260329
// at least one unsafe option.
261330
//

cpp/ql/test/query-tests/Security/CWE/CWE-611/XXE.expected

Lines changed: 12 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,9 @@
11
edges
22
| tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p |
33
| tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p |
4+
| tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p |
5+
| tests3.cpp:60:21:60:53 | call to createXMLReader | tests3.cpp:63:2:63:2 | p |
6+
| tests3.cpp:67:21:67:53 | call to createXMLReader | tests3.cpp:70:2:70:2 | p |
47
| tests.cpp:15:23:15:43 | XercesDOMParser output argument | tests.cpp:17:2:17:2 | p |
58
| tests.cpp:28:23:28:43 | XercesDOMParser output argument | tests.cpp:31:2:31:2 | p |
69
| tests.cpp:35:19:35:19 | VariableAddress [post update] | tests.cpp:37:2:37:2 | p |
@@ -32,6 +35,12 @@ nodes
3235
| tests2.cpp:22:2:22:2 | p | semmle.label | p |
3336
| tests2.cpp:33:17:33:31 | SAXParser output argument | semmle.label | SAXParser output argument |
3437
| tests2.cpp:37:2:37:2 | p | semmle.label | p |
38+
| tests3.cpp:23:21:23:53 | call to createXMLReader | semmle.label | call to createXMLReader |
39+
| tests3.cpp:25:2:25:2 | p | semmle.label | p |
40+
| tests3.cpp:60:21:60:53 | call to createXMLReader | semmle.label | call to createXMLReader |
41+
| tests3.cpp:63:2:63:2 | p | semmle.label | p |
42+
| tests3.cpp:67:21:67:53 | call to createXMLReader | semmle.label | call to createXMLReader |
43+
| tests3.cpp:70:2:70:2 | p | semmle.label | p |
3544
| tests4.cpp:26:34:26:48 | (int)... | semmle.label | (int)... |
3645
| tests4.cpp:36:34:36:50 | (int)... | semmle.label | (int)... |
3746
| tests4.cpp:46:34:46:68 | ... \| ... | semmle.label | ... \| ... |
@@ -76,6 +85,9 @@ subpaths
7685
#select
7786
| tests2.cpp:22:2:22:2 | p | tests2.cpp:20:17:20:31 | SAXParser output argument | tests2.cpp:22:2:22:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:20:17:20:31 | SAXParser output argument | XML parser |
7887
| tests2.cpp:37:2:37:2 | p | tests2.cpp:33:17:33:31 | SAXParser output argument | tests2.cpp:37:2:37:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests2.cpp:33:17:33:31 | SAXParser output argument | XML parser |
88+
| tests3.cpp:25:2:25:2 | p | tests3.cpp:23:21:23:53 | call to createXMLReader | tests3.cpp:25:2:25:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:23:21:23:53 | call to createXMLReader | XML parser |
89+
| tests3.cpp:63:2:63:2 | p | tests3.cpp:60:21:60:53 | call to createXMLReader | tests3.cpp:63:2:63:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:60:21:60:53 | call to createXMLReader | XML parser |
90+
| tests3.cpp:70:2:70:2 | p | tests3.cpp:67:21:67:53 | call to createXMLReader | tests3.cpp:70:2:70:2 | p | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests3.cpp:67:21:67:53 | call to createXMLReader | XML parser |
7991
| tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | tests4.cpp:26:34:26:48 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:26:34:26:48 | (int)... | XML parser |
8092
| tests4.cpp:36:34:36:50 | (int)... | tests4.cpp:36:34:36:50 | (int)... | tests4.cpp:36:34:36:50 | (int)... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:36:34:36:50 | (int)... | XML parser |
8193
| tests4.cpp:46:34:46:68 | ... \| ... | tests4.cpp:46:34:46:68 | ... \| ... | tests4.cpp:46:34:46:68 | ... \| ... | This $@ is not configured to prevent an XML external entity (XXE) attack. | tests4.cpp:46:34:46:68 | ... \| ... | XML parser |

cpp/ql/test/query-tests/Security/CWE/CWE-611/tests.h

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -21,5 +21,6 @@ class XMLUni
2121
{
2222
public:
2323
static const XMLCh fgXercesDisableDefaultEntityResolution[];
24+
static const XMLCh fgXercesHarmlessOption[];
2425
};
2526

cpp/ql/test/query-tests/Security/CWE/CWE-611/tests3.cpp

Lines changed: 25 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -55,3 +55,28 @@ void test3_5(InputSource &data) {
5555
test3_5_init();
5656
p_3_5->parse(data); // GOOD
5757
}
58+
59+
void test3_6(InputSource &data) {
60+
SAX2XMLReader *p = XMLReaderFactory::createXMLReader();
61+
62+
p->setFeature(XMLUni::fgXercesDisableDefaultEntityResolution, false);
63+
p->parse(data); // BAD (parser not correctly configured)
64+
}
65+
66+
void test3_7(InputSource &data) {
67+
SAX2XMLReader *p = XMLReaderFactory::createXMLReader();
68+
69+
p->setFeature(XMLUni::fgXercesHarmlessOption, true);
70+
p->parse(data); // BAD (parser not correctly configured)
71+
}
72+
73+
void test3_8(InputSource &data) {
74+
SAX2XMLReader *p = XMLReaderFactory::createXMLReader();
75+
const XMLCh *feature = XMLUni::fgXercesDisableDefaultEntityResolution;
76+
77+
p->setFeature(feature, true);
78+
p->parse(data); // GOOD
79+
}
80+
81+
82+

0 commit comments

Comments
 (0)