[Python-Dev] PEP 578: Python Runtime Audit Hooks

Victor Stinner vstinner at redhat.com
Thu Mar 28 20:02:16 EDT 2019


Hi,

I read quickly the PEP, I'm not sure that I understood it correctly,
so here are some early questions more about the usage of the PEP, than
its implementation.

> This is not sandboxing, as this proposal does not attempt to prevent
> malicious behavior (though it enables some new options to do so).
> See the `Why Not A Sandbox`_ section below for further discussion.

I don't understand well the overall security model. If malicious
behaviors can still occur, what is the the purpose of auditing? For
example, if an audit hook writes events into a local log file, the
attacker can easily remove this log file, no?

While you say that it's not a sandbox, you designed multiple
protections to protect auditing, and the design is very close to a
sandbox.

Example:

    "``_PyObject_GenericSetAttr``, ``check_set_special_type_attr``,
    ``object_set_class``, ``func_set_code``, ``func_set_[kw]defaults``","
    ``object.__setattr__``","``(object, attr, value)``","Detect monkey
    patching of types and objects. This event
    is raised for the ``__class__`` attribute and any attribute on
    ``type`` objects.

It reminds me Python 2 Bastion module which has simply been removed
because it was unsafe:
https://docs.python.org/2/library/bastion.html

For example, using ctypes, you can access directly the underlying
dictionary of a type and modify "private" attributes. It's just an
example.


I wrote pysandbox in the past, and it was broken by default, as you
wrote in the PEP :-)

   The failure of pysandbox (2013)
   https://lwn.net/Articles/574215/

If you want to secure Python, run Python in a sandbox, don't try to
"add" a sandbox "on top" of Python (I know that it's more complicated
in practice).

Or use PyPy sandbox which has a very different design.


Le jeu. 28 mars 2019 à 23:39, Steve Dower <steve.dower at python.org> a écrit :
> This PEP describes additions to the Python API and specific behaviors
> for the CPython implementation that make actions taken by the Python
> runtime visible to auditing tools. Visibility into these actions
> provides opportunities for test frameworks, logging frameworks, and
> security tools to monitor and optionally limit actions taken by the
> runtime.

Is it possible to implement these features without adding a new API or
modifying Python?

Short example adding logs to open():
---
import builtins
import functools

def add_log(name, func):
    @functools.wraps(func)
    def wrapper(*args):
        print(name, args)
        return func(*args)
    return wrapper

builtins.open = add_log("open", builtins.open)

open(__file__).close()
---


> Verified Open Hook
> ------------------
>
> Most operating systems have a mechanism to distinguish between files
> that can be executed and those that can not. For example, this may be an
> execute bit in the permissions field, a verified hash of the file
> contents to detect potential code tampering, or file system path
> restrictions. These are an important security mechanism for preventing
> execution of data or code that is not approved for a given environment.
> Currently, Python has no way to integrate with these when launching
> scripts or importing modules.

In my experience, it doesn't work just because Python has too many
functions opening files indirectly or call external C libraries which
open files.

I vaguely recall an exploit in my pysandbox project which uses the
internal code of Python which displays a traceback... to read the
content of an arbitrary file on the disk :-( Game over. I would never
expect that there are so many ways to read a file in Python...

Even when I restricted pysandbox to the bare minimum of the Python
language (with no import), multiple exploits have been found.
Moreover, at the end, Python just became useful.

More generally, there are a lot of codes in Python which allow
arbitrary code injection :-( (Most of them are now fixed, hopefully!)

I did my best to modify as much functions as possible to implement the
PEP 446 "Make newly created file descriptors non-inheritable", but I
know that *many* functions call directly open() or fopen() and so
create inheritable file descriptors. For example, the Python ssl
module takes directly filenames and OpenSSL open directly files. It's
just one example.

You will never be able to cover all cases.

Having a single function which allows to open an arbitrary file
without triggering an audit event would defeat the whole purpose of
auditing, no? Again, maybe I didn't understand well the overall
purpose of the PEP, sorry.


> Table 2 provides further examples that are not required, but are
> likely to be available in CPython.
> (...)
> Performance Impact
> ==================
>
> The important performance impact is the case where events are being
> raised but there are no hooks attached. This is the unavoidable case -
> once a developer has added audit hooks they have explicitly chosen to
> trade performance for functionality.

(The Linux kernel uses advance tooling to inject hooks: it has no
impact on performances when no hook is used. Machine code of functions
is patched to inject a hook. Impressive stuff :-))

Here I expect a small overhead. But the global overhead will be
proportional to the number of hooks, no? Maybe it's not significant
with the proposed list of events, but it will be more significant with
100 or 1000 events?

I'm not saying that it's a blocker issue, I'm just thinking aloud to
make sure that I understood correctly :-)

Victor
--
Night gathers, and now my watch begins. It shall not end until my death.


More information about the Python-Dev mailing list