Python: Tarslip query; Add sink for members and sanitizers for tarinfo objects.

This commit is contained in:
Mark Shannon
2019-06-18 15:03:17 +01:00
parent e14f7ef466
commit 6f15c84bdc
4 changed files with 103 additions and 13 deletions

View File

@@ -49,6 +49,9 @@ class TarfileOpen extends TaintSource {
* this tarfile is safe.
*/
not this.(CallNode).getAnArg().refersTo(any(StringObject str))
and
/* Ignore opens within the tarfile module itself */
not this.(ControlFlowNode).getLocation().getFile().getBaseName() = "tarfile.py"
}
override predicate isSourceOf(TaintKind kind) {
@@ -73,13 +76,33 @@ class TarFileInfo extends TaintKind {
}
/* For efficiency we don't want to track the flow of taint
* around the tarfile module. */
class ExcludeTarFilePy extends Sanitizer {
ExcludeTarFilePy() {
this = "Tar sanitizer"
}
override predicate sanitizingNode(TaintKind taint, ControlFlowNode node) {
node.getLocation().getFile().getBaseName() = "tarfile.py" and
(
taint instanceof OpenTarFile
or
taint instanceof TarFileInfo
)
}
}
/* Any call to an extractall method */
class ExtractAllSink extends TaintSink {
CallNode call;
ExtractAllSink() {
this = call.getFunction().(AttrNode).getObject("extractall")
this = call.getFunction().(AttrNode).getObject("extractall") and
count(call.getAnArg()) = 0
}
override predicate sinks(TaintKind kind) {
@@ -104,6 +127,25 @@ class ExtractSink extends TaintSink {
}
/* Members argument to extract method */
class ExtractMembersSink extends TaintSink {
CallNode call;
ExtractMembersSink() {
call.getFunction().(AttrNode).getName() = "extractall" and
(this = call.getArg(0) or this = call.getArgByName("members"))
}
override predicate sinks(TaintKind kind) {
kind.(SequenceKind).getItem() instanceof TarFileInfo
or
kind instanceof OpenTarFile
}
}
class TarFileInfoSanitizer extends Sanitizer {
TarFileInfoSanitizer() {
@@ -114,33 +156,35 @@ class TarFileInfoSanitizer extends Sanitizer {
path_sanitizing_test(test.getTest()) and
taint instanceof TarFileInfo
}
}
private predicate path_sanitizing_test(ControlFlowNode test) {
checks_not_absolute(test) and
test.getAChild+().getNode().(StrConst).getText() = ".."
}
private predicate checks_not_absolute(ControlFlowNode test) {
test.getAChild+().(CallNode).getFunction().pointsTo(Module::named("os.path").attr("absfile"))
/* Assume that any test with "path" in it is a sanitizer */
test.getAChild+().(AttrNode).getName() = "path"
or
test.getAChild+().getNode().(StrConst).getText() = "/"
test.getAChild+().(NameNode).getId() = "path"
}
class TarSlipConfiguration extends TaintTracking::Configuration {
TarSlipConfiguration() { this = "TarSlip configuration" }
override predicate isSource(TaintTracking::Source source) { source instanceof TarfileOpen }
override predicate isSink(TaintTracking::Sink sink) {
sink instanceof ExtractSink or sink instanceof ExtractAllSink
override predicate isSink(TaintTracking::Sink sink) {
sink instanceof ExtractSink or
sink instanceof ExtractAllSink or
sink instanceof ExtractMembersSink
}
override predicate isSanitizer(Sanitizer sanitizer) {
sanitizer instanceof TarFileInfoSanitizer
or
sanitizer instanceof ExcludeTarFilePy
}
}
@@ -148,4 +192,3 @@ from TarSlipConfiguration config, TaintedPathSource src, TaintedPathSink sink
where config.hasFlowPath(src, sink)
select sink.getSink(), src, sink, "Extraction of tarfile from $@", src.getSource(), "a potentially untrusted source"

View File

@@ -703,7 +703,7 @@ class TaintedNode extends TTaintedNode {
/** Holds if the underlying CFG node for this node is a vulnerable node
* and is vulnerable to this node's taint.
*/
predicate isVulnerableSink() {
predicate isSink() {
exists(TaintedNode src, TaintSink vuln |
src.isSource() and
src.getASuccessor*() = this and
@@ -712,6 +712,13 @@ class TaintedNode extends TTaintedNode {
)
}
/** DEPRECATED -- Use `TaintedNode.isSink()` instead
* Sinks are not necessarily vulnerable
* For removal 2020-07-01 */
deprecated predicate isVulnerableSink() {
this.isSink()
}
TaintFlowImplementation::TrackedTaint fromAttribute(string name) {
result = this.getTrackedValue().(TaintFlowImplementation::TrackedAttribute).fromAttribute(name)
}

View File

@@ -1,13 +1,38 @@
edges
| tarslip.py:12:7:12:39 | tarfile.open | tarslip.py:13:1:13:3 | tarfile.open |
| tarslip.py:12:7:12:39 | tarfile.open | tarslip.py:14:1:14:3 | tarfile.open |
| tarslip.py:16:7:16:39 | tarfile.open | tarslip.py:17:14:17:16 | tarfile.open |
| tarslip.py:16:7:16:39 | tarfile.open | tarslip.py:18:5:18:7 | tarfile.open |
| tarslip.py:17:5:17:9 | tarfile.entry | tarslip.py:18:17:18:21 | tarfile.entry |
| tarslip.py:17:14:17:16 | tarfile.open | tarslip.py:17:5:17:9 | tarfile.entry |
| tarslip.py:26:7:26:39 | tarfile.open | tarslip.py:27:14:27:16 | tarfile.open |
| tarslip.py:26:7:26:39 | tarfile.open | tarslip.py:30:5:30:7 | tarfile.open |
| tarslip.py:27:5:27:9 | tarfile.entry | tarslip.py:28:22:28:26 | tarfile.entry |
| tarslip.py:27:14:27:16 | tarfile.open | tarslip.py:27:5:27:9 | tarfile.entry |
| tarslip.py:28:22:28:26 | tarfile.entry | tarslip.py:28:22:28:31 | tarfile.entry |
| tarslip.py:33:7:33:39 | tarfile.open | tarslip.py:34:14:34:16 | tarfile.open |
| tarslip.py:33:7:33:39 | tarfile.open | tarslip.py:37:5:37:7 | tarfile.open |
| tarslip.py:34:5:34:9 | tarfile.entry | tarslip.py:35:16:35:20 | tarfile.entry |
| tarslip.py:34:5:34:9 | tarfile.entry | tarslip.py:37:17:37:21 | tarfile.entry |
| tarslip.py:34:14:34:16 | tarfile.open | tarslip.py:34:5:34:9 | tarfile.entry |
| tarslip.py:35:16:35:20 | tarfile.entry | tarslip.py:35:16:35:25 | tarfile.entry |
| tarslip.py:40:7:40:39 | tarfile.open | tarslip.py:41:1:41:3 | tarfile.open |
| tarslip.py:40:7:40:39 | tarfile.open | tarslip.py:41:24:41:26 | tarfile.open |
| tarslip.py:45:17:45:23 | tarfile.open | tarslip.py:46:17:46:23 | tarfile.open |
| tarslip.py:46:9:46:12 | tarfile.entry | tarslip.py:47:20:47:23 | tarfile.entry |
| tarslip.py:46:9:46:12 | tarfile.entry | tarslip.py:49:15:49:18 | tarfile.entry |
| tarslip.py:46:17:46:23 | tarfile.open | tarslip.py:46:9:46:12 | tarfile.entry |
| tarslip.py:51:7:51:39 | tarfile.open | tarslip.py:52:1:52:3 | tarfile.open |
| tarslip.py:51:7:51:39 | tarfile.open | tarslip.py:52:36:52:38 | tarfile.open |
| tarslip.py:52:36:52:38 | tarfile.open | tarslip.py:45:17:45:23 | tarfile.open |
parents
| tarslip.py:45:17:45:23 | tarfile.open | tarslip.py:52:36:52:38 | tarfile.open |
| tarslip.py:46:9:46:12 | tarfile.entry | tarslip.py:52:36:52:38 | tarfile.open |
| tarslip.py:46:17:46:23 | tarfile.open | tarslip.py:52:36:52:38 | tarfile.open |
| tarslip.py:47:20:47:23 | tarfile.entry | tarslip.py:52:36:52:38 | tarfile.open |
| tarslip.py:49:15:49:18 | tarfile.entry | tarslip.py:52:36:52:38 | tarfile.open |
#select
| tarslip.py:13:1:13:3 | Taint sink | tarslip.py:12:7:12:39 | tarfile.open | tarslip.py:13:1:13:3 | tarfile.open | Extraction of tarfile from $@ | tarslip.py:12:7:12:39 | Taint source | a potentially untrusted source |
| tarslip.py:18:17:18:21 | Taint sink | tarslip.py:16:7:16:39 | tarfile.open | tarslip.py:18:17:18:21 | tarfile.entry | Extraction of tarfile from $@ | tarslip.py:16:7:16:39 | Taint source | a potentially untrusted source |
| tarslip.py:37:17:37:21 | Taint sink | tarslip.py:33:7:33:39 | tarfile.open | tarslip.py:37:17:37:21 | tarfile.entry | Extraction of tarfile from $@ | tarslip.py:33:7:33:39 | Taint source | a potentially untrusted source |
| tarslip.py:41:24:41:26 | Taint sink | tarslip.py:40:7:40:39 | tarfile.open | tarslip.py:41:24:41:26 | tarfile.open | Extraction of tarfile from $@ | tarslip.py:40:7:40:39 | Taint source | a potentially untrusted source |

View File

@@ -35,3 +35,18 @@ for entry in tar:
if ".." in entry.name:
raise ValueError("Illegal tar archive entry")
tar.extract(entry, "/tmp/unpack/")
#Unsanitized members
tar = tarfile.open(unsafe_filename_tar)
tar.extractall(members=tar)
#Sanitize members
def safemembers(members):
for info in members:
if badpath(info):
raise
yield info
tar = tarfile.open(unsafe_filename_tar)
tar.extractall(members=safemembers(tar))