Skip to content

Commit 54cc4d6

Browse files
committed
Opt for any source from RemoteFlowSource.
1 parent aaa0040 commit 54cc4d6

File tree

3 files changed

+79
-85
lines changed

3 files changed

+79
-85
lines changed

python/ql/src/experimental/Security/CWE-022bis/UnsafeUnpackQuery.qll

Lines changed: 2 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,5 @@
11
/**
2-
*
32
* Provides a taint-tracking configuration for detecting "UnsafeUnpacking" vulnerabilities.
4-
*
53
*/
64

75
import python
@@ -10,13 +8,14 @@ import semmle.python.dataflow.new.internal.DataFlowPublic
108
import semmle.python.ApiGraphs
119
import semmle.python.dataflow.new.TaintTracking
1210
import semmle.python.frameworks.Stdlib
11+
import semmle.python.dataflow.new.RemoteFlowSources
1312

1413
class UnsafeUnpackingConfig extends TaintTracking::Configuration {
1514
UnsafeUnpackingConfig() { this = "UnsafeUnpackingConfig" }
1615

1716
override predicate isSource(DataFlow::Node source) {
1817
// A source coming from a remote location
19-
exists(Http::Client::Request request | source = request)
18+
source instanceof RemoteFlowSource
2019
or
2120
// A source coming from a CLI argparse module
2221
// see argparse: https://docs.python.org/3/library/argparse.html
Lines changed: 36 additions & 26 deletions
Original file line numberDiff line numberDiff line change
@@ -1,30 +1,40 @@
11
edges
2-
| UnsafeUnpack.py:5:12:5:41 | ControlFlowNode for Attribute() | UnsafeUnpack.py:9:15:9:26 | ControlFlowNode for Attribute |
3-
| UnsafeUnpack.py:9:15:9:26 | ControlFlowNode for Attribute | UnsafeUnpack.py:12:23:12:29 | ControlFlowNode for tarpath |
4-
| UnsafeUnpack.py:36:24:36:43 | ControlFlowNode for Attribute() | UnsafeUnpack.py:55:31:55:37 | ControlFlowNode for to_path |
5-
| UnsafeUnpack.py:70:50:70:65 | ControlFlowNode for local_ziped_path | UnsafeUnpack.py:71:23:71:38 | ControlFlowNode for local_ziped_path |
6-
| UnsafeUnpack.py:84:20:84:34 | ControlFlowNode for compressed_file | UnsafeUnpack.py:85:23:85:37 | ControlFlowNode for compressed_file |
7-
| UnsafeUnpack.py:88:19:88:36 | ControlFlowNode for Attribute() | UnsafeUnpack.py:89:23:89:37 | ControlFlowNode for compressed_file |
8-
| UnsafeUnpack.py:102:19:102:31 | ControlFlowNode for Attribute | UnsafeUnpack.py:103:23:103:37 | ControlFlowNode for compressed_file |
2+
| UnsafeUnpack.py:0:0:0:0 | ModuleVariableNode for UnsafeUnpack.request | UnsafeUnpack.py:11:16:11:22 | ControlFlowNode for request |
3+
| UnsafeUnpack.py:5:26:5:32 | ControlFlowNode for ImportMember | UnsafeUnpack.py:5:26:5:32 | GSSA Variable request |
4+
| UnsafeUnpack.py:5:26:5:32 | GSSA Variable request | UnsafeUnpack.py:0:0:0:0 | ModuleVariableNode for UnsafeUnpack.request |
5+
| UnsafeUnpack.py:11:16:11:22 | ControlFlowNode for request | UnsafeUnpack.py:11:16:11:27 | ControlFlowNode for Attribute |
6+
| UnsafeUnpack.py:11:16:11:27 | ControlFlowNode for Attribute | UnsafeUnpack.py:17:23:17:34 | ControlFlowNode for Attribute |
7+
| UnsafeUnpack.py:17:23:17:34 | ControlFlowNode for Attribute | UnsafeUnpack.py:20:31:20:37 | ControlFlowNode for tarpath |
8+
| UnsafeUnpack.py:34:50:34:65 | ControlFlowNode for local_ziped_path | UnsafeUnpack.py:35:23:35:38 | ControlFlowNode for local_ziped_path |
9+
| UnsafeUnpack.py:48:20:48:34 | ControlFlowNode for compressed_file | UnsafeUnpack.py:49:23:49:37 | ControlFlowNode for compressed_file |
10+
| UnsafeUnpack.py:52:19:52:36 | ControlFlowNode for Attribute() | UnsafeUnpack.py:53:23:53:37 | ControlFlowNode for compressed_file |
11+
| UnsafeUnpack.py:66:19:66:31 | ControlFlowNode for Attribute | UnsafeUnpack.py:67:23:67:37 | ControlFlowNode for compressed_file |
12+
| UnsafeUnpack.py:80:16:80:28 | ControlFlowNode for Attribute | UnsafeUnpack.py:86:15:86:26 | ControlFlowNode for Attribute |
13+
| UnsafeUnpack.py:86:15:86:26 | ControlFlowNode for Attribute | UnsafeUnpack.py:88:23:88:29 | ControlFlowNode for tarpath |
914
nodes
10-
| UnsafeUnpack.py:5:12:5:41 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
11-
| UnsafeUnpack.py:9:15:9:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
12-
| UnsafeUnpack.py:12:23:12:29 | ControlFlowNode for tarpath | semmle.label | ControlFlowNode for tarpath |
13-
| UnsafeUnpack.py:36:24:36:43 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
14-
| UnsafeUnpack.py:55:31:55:37 | ControlFlowNode for to_path | semmle.label | ControlFlowNode for to_path |
15-
| UnsafeUnpack.py:70:50:70:65 | ControlFlowNode for local_ziped_path | semmle.label | ControlFlowNode for local_ziped_path |
16-
| UnsafeUnpack.py:71:23:71:38 | ControlFlowNode for local_ziped_path | semmle.label | ControlFlowNode for local_ziped_path |
17-
| UnsafeUnpack.py:84:20:84:34 | ControlFlowNode for compressed_file | semmle.label | ControlFlowNode for compressed_file |
18-
| UnsafeUnpack.py:85:23:85:37 | ControlFlowNode for compressed_file | semmle.label | ControlFlowNode for compressed_file |
19-
| UnsafeUnpack.py:88:19:88:36 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
20-
| UnsafeUnpack.py:89:23:89:37 | ControlFlowNode for compressed_file | semmle.label | ControlFlowNode for compressed_file |
21-
| UnsafeUnpack.py:102:19:102:31 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
22-
| UnsafeUnpack.py:103:23:103:37 | ControlFlowNode for compressed_file | semmle.label | ControlFlowNode for compressed_file |
15+
| UnsafeUnpack.py:0:0:0:0 | ModuleVariableNode for UnsafeUnpack.request | semmle.label | ModuleVariableNode for UnsafeUnpack.request |
16+
| UnsafeUnpack.py:5:26:5:32 | ControlFlowNode for ImportMember | semmle.label | ControlFlowNode for ImportMember |
17+
| UnsafeUnpack.py:5:26:5:32 | GSSA Variable request | semmle.label | GSSA Variable request |
18+
| UnsafeUnpack.py:11:16:11:22 | ControlFlowNode for request | semmle.label | ControlFlowNode for request |
19+
| UnsafeUnpack.py:11:16:11:27 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
20+
| UnsafeUnpack.py:17:23:17:34 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
21+
| UnsafeUnpack.py:20:31:20:37 | ControlFlowNode for tarpath | semmle.label | ControlFlowNode for tarpath |
22+
| UnsafeUnpack.py:34:50:34:65 | ControlFlowNode for local_ziped_path | semmle.label | ControlFlowNode for local_ziped_path |
23+
| UnsafeUnpack.py:35:23:35:38 | ControlFlowNode for local_ziped_path | semmle.label | ControlFlowNode for local_ziped_path |
24+
| UnsafeUnpack.py:48:20:48:34 | ControlFlowNode for compressed_file | semmle.label | ControlFlowNode for compressed_file |
25+
| UnsafeUnpack.py:49:23:49:37 | ControlFlowNode for compressed_file | semmle.label | ControlFlowNode for compressed_file |
26+
| UnsafeUnpack.py:52:19:52:36 | ControlFlowNode for Attribute() | semmle.label | ControlFlowNode for Attribute() |
27+
| UnsafeUnpack.py:53:23:53:37 | ControlFlowNode for compressed_file | semmle.label | ControlFlowNode for compressed_file |
28+
| UnsafeUnpack.py:66:19:66:31 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
29+
| UnsafeUnpack.py:67:23:67:37 | ControlFlowNode for compressed_file | semmle.label | ControlFlowNode for compressed_file |
30+
| UnsafeUnpack.py:80:16:80:28 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
31+
| UnsafeUnpack.py:86:15:86:26 | ControlFlowNode for Attribute | semmle.label | ControlFlowNode for Attribute |
32+
| UnsafeUnpack.py:88:23:88:29 | ControlFlowNode for tarpath | semmle.label | ControlFlowNode for tarpath |
2333
subpaths
2434
#select
25-
| UnsafeUnpack.py:12:23:12:29 | ControlFlowNode for tarpath | UnsafeUnpack.py:5:12:5:41 | ControlFlowNode for Attribute() | UnsafeUnpack.py:12:23:12:29 | ControlFlowNode for tarpath | Unsafe extraction from a malicious tarball retrieved from a remote location. |
26-
| UnsafeUnpack.py:55:31:55:37 | ControlFlowNode for to_path | UnsafeUnpack.py:36:24:36:43 | ControlFlowNode for Attribute() | UnsafeUnpack.py:55:31:55:37 | ControlFlowNode for to_path | Unsafe extraction from a malicious tarball retrieved from a remote location. |
27-
| UnsafeUnpack.py:71:23:71:38 | ControlFlowNode for local_ziped_path | UnsafeUnpack.py:70:50:70:65 | ControlFlowNode for local_ziped_path | UnsafeUnpack.py:71:23:71:38 | ControlFlowNode for local_ziped_path | Unsafe extraction from a malicious tarball retrieved from a remote location. |
28-
| UnsafeUnpack.py:85:23:85:37 | ControlFlowNode for compressed_file | UnsafeUnpack.py:84:20:84:34 | ControlFlowNode for compressed_file | UnsafeUnpack.py:85:23:85:37 | ControlFlowNode for compressed_file | Unsafe extraction from a malicious tarball retrieved from a remote location. |
29-
| UnsafeUnpack.py:89:23:89:37 | ControlFlowNode for compressed_file | UnsafeUnpack.py:88:19:88:36 | ControlFlowNode for Attribute() | UnsafeUnpack.py:89:23:89:37 | ControlFlowNode for compressed_file | Unsafe extraction from a malicious tarball retrieved from a remote location. |
30-
| UnsafeUnpack.py:103:23:103:37 | ControlFlowNode for compressed_file | UnsafeUnpack.py:102:19:102:31 | ControlFlowNode for Attribute | UnsafeUnpack.py:103:23:103:37 | ControlFlowNode for compressed_file | Unsafe extraction from a malicious tarball retrieved from a remote location. |
35+
| UnsafeUnpack.py:20:31:20:37 | ControlFlowNode for tarpath | UnsafeUnpack.py:5:26:5:32 | ControlFlowNode for ImportMember | UnsafeUnpack.py:20:31:20:37 | ControlFlowNode for tarpath | Unsafe extraction from a malicious tarball retrieved from a remote location. |
36+
| UnsafeUnpack.py:35:23:35:38 | ControlFlowNode for local_ziped_path | UnsafeUnpack.py:34:50:34:65 | ControlFlowNode for local_ziped_path | UnsafeUnpack.py:35:23:35:38 | ControlFlowNode for local_ziped_path | Unsafe extraction from a malicious tarball retrieved from a remote location. |
37+
| UnsafeUnpack.py:49:23:49:37 | ControlFlowNode for compressed_file | UnsafeUnpack.py:48:20:48:34 | ControlFlowNode for compressed_file | UnsafeUnpack.py:49:23:49:37 | ControlFlowNode for compressed_file | Unsafe extraction from a malicious tarball retrieved from a remote location. |
38+
| UnsafeUnpack.py:53:23:53:37 | ControlFlowNode for compressed_file | UnsafeUnpack.py:52:19:52:36 | ControlFlowNode for Attribute() | UnsafeUnpack.py:53:23:53:37 | ControlFlowNode for compressed_file | Unsafe extraction from a malicious tarball retrieved from a remote location. |
39+
| UnsafeUnpack.py:67:23:67:37 | ControlFlowNode for compressed_file | UnsafeUnpack.py:66:19:66:31 | ControlFlowNode for Attribute | UnsafeUnpack.py:67:23:67:37 | ControlFlowNode for compressed_file | Unsafe extraction from a malicious tarball retrieved from a remote location. |
40+
| UnsafeUnpack.py:88:23:88:29 | ControlFlowNode for tarpath | UnsafeUnpack.py:80:16:80:28 | ControlFlowNode for Attribute | UnsafeUnpack.py:88:23:88:29 | ControlFlowNode for tarpath | Unsafe extraction from a malicious tarball retrieved from a remote location. |
Lines changed: 41 additions & 56 deletions
Original file line numberDiff line numberDiff line change
@@ -1,60 +1,24 @@
11
import requests
22
import shutil
3-
4-
url = "https://www.someremote.location/tarball.tar.gz"
5-
response = requests.get(url, stream=True)
6-
7-
tarpath = "/tmp/tmp456/tarball.tar.gz"
8-
with open(tarpath, "wb") as f:
9-
f.write(response.raw.read())
10-
11-
untarredpath = "/tmp/tmp123"
12-
shutil.unpack_archive(tarpath, untarredpath) # $result=BAD
13-
14-
15-
import tempfile
16-
import os
17-
from urllib import request
18-
import contextlib
19-
import shutil
20-
21-
unpack = True
22-
to_path = "/tmp/tmp123"
23-
uri = "https://www.goog.com/zzz.tar.gz"
24-
scheme = "https"
25-
26-
with tempfile.TemporaryDirectory() as temp_dir:
27-
if unpack and (str(uri).endswith("zip") or str(uri).endswith("tar.gz")):
28-
unpack_path = to_path
29-
to_path = temp_dir
30-
else:
31-
unpack_path = None
32-
if scheme in ["http", "https", "ftp"]:
33-
if os.path.isdir(to_path):
34-
to_path = os.path.join(to_path, os.path.basename(uri))
35-
url = uri
36-
url_response = request.urlopen(url)
37-
with contextlib.closing(url_response) as fp:
38-
with open(to_path, "wb") as out_file:
39-
block_size = DEFAULT_BUFFER_SIZE * 8
40-
while True:
41-
block = fp.read(block_size)
42-
if not block:
43-
break
44-
out_file.write(block)
45-
else:
46-
if scheme == "oci" and not storage_options:
47-
storage_options = default_signer()
48-
fs = fsspec.filesystem(scheme, **storage_options)
49-
if os.path.isdir(to_path):
50-
to_path = os.path.join(
51-
to_path, os.path.basename(str(uri).rstrip("/"))
52-
)
53-
fs.get(uri, to_path, recursive=True)
54-
if unpack_path:
55-
shutil.unpack_archive(to_path, unpack_path) # $result=BAD
56-
to_path = unpack_path
57-
3+
import os
4+
5+
from flask import Flask, request
6+
app = Flask(__name__)
7+
8+
# Consider any RemoteFlowSource as a source
9+
@app.route("/download_from_url")
10+
def download_from_url():
11+
filename = request.args.get('filename', '')
12+
if not filename:
13+
response = requests.get(filename, stream=True)
14+
15+
tarpath = "/tmp/tmp456/tarball.tar.gz"
16+
with open(tarpath, "wb") as f:
17+
f.write(response.raw.read())
18+
19+
untarredpath = "/tmp/tmp123"
20+
shutil.unpack_archive(tarpath, untarredpath) # $result=BAD
21+
5822

5923
# A source catching an S3 filename download
6024
# see boto3: https://boto3.amazonaws.com/v1/documentation/api/latest/reference/services/s3.html#S3.Client.download_file
@@ -100,4 +64,25 @@
10064

10165
args = parser.parse_args()
10266
compressed_file = args.filename
103-
shutil.unpack_archive(compressed_file, base_dir) # $result=BAD
67+
shutil.unpack_archive(compressed_file, base_dir) # $result=BAD
68+
69+
70+
# A source coming from a CLI and downloaded
71+
import argparse
72+
import requests
73+
74+
parser = argparse.ArgumentParser(description='Process some integers.')
75+
parser.add_argument('integers', metavar='N', type=int, nargs='+',
76+
help='an integer for the accumulator')
77+
parser.add_argument('filename', help='url to filename to be provided')
78+
79+
args = parser.parse_args()
80+
url_filename = args.filename
81+
82+
response = requests.get(url_filename, stream=True)
83+
84+
tarpath = "/tmp/tmp456/tarball.tar.gz"
85+
with open(tarpath, "wb") as f:
86+
f.write(response.raw.read())
87+
88+
shutil.unpack_archive(tarpath, base_dir) # $result=BAD

0 commit comments

Comments
 (0)