Make the seccomp filter more restrictive :3
This commit is contained in:
parent
6c3d5dbb21
commit
a4caa2065d
2 changed files with 112 additions and 87 deletions
180
libseccomp.py
180
libseccomp.py
|
@ -1,7 +1,9 @@
|
||||||
"""
|
"""
|
||||||
Applies a seccomp filter on Linux via libseccomp's Python bindings.
|
Applies a seccomp filter on Linux via libseccomp's Python bindings.
|
||||||
May have some security benefits, but since Python and Qt use many calls
|
Has some security benefits, but since Python and Qt use many calls
|
||||||
and are pretty high-level, things are very prone to breaking.
|
and are pretty high-level, things are prone to breaking.
|
||||||
|
|
||||||
|
Certain features like opening links almost always break.
|
||||||
|
|
||||||
Libseccomp's Python bindings aren't available on the pypi, check your distro's
|
Libseccomp's Python bindings aren't available on the pypi, check your distro's
|
||||||
package manager for python-libseccomp (Arch) or python3-seccomp (Debian).
|
package manager for python-libseccomp (Arch) or python3-seccomp (Debian).
|
||||||
|
@ -21,7 +23,11 @@ pesterchum_log = logging.getLogger("pchumLogger")
|
||||||
def activate_seccomp():
|
def activate_seccomp():
|
||||||
"""Sets the process into seccomp filter mode."""
|
"""Sets the process into seccomp filter mode."""
|
||||||
if seccomp is None:
|
if seccomp is None:
|
||||||
pesterchum_log.error("Failed to import seccomp.")
|
pesterchum_log.error(
|
||||||
|
"Failed to import seccomp, verify you have"
|
||||||
|
" python-libseccomp (Arch) or python3-seccomp (Debian) installed"
|
||||||
|
" and aren't running a pyinstaller build."
|
||||||
|
)
|
||||||
return
|
return
|
||||||
# Violation gives "Operation not permitted".
|
# Violation gives "Operation not permitted".
|
||||||
sec_filter = seccomp.SyscallFilter(defaction=seccomp.ERRNO(1))
|
sec_filter = seccomp.SyscallFilter(defaction=seccomp.ERRNO(1))
|
||||||
|
@ -31,89 +37,76 @@ def activate_seccomp():
|
||||||
|
|
||||||
# Allow only UNIX and INET sockets, see the linux manual and source on socket for reference.
|
# Allow only UNIX and INET sockets, see the linux manual and source on socket for reference.
|
||||||
# Arg(0, seccomp.EQ, 1) means argument 0 must be equal to 1, 1 being the value of AF_UNIX.
|
# Arg(0, seccomp.EQ, 1) means argument 0 must be equal to 1, 1 being the value of AF_UNIX.
|
||||||
sec_filter.add_rule(seccomp.ALLOW, "socket", seccomp.Arg(0, seccomp.EQ, 1)) # AF_UNIX
|
# Allow AF_UNIX
|
||||||
sec_filter.add_rule(seccomp.ALLOW, "socket", seccomp.Arg(0, seccomp.EQ, 2)) # AF_INET
|
sec_filter.add_rule(seccomp.ALLOW, "socket", seccomp.Arg(0, seccomp.EQ, 1))
|
||||||
|
# Allow AF_INET
|
||||||
|
sec_filter.add_rule(seccomp.ALLOW, "socket", seccomp.Arg(0, seccomp.EQ, 2))
|
||||||
|
|
||||||
# We can kill ourselves in case of skill issues but not others.
|
# Python/Qt might close itself via kill call in case of error.
|
||||||
sec_filter.add_rule(seccomp.ALLOW, "kill", seccomp.Arg(0, seccomp.EQ, os.getpid()))
|
sec_filter.add_rule(seccomp.ALLOW, "kill", seccomp.Arg(0, seccomp.EQ, os.getpid()))
|
||||||
sec_filter.add_rule(seccomp.ALLOW, "tgkill", seccomp.Arg(1, seccomp.EQ, threading.get_native_id()))
|
sec_filter.add_rule(
|
||||||
|
seccomp.ALLOW, "tgkill", seccomp.Arg(1, seccomp.EQ, threading.get_native_id())
|
||||||
|
)
|
||||||
|
|
||||||
|
# Allow openat as along as it's not in R+W mode.
|
||||||
|
# We can't really lock down open/openat further without breaking everything,
|
||||||
|
# even though it's one of the most important calls to lock down.
|
||||||
|
# Could probably allow breaking out of the sandbox in the case of full-on RCE/ACE.
|
||||||
|
sec_filter.add_rule(seccomp.ALLOW, "openat", seccomp.Arg(2, seccomp.NE, 2))
|
||||||
|
|
||||||
sec_filter.load()
|
sec_filter.load()
|
||||||
|
|
||||||
def restrict_open():
|
|
||||||
# Allow only opening for reading/writing
|
|
||||||
sec_filter.add_rule(seccomp.ALLOW, "openat", seccomp.Arg(0, seccomp.EQ, os.getpid()))
|
|
||||||
|
|
||||||
# Required for Pesterchum to run.
|
# Required for Pesterchum to function normally.
|
||||||
|
# Pesterchum doesn't call most of these directly, there's a lot of abstraction with Python and Qt.
|
||||||
PCHUM_SYSTEM_CALLS = [
|
PCHUM_SYSTEM_CALLS = [
|
||||||
"access",
|
"access", # Files
|
||||||
"bind",
|
"brk", # Required
|
||||||
"brk",
|
"clone3", # Required
|
||||||
"clone3",
|
"close", # Sockets (Audio + Network)
|
||||||
"close",
|
"connect", # Sockets (Audio + Network)
|
||||||
"connect",
|
"exit", # Exiting
|
||||||
"eventfd2",
|
"exit_group", # Exiting
|
||||||
"exit",
|
"fallocate", # Qt
|
||||||
"exit_group",
|
"fcntl", # Required (+ Audio)
|
||||||
"faccessat",
|
"fsync", # Fsync log files
|
||||||
"faccessat2",
|
"ftruncate", # Required
|
||||||
"fallocate",
|
"futex", # Required
|
||||||
"fcntl",
|
"getcwd", # Get working directory
|
||||||
"fsync",
|
"getdents64", # Files? Required.
|
||||||
"ftruncate",
|
"getgid", # Audio
|
||||||
"futex",
|
"getpeername", # Connect
|
||||||
"getcwd",
|
"getpid", # Audio
|
||||||
"getdents64",
|
"getrandom", # Malloc
|
||||||
"getegid",
|
"getsockname", # Required for sockets
|
||||||
"geteuid",
|
"getsockopt", # Required for sockets
|
||||||
"getgid",
|
"getuid", # Audio
|
||||||
"getpeername",
|
"ioctl", # Socket/Network
|
||||||
"getpid",
|
"lseek", # Files
|
||||||
"getrandom",
|
"memfd_create", # Required (For Qt?)
|
||||||
"getresgid",
|
"mkdir", # Gotta make folderz sometimez
|
||||||
"getresuid",
|
"mmap", # Audio
|
||||||
"getsockname",
|
"mprotect", # QThread::start
|
||||||
"getsockopt",
|
"munmap", # Required (segfault)
|
||||||
"gettid",
|
"newfstatat", # Required (Audio + Path?)
|
||||||
"getuid",
|
"pipe2", # Audio
|
||||||
"ioctl",
|
"poll", # Required for literally everything
|
||||||
"lseek",
|
"pselect6", # Sockets/Network
|
||||||
"memfd_create",
|
"read", # It's read :3
|
||||||
"mkdir",
|
"readlink", # Files
|
||||||
"mmap",
|
"recv", # Network
|
||||||
"mprotect",
|
"recvfrom", # Network + DNS
|
||||||
"munmap",
|
"recvmsg", # Sockets (incl. Audio + Network)
|
||||||
"newfstatat",
|
"rseq", # Required
|
||||||
"openat",
|
"rt_sigprocmask", # Required (segfault)
|
||||||
"pipe2",
|
"select", # Useful for sockets
|
||||||
"poll",
|
"sendmmsg", # Network
|
||||||
"prctl",
|
"sendmsg", # Sockets
|
||||||
"pselect6",
|
"sendto", # Eternal eepy!! Sockets + required for waking up mainloop.
|
||||||
"pwrite64",
|
"setsockopt", # Audio
|
||||||
"read",
|
"statx", # File info
|
||||||
"recv",
|
"uname", # Required
|
||||||
"recvfrom",
|
"write", # Required
|
||||||
"recvfrom",
|
|
||||||
"recvmmsg",
|
|
||||||
"recvmsg",
|
|
||||||
"restart_syscall",
|
|
||||||
"rseq",
|
|
||||||
"rt_sigaction",
|
|
||||||
"rt_sigprocmask",
|
|
||||||
"rt_sigreturn",
|
|
||||||
"sched_getaffinity",
|
|
||||||
"sched_getattr",
|
|
||||||
"sched_setattr",
|
|
||||||
"select",
|
|
||||||
"sendmmsg",
|
|
||||||
"sendmsg",
|
|
||||||
"sendto",
|
|
||||||
"setsockopt",
|
|
||||||
"shutdown",
|
|
||||||
"statx",
|
|
||||||
"umask",
|
|
||||||
"uname",
|
|
||||||
"write",
|
|
||||||
]
|
]
|
||||||
|
|
||||||
"""
|
"""
|
||||||
|
@ -130,6 +123,35 @@ EXTRA_CALLS = [
|
||||||
"open",
|
"open",
|
||||||
"time",
|
"time",
|
||||||
"listen",
|
"listen",
|
||||||
|
]
|
||||||
|
# Required on launch
|
||||||
|
LAUNCH_CALLS = [
|
||||||
|
"prctl",
|
||||||
|
"faccessat",
|
||||||
|
"faccessat2",
|
||||||
|
"pwrite64",
|
||||||
|
]
|
||||||
|
# Required before full initialize
|
||||||
|
PRE_INITIALIZE_CALLS = [
|
||||||
|
"bind",
|
||||||
|
"eventfd2",
|
||||||
|
"getegid",
|
||||||
|
"geteuid",
|
||||||
|
"getresgid",
|
||||||
|
"getresuid",
|
||||||
|
"gettid",
|
||||||
|
"recvmmsg",
|
||||||
|
"restart_syscall",
|
||||||
|
"rt_sigaction",
|
||||||
|
"rt_sigreturn",
|
||||||
|
"sched_getaffinity",
|
||||||
|
"sched_getattr",
|
||||||
|
"sched_setattr",
|
||||||
|
"shutdown",
|
||||||
|
"umask",
|
||||||
|
]
|
||||||
|
# Required for opening links, but opening links still doesn't work anyway.
|
||||||
|
LINK_CALLS = [
|
||||||
"wait4", # for links?
|
"wait4", # for links?
|
||||||
"clone", # for links?
|
"clone", # for links?
|
||||||
]
|
]
|
||||||
|
|
|
@ -17,6 +17,7 @@ if os.path.dirname(sys.argv[0]):
|
||||||
import ostools
|
import ostools
|
||||||
import nickservmsgs
|
import nickservmsgs
|
||||||
import pytwmn
|
import pytwmn
|
||||||
|
|
||||||
if ostools.isLinux():
|
if ostools.isLinux():
|
||||||
import libseccomp
|
import libseccomp
|
||||||
|
|
||||||
|
@ -113,8 +114,9 @@ if ostools.isLinux():
|
||||||
action="store_true",
|
action="store_true",
|
||||||
help=(
|
help=(
|
||||||
"Restrict the system calls Pesterchum is allowed to make via seccomp."
|
"Restrict the system calls Pesterchum is allowed to make via seccomp."
|
||||||
" May have some security benefits, but since Python and Qt use many calls"
|
" Has some security benefits, but since Python and Qt use many calls"
|
||||||
" and are pretty high-level, things are very prone to breaking."
|
" and are pretty high-level, things are prone to breaking."
|
||||||
|
" Certain features like opening links always break."
|
||||||
" (Requires Linux and libseccomp's Python bindings, not available in frozen builds.)"
|
" (Requires Linux and libseccomp's Python bindings, not available in frozen builds.)"
|
||||||
),
|
),
|
||||||
)
|
)
|
||||||
|
@ -1401,10 +1403,6 @@ class PesterWindow(MovingWindow):
|
||||||
self.honk = options["honk"]
|
self.honk = options["honk"]
|
||||||
else:
|
else:
|
||||||
self.honk = True
|
self.honk = True
|
||||||
# Activate seccomp if enabled
|
|
||||||
if "seccomp" in options:
|
|
||||||
if options["seccomp"]:
|
|
||||||
libseccomp.activate_seccomp()
|
|
||||||
self.modes = ""
|
self.modes = ""
|
||||||
|
|
||||||
self.sound_type = None
|
self.sound_type = None
|
||||||
|
@ -1694,6 +1692,11 @@ class PesterWindow(MovingWindow):
|
||||||
self.sincerecv = 0 # Time since last recv
|
self.sincerecv = 0 # Time since last recv
|
||||||
self.lastCheckPing = None
|
self.lastCheckPing = None
|
||||||
|
|
||||||
|
# Activate seccomp on Linux if enabled
|
||||||
|
if "seccomp" in options:
|
||||||
|
if options["seccomp"]:
|
||||||
|
libseccomp.activate_seccomp()
|
||||||
|
|
||||||
@QtCore.pyqtSlot(QString, QString)
|
@QtCore.pyqtSlot(QString, QString)
|
||||||
def updateMsg(self, ver, url):
|
def updateMsg(self, ver, url):
|
||||||
if not hasattr(self, "updatemenu"):
|
if not hasattr(self, "updatemenu"):
|
||||||
|
|
Loading…
Reference in a new issue