Merge Prevent ANSI terminal injection in aa-unconfined

/proc/$pid/cmdline can be changed by an application, therefore escape it
before printing.

The program name in /proc/$pid/exe can also contain any characters
(except \0 and shashes) and needs escaping.

Note: repr() wraps the string into single quotes, which we have to
remove to avoid changing the output format.

The test program from issue 364 now gets displayed as

    28443 /path/to/issue364 (/\x1b]0;X\x07) not confined

Fixes: https://gitlab.com/apparmor/apparmor/-/issues/364

I propose this patch for 2.13..master

Closes #364
MR: https://gitlab.com/apparmor/apparmor/-/merge_requests/1142
Approved-by: John Johansen <john@jjmx.net>
Merged-by: John Johansen <john@jjmx.net>

(cherry picked from commit e63c1e3a76)
Signed-off-by: John Johansen <john.johansen@canonical.com>
This commit is contained in:
John Johansen 2024-01-30 09:43:23 +00:00 committed by John Johansen
parent 75c7c5d16a
commit d51b102c3a

View file

@ -99,6 +99,15 @@ def get_pids_netstat(netstat='netstat'):
return pids return pids
def escape_special_chars(data):
"""escape special characters in program names so that they can't mess up the terminal"""
data = repr(data)
if len(data) > 1 and data.startswith("'") and data.endswith("'"):
return data[1:-1]
else:
return data
pids = set() pids = set()
if paranoid: if paranoid:
pids = get_all_pids() pids = get_all_pids()
@ -110,6 +119,7 @@ else:
for pid in sorted(map(int, pids)): for pid in sorted(map(int, pids)):
try: try:
prog = os.readlink("/proc/%s/exe" % pid) prog = os.readlink("/proc/%s/exe" % pid)
prog = escape_special_chars(prog)
except OSError: except OSError:
continue continue
attr = None attr = None
@ -127,6 +137,7 @@ for pid in sorted(map(int, pids)):
pname = cmdline.split("\0")[0] pname = cmdline.split("\0")[0]
if '/' in pname and pname != prog: if '/' in pname and pname != prog:
pname = "(%s)" % pname pname = "(%s)" % pname
pname = escape_special_chars(pname)
else: else:
pname = "" pname = ""
regex_interpreter = re.compile(r"^(/usr)?/bin/(python|perl|bash|dash|sh)$") regex_interpreter = re.compile(r"^(/usr)?/bin/(python|perl|bash|dash|sh)$")
@ -134,6 +145,7 @@ for pid in sorted(map(int, pids)):
if regex_interpreter.search(prog): if regex_interpreter.search(prog):
cmdline = re.sub(r"\x00", " ", cmdline) cmdline = re.sub(r"\x00", " ", cmdline)
cmdline = re.sub(r"\s+$", "", cmdline).strip() cmdline = re.sub(r"\s+$", "", cmdline).strip()
cmdline = escape_special_chars(cmdline)
ui.UI_Info(_("%(pid)s %(program)s (%(commandline)s) not confined") % {'pid': pid, 'program': prog, 'commandline': cmdline}) ui.UI_Info(_("%(pid)s %(program)s (%(commandline)s) not confined") % {'pid': pid, 'program': prog, 'commandline': cmdline})
else: else:
@ -144,6 +156,7 @@ for pid in sorted(map(int, pids)):
if regex_interpreter.search(prog): if regex_interpreter.search(prog):
cmdline = re.sub(r"\0", " ", cmdline) cmdline = re.sub(r"\0", " ", cmdline)
cmdline = re.sub(r"\s+$", "", cmdline).strip() cmdline = re.sub(r"\s+$", "", cmdline).strip()
cmdline = escape_special_chars(cmdline)
ui.UI_Info(_("%(pid)s %(program)s (%(commandline)s) confined by '%(attribute)s'") % {'pid': pid, 'program': prog, 'commandline': cmdline, 'attribute': attr}) ui.UI_Info(_("%(pid)s %(program)s (%(commandline)s) confined by '%(attribute)s'") % {'pid': pid, 'program': prog, 'commandline': cmdline, 'attribute': attr})
else: else:
if pname and pname[-1] == ')': if pname and pname[-1] == ')':