Starting programs with stdin/stdout/stderr closed — from within bash

Lately I found a nice article giving some deeper insight about shell in- and output redirection for bash. The most common used form is like PROGRAM >FILE.OUT which redirects the standard output of PROGRAM to FILE.OUT. One lesser know but nevertheless neat feature is about closing file descriptors. It is actually not covered by the documentation, so here is the syntax for it: [N]>&-. This will close the file descriptor N or stdout when N is omitted. It’s a pretty nice feature when you’ve opened additional files within a shell script via ‘exec N>FILE‘ and want to close those file descriptors. But beside that, it can also be used to start a program with some or all of its default file descriptors closed.

Take a look at those two calls to ls:

minipli@zaphod:~$ ls -g /proc/self/fd
total 0
lrwx------ 1 minipli 64 May 14 21:43 0 -> /dev/pts/0
lrwx------ 1 minipli 64 May 14 21:43 1 -> /dev/pts/0
lrwx------ 1 minipli 64 May 14 21:43 2 -> /dev/pts/0
lr-x------ 1 minipli 64 May 14 21:43 3 -> /proc/13868/fd
minipli@zaphod:~$
minipli@zaphod:~$ ls -g /proc/self/fd 0>&- 2>&-
total 0
lr-x------ 1 minipli 64 May 14 21:43 0 -> /proc/13869/fd
lrwx------ 1 minipli 64 May 14 21:43 1 -> /dev/pts/0

The first listing shows us file descriptors 0, 1 and 2 connected to the terminal; fd 3 is opened by ls for reading the directory content itself. In the second listing stdin and stderr were closed prior starting the ls program so the fd for the directory ends up as 0 — which is normaly used for stdin. With that in mind, you have a tool at hand, right within your shell, that gives you the ability to test how programs behave with there standard file descriptors closed. For example, you can try something like buggy_programm 0>&- 1>&- 2>&- or variations of it with just stdin or stdout closed and see how it breaks. A good starter may be: ls /proc/self/fd 1>&-

Constructing labels from expressions with GNU as

Did you ever wanted to generate symbol names using the C preprocessor using it’s token concatenation feature via ##? That’ll work as long as the tokens to concatenate are simple and do not contain expressions. If the latter is the case, the preprocessor will fail because it won’t evaluate the expression but instead will use it verbatim and generate an invalid symbol name. It’s annoying that cpp is missing this feature when you want to generate symbols with numbers in there name and have those numbers being no simple value but an arithmetic expression instead. But that problem can be solved by other means. The GNU assembler (gas) is capable of evaluating arithmetic expressions within macros. Not by default, though. To enable this feature you have to either specify .altmacro within your assembler source file or invoke as with the command line option --alternate. I prefer the former method because it’s less error prone.

So how to do it then? Take a look at this little example:

.altmacro	/* enable alternative macro mode */

.macro	mksym name, number
__mksym \name, %number
.endm

.macro	__mksym, name, number
\name\number\():
	.long \number
.endm

It defines two macros mksym and __mksym. The former is just a helper for the latter. It evaluates the expression in it’s second argument number by not referencing it via \number but %number instead. So __mksym gets called with the computed value given for it’s possible complex argument number. Within __mksym the label gets created by just putting the two arguments \name and \number together. The preceding \() before the : is just needed to terminate the token \number. Now we can use those macros like that:

.section	.rodata

/* now define symbols by hand...  */
mksym	bar, 1
mksym	bar, (9/3 + (7-1))

/* ...or use other pseudo ops to do it in a loop */
.irp	i, 3, 2, 1
mksym	foobar, (\i * 2 + 1)
.endr

Ensuring, that this really works (assuming mksym.S contains the code of the above two listings):

minipli@zaphod:~/src$ as -o mksym.o mksym.S
minipli@zaphod:~/src$ nm -n mksym.o
00000000 r bar1
00000004 r bar9
00000008 r foobar7
0000000c r foobar5
00000010 r foobar3
minipli@zaphod:~/src$

Sure It does! :) This way we’re able to generate labels with parts of the name being an arithmetic expression.

LD_PRELOAD vs. /etc/ld.so.preload

Ever wondered what those very first few syscalls where all about when you watched a program starting with strace? For me it was always some kind of static noise the loader generates, so was of no real interest. But then, one day, I looked a little closer and noticed a weird line testing for the existence of a file called /etc/ld.so.preload. I haven’t yet encountered a system that had such a file. The name was promising so I was curios what that file would be all about and started looking at the glibc sources searching for the corresponding code that does this test. Found it, like expected, in the loader code (elf/rtld.c). It is testing for the existence of that file and if it does it’ll load all shared objects listed in there just like setting the environment variable LD_PRELOAD would do. But wait! LD_PRELOAD was evil when combined with suid binaries so it will be ignored by the loader. That’s because otherwise you could abuse those binaries to raise your privileges by preloading some code that spawns a shell — e.g. by hooking __libc_start_main(). However, those restrictions do not apply for this file. The loader will bravely load the shared objects listed in /etc/ld.so.preload even for suid binaries. So if someone manages to create such a file he effectively owns the system. But since only root should be able to create files under /etc it should be safe for most systems. Anyway, good to know, by using this technique, you can hook your suid programs and for example get a root shell when needed by preloading a shared library that hooks setuid() with a wrapper that tests for a trigger (e.g. an environment variable) and execve()’s /bin/sh for you. Otherwise the wrapper just forwards the setuid() syscall. Now you just have to set your trigger, ping localhost and you’re root. :) On the other hand you can do serious things with it like analysing or instrumenting suid binaries you have no source code of. It’s an elegant way to dig into the program without actually touching the binary.

Security in LSM – a myth?!

Linux security modules (LSMs) are – disregarding there name – no modules at all. Beside that, they are a way the Linux kernel provides hooks for a security framework which then has the ability to do additional checks for a bunch of kernel functions. Technically a LSM therefore registers itself with a pointer to a structure containing function pointers for all functions it’s interested in. That structure then is used within the kernel to do those additional checks. To keep the overhead of the whole concept low, those function pointers aren’t tested for NULL pointers, there just called. Even if no LSM is registered (yet). This is possible because for two reasons. The first: Regardless of which LSMs are compiled into the kernel or are (de-)activated at boot time, there always exists a default LSM that implements a default behaviour that is based on the capability system the Linux kernel implements. The second reason is: When registering a LSM the registration routine ensures all possible function pointers are set and if one of them is missing it gets initialized with the corresponding one from the default LSM.

This generic interface – a structure with function pointers – also is the vulnerable point of the whole system. A single pointer variable is holding a reference to the LSM in use. By exchanging that pointer an attacker can deactivate it with just a single write operation. The simplest one would be to replace it with the default LSM. That way SELinux and friends will we be gentle like a puppy.



Follow

Get every new post delivered to your Inbox.