Tuesday, November 27, 2007

Your favorite "better than C" scripting language is probably implemented in C

I was writing an application front-end in Ruby/Gnome2 and I needed to produce an error message for the user that contained a string the user had previously input. My MessageDialog code looked like this:
-------------------------------------------------------------------------
dialog = Gtk::MessageDialog.new(@main_app_window, Gtk::Dialog::MODAL,
Gtk::MessageDialog::INFO,
Gtk::MessageDialog::BUTTONS_CLOSE,
"%s - Was your string!" % my_string)
-------------------------------------------------------------------------
As you can see the variable my_string is placed in the message dialog text using a format specifier correctly according to the man page. I started to wonder what happened if this string contained a format specifier, would the underlying C libraries and bindings display it correctly? Surprise!



No it was not displayed correctly. In fact it was vulnerable to a format string attack straight from the year 2001. UGH! Now you might argue - "Your fault for not sanitizing your string". Well thats true to a point. But the MessageDialog interface is just a very deep abstraction layer to a printf() style function in the GTK C library. But unlike those functions MessageDialog is not well documented as an 'easily mis-used' function.

Programmers typically trust their API to correctly sanitize and display their input, especially in scripting languages. This is because in scripting languages programmers feel they are safe from traditional C language vulnerabilities. This isn't always the case when your abstraction layers don't handle data correctly. My audit to find the offending code took about ten minutes but I narrowed it down to

ruby-gnome2-all-0.16.0/gtk/src/rbgtkmessagedialog.c

Where it calls GTK like this:
w = gtk_message_dialog_new(NIL_P(parent) ? NULL : GTK_WINDOW(RVAL2GOBJ(parent)),
RVAL2GFLAGS(flags, GTK_TYPE_DIALOG_FLAGS),
RVAL2GENUM(type, GTK_TYPE_MESSAGE_TYPE),
RVAL2GENUM(buttons, GTK_TYPE_BUTTONS_TYPE),
(const gchar*)(NIL_P(message) ? "": RVAL2CSTR(message)));

The variable 'message' is passed directly to GTK. I don't blame GTK authors for this one, it would be like blaming libc authors for printf()'s ability to print a variable without a format specifier. The GTK MessageDialog page shows the function prototype for gtk_message_dialog_new()

GtkWidget* gtk_message_dialog_new
(GtkWindow *parent, GtkDialogFlags flags, GtkMessageType type,
GtkButtonsType buttons, const gchar *message_format, ...);


parent: transient parent, or NULL for none
flags: flags
type: type of message
buttons: set of buttons to use
message_format: printf()-style format string, or NULL
...: arguments for message_format

So GTK is clearly expecting a proper format string, which should be properly passed to it by whatever API called it.

Example vulnerable code:
-------------------------------------------------------------------------
#!/usr/bin/env ruby

# ruby rubber.rb %x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x.%x

require 'gtk2'

my_string = ARGV[0]

dialog = Gtk::MessageDialog.new(@main_app_window, Gtk::Dialog::MODAL,
Gtk::MessageDialog::INFO,
Gtk::MessageDialog::BUTTONS_CLOSE,
"%s - Was your string!" % my_string)
dialog.run
dialog.destroy
-------------------------------------------------------------------------
To avoid this issue in your ruby code you could use the markup member. This will use the Pango markup language on your text. Its a workaround but it gets the job done.
-------------------------------------------------------------------------
my_string = ARGV[0]

dialog = Gtk::MessageDialog.new(@main_app_window, Gtk::Dialog::MODAL,
Gtk::MessageDialog::INFO,
Gtk::MessageDialog::BUTTONS_CLOSE)

dialog.markup = "#{my_string} - Was your string!"
dialog.run
dialog.destroy
-------------------------------------------------------------------------
Or alternatively you could do something like "my_string = my_string.gsub(/%/, "%%")" before calling messagedialog.

Using google we can find some other projects vulnerable to similar bugs. Most just stick #{my_string} in the message, including example applications from the official Ruby/Gnome2 website.


That about wraps up this post. Other Ruby/Gnome2 API's may have similar 'functionality'. This should teach all the scripters out there a security lesson. Always remember your favorite "better than C" scripting language is probably implemented in C. Ruby/Gnome2 authors have been notified and they have committed a patch to SVN.

3 comments:

Townsend Ladd Harris said...

My perl still beats your C :)

John Fellers said...

umm.... really? i never knew that most other languages were build on top of the C programming language.

Geez... my whole world is flipped, turned upside down now!!! thanks chris!!!!


hahahaahhha..... just kidding man. nice post! ^_^

john fellers

heyme said...

hey guys see hw can we run a c program without main function...
to see visit on http://technoease.com/520/programming/c-programming/can-we-run-c-program-without-main-function/