Guest User

Untitled

a guest
Nov 24th, 2025
124
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 5.11 KB | Cybersecurity | 0 0
  1. import os
  2. import sys
  3. import errno
  4. from os.path import realpath
  5. import re
  6. import stat
  7. import fuse
  8. import logging
  9. from fuse import Direntry, Fuse, Stat
  10.  
  11. # Emulates a /proc filesystem but allows only reading /proc/self/exe path
  12. fuse.fuse_python_api = (0, 2)
  13.  
  14. logger = logging.getLogger('fproc')
  15. logging.basicConfig(level=logging.INFO)
  16.  
  17. def find_pid_in_process_namespace(host_pid: int) -> int:
  18.     with open(f"/proc/{host_pid}/status", "r", encoding="ascii") as f:
  19.         data = f.read(100_000)
  20.  
  21.     if m := re.search(r'^NSpid\s*:([\s\d]+)$', data, re.MULTILINE | re.IGNORECASE):        
  22.         pids = m[1].split()
  23.         return int(pids[-1])
  24.  
  25.     logger.warning(f"Cannot find container PID for host PID {host_pid} in proc")
  26.     raise OSError(errno.EINVAL, "Cannot find children PID in its namespace")
  27.  
  28.  
  29. class FakeProc(Fuse):
  30.     def _is_path_visible(self, container_pid: int, path: str) -> bool:
  31.         directory, file = os.path.split(path)
  32.  
  33.         if path == '/':
  34.             return True
  35.  
  36.         return self._is_item_visible(container_pid, directory, file)
  37.  
  38.     def _is_item_visible(self, container_pid: int, path: str, name: str) -> bool:
  39.         if path == '/':
  40.             return name in ('self', str(container_pid))
  41.  
  42.         if path == '/self' or path == f"/{container_pid}":
  43.             return name in ('exe')
  44.  
  45.         return False
  46.  
  47.     def _check_access(self, path: str, container_pid: int, op: str):
  48.         if not self._is_path_visible(container_pid, path):
  49.             logger.info(f"DENY {op} on {path} by ctnr PID {container_pid}")
  50.             raise OSError(errno.ENOENT, "Access denied to {path} for cntr PID {container_pid}")
  51.         # print(f"ALLOW {op} on {path} by cntr PID {container_pid}")
  52.  
  53.     def _replace_self(self, path: str, container_pid: int) -> str:
  54.         return re.sub(r'/self(?=/|\Z)', f"/{container_pid}", path)
  55.  
  56.     def _convert_to_host_path(self, path: str, container_pid: int, host_pid: int) -> str:
  57.         assert not path.startswith('/self')
  58.         segments = path.split('/')
  59.         if segments[1] == str(container_pid):
  60.             segments[1] = str(host_pid)
  61.         elif segments[1] != '':
  62.             raise ValueError(f"Invalid container path {path} given (cntr PID={container_pid})")
  63.  
  64.         return '/'.join(segments)
  65.  
  66.     def _transform_path(self, path: str, op: str) -> tuple[str, str]:
  67.         host_pid, container_pid = self._get_child_pids()
  68.         self._check_access(path, container_pid, op)
  69.         container_path = self._replace_self(path, container_pid)
  70.         host_path = self._convert_to_host_path(container_path, container_pid, host_pid)
  71.         assert host_path.startswith('/')
  72.  
  73.         return container_path, f"/proc{host_path}"
  74.  
  75.     def _get_child_pids(self) -> tuple[int, int]:
  76.         host_pid = self.GetContext()['pid']
  77.         container_pid = find_pid_in_process_namespace(host_pid)
  78.         return host_pid, container_pid
  79.  
  80.     def readlink(self, path: str):
  81.         cntr_path, host_path = self._transform_path(path, 'readlink')
  82.         # Special case
  83.         if path == '/self':
  84.             host_pid, container_pid = self._get_child_pids()
  85.             return f"{container_pid}"
  86.  
  87.         return os.readlink(host_path)
  88.  
  89.     def getattr(self, path: str):
  90.         cntr_path, host_path = self._transform_path(path, 'getattr')
  91.         if path == '/self':
  92.             return Stat(
  93.                 st_mode = stat.S_IFLNK | stat.S_IRUSR | stat.S_IXUSR | stat.S_IRGRP | stat.S_IXGRP | stat.S_IROTH | stat.S_IXOTH,
  94.                 st_nlink = 2,
  95.                 st_ino = 1,
  96.                 st_uid = 0,
  97.                 st_gid = 0,
  98.                 st_size = len(self.readlink(path))
  99.             )
  100.  
  101.         return os.lstat(host_path)
  102.  
  103.     def readdir(self, path: str, offset):
  104.         cntr_path, host_path = self._transform_path(path, 'readdir')
  105.         host_pid, container_pid = self._get_child_pids()
  106.  
  107.         with os.scandir(host_path) as it:
  108.             for entry in it:
  109.                 if not self._is_item_visible(container_pid, cntr_path, entry.name):
  110.                     # print(f"HIDE name {entry.name}")
  111.                     continue
  112.  
  113.                 # print(f"ALLOW Show {entry.name} at {cntr_path}")
  114.                 # print(entry.is_symlink())
  115.                 yield Direntry(entry.name,
  116.                     ino=entry.inode(),
  117.                     type=(stat.S_IFLNK if entry.is_symlink() else
  118.                         stat.S_IFDIR if entry.is_dir(follow_symlinks = False) else
  119.                         stat.S_IFREG if entry.is_file(follow_symlinks = False)
  120.                         else None)
  121.                 )
  122.  
  123.     def access(self, path: str, mode):
  124.         path, host_path = self._transform_path(path, 'access')
  125.         if not os.access(host_path, mode):
  126.             return -errno.EACCES
  127.  
  128. def main():
  129.     usage="""
  130. Fakeproc - allow access to /proc/self/exe in a sanbox (using FUSE)
  131.  
  132. """ + Fuse.fusage
  133.  
  134.     server = FakeProc(version="%prog " + fuse.__version__,
  135.                      usage=usage,
  136.                      dash_s_do='setsingle')
  137.  
  138.     server.parse(errex=1)
  139.     server.main()
  140.  
  141. if __name__ == '__main__':
  142.     main()
  143.  
Advertisement
Add Comment
Please, Sign In to add comment