Movatterモバイル変換


[0]ホーム

URL:


Skip to content

Navigation Menu

Sign in
Appearance settings

Search code, repositories, users, issues, pull requests...

Provide feedback

We read every piece of feedback, and take your input very seriously.

Saved searches

Use saved searches to filter your results more quickly

Sign up
Appearance settings

Commit9f81b10

Browse files
committed
fix(enumerate): fixCVE-2019-14287 detection and restore pickle compatibility
FixedCVE-2019-14287 detection logic.Also reverted a previous accidental change that broke pickle serialization, preventing persistent storage from working properly.
1 parent6ac5afd commit9f81b10

File tree

5 files changed

+57
-43
lines changed

5 files changed

+57
-43
lines changed

‎pwncat/manager.py‎

Lines changed: 19 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -923,34 +923,37 @@ def create_db_session(self):
923923
returnself.db.open()
924924

925925
defload_modules(self,*paths):
926-
"""Dynamically load modules from the specified paths
926+
"""
927+
Dynamically load modules from the specified paths.
927928
928929
If a module has the same name as an already loaded module, it will
929-
takeit's place in the module list. This includes built-in modules.
930+
takeits place in the module list. This includes built-in modules.
930931
"""
931932

932-
forloader,module_name,_inpkgutil.walk_packages(
933-
paths,prefix="pwncat.modules."
934-
):
933+
forloader,module_name,is_pkginpkgutil.walk_packages(paths,prefix="pwncat.modules."):
934+
# Locate the module spec
935+
spec=importlib.util.find_spec(module_name)
936+
ifspecisNone:
937+
continue
935938

936-
ifmodule_namenotinsys.modules:
937-
spec=importlib.util.find_spec(module_name)
938-
ifspecisNone:
939-
continue
939+
# Always check `sys.modules` under `spec.name`.
940+
# If it's already loaded, reuse that module; if not, load anew.
941+
ifspec.nameinsys.modules:
942+
module=sys.modules[spec.name]
943+
else:
940944
module=importlib.util.module_from_spec(spec)
945+
sys.modules[spec.name]=module
941946
spec.loader.exec_module(module)
942-
else:
943-
module=sys.modules[module_name]
944947

945-
ifgetattr(module,"Module",None)isNone:
948+
# We only care about modules that actually define `Module`
949+
ifnothasattr(module,"Module"):
946950
continue
947951

948952
# Create an instance of this module
949-
module_name=module_name.split("pwncat.modules.")[1]
950-
self.modules[module_name]=module.Module()
953+
short_name=module_name.split("pwncat.modules.",1)[1]
954+
self.modules[short_name]=module.Module()
955+
self.modules[short_name].name=short_name
951956

952-
# Store it's name so we know it later
953-
setattr(self.modules[module_name],"name",module_name)
954957

955958
deflog(self,*args,**kwargs):
956959
"""Output a log entry"""

‎pwncat/modules/linux/enumerate/file/suid.py‎

Lines changed: 21 additions & 11 deletions
Original file line numberDiff line numberDiff line change
@@ -1,7 +1,6 @@
11
#!/usr/bin/env python3
22
importsubprocess
3-
4-
importrich.markup
3+
importrich.markupasrmk
54

65
importpwncat
76
frompwncat.dbimportFact
@@ -18,16 +17,29 @@ class Binary(Fact):
1817

1918
def__init__(self,source,path,uid):
2019
super().__init__(source=source,types=["file.suid"])
21-
2220
""" The path to the binary """
2321
self.path:str=path
24-
2522
""" The uid of the binary """
2623
self.uid:int=uid
2724

2825
deftitle(self,session):
2926
color="red"ifself.uid==0else"green"
30-
returnf"[cyan]{rich.markup.escape(self.path)}[/cyan] owned by [{color}]{rich.markup.escape(session.find_user(uid=self.uid).name)}[/{color}]"
27+
returnf"[cyan]{rmk.escape(self.path)}[/cyan] owned by [{color}]{rmk.escape(session.find_user(uid=self.uid).name)}[/{color}]"
28+
29+
def__getstate__(self):
30+
# Return a dict representing the state of the object
31+
return {"source":self.source,"types":self.types,"path":self.path,"uid":self.uid}
32+
33+
def__setstate__(self,state):
34+
# Reinitialize the object from the state dictionary
35+
self.__init__(state["source"],state["path"],state["uid"])
36+
37+
def__reduce__(self):
38+
# Force the pickling to use the canonical 'Binary' class in suid module
39+
mod=__import__("pwncat.modules.linux.enumerate.file.suid",fromlist=["Binary"])
40+
return (getattr, (mod,"Binary"))
41+
42+
Binary.__module__="pwncat.modules.linux.enumerate.file.suid"
3143

3244

3345
classModule(EnumerateModule):
@@ -43,7 +55,6 @@ class Module(EnumerateModule):
4355
SCHEDULE=Schedule.PER_USER
4456

4557
defenumerate(self,session:"pwncat.manager.Session"):
46-
4758
# This forces the session to enumerate users FIRST, so we don't run
4859
# into trying to enumerate _whilst_ enumerating SUID binaries...
4960
# since we can't yet run multiple processes at the same time
@@ -59,11 +70,10 @@ def enumerate(self, session: "pwncat.manager.Session"):
5970

6071
try:
6172
withproc.stdoutasstream:
62-
forpathinstream:
73+
forlineinstream:
6374
# Parse out owner ID and path
64-
path=path.strip().split(" ")
65-
uid,path=int(path[0])," ".join(path[1:])
66-
75+
parts=line.strip().split(" ")
76+
uid,path=int(parts[0])," ".join(parts[1:])
6777
fact=Binary(self.name,path,uid)
6878
yieldfact
6979

@@ -76,4 +86,4 @@ def enumerate(self, session: "pwncat.manager.Session"):
7686
)
7787
)
7888
finally:
79-
proc.wait()
89+
proc.wait()

‎pwncat/modules/linux/enumerate/software/sudo/cve_2019_14287.py‎

Lines changed: 7 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
#!/usr/bin/env python3
2-
frompackagingimportversion
2+
frompackaging.versionimportparse,InvalidVersion
33

44
importpwncat
55
frompwncat.factsimportbuild_gtfo_ability
@@ -26,7 +26,11 @@ def enumerate(self, session: "pwncat.manager.Session"):
2626
return
2727

2828
# This vulnerability was patched in 1.8.28
29-
ifversion.parse(sudo_info.version)>=version.parse("1.8.28"):
29+
try:
30+
parsed_version=parse(sudo_info.version)
31+
ifparsed_version>=parse("1.8.28"):
32+
return
33+
exceptInvalidVersion:
3034
return
3135

3236
# Grab the current user/group
@@ -74,4 +78,4 @@ def enumerate(self, session: "pwncat.manager.Session"):
7478
source_uid=current_user.id,
7579
user="\\#-1",
7680
spec=command,
77-
)
81+
)

‎pwncat/platform/__init__.py‎

Lines changed: 5 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -515,12 +515,11 @@ def __init__(
515515
target=self
516516

517517
classRemotePath(base_path,Path):
518-
519-
_target=target
520-
_stat=None
521-
522-
def__init__(self,*args):
523-
base_path.__init__(*args)
518+
def__new__(cls,*args,**kwargs):
519+
obj=super().__new__(cls,*args,**kwargs)
520+
obj._target=target
521+
obj._stat=None
522+
returnobj
524523

525524
self.Path=RemotePath
526525
""" A concrete Path object for this platform conforming to pathlib.Path """

‎pwncat/platform/linux.py‎

Lines changed: 5 additions & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -1635,14 +1635,12 @@ def context_changed(self):
16351635

16361636
# Update self.shell just in case the user changed shells
16371637
try:
1638-
# Get the PID of the running shell
1639-
pid=self.getenv("$")
1640-
# Grab the path to the executable representing the shell
1641-
self.shell=self.Path("/proc",pid,"exe").readlink()
1642-
except (FileNotFoundError,PermissionError,OSError):
1643-
# Fall back to SHELL even though it's not really trustworthy
1638+
pid=self.getenv("$").strip()
1639+
proc_exe_path=f"/proc/{pid}/exe"
1640+
self.shell=self.readlink(proc_exe_path)
1641+
except (FileNotFoundError,PermissionError,OSError,AttributeError):
16441642
self.shell=self.getenv("SHELL")
1645-
ifself.shellisNoneorself.shell=="":
1643+
ifnotself.shell:
16461644
self.shell="/bin/sh"
16471645

16481646
# Refresh the currently tracked user and group IDs

0 commit comments

Comments
 (0)

[8]ページ先頭

©2009-2025 Movatter.jp