Skip to content

Commit 7d60f1f

Browse files
committed
Modified the QL ref file and add TarSlip examples
1 parent 7319052 commit 7d60f1f

File tree

2 files changed

+317
-1
lines changed

2 files changed

+317
-1
lines changed
Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -1 +1 @@
1-
Security/CWE-022bis/TarSlipImprov.ql
1+
experimental/Security/CWE-022bis/TarSlipImprov.ql
Lines changed: 316 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,316 @@
1+
#!/usr/bin/python
2+
3+
import sys
4+
import tarfile
5+
import tempfile
6+
import os
7+
from tarfile import TarFile
8+
from contextlib import closing, contextmanager
9+
import subprocess
10+
import os.path
11+
12+
unsafe_filename_tar = sys.argv[2]
13+
safe_filename_tar = "safe_path.tar"
14+
15+
tar = tarfile.open(unsafe_filename_tar)
16+
result = []
17+
for member in tar:
18+
if ".." in member.name:
19+
raise ValueError("Path in member name !!!")
20+
result.append(member)
21+
path = unsafe_filename_tar
22+
tar.extractall(path=path, members=result)
23+
tar.close()
24+
25+
26+
def members_filter1(tarfile):
27+
result = []
28+
for member in tarfile:
29+
if '../' in member.name:
30+
print('Member name container directory traversal sequence')
31+
continue
32+
elif member.issym() or member.islnk():
33+
print('Symlink to external resource')
34+
continue
35+
result.append(member)
36+
return result
37+
38+
tar = tarfile.open(unsafe_filename_tar)
39+
tar.extractall(path=tempfile.mkdtemp(), members=members_filter1(tar))
40+
tar.close()
41+
42+
43+
with tarfile.open(unsafe_filename_tar) as tar:
44+
for entry in tar:
45+
if ".." in entry.name:
46+
raise ValueError("Illegal tar archive entry")
47+
tar.extract(entry, "/tmp/unpack/")
48+
49+
50+
def _validate_archive_name(name, target):
51+
if not os.path.abspath(os.path.join(target, name)).startswith(target + os.path.sep):
52+
raise ValueError(f"Provided language pack contains invalid name {name}")
53+
54+
with tarfile.open(unsafe_filename_tar) as tar:
55+
target = "/tmp/unpack"
56+
for entry in tar:
57+
_validate_archive_name(entry.name, target)
58+
tar.extract(entry, target)
59+
60+
61+
def members_filter2(tarfile):
62+
result = []
63+
for member in tarfile.getmembers():
64+
if '../' in member.name:
65+
print('Member name container directory traversal sequence')
66+
continue
67+
elif (member.issym() or member.islnk()) and ('../' in member.linkname):
68+
print('Symlink to external resource')
69+
continue
70+
result.append(member)
71+
return result
72+
73+
tar = tarfile.open(unsafe_filename_tar)
74+
tar.extractall(path=tempfile.mkdtemp(), members=members_filter2(tar))
75+
tar.close()
76+
77+
78+
def _validate_tar_info(info, target):
79+
_validate_archive_name(info.name, target)
80+
if not (info.isfile() or info.isdir()):
81+
raise ValueError("Provided language pack contains invalid file type")
82+
83+
def _validate_archive_name(name, target):
84+
if not os.path.abspath(os.path.join(target, name)).startswith(target + os.path.sep):
85+
raise ValueError(f"Provided language pack contains invalid name {name}")
86+
87+
target = "/tmp/unpack"
88+
with tarfile.open(unsafe_filename_tar, "r") as tar:
89+
for info in tar.getmembers():
90+
_validate_tar_info(info, target)
91+
tar.extractall(target)
92+
93+
94+
def members_filter3(tarfile):
95+
result = []
96+
for member in tarfile.getmembers():
97+
if '../' in member.name:
98+
print('Member name container directory traversal sequence')
99+
continue
100+
elif member.issym() or member.islnk():
101+
print('Symlink to external resource')
102+
continue
103+
result.append(member)
104+
return result
105+
106+
tar = tarfile.open(unsafe_filename_tar)
107+
tar.extractall(path=tempfile.mkdtemp(), members=members_filter3(tar))
108+
tar.close()
109+
110+
111+
tar = tarfile.open(unsafe_filename_tar)
112+
tarf = tar.getmembers()
113+
for f in tarf:
114+
if not f.issym():
115+
tar.extractall(path=tempfile.mkdtemp(), members=[f])
116+
tar.close()
117+
118+
119+
class MKTar(TarFile):
120+
pass
121+
122+
tarball = unsafe_filename_tar
123+
with MKTar.open(name=tarball) as tar:
124+
for entry in tar:
125+
tar._extract_member(entry, entry.name)
126+
127+
128+
tarball = unsafe_filename_tar
129+
with tarfile.open(tarball) as tar:
130+
tar.extractall()
131+
132+
133+
tar = tarfile.open(unsafe_filename_tar)
134+
tar.extractall(path=tempfile.mkdtemp(), members=None)
135+
136+
137+
class MKTar(tarfile.TarFile):
138+
pass
139+
140+
tarball = unsafe_filename_tar
141+
with MKTar.open(name=tarball) as tar:
142+
for entry in tar:
143+
tar._extract_member(entry, entry.name)
144+
145+
146+
@contextmanager
147+
def py2_tarxz(filename):
148+
with tempfile.TemporaryFile() as tmp:
149+
subprocess.check_call(["xz", "-dc", filename], stdout=tmp.fileno())
150+
tmp.seek(0)
151+
with closing(tarfile.TarFile(fileobj=tmp)) as tf:
152+
yield tf
153+
154+
def unpack_tarball(tar_filename, dest):
155+
if sys.version_info[0] < 3 and tar_filename.endswith('.xz'):
156+
# Py 2.7 lacks lzma support
157+
tar_cm = py2_tarxz(tar_filename)
158+
else:
159+
tar_cm = closing(tarfile.open(tar_filename))
160+
161+
base_dir = None
162+
with tar_cm as tarc:
163+
for member in tarc:
164+
base_name = member.name.split('/')[0]
165+
if base_dir is None:
166+
base_dir = base_name
167+
elif base_dir != base_name:
168+
print('Unexpected path in %s: %s' % (tar_filename, base_name))
169+
tarc.extractall(dest)
170+
return os.path.join(dest, base_dir)
171+
172+
unpack_tarball(unsafe_filename_tar, "/tmp/unpack")
173+
174+
175+
tarball = unsafe_filename_tar
176+
with tarfile.open(name=tarball) as tar:
177+
for entry in tar:
178+
tar._extract_member(entry, entry.name)
179+
180+
181+
tarball = unsafe_filename_tar
182+
with tarfile.open(name=tarball) as tar:
183+
for entry in tar:
184+
tar.extract(entry, "/tmp/unpack/")
185+
186+
187+
tarball = unsafe_filename_tar
188+
tar = tarfile.open(tarball)
189+
tar.extractall("/tmp/unpack/")
190+
191+
192+
tarball = unsafe_filename_tar
193+
with tarfile.open(tarball, "r") as tar:
194+
tar.extractall(path="/tmp/unpack/", members=tar)
195+
196+
197+
def members_filter4(tarfile):
198+
result = []
199+
for member in tarfile.getmembers():
200+
if member.issym() or member.islnk():
201+
print('Symlink to external resource')
202+
continue
203+
result.append(member)
204+
return result
205+
tar = tarfile.open(unsafe_filename_tar)
206+
tar.extractall(path=tempfile.mkdtemp(), members=members_filter4(tar))
207+
tar.close()
208+
209+
210+
with tarfile.open(unsafe_filename_tar, "r") as tar:
211+
tar.extractall(path="/tmp/unpack")
212+
213+
214+
def members_filter5(tarfile):
215+
result = []
216+
for member in tarfile.getmembers():
217+
if member.issym():
218+
print('Symlink to external resource')
219+
continue
220+
result.append(member)
221+
return result
222+
tar = tarfile.open(unsafe_filename_tar)
223+
tar.extractall(path=tempfile.mkdtemp(), members=members_filter5(tar))
224+
tar.close()
225+
226+
227+
filename = unsafe_filename_tar
228+
tmp_dir = "/tmp/"
229+
230+
read_type = "r:gz" if filename.endswith("tgz") else "r"
231+
with tarfile.open(filename, read_type) as corpus_tar:
232+
members = []
233+
for f in corpus_tar:
234+
if not os.path.isfile(os.path.join(tmp_dir, f.name)):
235+
members.append(f)
236+
corpus_tar.extractall(tmp_dir, members=members)
237+
238+
239+
def members_filter6(tarfile):
240+
result = []
241+
for member in tarfile.getmembers():
242+
if not member.isreg():
243+
print('Symlink to external resource')
244+
continue
245+
result.append(member)
246+
return result
247+
tar = tarfile.open(filename)
248+
tar.extractall(path=tempfile.mkdtemp(), members=members_filter6(tar))
249+
tar.close()
250+
251+
252+
archive_path = unsafe_filename_tar
253+
target_dir = "/tmp/unpack"
254+
tarfile.open(archive_path, "r").extractall(path=target_dir)
255+
256+
257+
tarball = unsafe_filename_tar
258+
with tarfile.open(tarball) as tar:
259+
for entry in tar:
260+
if entry.isfile():
261+
tar.extract(entry, "/tmp/unpack/")
262+
263+
264+
with tarfile.open(unsafe_filename_tar) as tar:
265+
for entry in tar:
266+
if entry.name.startswith("/"):
267+
raise ValueError("Illegal tar archive entry")
268+
tar.extract(entry, "/tmp/unpack/")
269+
270+
tarball = unsafe_filename_tar
271+
with tarfile.TarFile(tarball, mode="r") as tar:
272+
for entry in tar:
273+
if entry.isfile():
274+
tar.extract(entry, "/tmp/unpack/")
275+
276+
with tarfile.open(unsafe_filename_tar) as tar:
277+
for entry in tar:
278+
if os.path.isabs(entry.name):
279+
raise ValueError("Illegal tar archive entry")
280+
tar.extract(entry, "/tmp/unpack/")
281+
282+
283+
with tarfile.TarFile(unsafe_filename_tar, mode="r") as tar:
284+
tar.extractall(path="/tmp/unpack")
285+
286+
287+
tar = tarfile.open(filename)
288+
tar.extractall(path=tempfile.mkdtemp(), members=tar.getmembers())
289+
tar.close()
290+
291+
292+
tar = tarfile.open(unsafe_filename_tar)
293+
tar.extractall(path=tempfile.mkdtemp(), members=None)
294+
295+
296+
tar.extractall(path=tempfile.mkdtemp(), members=members_filter4(tar))
297+
tar.close()
298+
299+
300+
with tarfile.TarFile(unsafe_filename_tar, mode="r") as tar:
301+
tar.extractall(path="/tmp/unpack/", members=tar)
302+
303+
304+
tar = tarfile.open(unsafe_filename_tar)
305+
result = []
306+
for member in tar:
307+
if member.issym():
308+
raise ValueError("But it is a symlink")
309+
result.append(member)
310+
tar.extractall(path=tempfile.mkdtemp(), members=result)
311+
tar.close()
312+
313+
314+
archive_path = unsafe_filename_tar
315+
target_dir = "/tmp/unpack"
316+
tarfile.TarFile(unsafe_filename_tar, mode="r").extractall(path=target_dir)

0 commit comments

Comments
 (0)