Going after an undocumented Local Privilege Escalation OS vulnerability
First thing’s first.. getting started with Solaris 8 mdb, a kernel debugging utility. a modular debugger.
The vulnerability is in NameFS, according to these articles:
Sun Bug Id# 6581308
https://blogs.oracle.com/sunsecurity/entry/sun_alert_237986_a_security
http://dl.packetstormsecurity.net/0808-advisories/sa31356.txt
http://www.securityfocus.com/bid/30513/discuss
Solaris 8: Apply patch 114984-02 or later.
Good hint here:
http://www.rapid7.com/db/vulnerabilities/sunpatch-114984
6581308 namefs wants to participate in the VFS_HOLD/VFS_RELE/VFS_FREEVFS protocol
I did some digging and found this patch to the code:
https://hg.openindiana.org/upstream/illumos/illumos-gate/rev/be657a5515de
Although, OpenSolaris uses some different functions than 2.8, such as vfs_matchops(),
https://hg.openindiana.org/upstream/illumos/illumos-gate/file/be657a5515de/usr/src/uts/common/fs/vfs.c
But perhaps the nature of the vulnerability will shine through these differences. I am already starting to think it’s being able to do operations that the filesystem wasn’t meant to do.
from /usr/src/uts/common/sys/vfs.h:
(vfs.c is in /usr/src/uts/common/fs)
/*
* Structure per mounted file system. Each mounted file system has
* an array of operations and an instance record.
*
* The file systems are kept on a singly linked list headed by "rootvfs" and
* terminated by NULL. File system implementations should not access this
* list; it's intended for use only in the kernel's vfs layer.
*/
typedef struct vfs {
struct vfs *vfs_next; /* next VFS in VFS list */
struct vfsops *vfs_op; /* operations on VFS */
struct vnode *vfs_vnodecovered; /* vnode mounted on */
uint_t vfs_flag; /* flags */
uint_t vfs_bsize; /* native block size */
int vfs_fstype; /* file system type index */
fsid_t vfs_fsid; /* file system id */
caddr_t vfs_data; /* private data */
dev_t vfs_dev; /* device of mounted VFS */
ulong_t vfs_bcount; /* I/O count (accounting) */
ushort_t vfs_nsubmounts; /* immediate sub-mount count */
struct vfs *vfs_list; /* sync list pointer */
struct vfs *vfs_hash; /* hash list pointer */
ksema_t vfs_reflock; /* mount/unmount/sync lock */
uint_t vfs_count; /* vfs reference count */
mntopts_t vfs_mntopts; /* options mounted with */
char *vfs_resource; /* mounted resource string */
char *vfs_mntpt; /* mount point string */
time_t vfs_mtime; /* time we were mounted */
} vfs_t;
typedef struct vfsops {
int (*vfs_mount)(struct vfs *, struct vnode *, struct mounta *,
struct cred *);
int (*vfs_unmount)(struct vfs *, int, struct cred *);
int (*vfs_root)(struct vfs *, struct vnode **);
int (*vfs_statvfs)(struct vfs *, struct statvfs64 *);
int (*vfs_sync)(struct vfs *, short, struct cred *);
int (*vfs_vget)(struct vfs *, struct vnode **, struct fid *);
int (*vfs_mountroot)(struct vfs *, enum whymountroot);
int (*vfs_swapvp)(struct vfs *, struct vnode **, char *);
void (*vfs_freevfs)(struct vfs *);
} vfsops_t;
[..]
/*
* Filesystem type switch table.
*/
typedef struct vfssw {
char *vsw_name; /* type name string */
int (*vsw_init)(struct vfssw *, int);
/* init routine */
struct vfsops *vsw_vfsops; /* filesystem operations vector */
int vsw_flag; /* flags */
mntopts_t *vsw_optproto; /* mount options table prototype */
} vfssw_t;
It looks like a bunch of function pointers!! I noticed one check in the patched OpenIndiana code is that they “match” the ops.. Perhaps somehow these function pointers can be overwritten..
That brings me to the namefs files:
/usr/src/uts/common/fs/namefs/namevfs.c
int
nameinit(struct vfssw *vswp, int fstype)
{
int dev;
namefstype = fstype;
vswp->vsw_vfsops = &nmvfsops;
if ((dev = getudev()) == (major_t)-1) {
cmn_err(CE_WARN, "nameinit: can't get unique device");
dev = 0;
}
mutex_init(&ntable_lock, NULL, MUTEX_DEFAULT, NULL);
namedev = makedevice(dev, 0);
bzero(nm_filevp_hash, sizeof (nm_filevp_hash));
namevfs.vfs_op = &dummyvfsops;
namevfs.vfs_vnodecovered = NULL;
namevfs.vfs_bsize = 1024;
namevfs.vfs_fstype = namefstype;
vfs_make_fsid(&namevfs.vfs_fsid, namedev, namefstype);
namevfs.vfs_dev = namedev;
return (0);
}
/*
* Define the vfs operations vector.
*/
struct vfsops nmvfsops = {
nm_mount,
nm_unmount,
nm_root,
nm_statvfs,
nm_sync,
fs_nosys, /* vget */
fs_nosys, /* mountroot */
fs_nosys, /* swapvp */
fs_freevfs
};
struct vfsops dummyvfsops = {
fs_nosys, /* mount */
fs_nosys, /* unmount */
fs_nosys, /* root */
nm_statvfs,
nm_sync,
fs_nosys, /* vget */
fs_nosys, /* mountroot */
fs_nosys, /* swapvp */
fs_freevfs
};
usr/src/uts/common/sys/vnode.h
/*
* All of the fields in the vnode are read-only once they are initialized
* (created) except for:
* v_flag: protected by v_lock
* v_count: protected by v_lock
* v_pages: protected by v_lock
* v_filocks: protected by flock_lock in flock.c
*/
typedef struct vnode {
kmutex_t v_lock; /* protects vnode fields */
u_short v_flag; /* vnode flags (see below) */
u_short v_count; /* reference count */
struct vfs _FAR_ *v_vfsmountedhere; /* ptr to vfs mounted here */
struct vnodeops _FAR_ *v_op; /* vnode operations */
struct vfs _FAR_ *v_vfsp; /* ptr to containing VFS */
struct stdata _FAR_ *v_stream; /* associated stream */
struct page _FAR_ *v_pages; /* vnode pages list */
enum vtype v_type; /* vnode type */
dev_t v_rdev; /* device (VCHR, VBLK) */
caddr_t v_data; /* private data for fs */
struct filock _FAR_ *v_filocks; /* ptr to filock list */
kcondvar_t v_cv; /* synchronize locking */
} vnode_t;
RECAP
So, NameFS wasn’t participating in this “VFS_HOLD/VFS_RELE/VFS_FREEVFS” protocol. It looks like some kind of record keeping wasn’t in place, the V filesystem was never getting freed, and perhaps the function pointers in the vops fields were getting overwritten… HMMMM
root@life[pts/4][~] mdb -k
Loading modules: [ unix krtld genunix ip usba s1394 nfs ipc ptm random lofs cpc ]
> nmvfsops/48X
nmvfsops:
nmvfsops: 0 780982c8 0 7809868c
0 78098800 0 78098830
0 780988a0 0 1009a0e4
0 1009a0e4 0 1009a0e4
0 1009a0ec 0 1009a0e4
0 1009a0e4 0 1009a0e4
0 78098830 0 780988a0
0 1009a0e4 0 1009a0e4
0 1009a0e4 0 1009a0ec
0 78041fd8 0 780988f0
0 78041d70 0 0
0 0 0 10438890
> 780982c8/i
nm_mount:
nm_mount: save %sp, -0x140, %sp
>
Is it really possible to somehow overwrite this table?? … or is the reference to the table simply changed??
I have no idea what I’m looking for..
Leave a Reply