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