Latest News >> 2008-09-29

I read Obie’s most recent post about his intense passion for Loverboy’s quintessential anthem, “Lovin’ Every Minute Of It”. I find the early 1980’s music is inspiring and uplifting and definitely suited to such important things as corporate culture, recruiting, and motivating the troops to do better. Yes, nothing gets a worker working better than a little Loverboy right in their ear.

2008-09-25

Don’t forget folks, the FU NYC show will be in a few hours (7pm-9pm). I’ll be icecasting this one at http://zedshaw.com:8000/fu_nyc and as usual you can use VLC, mplayer or many other players to play the stream.

2008-09-17

I got into music school last week and I’m going to study guitar exclusively for the next year. This is something I’ve always wanted to do, but just never had the chance. Either I wasn’t good enough (being self-taught for so many years) or I just didn’t have the money. After being laid off and getting a small package I decided to practice my ass off on the guitar, do a few live shows to get ready, and then audition for a school in the city.

2008-09-04

The Freehackers Union NYC show went insanely well. I managed to pull off a full live internet feed of the audio to people in FU and the show at the same time. We had about 10 newbies show up to give their first five minutes and 11 listeners on IRC/icecast. Some people showed up just to hang out, so we relaxed the rules and let them stay to build an audience. Overall, there were some cool projects presented and everyone had a good time.

Wanna know what all the Ruby vulnerabilities are? Or at least have a fun look at how to search through code for clues? It’s a blast.

I believe in full disclosure because I think people should know what they’re getting. You see my friends, when you see a vulnerability announced and you don’t know what causes it, you are actually the last person to hear about it. Before you get this defect hidden in a massive batch of patches, the Ruby team has already told people way more important than you. People running Big Important IT™ like Twitter. Just like everything else in this world, the people with money get to know what’s going on, and you and me just get to take it up the ass and wait to be handed scraps of information.

Well, my Ruby friends, I am on your side. That’s why I’m going to show you how to educate yourself about the quality of Ruby. Yes, you too can look deep into Satan’s anus and get a full glimpse of Ruby’s true glory.

UPDATE: Just Fucking Report It

Jesus some people don’t get humor at all, or I’m just not that funny. Yes, you can read the fucking CSV/SVN/git logs and find all the places they changed things and how. You can also do what I did to troll through the source and find them nearly as easily. I actually did this to show people how easy it is, by not looking at the changelog, as an educational device. I didn’t go through the trouble to prove I’m a badass. I did it to prove you don’t have to be a bad motherfucker to do this. It’s easy, so hiding it from the general public is retarded.

Think about it, if they have all the information in the fucking source and change history, why the fuck isn’t the information in the mother fucking security announcement?

If the change log says this:

Wed Jun 18 21:52:38 2008  URABE Shyouhei  <shyouhei@ruby-lang.org>

    * array.c (ary_new, rb_ary_initialize, rb_ary_store,
      rb_ary_aplice, rb_ary_times): integer overflows should be
      checked. based on patches from Drew Yao <ayao at apple.com>
      fixed CVE-2008-2726

    * string.c (rb_enc_cr_str_buf_cat): fixed unsafe use of alloca,
      which led memory corruption. based on a patch from Drew Yao
      <ayao at apple.com> fixed CVE-2008-2726

Then you realize that these vulnerabilities were in the change log, tagged with the CVE identifiers, and easily found three days before the general public announcement on the 21st? Yes my friends, this is what should be pissing you off.

When I wrote this rant there wasn’t anything in the security announcements, just a vague description. It took me a total of maybe 20-40 minutes to find tons of these defects in the hardest way possible. Using their source control you could find it even easier. This makes their practice of not fully disclosing things all that much more stupid.

That’s the point. Regular Joe’s not in the know (and their managers) who aren’t able or willing to read the source have to wait until they get the real scoop later, and meanwhile, hackers and the Ruby Illuminati get to know ahead of time. I hope you enjoy being second class citizens people.

The Rundown

I will be using the following tools to take you on a tour of the many buffer overflows and path attacks discovered in Ruby and hidden from you. To follow along, you’ll need these wonderful tools:

I know this sounds like the tools of an Evil Hacker™, but trust me, despite today’s FISA Bill Vote you will not go to jail for using these tools to find vulnerabilities.

Getting The Source

The vulnerability mentions Ruby patch-level 229 is vulnerable, but you can’t find Ruby 1.8.6 p229 at the Ruby FTP You’ll have to get Ruby 1.8.6 p114 and compare it with Ruby 1.8.6 p230 but that won’t be hard because I’ve done some detective work!

You see my friends, Ruby 1.8.7 came out and had a ton of “enhancements” to it. Things like everything returning an Enumerator. It was glorious and made JRuby, Rubinius, and IronRuby so happy. MagLev wasn’t phased I hear. Anyway, those changes were also in Ruby 1.9. Now, I did the hard work of analyzing the diffs between 1.8.7 and 1.9 to figure out what is common based on the announced 1.8.7 change sets, and then used that information to analyze the 1.8.6 versions. This helped me narrow it down to a set of changes by user shyouhei” in the month of 2008-06.

First thing I did was crack open the two Ruby tar.bz2 archives and run this nice diff command in a big terminal window:

diff -x ".txt" -x "\." -x "[A-Z]" -x ".rb" \
    --suppress-common-lines -ry ruby-1.8.6-p114 ruby-1.8.6-p230 | less

This command gives you a nice side-by-side compare between those two versions of Ruby. Now doing this you can right away see that array.c had some trouble:

22a23
> #define ARY_MAX_SIZE (LONG_MAX / sizeof(VALUE))

Uh oh! I smell a buffer overflow! I feel like a nature show host. “And now, the elusive Bufferius Overflowius Maximinus will show its mating stripes, hopefully attracting a fellow Shellcodiant Incomprehensivisti for a mating session and produce wonderful little remote executions.” Tasty.

Now, we know this is all being updated by shyouhei this month, so if we use ack we can get a good guide to the world of Ruby Security Monkey Patch Profiteering. Watch and learn:

  $ ack -o "Date: 2008-06-[0-9][0-9]" ruby-1.8.6-p230
    ruby-1.8.6-p230/array.c 6:Date: 2008-06-20
    ruby-1.8.6-p230/bignum.c 6:Date: 2008-06-15
    ruby-1.8.6-p230/class.c 6:Date: 2008-06-15
    ruby-1.8.6-p230/defines.h 6:Date: 2008-06-15
    ruby-1.8.6-p230/dln.c 6:Date: 2008-06-15
    ruby-1.8.6-p230/eval.c 6:Date: 2008-06-16
    ruby-1.8.6-p230/ext/iconv/iconv.c 7:Date: 2008-06-15
    ruby-1.8.6-p230/ext/socket/socket.c 6:Date: 2008-06-08
    ruby-1.8.6-p230/ext/syck/rubyext.c 6:Date: 2008-06-15
    ruby-1.8.6-p230/ext/win32ole/win32ole.c 15:Date: 2008-06-08
    ruby-1.8.6-p230/file.c 6:Date: 2008-06-18
    ruby-1.8.6-p230/gc.c 6:Date: 2008-06-15
    ruby-1.8.6-p230/intern.h 6:Date: 2008-06-20
    ruby-1.8.6-p230/io.c 6:Date: 2008-06-13
    ruby-1.8.6-p230/lib/irb.rb 5:Date: 2008-06-13
    ruby-1.8.6-p230/marshal.c 6:Date: 2008-06-18
    ruby-1.8.6-p230/misc/ruby-mode.el 5:Date: 2008-06-15
    ruby-1.8.6-p230/numeric.c 6:Date: 2008-06-15
    ruby-1.8.6-p230/object.c 6:Date: 2008-06-18
    ruby-1.8.6-p230/process.c 6:Date: 2008-06-15
    ruby-1.8.6-p230/signal.c 6:Date: 2008-06-15
    ruby-1.8.6-p230/sprintf.c 6:Date: 2008-06-20
    ruby-1.8.6-p230/string.c 6:Date: 2008-06-20
    ruby-1.8.6-p230/struct.c 6:Date: 2008-06-15
    ruby-1.8.6-p230/time.c 6:Date: 2008-06-15
    ruby-1.8.6-p230/util.c 6:Date: 2008-06-15

Ah, sneaky guy. Now, did shyouhei make all of these changes?

    $ ack "shyouhei" (ack -l "Date: 2008-06-[0-9][0-9]" ruby-1.8.6-p230 ) | wc -l
    25
    $ ack -o "Date: 2008-06-[0-9][0-9]" ruby-1.8.6-p230 | wc -l
    26

Pretty close! This is a good way to narrow down what we have to search through and now we can turn to good old gvimdiff to check this out.

NOTE: Another thing to notice is that many of these files haven’t been changed in close to a year or more. If you suddenly see one user change a bunch of files in the space of a few days that haven’t changed in a long time, well those are probably the big changes you need to look for.

Enter Diff Goodies

If you’re a vim user then gvimdiff should be very fun. If you’re not a vim user then use whatever visual diff you have to check out these spots in the source I’ll show you. We’ll just go through each file, and I’ll point out the fun lines you should look at. They could be defects, they could just be cruft or merged in fixes to hide things. But, the experience will be illuminating.

I use fish for my shell, so this little bit of code will give you a nice walk through the code:

for f in (ack -l "Date: 2008-06-[0-9][0-9]" ruby-1.8.6-p230 | sed -e s/ruby-1.8.6-p230//)
     gvimdiff -f ruby-1.8.6-p114$f ruby-1.8.6-p230$f
end

array.c

First off, we have these awesome lines of code in the p114 version:

123: if (len > 0 && len * sizeof(VALUE) <= len) {
...
296: if (len > 0 && len * (long)sizeof(VALUE) <= len) {
...
370: if (new_capa * (long)sizeof(VALUE) <= new_capa) {
...
2381: if (LONG_MAX/len < RARRAY(ary)->len) {
...

Which are replaced with in the p230 version:

124: if (len > ARY_MAX_SIZE) {
...
297: if (len > ARY_MAX_SIZE) {
...
373: if (new_capa >= ARY_MAX_SIZE - idx) {
374:    new_capa = (ARY_MAX_SIZE - idx) / 2;
...
2388:     if (ARY_MAX_SIZE/len < RARRAY(ary)->len) {
...

It would seem that you can give the Array type a bad length that can overflow the backing buffer for it. Array has always been a giant piece of shit, being the cause of the Array shift bug that caused memory leaks and was actually fixed by Eric Mahurin one year earlier. Oh well, they have a VM now.

There also seems to be more complicated fixes withing array.c related to recursive equality tests, but I’ll assume that’s not a security defect and just noise. It sure look important though (from the p230):

static VALUE
rb_ary_eql(ary1, ary2)
    VALUE ary1, ary2;
{
    if (ary1 == ary2) return Qtrue;
    if (TYPE(ary2) != T_ARRAY) return Qfalse;
    if (RARRAY(ary1)->len != RARRAY(ary2)->len) return Qfalse;
    if (rb_inspecting_p(ary1)) return Qfalse;
    return rb_protect_inspect(recursive_eql, ary1, ary2);
}

Who the hell still uses K&R style anyway? Lame.

bignum.c

I’m gonna speed this up since there’s a whole lot of files, and I’ll probably just stop after I get tired of exposing all the potential defects. Bignum seems to have a few of these size and buffer related defects as well.

There seems to be a slight change in the check for BIGZEROP (bottom half is p230):

***************
*** 38,40 ****

! #define BIGZEROP(x) (RBIGNUM(x)->len == 0 || (RBIGNUM(x)->len == 1 && BDIGITS(x)[0] == 0))

=== 38,54 ===-

! #define BIGZEROP(x) (RBIGNUM(x)->len == 0 || \
!              (BDIGITS(x)[0] == 0 && \
!               (RBIGNUM(x)->len == 1 || bigzero_p(x))))
! 
! static int bigzero_p(VALUE);
! static int
! bigzero_p(x)
!     VALUE x;
! {
!     long i;
!     for (i = 0; i < RBIGNUM(x)->len; ++i) {
!     if (BDIGITS(x)[i]) return 0;
!     }
!     return 1;
! }

Well, now that’s a lot more complex for sure. I guess it was important to know that every single digit in the Bignum is actually 0 rather than just the first one. But, not quite sure that’s a security defect. Let’s look at the next one though (p230 on the bottom):

***************
*** 448,450 ****
    while (*++str == '0');
!     if (!*str) --str;
      }
=== 462,464 ===-
    while (*++str == '0');
!     if (!(c = *str) || ISSPACE(c)) --str;
!  }

Getting closer, looks like bignum didn’t compensate for a space in the string it was reading, which leads to this nice change:

*** 654,655 ****
=== 668,672 ===-
      }
+     if (i >= LONG_MAX/SIZEOF_BDIGITS/CHAR_BIT) {
+     rb_raise(rb_eRangeError, "bignum too big to convert into `string'");
+     }
j = SIZEOF_BDIGITS*CHAR_BIT*i;

Well, as many people have said, I’m a total scrub and can’t code for shit, so I don’t really know what’s going on here. I’ll let you figure out why they’d suddenly care about a Bignum being too big for a string.

dln.c

This is a fun one, since the defects were reported by an Apple employee, so we see some changes that could be specific to the Mac OSX platform. In this file there’s a chunk of code that’s just moved around related to the Mac’s weird ass filesystem:

***************
*** 1774,1795 ****

- #ifndef __MACOS__
-     if (stat(fbuf, &st) == 0) {
-         if (exe_flag == 0) return fbuf;
-         /* looking for executable */
-         if (!S_ISDIR(st.st_mode) && eaccess(fbuf, X_OK) == 0)
-         return fbuf;
-     }
- #else
-     if (mac_fullpath = _macruby_exist_file_in_libdir_as_posix_name(fbuf)) {
-         if (exe_flag == 0) return mac_fullpath;
-         /* looking for executable */
-         if (stat(mac_fullpath, &st) == 0) {
-         if (!S_ISDIR(st.st_mode) && eaccess(mac_fullpath, X_OK) == 0)
-             return mac_fullpath;
-         }
-     }
- #endif

But, the real kick is a change to the list of allowed extensions during the operation of the dln_find_1 function:

  #if defined(DOSISH)
    if (exe_flag) {
!         static const char *extension[] = {
  #if defined(MSDOS)
=== 1774,1778 ===-

  #if defined(DOSISH)
    if (exe_flag) {
!         static const char extension[][5] = {
  #if defined(MSDOS)

Oh, I smell another buffer overflow. If you look at that extension array you’ll see that it contains a set of extensions, the change is to set the max size of these so that they are fixed to a max of 5. That’s a size change which is used in:

*** 1810,1812 ****

!         for (j = 0; extension[j]; j++) {
        if (fspace < strlen(extension[j])) {
=== 1792,1794 ===-

!         for (j = 0; j < sizeof(extension) / sizeof(extension[0]); j++) {
        if (fspace < strlen(extension[j])) {
***************
*** 1827,1828 ****
=== 1809,1811 ===-
        }
+         goto next;
    }

Nice, so the change was to stop traversing this list of extensions on Windows until you hit a NULL and instead rely on a fixed sized array, and then a goto to a “next:” label that seemed to not be included in the p114 version. I’m betting that goto is pretty significant, and that the size change in the array is just a precaution.

eval.c and class.c

At first when I was looking at the changes to these two files they just seemed like fixes to Ruby’s meta programming “magic” (Jazz hands! You gotta put the jazz hands out when you say “meta programming” in Ruby). Then I got to taking a closer look, and I saw this addition to the eval.c file:

*** 1295,1298 ****
        }
!         if (tail) {
        warn_print2(tail, elen-len-1);
        }
=== 1301,1305 ===-
        }
!         if (tail && elen>len+1) {
        warn_print2(tail, elen-len-1);
+         if (einfo[elen-1] != '\n') warn_print2("\n", 1);
        }

Another length ending change by adding a test to check the elen as well as the tail, which means these files could have a serious vulnerability about how the symbol table in Ruby is used to attach new nodes to Ruby classes. That’s just my first look, but someone who knows Ruby’s internals better should check this out. This could mean some really big hacks and possibly injecting Ruby into the interpreter remotely if it’s one of the defects.

iconv.c

There’s just a tiny little change starting on line 426 of p230’s iconv.c file to ensure that the length isn’t greater than the slen. This could mean another buffer overflow from reading files via iconv.

win32ole.c

In several of the other files there’s changes of this kind too:

*** 2105,2107 ****
          /* retry to call args by value */
!         if(op.dp.cArgs > cNamedArgs) {
              for(i = cNamedArgs; i < op.dp.cArgs; i++) {
=== 2105,2107 ===-
          /* retry to call args by value */
!         if(op.dp.cArgs >= cNamedArgs) {

Where, in addition to fixing up range checks, there will be a change from a < or > to be a <= or >= test instead. This is also a common fix for buffer overflow defects.

But I doubt some dude from Apple told them to fix this windows OLE file. Hell, I don’t even think Microsoft would look at OLE these days.

file.c

Alright, there’s a lot of changes in this file related to directory and file names on DOSISH platforms, and NTFS. A lot of these changes replace the use of strrchr with alternative code. Yes, ruby uses strrchr, probably one of the most evil fucking cunt functions in the history of the str… family.

When you code, you can use this simple regex to look for bad things:

  egrep '[^_.>a-zA-Z0-9](str(n?cpy|n?cat|xfrm|n?dup|str|pbrk|tok|_)|stpn?cpy|r?index[^.]|a?sn?printf|byte_)' *.c

Try it out on the Ruby source, it’ll find tons of potential places where strings might be used wrong. In file.c it looks like the changes are all about getting the paths right on NTFS or Windows specifically, and then in general getting the path analysis correct. I’m tired but this basically looks like a series of path traversal and potentially buffer overflows from paths.

Since file.c is used all over, this means many web servers could be vulnerable to path traversal or even just buffer overflows from simple web requests.

gc.c

Ah, my favorite file in all of Ruby. The GC. We’re old friends. I remember fondly trolling through its horrid festering pile of shit looking for a memory leak in Mongrel with no success. That’s because the leak was actually in array and not gc, since you know I tend to find my memory leaks in the array operations of a garbage collected language.

Now, most of this doesn’t seem like a security vulnerability, but one very curious change caught my eye about the stack:

*** 440,447 ****
#  endif
! static VALUE *
! stack_end_address(void)
  {
!     return (VALUE *)__builtin_frame_address(0);
  }
! #  define  SET_STACK_END    VALUE *stack_end = stack_end_address()
  # else
=== 443,451 ===-
  #  endif
! static void
! stack_end_address(VALUE **stack_end_p)
  {
!     VALUE stack_end;
!     *stack_end_p = &stack_end;
  }
! #  define  SET_STACK_END    VALUE *stack_end; stack_end_address(&stack_end)

Seems like there’s some changes here to determine correct stack direction on the native CPU. Why, that could be a stack smash exploit in the making! Oh nice, those are super hot to work with since, when the function returns, you can run your code. Not sure how you’d exploit it in the GC though.

io.c

My runner up for greatest Ruby source file ever. In this file none of the changes seem all that big of a deal, but in rb_open_file there’s a bit of string messing with the path. Could potentially be a place to exploit files that get opened. Combined with all the changes in file.c for path analysis this is a good guess.

All of the changes are along these lines:

***************
*** 4404,4406 ****
        filename = rb_ary_shift(rb_argv);
!         fn = StringValuePtr(filename);
        if (strlen(fn) == 1 && fn[0] == '-') {
=== 4407,4409 ===-
        filename = rb_ary_shift(rb_argv);
!         fn = StringValueCStr(filename);
        if (strlen(fn) == 1 && fn[0] == '-') {
***************
*** 5065,5067 ****
        rb_str_modify(v);
!         arg[i] = (unsigned long)RSTRING(v)->ptr;
    }
=== 5068,5070 ===-
        rb_str_modify(v);
!         arg[i] = (unsigned long)StringValueCStr(v);
    }

Where access to pointers is changed to a CStr. This could just be fixings for 1.8.7 to get rid of the use of RBSTRING everywhere. But, hidden in here could be a defect related to the changes in file.c.

process.c

This file has a few changes related to the arguments passed to forked processes, another classic vector of attack. The changes look more like a clean-up than a defect fix, so I’ll assume they aren’t for any security fixes. Do a search for “proc_exec_args” to see for yourself.

Read The Code To Your Software

Hopefully going through all this Ruby code to see what’s changed in these security fixes will get you motivated to start reading your software’s code. I try to glance through at least the biggest files to see what the quality is in general.

Generally, the way the Ruby and Rails teams have handled security isn’t so hot. They do this hiding fixes thing as if it stops people from figuring out what happened, but it took me a total of 20 minutes to find a lot of the defects already. This practice doesn’t work, and its only purpose is really to hide shame.

But, I say own it. When I find defects, I own them. I fucked up. There’s no shame in that. Fucking step up, be a man about it, admit you’re an idiot, and fix the shit.

Hiding it and convincing everyone that everything is just fine will only work for so long.

Where’s The Remote Execution?

Looking at these in total, I see them mostly as just general buffer overflow defects, signed integer problems, and path traversal problems. However, the file that gives me the most concern is the changes to eval.c and class.c. These seem to be protections against code being added in weird ways to the Ruby classes. If that’s true, the real defect could be that you can easily just hand a simple string to some Rails application and it’ll run your Ruby.

I guess we’ll find out after the Ruby guys passively aggressively kill me for looking at their open source and …. telling people things.