Thanks to visit codestin.com
Credit goes to github.com

Skip to content

Commit 0d8bd01

Browse files
committed
Python: Port query and add test
1 parent b0d01cf commit 0d8bd01

12 files changed

Lines changed: 186 additions & 0 deletions

File tree

Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
from django.conf.urls import url
3+
import json
4+
5+
def safe(pickled):
6+
return json.loads(pickled)
7+
8+
urlpatterns = [
9+
url(r'^(?P<object>.*)$', safe)
10+
]
Lines changed: 10 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,10 @@
1+
2+
from django.conf.urls import url
3+
import pickle
4+
5+
def unsafe(pickled):
6+
return pickle.loads(pickled)
7+
8+
urlpatterns = [
9+
url(r'^(?P<object>.*)$', unsafe)
10+
]
Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
<!DOCTYPE qhelp PUBLIC "-//Semmle//qhelp//EN" "qhelp.dtd">
2+
<qhelp>
3+
4+
<overview>
5+
<p>
6+
Deserializing untrusted data using any deserialization framework that
7+
allows the construction of arbitrary serializable objects is easily exploitable
8+
and in many cases allows an attacker to execute arbitrary code. Even before a
9+
deserialized object is returned to the caller of a deserialization method a lot
10+
of code may have been executed, including static initializers, constructors,
11+
and finalizers. Automatic deserialization of fields means that an attacker may
12+
craft a nested combination of objects on which the executed initialization code
13+
may have unforeseen effects, such as the execution of arbitrary code.
14+
</p>
15+
<p>
16+
There are many different serialization frameworks. This query currently
17+
supports Pickle, Marshal and Yaml.
18+
</p>
19+
</overview>
20+
21+
<recommendation>
22+
<p>
23+
Avoid deserialization of untrusted data if at all possible. If the
24+
architecture permits it then use other formats instead of serialized objects,
25+
for example JSON.
26+
</p>
27+
</recommendation>
28+
29+
<example>
30+
<p>
31+
The following example calls <code>pickle.loads</code> directly on a
32+
value provided by an incoming HTTP request. Pickle then creates a new value from untrusted data, and is
33+
therefore inherently unsafe.
34+
</p>
35+
<sample src="UnpicklingBad.py" />
36+
37+
<p>
38+
Changing the code to use <code>json.loads</code> instead of <code>pickle.loads</code> removes the vulnerability.
39+
</p>
40+
<sample src="JsonGood.py" />
41+
42+
</example>
43+
44+
<references>
45+
46+
<li>
47+
OWASP vulnerability description:
48+
<a href="https://www.owasp.org/index.php/Deserialization_of_untrusted_data">Deserialization of untrusted data</a>.
49+
</li>
50+
<li>
51+
OWASP guidance on deserializing objects:
52+
<a href="https://cheatsheetseries.owasp.org/cheatsheets/Deserialization_Cheat_Sheet.html">Deserialization Cheat Sheet</a>.
53+
</li>
54+
<li>
55+
Talks by Chris Frohoff &amp; Gabriel Lawrence:
56+
<a href="http://frohoff.github.io/appseccali-marshalling-pickles/">
57+
AppSecCali 2015: Marshalling Pickles - how deserializing objects will ruin your day</a>
58+
</li>
59+
</references>
60+
61+
</qhelp>
Lines changed: 31 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,31 @@
1+
/**
2+
* @name Deserializing untrusted input
3+
* @description Deserializing user-controlled data may allow attackers to execute arbitrary code.
4+
* @kind path-problem
5+
* @id py/unsafe-deserialization
6+
* @problem.severity error
7+
* @sub-severity high
8+
* @precision high
9+
* @tags external/cwe/cwe-502
10+
* security
11+
* serialization
12+
*/
13+
14+
import python
15+
import experimental.dataflow.DataFlow
16+
import experimental.dataflow.TaintTracking
17+
import experimental.semmle.python.Concepts
18+
import experimental.dataflow.RemoteFlowSources
19+
import DataFlow::PathGraph
20+
21+
class UnsafeDeserializationConfiguration extends TaintTracking::Configuration {
22+
UnsafeDeserializationConfiguration() { this = "Unsafe deserialization configuration" }
23+
24+
override predicate isSource(DataFlow::Node source) { source instanceof RemoteFlowSource }
25+
26+
override predicate isSink(DataFlow::Node sink) { sink = any(DeserializationSink d).getData() }
27+
}
28+
29+
from UnsafeDeserializationConfiguration config, DataFlow::PathNode source, DataFlow::PathNode sink
30+
where config.hasFlowPath(source, sink)
31+
select sink.getNode(), source, sink, "Deserializing of $@.", source.getNode(), "untrusted input"

python/ql/src/experimental/semmle/python/Concepts.qll

Lines changed: 30 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -38,3 +38,33 @@ module SystemCommandExecution {
3838
abstract DataFlow::Node getCommand();
3939
}
4040
}
41+
42+
/**
43+
* A data-flow node that performs deserialization in a potentially unsafe way.
44+
*
45+
* Extend this class to refine existing API models. If you want to model new APIs,
46+
* extend `DeserializationSink::Range` instead.
47+
*/
48+
class DeserializationSink extends DataFlow::Node {
49+
DeserializationSink::Range self;
50+
51+
DeserializationSink() { this = self }
52+
53+
/** Gets the argument that specifies the command to be executed. */
54+
DataFlow::Node getData() { result = self.getData() }
55+
}
56+
57+
/** Provides a class for modeling new system-command execution APIs. */
58+
module DeserializationSink {
59+
/**
60+
* A data-flow node that executes an operating system command,
61+
* for instance by spawning a new process.
62+
*
63+
* Extend this class to model new APIs. If you want to refine existing API models,
64+
* extend `DeserializationSink` instead.
65+
*/
66+
abstract class Range extends DataFlow::Node {
67+
/** Gets the argument that specifies the command to be executed. */
68+
abstract DataFlow::Node getData();
69+
}
70+
}

python/ql/test/experimental/meta/ConceptsTest.qll

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -32,3 +32,20 @@ class SystemCommandExecutionTest extends InlineExpectationsTest {
3232
)
3333
}
3434
}
35+
36+
class DeserializationSinkTest extends InlineExpectationsTest {
37+
DeserializationSinkTest() { this = "DeserializationSinkTest" }
38+
39+
override string getARelevantTag() { result = "getData" }
40+
41+
override predicate hasActualResult(Location location, string element, string tag, string value) {
42+
exists(DeserializationSink ds, DataFlow::Node data |
43+
exists(location.getFile().getRelativePath()) and
44+
data = ds.getData() and
45+
location = data.getLocation() and
46+
element = data.toString() and
47+
value = value_from_expr(data.asExpr()) and
48+
tag = "getData"
49+
)
50+
}
51+
}
Lines changed: 4 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,4 @@
1+
| unsafe_deserialization.py:12:28:12:45 | Comment # $getData=payload | Missing result:getData=payload |
2+
| unsafe_deserialization.py:13:25:13:42 | Comment # $getData=payload | Missing result:getData=payload |
3+
| unsafe_deserialization.py:14:29:14:46 | Comment # $getData=payload | Missing result:getData=payload |
4+
| unsafe_deserialization.py:16:26:16:43 | Comment # $getData=payload | Missing result:getData=payload |
Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,2 @@
1+
import python
2+
import experimental.meta.ConceptsTest
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
edges
2+
nodes
3+
#select
Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1 @@
1+
experimental/Security-new-dataflow/CWE-502/UnsafeDeserialization.ql

0 commit comments

Comments
 (0)