Last active
September 29, 2020 15:23
-
-
Save linuxthor/55932d68a58ba0e314f6e2e60ac94b7f to your computer and use it in GitHub Desktop.
Visualise the effect of the GCC randstruct plugin on some struct layout
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
The 'mizers dream | |
================= | |
The GCC randstruct plugin (randomize_layout_plugin.c) by Open Source Security, Inc., Brad Spengler and PaX Team | |
allows some sensitive structures in the Linux Kernel to have their layout shuffled. The aim is to obfuscate the | |
location of sensitive data (e.g some function pointers) and make certain types of exploitation more difficult. | |
It's explained in detail here: https://lwn.net/Articles/722293/ | |
The randstruct plugin is built with a randomisation seed included (randomize_layout_seed.h) which is generated | |
at compile time by the gen-random-seed.sh script: | |
if [ ! -f "$1" ]; then | |
SEED=`od -A n -t x8 -N 32 /dev/urandom | tr -d ' \n'` | |
echo "const char *randstruct_seed = \"$SEED\";" > "$1" | |
HASH=`echo -n "$SEED" | sha256sum | cut -d" " -f1 | tr -d ' \n'` | |
echo "#define RANDSTRUCT_HASHED_SEED \"$HASH\"" > "$2" | |
fi | |
This results in a layout seed like: | |
const char *randstruct_seed = "00ef6c6511fa1332ee6d0b07d519b815d24708000248ff24f810cb84f8afbeef"; | |
Which is necessarily the same for all builds of e.g your distro kernel but can be something secret for your | |
special private build. The security of this feature rests entirely on keeping the seed value hidden. If the | |
distro needs to support the building of third party modules then it may be difficult to prevent the seed | |
value from becoming known. | |
I was speaking about this feature with someone and wanted to show how it works I (stupidly) said "oh! I can | |
probably write a script or something if we know the seed value and just print out the struct layout.." I tried | |
a couple of ways to do it 'properly' but it was too complicated and my spirit was crushed so I found the cheats | |
way out..! | |
1) I grabbed a copy of the Kernel source | |
2) I built a debug copy of the randstruct plugin (-D__DEBUG_PLUGIN but really just needs the debug_gimple_stmt(stmt)) | |
CONFIG_GCC_PLUGINS=y | |
CONFIG_GCC_PLUGIN_RANDSTRUCT=y | |
3) I picked a struct dentry_operations to show this as it's full of loads of function pointer goodness | |
struct dentry_operations { | |
int (*d_revalidate)(struct dentry *, unsigned int); | |
int (*d_weak_revalidate)(struct dentry *, unsigned int); | |
int (*d_hash)(const struct dentry *, struct qstr *); | |
int (*d_compare)(const struct dentry *, const struct dentry *, | |
unsigned int, const char *, const struct qstr *); | |
int (*d_delete)(const struct dentry *); | |
void (*d_release)(struct dentry *); | |
void (*d_prune)(struct dentry *); | |
void (*d_iput)(struct dentry *, struct inode *); | |
char *(*d_dname)(struct dentry *, char *, int); | |
struct vfsmount *(*d_automount)(struct path *); | |
int (*d_manage)(struct dentry *, bool); | |
} ____cacheline_aligned; | |
4) LKM (it gets weird now - this is just to compile - not to load!) | |
int init_module(void) | |
{ | |
printk("%ld d_revalidate\n", offsetof(struct dentry_operations, d_revalidate)); | |
printk("%ld d_weak_revalidate\n", offsetof(struct dentry_operations, d_weak_revalidate)); | |
printk("%ld d_hash\n", offsetof(struct dentry_operations, d_hash)); | |
printk("%ld d_compare\n", offsetof(struct dentry_operations, d_compare)); | |
printk("%ld d_delete\n", offsetof(struct dentry_operations, d_delete)); | |
printk("%ld d_release\n", offsetof(struct dentry_operations, d_release)); | |
printk("%ld d_prune\n", offsetof(struct dentry_operations, d_prune)); | |
printk("%ld d_iput\n", offsetof(struct dentry_operations, d_iput)); | |
printk("%ld d_dname\n", offsetof(struct dentry_operations, d_dname)); | |
printk("%ld d_automount\n", offsetof(struct dentry_operations, d_automount)); | |
printk("%ld d_manage\n", offsetof(struct dentry_operations, d_manage)); | |
return 0; | |
} | |
5) Makefile: | |
obj-m := testo.o | |
KDIR := /lib/modules/$(shell uname -r)/build | |
PWD := $(shell pwd) | |
CFLAGS_testo.o :=-fplugin=/path/to/the/plugin/randomize_layout_plugin.so -DRANDSTRUCT_PLUGIN | |
default: | |
$(MAKE) -C $(KDIR) M=$(PWD) modules | |
clean: | |
$(MAKE) -C $(KDIR) M=$(PWD) clean | |
6) Now using a hex editor (or something more civilised.. ;) the seed value in randomize_layout_plugin.so can | |
be adjusted and the module rebuilt with something like this one liner or better: | |
make clean 2>&1>/dev/null && make 2>&1 | grep printk | sed -e s/\)\;//g | awk '{print $4":"$3}' | sort -n | sed -e s/\\\\n\",//g | |
0:d_release | |
8:d_iput | |
24:d_manage | |
32:d_dname | |
40:d_delete | |
48:d_prune | |
56:d_weak_revalidate | |
64:d_automount | |
80:d_revalidate | |
88:d_hash | |
96:d_compare | |
then again with a different seed value: | |
0:d_prune | |
8:d_release | |
24:d_delete | |
32:d_manage | |
40:d_automount | |
48:d_compare | |
56:d_iput | |
64:d_hash | |
72:d_revalidate | |
80:d_dname | |
96:d_weak_revalidate | |
In this way it seems possible to see the struct layout from an entirely different kernel to the running one if the | |
seed value is known and can be used with the plugin. |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment