[BusyBox] bug report (?)
Gerardo Puga
gpuga at gioia.ing.unlp.edu.ar
Wed Jun 5 17:09:03 UTC 2002
Hi!
I don't know if this is the right way to present a bug report (if that's what
this is).
Last week I was playing with stack overflows and decided to take a look at
busybox (no special reason, it was the only source code I had around) 0.60.2
I found a few things; most of them are just silly and harmless errors, but
one of them in syslogd is, at least, "interesting". This is what I found:
- In the file lash.c, the "string" buffer in builtin_read() can be overflown
just y giving the "read" command an argument longer than MAX_READ bytes.
This is easily exploitable, as long as the shellcode is prepared to survive
the parsing in parse_command();
The evil line is
lash.c:417 strcpy(string, child->argv[1]);
I had never used the read command before, so I don't know if this is the
way the command is suppossed to work, but there's an fgets() call at the
end of the function (it is executed when no arguments are given in the
command line) that doesn't seem to do anything.
- In the file insmod.c there's another buffer overflow. Actually, there
are two different overflows there, both located in insmod_main(). The
first one starts in line 3267.
insmod.c:3264 if ((tmp = strrchr(argv[optind], '/')) != NULL) {
insmod.c:3265 tmp++;
insmod.c:3266 } else {
insmod.c:3267 tmp = argv[optind];
insmod.c:3268 }
insmod.c:3269 len = strlen(tmp);
insmod.c:3271 if (len > 2 && tmp[len - 2] == '.' && tmp[len - 1]
== 'o')
insmod.c:3272 len -= 2;
insmod.c:3273 memcpy(m_fullname, len);
insmod.c:3274 m_fullname[len] = '\0';
The module name is extracted from it's path in line 3264 and assigned to tmp.
Then it's lenght is found out, but only to be used when taking the ".o"
off the name. At last, memcpy copies what's left from the name to m_fullname
but doesn't check if it fits in there. Thus, if the remmanent name is longer
than FILENAME_MAX+1 bytes (which is the size of m_fullname), the buffer will
be
overflown.
The second overflow is inmediatly after this one; the buffer being overflown
is m_name, which is also FILENAME_MAX+1 bytes long.
insmod.c:3275 if (*m_name == '\0') {
insmod.c:3276 strcpy(m_name, m_fullname);
insmod.c:3277 }
isnmod.c:3278 strcat(m_name, ".o");
If an output module name was not given using "-o", m_name will be always a
void string when the program gets to line 3273. That being the case,
m_fullname will be copied into m_name. Since m_fullname can have an arbitrary
lenght, even bigger than FILENAME_MAX+1 bytes, m_name can be overflown using
m_fullname. Note that m_name cannot be overflown using the -o option,
since strncpy() was used there. m_name is declared inside insmod_main(),
thus this can be used to perform a stack overflow.
This can be exploited... sometimes. Since a lot of data beyond m_fullname
must be overwritten in order to later reach the return address when
overflowing m_name, sometimes the program will die signaled by SIGSEGV
while memcpy()'ing. It depends on the options used when compiling busybox.
- In file makedevs.c, function makedevs_main().
makedevs.c:28 basedev = argv[1];
(...)
makedevs.c:
(...)
makedevs.c:55 strcpy(devname, basedev);
If sbase == 0, a strcat() call later appends a number to devname.
Neither strcpy() nor strcat() check to see if there's room in devname
for the string they are copying into it. devname is only 255 bytes
long, so it doesn't take a lot to make a stack overflow on it. This is
exploitable very easily.
- In file modprobe.c, lines 264 and 292. The cmd[] global variable will be
overflown if too many (or too long) module names/arguments are given.
cmd[] is only 128 bytes long, so it can even be done manually.
Theres another potential buffer overflow in line 192 (function check_dep()).
- In file dos2unix.c,
dos2unix.c:61 if ((in = wfopen(fn, "rw")) == NULL) {
dos2unix.c:62 return -1;
dos2unix.c:63 }
dos2unix.c:64 strcpy(tempFn, fn);
tempFn is BUFSIZ bytes long, and fn is whatever the user has passed as
argument to the program. tempFn probably cannot be overflown, though, since
line 64 will never be executed if the program doesn't find a file named after
the content of fn.
However, strcpy() should be replaced with strncpy() or safe_strncpy()
(IMHO :).
- In file mount.c, function mount_main(). The buffer string_flags_buff can
be overflown very easily through parse_mount_options() just by sending
a very long option name, or simply too many options, through the -o
command line argument.
There's no single line responsible for the flaw. The whole parse_mount_options
()
was thought supposing that there was room enough in the buffer strflags
for whatever might be given as an option. The problem is that whatever
is given in the command line is passed to parse_argument_options(),
allowing anyone willing to do it to overflow the stack and execute
whatever code s/he wants.
This is exploitable.
BTW, there seems to be another error in function parse_mount_options(),
but it's one of those errors that incredibly enough, end up up doing
what they were INTENDED to to but not THE WAY they were supposed to do it.
mount.c:218 if (*strflags && strflags != '\0' && gotone == false) {
I'm not sure, but I think the intention was to check if strflags was
NULL and then to see if there was some text in it. Instead, it checks
if there's a char different than 0 in the address pointed by strflags,
and then compares the pointer strflags with '\0'. It'll work as long as
strflags is not NULL... fun stuff :)
- In file syslogd.c, function message(). There's an unprotected vsprintf() in
line 283, that allows an "atacker" to overwrite part of the stack above
the b[] (char b[1024]) buffer and, if enough data is overwritten, change
the return address of message(). This will only be possible if the
syslogd daemon was run with the -C option (to use circular logging). This is
not possible by default, since BB_FEATURE_IPC_SYSLOG is disabled by
default in Config.h.
The stack overflow is possible even though syslogd reads and procesess
logs shorter thant 1023 bytes, because it is possible for the user to force
logMessage() to prefix up to about 80 bytes (date, time, hostname,
priority-facility) at the start of the string being logged.
At best, the atacker can quietly kill syslogd. All you need it a 6 lines
C program sending a 2000 bytes long log to syslogd in order to
unleash the stack overflow and kill syslogd.
At worst, the attacker can execute one arbitrary command with an
arbitrary number of arguments with the uid of syslogd.
There are two problems in doing that; the first one is that
the shellcode can't contain any non-printable ascii control codes
or it will be reduced to pieces by serveConnection(). The second is
that there's only one chance to succesfully exploit syslogd, since
if the attack fails (wrong offset value) syslogd will die SIGSEGVed.
I wrote the exploit for this bug, and sent it attached in this mail.
With that exploit it is possible to execute things like
a harmless "/bin/wall hello world", or a much more interesting
"/usr/sbin/adduser -u 0 -o -d /root -G root -p acryptedpass barneythedino"
- Finally, in file libbb/vdprintf.c, function vdprintf(), there's an
unchecked sprintf() writing to a local buffer (char buf[BUF_SIZE]).
This is dangerous but not serious, since every function that
uses vdprintf() currently limits the lenght of the arguments it gives to
vdprintf() to some value that is fortunately always far below BUF_SIZE.
As for version 0.60.3, BUF_SIZE = 8192, but if in the future it were
reduced to some other common value, say 1024, it might automaticaly
vulnerate a few members of the busybox program.
For example, if BUF_SIZE = 1024, it would be possible to exploit syslogd
overflowing the return address of vdprintf(); vdprintf() is called
by syslogd in function message() when circular logging is not enabled,
so it would be possible to exploit this using the same aproach used in
the syslogd exploit I sent you; probably even the same exploit could
be used.
Well, I hope I didn't miss anything else.
Both the exploits and patches (for version 0.60.3) for a few of the bugs
I pointed up before are attached to this mail. Most of the patches only
consist on replacing strcpy() or sprintf() with strncpy() or snprintf().
Note that...
- I corrected the lash/read overflow, but not that
only-god-knows-what-it-does fgets() call at the end of builtin_read().
- In mount.c, I modified parse_mount_options() to receive one more
argument with the lenght of the strflags buffer.
I also modified the two calls to parse_mount_options() in mount_main() to
pass string_flags_buf instead of string_flags. As far as I've seen in the
code, the way I did it it shouldn't have any side effects, but
someone that knows mount.c better than me should check.
Why would anyone want to exploit any of these things? I don't know, just
for the fun I guess :)
Time to go...Keep the work up! Thanks for busybox, I just love it :)
Bye!
Gerardo.-
PS: As you have noted, I'm not an english native speaker. Sorry about
that. ;)
PS2: (after taking a look at the busybox mailing list archives) Congrats for
your little girl!!!
-------------- next part --------------
A non-text attachment was scrubbed...
Name: Patches.tgz
Type: application/x-gzip-compressed
Size: 9401 bytes
Desc: not available
Url : http://lists.busybox.net/pipermail/busybox/attachments/20020605/c96a0137/attachment.bin
More information about the busybox
mailing list