Closed Lucki closed 3 years ago
Hm. I don't like this solution at all. I think you can "smuggle in" malicious python modules, that replace some of the system wide modules. And with that you can easily build a complete memory watcher. (Even without touching pyautosplit and the wrapper...)
Have you tried using PTRACE_O_TRACEFORK
? I think you can enable it in python-ptrace
with PtraceDebugger.traceFork()
Unfortunately the documentation for python-ptrace
is less than stellar, but according to the comments in the code, this should be exactly, what is needed.
Have you tried using
PTRACE_O_TRACEFORK
? I think you can enable it inpython-ptrace
withPtraceDebugger.traceFork()
Unfortunately the documentation forpython-ptrace
is less than stellar, but according to the comments in the code, this should be exactly, what is needed.
Yes, I've tried that and unfortunately it's not working that way. Most of the time there's the following error despite I'm not using breakpoints:
line 93, in handle_breakpoints
varname = self.breakpoints[rip]
KeyError: 140711710601359
But I also got a few clean startups but the values which it has to read are all None
:
readBytes(0x0000000000b7cb84, 4) error: [Errno 5] Input/output error
Hm. I don't like this solution at all. I think you can "smuggle in" malicious python modules, that replace some of the system wide modules. And with that you can easily build a complete memory watcher. (Even without touching pyautosplit and the wrapper...)
You have much bigger problems if a file by root is changed maliciously than this script reading memory. But you're right in user space, according to this comment I have to clean up PYTHONPATH
(maybe more) or somehow start cpython with -E
.
You have much bigger problems if a file by root is changed maliciously than this script reading memory
The problem is, that you do not need to overwrite any file owned by root and do you not even have to be root. PYTHONPATH
is only one way to inject malicious modules. If this binary is linked dynamically to libpython, one could override the librarypath, to use ones own malicious libpython. And if we link it statically, I fear this will be a deployment nightmare. One way to avoid all of this, would be to run pyautosplit via sudo
, like GameConqueror does, but I want to avoid that. An autosplitter does not need to have be superuser, or read all of memory, but only the memory of some dedicated processes. And this should be possible with user rights.
Concerning the error with the breakpoint, I think this comes from the fact, that the architecture of pyautosplit just cannot deal with multiple/switching processes. But I think that can be added.
Maybe we should start with building a "proof of concept" memory reader for wine processes, and then add it to the project.
I also tried to read the memory of a wine process with GameConqueror, and also just got zeroes, but maybe I was doing something wrong.
PYTHONPATH
is only one way to inject malicious modules. If this binary is linked dynamically to libpython, one could override the librarypath, to use ones own malicious libpython. And if we link it statically, I fear this will be a deployment nightmare.
Unfortunately, I don't know enough about these things to be of any further help here.
Edit: It seems user namespaces would limit cap_sys_ptrace
to processes in the same namespace. But it looks really complicated.
Having a capability inside a user namespace permits a process to perform operations (that require privilege) only on resources governed by that namespace. In other words, having a capability in a user namespace permits a process to perform privileged operations on resources that are governed by (nonuser) namespaces owned by (associated with) the user namespace (see the next subsection).
One way to avoid all of this, would be to run pyautosplit via
sudo
, like GameConqueror does, but I want to avoid that. An autosplitter does not need to have be superuser, or read all of memory, but only the memory of some dedicated processes.
AFAIK that's what these capabilities are trying to solve. These allow ping
to run without root permissions for example.
I also tried to read the memory of a wine process with GameConqueror, and also just got zeroes, but maybe I was doing something wrong.
Only zeroes does indeed sounds strange.
At my testing I've seen the address is a bit shifted compared to the address they're using in the LiveSplit component (0x64A060
→ 0xA4A060
). Might be the same for your test.
It seems ptrace can't follow the wine process because the way it get set up. The preloader doesn't use fork() but prepares and allocates all necessary memory beforehand and somehow starts the process from this. But I also can be completely wrong on this.
I guess it would be possible to hook into this by
Some links I stumbled on:
Tracking wine processes seems not possible in the current state because of the way wine spawns the executable.
This PR allows to track wine (or any other executable) by providing an optional wrapper binary which then gets the capability
cap_sys_ptrace=ep
and allows execution without root privileges. While this would be possible without the wrapper it would be necessary to givepython
this capability - making essentially all memory accessible to all python scripts. While this is probably a security issue for wholepython
I hope to limit the security issue with this wrapper and by making it very clear in the readme that this wrapper and dependent scripts (pyautosplit
,python-ptrace
, …) should be secured against malicious activity.To know wether PyAutoSplit is called by the wrapper I've added a silent argument
--from-wrapper
. Together with anexe
in the game json the script goes into the "wrapper" mode and attaches to the pid of the given executable instead of the direct child process.While at it I also added the possibility to add environment variables in the game json (like
WINEPREFIX=/path/asd
):