Skip to content

Instantly share code, notes, and snippets.

@linuxthor
Last active September 29, 2020 15:23
Show Gist options
  • Save linuxthor/55932d68a58ba0e314f6e2e60ac94b7f to your computer and use it in GitHub Desktop.
Save linuxthor/55932d68a58ba0e314f6e2e60ac94b7f to your computer and use it in GitHub Desktop.
Visualise the effect of the GCC randstruct plugin on some struct layout
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