Permalink
Browse files

upgrade zipjail, adds max cpu/time limitations

This commit implements the long awaited requirement to limit CPU and
harddisk resources. From now on, submitted Zip bombs and alike will
affect the Cuckoo host in a limited fashion since, i.e., write a maximum
of one gigabyte (by default) to the harddisk and do this within a
maximum of two minutes (120 seconds).
  • Loading branch information...
jbremer committed Sep 12, 2018
1 parent d61a8f3 commit 5d511c9fa4a4e23964046bdeb0be32829008eecb
@@ -1,4 +1,5 @@
# Copyright (C) 2015-2018 Jurriaan Bremer.
# Copyright (C) 2018 Hatching B.V.
# This file is part of SFlock - http://www.sflock.org/.
# See the file 'docs/LICENSE.txt' for copying permission.

@@ -10,6 +11,7 @@
import re
import six
import shutil
import subprocess
import tempfile

from sflock.compat import magic
@@ -39,9 +41,21 @@ def init(self):
def supported(self):
return os.path.exists(self.exe)

@property
def zipjail(self):
return data_file(b"zipjail.elf")
def zipjail(self, filepath, dirpath, *args):
zipjail = data_file(b"zipjail.elf")

p = subprocess.Popen((
zipjail, filepath, dirpath, "--", self.exe
) + args, stdout=subprocess.PIPE, stderr=subprocess.PIPE)

return_code = p.wait()
out, err = p.communicate()

if b"Excessive writing caused incomplete unpacking!" in err:
self.f.error = "files_too_large"
return False

return not return_code

def handles(self):
if self.f.filename and self.f.filename.lower().endswith(self.exts):
@@ -1,7 +1,12 @@
# Copyright (C) 2015-2018 Jurriaan Bremer.
# Copyright (C) 2018 Hatching B.V.
# This file is part of SFlock - http://www.sflock.org/.
# See the file 'docs/LICENSE.txt' for copying permission.

# By default we don't accept a collection of files to be larger than 1GB.
# May be tweaked in the future including modifying this at runtime.
MAX_TOTAL_SIZE = 1024 * 1024 * 1024

def iter_passwords():
import pkg_resources
filepath = pkg_resources.resource_filename("sflock", "data/password.txt")
BIN +48.3 KB (110%) sflock/data/zipjail.elf
Binary file not shown.
@@ -1,4 +1,5 @@
# Copyright (C) 2016 Jurriaan Bremer.
# Copyright (C) 2018 Hatching B.V.
# This file is part of SFlock - http://www.sflock.org/.
# See the file 'docs/LICENSE.txt' for copying permission.

@@ -24,14 +25,11 @@ def unpack(self, password=None, duplicates=None):
filepath = self.f.temp_path(".ace")
temporary = True

try:
subprocess.check_call([
self.zipjail, filepath, dirpath,
self.exe, "x", filepath, dirpath + os.sep,
], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
except subprocess.CalledProcessError as e:
self.f.mode = "failed"
self.f.error = e
ret = self.zipjail(
filepath, dirpath, "x", filepath, dirpath + os.sep
)
if not ret:
return []

if temporary:
os.unlink(filepath)
@@ -1,4 +1,5 @@
# Copyright (C) 2017-2018 Jurriaan Bremer.
# Copyright (C) 2018 Hatching B.V.
# This file is part of SFlock - http://www.sflock.org/.
# See the file 'docs/LICENSE.txt' for copying permission.

@@ -24,14 +25,11 @@ def unpack(self, password=None, duplicates=None):
filepath = self.f.temp_path(".cab")
temporary = True

try:
subprocess.check_call([
self.zipjail, filepath, dirpath,
self.exe, "-d%s" % dirpath, filepath,
], stdin=subprocess.PIPE, stdout=subprocess.PIPE)
except subprocess.CalledProcessError as e:
self.f.mode = "failed"
self.f.error = e
ret = self.zipjail(
filepath, dirpath, "-d%s" % dirpath, filepath
)
if not ret:
return []

if temporary:
os.unlink(filepath)
@@ -1,4 +1,5 @@
# Copyright (C) 2015-2018 Jurriaan Bremer.
# Copyright (C) 2018 Hatching B.V.
# This file is part of SFlock - http://www.sflock.org/.
# See the file 'docs/LICENSE.txt' for copying permission.

@@ -24,15 +25,12 @@ def unpack(self, password=None, duplicates=None):
filepath = self.f.temp_path()
temporary = True

try:
subprocess.check_output([
self.zipjail, filepath, dirpath,
self.exe, "x", "-mt1", b"-p%s" % (password or b"-"),
filepath, dirpath,
])
except subprocess.CalledProcessError as e:
self.f.mode = "failed"
self.f.error = e
ret = self.zipjail(
filepath, dirpath, "x", "-mt1", b"-p%s" % (password or b"-"),
filepath, dirpath
)
if not ret:
return []

if temporary:
os.unlink(filepath)
@@ -1,4 +1,5 @@
# Copyright (C) 2015-2018 Jurriaan Bremer.
# Copyright (C) 2018 Hatching B.V.
# This file is part of SFlock - http://www.sflock.org/.
# See the file 'docs/LICENSE.txt' for copying permission.

@@ -8,6 +9,7 @@
import tarfile

from sflock.abstracts import Unpacker, File
from sflock.config import MAX_TOTAL_SIZE

class TarFile(Unpacker):
name = "tarfile"
@@ -26,12 +28,19 @@ def unpack(self, password=None, duplicates=None):
self.f.error = e
return []

entries = []
entries, total_size = [], 0
for entry in archive:
# Ignore anything that's not a file for now.
if not entry.isfile():
if not entry.isfile() or entry.size < 0:
continue

# TODO Improve this. Also take precedence for native decompression
# utilities over the Python implementation in the future.
total_size += entry.size
if total_size >= MAX_TOTAL_SIZE:
self.f.error = "files_too_large"
return []

relapath = entry.path
if six.PY3:
relapath = relapath.encode()
@@ -1,4 +1,5 @@
# Copyright (C) 2015-2018 Jurriaan Bremer.
# Copyright (C) 2018 Hatching B.V.
# This file is part of SFlock - http://www.sflock.org/.
# See the file 'docs/LICENSE.txt' for copying permission.

@@ -8,6 +9,7 @@
import zlib

from sflock.abstracts import File, Unpacker
from sflock.config import MAX_TOTAL_SIZE
from sflock.exception import UnpackException

class ZipFile(Unpacker):
@@ -65,16 +67,23 @@ def unpack(self, password=None, duplicates=None):
self.f.error = e
return []

entries, directories = [], []
entries, directories, total_size = [], [], 0
for entry in archive.infolist():
if entry.filename.endswith("/"):
if entry.filename.endswith("/") or entry.file_size < 0:
continue

# TODO We should likely move this to self.process(), assuming
# this is also an issue with other archive formats.
if not entry.filename.strip():
continue

# TODO Improve this. Also take precedence for native decompression
# utilities over the Python implementation in the future.
total_size += entry.file_size
if total_size >= MAX_TOTAL_SIZE:
self.f.error = "files_too_large"
return []

f = self.bruteforce(password, archive, entry)
entries.append(f or File(
filename=entry.filename,
@@ -1,4 +1,5 @@
# Copyright (C) 2015-2018 Jurriaan Bremer.
# Copyright (C) 2018 Hatching B.V.
# This file is part of SFlock - http://www.sflock.org/.
# See the file 'docs/LICENSE.txt' for copying permission.

@@ -33,14 +34,12 @@ def unpack(self, password=None, duplicates=None):
filepath = self.f.temp_path(b".7z")
temporary = True

try:
subprocess.check_output([
self.zipjail, filepath, dirpath,
self.exe, "x", "-mmt=off", "-o%s" % dirpath.decode(), filepath,
])
except subprocess.CalledProcessError as e:
self.f.mode = "failed"
self.f.error = e
ret = self.zipjail(
filepath, dirpath, "x", "-mmt=off",
"-o%s" % dirpath.decode(), filepath
)
if not ret:
return []

if temporary:
os.unlink(filepath)
@@ -63,14 +62,11 @@ def unpack(self, password=None, duplicates=None):
filepath = self.f.temp_path(".7z")
temporary = True

try:
subprocess.check_output([
self.zipjail, filepath, dirpath,
self.exe, "x", "-o%s" % dirpath, filepath,
])
except subprocess.CalledProcessError as e:
self.f.mode = "failed"
self.f.error = e
ret = self.zipjail(
filepath, dirpath, "x", "-o%s" % dirpath, filepath
)
if not ret:
return []

if temporary:
os.unlink(filepath)
@@ -93,14 +89,11 @@ def unpack(self, password=None, duplicates=None):
filepath = self.f.temp_path(".7z")
temporary = True

try:
subprocess.check_output([
self.zipjail, filepath, dirpath,
self.exe, "x", "-o%s" % dirpath, filepath,
])
except subprocess.CalledProcessError as e:
self.f.mode = "failed"
self.f.error = e
ret = self.zipjail(
filepath, dirpath, "x", "-o%s" % dirpath, filepath
)
if not ret:
return []

if temporary:
os.unlink(filepath)
BIN +148 KB tests/files/1025mb.7z
Binary file not shown.
Binary file not shown.
BIN +1020 KB tests/files/1025mb.zip
Binary file not shown.
@@ -1,4 +1,5 @@
# Copyright (C) 2016-2018 Jurriaan Bremer.
# Copyright (C) 2018 Hatching B.V.
# This file is part of SFlock - http://www.sflock.org/.
# See the file 'docs/LICENSE.txt' for copying permission.

@@ -442,3 +443,24 @@ def test_duplicate2():
assert unpack(
b"tests/files/7z_nested.7z", duplicates=duplicates
).children[0].duplicate is True

def test_maxsize_7z():
if ".7z" not in supported():
return

f = unpack(b"tests/files/1025mb.7z")
assert f.unpacker == "7zfile"
assert not f.children
assert f.error == "files_too_large"

def test_maxsize_tar():
f = unpack(b"tests/files/1025mb.tar.bz2")
assert f.unpacker == "tarbz2file"
assert not f.children
assert f.error == "files_too_large"

def test_maxsize_zip():
f = unpack(b"tests/files/1025mb.zip")
assert f.unpacker == "zipfile"
assert not f.children
assert f.error == "files_too_large"

0 comments on commit 5d511c9

Please sign in to comment.