Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
8 changes: 4 additions & 4 deletions requirements.txt
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
twine==4.0.1
flake8==5.0.4
pytest==7.1.3
pytest-cov==3.0.0
pytest==8.3.5
pytest-cov==6.0.0
requests==2.32.5
aiohttp==3.13.2
black==22.8.0
aiohttp==3.13.3
black==24.10.0
numpy>1.24.0,<2.0.0
py7zr==0.22.0
2 changes: 1 addition & 1 deletion setup.cfg
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
[metadata]
name = picklescan
version = 0.0.34
version = 0.0.35
author = Matthieu Maitre
author_email = mmaitre314@users.noreply.github.com
description = Security scanner detecting Python Pickle files performing suspicious actions
Expand Down
2 changes: 2 additions & 0 deletions src/picklescan/scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,7 @@ def __str__(self) -> str:
"ctypes": "*", # Foreign function interface, can load DLLs, call C functions, manipulate raw memory
"functools": "partial", # functools.partial(os.system, "echo pwned")
"httplib": "*", # Includes http.client.HTTPSConnection()
"_io": {"FileIO"}, # io.FileIO is stored as _io.FileIO, can read arbitrary files bypassing builtins.open blocklist
"numpy.f2py": "*", # Multiple unsafe functions (e.g., getlincoef, _eval_length) that call eval on arbitrary strings
"numpy.testing._private.utils": "*", # runstring() in this module is a synonym for exec()
"nt": "*", # Alias for 'os' on Windows. Includes os.system()
Expand Down Expand Up @@ -181,6 +182,7 @@ def __str__(self) -> str:
"basichandlers"
}, # allows storing a pickle inside a pickle (if this has valid use cases, scan the input bytes instead of flagging the global)
"trace": {"Trace.run", "Trace.runctx"},
"urllib.request": "*", # urllib.request.urlopen can be used for SSRF and data exfiltration
"venv": "*",
"webbrowser": "*", # Includes webbrowser.open()
}
Expand Down
Binary file added tests/data2/io_FileIO.pkl
Binary file not shown.
Binary file added tests/data2/urllib_request_urlopen.pkl
Binary file not shown.
14 changes: 14 additions & 0 deletions tests/init_data_files.py
Original file line number Diff line number Diff line change
Expand Up @@ -355,6 +355,18 @@ def reduce_GHSA_r8g5_cgf2_4m4m():
return getlincoef, (_payload, [])


def reduce_io_FileIO():
import io

return io.FileIO, ("/etc/hosts", "r")


def reduce_urllib_request_urlopen():
import urllib.request

return urllib.request.urlopen, ("https://example.invalid",)


def initialize_pickle_file(path: str, obj: Any, version: int):
if os.path.exists(path):
print(f"File {path} already exists, skipping initialization.")
Expand Down Expand Up @@ -781,6 +793,8 @@ def initialize_pickle_files():
initialize_pickle_file_from_reduce("GHSA-84r2-jw7c-4r5q.pkl", reduce_GHSA_84r2_jw7c_4r5q)
initialize_pickle_file_from_reduce("GHSA-vqmv-47xg-9wpr.pkl", reduce_GHSA_vqmv_47xg_9wpr)
initialize_pickle_file_from_reduce("GHSA-r8g5-cgf2-4m4m.pkl", reduce_GHSA_r8g5_cgf2_4m4m)
initialize_pickle_file_from_reduce("io_FileIO.pkl", reduce_io_FileIO)
initialize_pickle_file_from_reduce("urllib_request_urlopen.pkl", reduce_urllib_request_urlopen)


def initialize_numpy_files():
Expand Down
2 changes: 2 additions & 0 deletions tests/test_scanner.py
Original file line number Diff line number Diff line change
Expand Up @@ -393,6 +393,8 @@ def test_scan_file_path():
"GHSA-46h3-79wf-xr6c.pkl",
[Global("_operator", "attrgetter", SafetyLevel.Dangerous), Global("builtins", "__import__", SafetyLevel.Suspicious)],
)
assert_scan("io_FileIO.pkl", [Global("_io", "FileIO", SafetyLevel.Dangerous)])
assert_scan("urllib_request_urlopen.pkl", [Global("urllib.request", "urlopen", SafetyLevel.Dangerous)])


def test_scan_file_path_npz():
Expand Down
Loading