[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