** Information type changed from Private Security to Public Security

-- 
You received this bug notification because you are a member of Ubuntu
Touch seeded packages, which is subscribed to apport in Ubuntu.
https://bugs.launchpad.net/bugs/1862933

Title:
  Apport crash report & cron script TOCTTOU

Status in Apport:
  New
Status in apport package in Ubuntu:
  Fix Released

Bug description:
  Vulnerable code (from data/apport):

     700                # we prefer having a file mode of 0 while writing; this 
doesn't work
     701                # for suid binaries as we completely drop privs and 
thus can't chmod
     702                # them later on
     703                if pidstat.st_uid != os.getuid():
     704                    mode = 0o640
     705                else:
     706                    mode = 0
     707                reportfile = os.fdopen(os.open(report, os.O_RDWR | 
os.O_CREAT | os.O_EXCL, mode), 'w+b')
     708                assert reportfile.fileno() > sys.stderr.fileno()
     709
     710                # Make sure the crash reporting daemon can read this 
report
     711                try:
     712                    gid = pwd.getpwnam('whoopsie').pw_gid
     713                    os.chown(report, pidstat.st_uid, gid)
     714                except (OSError, KeyError):
     715                    os.chown(report, pidstat.st_uid, pidstat.st_gid)
     716            except (OSError, IOError) as e:
     717                error_log('Could not create report file: %s' % str(e))
     718                sys.exit(1)

  The TOCTTOU takes place between the os.open and the os.chown call and
  can be fully achieved thanks to the Apport cron script
  (etc/cron.daily/apport):

       1        #!/bin/sh -e
       2        # clean all crash reports which are older than a week.
       3        [ -d /var/crash ] || exit 0
       4        find /var/crash/. ! -name . -prune -type f \( \( -size 0 -a \! 
-name '*.upload*' -a \! -name '*.drkonqi*' \) -o -mtime +7 \) -exec rm -f -- 
'{}' \;
       5        find /var/crash/. ! -name . -prune -type d -regextype 
posix-extended -regex '.*/[0-9]{12}$' \( -mtime +7 \) -exec rm -Rf -- '{}' \;

  The interesting part in this daily script is that the crash reports
  gets removed if their size is 0.

  Since Apport drops real uid and gid, the crashed process owner can
  send signals during the report file creation. At this time, effective
  uid and gid are still root.

  We can also block Apport by replacing user settings file with a FIFO
  (~/.config/apport/settings). I'm using the FIFO way in my PoC but it
  can be done without it.

  To make Apport read user settings, the crashing program must not be
  located in one of those directories (taken from apport/fileutils.py):

      78            pkg_whitelist = ['/bin/', '/boot', '/etc/', '/initrd', 
'/lib', '/sbin/',
      79                             '/opt', '/usr/', '/var']  # packages only 
ship executables in these directories

  Once Apport is blocked into FIFO reading, we send the SIGSTOP signal
  then we write "[main]\nunpackaged=1" into the FIFO so Apport won't
  exit after resuming (if unpackaged is 0 Apport directly exits because
  we're not in a "packaged" directory).

  After that we "single step" through Apport by sending SIGCONT and
  SIGSTOP consecutively in a loop until the report file is created with
  os.open. We must make sure os.chown hasn't been called and then we
  wait for the cron script to remove the report (it's created as root
  with mode 0 so only root can remove it).

  Once removed, we can replace it with a symbolic link/file of the same
  name, resume Apport with SIGCONT then the file will now be owned by
  the crashed process user and group.

  I think the impact of this vulnerability alone is low because
  fs.protected_symlinks prevents symlink resolution since we're in a
  sticky world writable directory (/var/crash), but if it's disabled,
  you can escalate privileges very easily. It still can be used in some
  kind of exploit chain though.

  My PoC does everything for you except symbolic link/file creation once
  the report gets removed by the cron script, you have to create it
  manually then press enter to resume Apport and let the chown happen.
  You could also create a new crontab entry and directory then copy
  Apport cron script into it so you don't have to wait the entire day.

  Fix suggestions:
  - Use reportfile.fileno() instead of the report string for the os.chown 
calls, and also add follow_symlinks=False argument just in case.
  - Remove the size 0 condition in the cron script (not sure about this one, I 
suppose the condition was there for a reason).

To manage notifications about this bug go to:
https://bugs.launchpad.net/apport/+bug/1862933/+subscriptions

-- 
Mailing list: https://launchpad.net/~touch-packages
Post to     : touch-packages@lists.launchpad.net
Unsubscribe : https://launchpad.net/~touch-packages
More help   : https://help.launchpad.net/ListHelp

Reply via email to