Skip to content

Commit 29d5ed6

Browse files
authored
Merge pull request #460 from onekey-sec/459-fix-tar-extractor
Resolve path traversal and unhandled permission error in tar handler
2 parents 91a2b04 + 01d022f commit 29d5ed6

File tree

5 files changed

+45
-4
lines changed

5 files changed

+45
-4
lines changed
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:0c0558a0d64c93f706175b7a38158a2909c07bf174ed54572e9f31896ddb20a6
3+
size 260
Lines changed: 3 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,3 @@
1+
version https://git-lfs.github.com/spec/v1
2+
oid sha256:9d4ab5759508af8d62e4686b236f896210a4145a146ab399797993dcddcc9b22
3+
size 10240

tests/integration/archive/tar/__output__/traversal.tar_extract/traversal_extract/.gitkeep

Whitespace-only changes.
Lines changed: 29 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,29 @@
1+
import os
2+
from pathlib import Path
3+
from tarfile import TarFile
4+
5+
from structlog import get_logger
6+
7+
from unblob.extractor import is_safe_path
8+
9+
logger = get_logger()
10+
11+
RUNNING_AS_ROOT = os.getuid() == 0
12+
13+
14+
class SafeTarFile(TarFile):
15+
def extract(self, member, path="", set_attrs=True, *, numeric_owner=False):
16+
path_as_path = Path(str(path))
17+
member_name_path = Path(str(member.name))
18+
19+
if not RUNNING_AS_ROOT and (member.ischr() or member.isblk()):
20+
logger.warn(
21+
"missing elevated permissions, skipping block and character device creation",
22+
path=member_name_path,
23+
)
24+
return
25+
if not is_safe_path(path_as_path, member_name_path):
26+
logger.warn("traversal attempt", path=member_name_path)
27+
return
28+
29+
super().extract(member, path, set_attrs, numeric_owner=numeric_owner)

unblob/handlers/archive/tar.py

Lines changed: 10 additions & 4 deletions
Original file line numberDiff line numberDiff line change
@@ -1,12 +1,12 @@
11
import tarfile
2+
from pathlib import Path
23
from typing import Optional
34

45
from structlog import get_logger
56

6-
from unblob.extractors.command import Command
7-
87
from ...file_utils import OffsetFile, decode_int, round_up, snull
9-
from ...models import File, HexString, StructHandler, ValidChunk
8+
from ...models import Extractor, File, HexString, StructHandler, ValidChunk
9+
from ._safe_tarfile import SafeTarFile as safe_tarfile
1010

1111
logger = get_logger()
1212

@@ -66,6 +66,12 @@ def _find_end_of_padding(file, *, find_from: int) -> int:
6666
return find_from + padding_blocks * BLOCK_SIZE
6767

6868

69+
class TarExtractor(Extractor):
70+
def extract(self, inpath: Path, outdir: Path):
71+
tf = safe_tarfile.open(inpath.as_posix())
72+
tf.extractall(outdir.as_posix())
73+
74+
6975
class TarHandler(StructHandler):
7076
NAME = "tar"
7177

@@ -102,7 +108,7 @@ class TarHandler(StructHandler):
102108
"""
103109
HEADER_STRUCT = "posix_header_t"
104110

105-
EXTRACTOR = Command("python3", "-m", "tarfile", "-e", "{inpath}", "{outdir}")
111+
EXTRACTOR = TarExtractor()
106112

107113
def calculate_chunk(self, file: File, start_offset: int) -> Optional[ValidChunk]:
108114
file.seek(start_offset)

0 commit comments

Comments
 (0)