trailofbits.python.tarfile-extractall-traversal.tarfile-extractall-traversal

profile photo of trailofbitstrailofbits
Author
232
Download Count*

Possible path traversal through tarfile.open($PATH).extractall() if the source tar is controlled by an attacker

Run Locally

Run in CI

Defintion

rules:
  - id: tarfile-extractall-traversal
    message: Possible path traversal through `tarfile.open($PATH).extractall()` if
      the source tar is controlled by an attacker
    languages:
      - python
    severity: ERROR
    metadata:
      category: security
      cwe: "CWE-22: Improper Limitation of a Pathname to a Restricted Directory ('Path
        Traversal')"
      subcategory:
        - vuln
      confidence: MEDIUM
      likelihood: MEDIUM
      impact: MEDIUM
      technology:
        - --no-technology--
      description: Potential path traversal in call to `extractall` for a `tarfile`
      references:
        - https://docs.python.org/3/library/tarfile.html#tarfile.TarFile.extractall
      license: AGPL-3.0 license
      vulnerability_class:
        - Path Traversal
    patterns:
      - pattern-either:
          - pattern: |
              with tarfile.open(...) as $TAR:
                  ...
                  $TAR.extractall(...)
          - pattern: |
              tarfile.open(...).extractall(...)
          - pattern: |
              $TAR = tarfile.open(...)
              ...
              $TAR.extractall(...)
      - pattern-not: |
          with tarfile.open(...) as $TAR:
              ...
              $TAR.extractall(..., members=$MEMBERS, ...)
      - pattern-not: |
          tarfile.open(...).extractall(..., members=$MEMBERS, ...)
      - pattern-not: |
          $TAR = tarfile.open(...)
          ...
          $TAR.extractall(..., members=$MEMBERS, ...)

Examples

tarfile-extractall-traversal.py

import argparse
import tarfile

def get_parser():
    parser = argparse.ArgumentParser(description='Process zip files.')
    parser.add_argument('--path', type=str, required=True,
                        help='Path to zip file')
    parser.add_argument('--save-path', type=str, required=True,
                            help='Output path')
    return parser


def extract_1(args):
    path = args.path
    save_path = args.save_path
    # ruleid: tarfile-extractall-traversal
    with tarfile.open(path) as f:
        f.extractall(save_path)


def extract_2(args):
    path = args.path
    save_path = args.save_path
    # ruleid: tarfile-extractall-traversal
    tarfile.open(path).extractall(save_path)


def extract_3(args):
    path = args.path
    save_path = args.save_path
    # ruleid: tarfile-extractall-traversal
    tf = tarfile.open(path)
    tf.extractall(save_path)

def extract_4(args, tarobj):
    # ruleid: tarfile-extractall-traversal
    tf = tarfile.open(mode='r', fileobj=None)
    tf.extractall(save_path)

def extract_5(args, tarobj):
    # ok: tarfile-extractall-traversal
    tf = tarfile.open(mode='r', fileobj=None)
    tf.extractall(save_path, members=safu_members())

def extract_6(args, tarobj):
    # ok: tarfile-extractall-traversal
    tf = tarfile.open(mode='r', fileobj=None)
    tf.extractall(members=safu_members(), numeric_owner=True, path=save_path)

def extract_7(args, tarobj):
    # todoruleid: tarfile-extractall-traversal
    tf = tarfile.open(mode='r', fileobj=None)
    tf.extractall(members=None, numeric_owner=True, path=save_path)

def run():
    parser = get_parser()
    args = parser.parse_args()
    extract_1(args)
    extract_2(args)
    extract_3(args)
    extract_4(args)
    extract_5(args)
    extract_6(args)
    extract_7(args)


if __name__ == '__main__':
    run()