coderrr

October 29, 2008

Secure alias method chaining

Filed under: ruby, security — Tags: , — coderrr @ 9:24 pm

Have you ever wanted to redefine a method, chaining it to the original method, but make sure that the original method was uncallable? No? Well yea, most people probably haven’t. But it’s an interesting idea and I actually have a somewhat legitimate use case for it, so I’m going to talk about it. Please note, the below are examples, not what I actually used it for.

The usual way to chain a method is:

class String
  alias_method :original_upcase, :upcase
  def upcase
    s = original_upcase
    "upcased!: #{s}"
  end
end

But if someone wanted to call the original upcase all they would need to do is call original_upcase. Maybe you think you could remove_method :original_upcase. But no, that would break the new upcase when it tries to call the original.

Luckily there is a way to do this with lambdas, method objects, and enclosed local variables.

class String
  m = instance_method(:upcase)
  define_method :upcase do
    s = m.bind(self).call
    "upcased!: #{s}"
  end
end

We have now overwritten the original upcase method without having to first alias it. The original method only exists in the local variable m, which was enclosed in the block sent to define_method. After the end of class String that local variable is now out of scope and effectively non-existent. It only exists in the block, but there is no way to extract the value of it from the block without being able to modify the block.

Of course the method object is still in existence, which means it could be found with

methods = []
ObjectSpace.each_object(UnboundMethod) { |m| methods << m }

This is averted by simply removing the ObjectSpace constant:

class Object
  remove_const :ObjectSpace
end

Update: Pat Maddox pointed out that you could get access to the original Method objects through modification of the Method or UnboundMethod classes. We can prevent this by freezing both classes so that no further modification of them is possible. This includes adding, removing, redefining methods, etc.

[Method, UnboundMethod].each{|klass| klass.freeze }

So there you have it, secure alias method chaining. Or…. can anyone figure out a way to access the original method without using ObjectSpace (and without using C extensions of course)?


Update: Ok this has already been pwned by Maddox. If you redefine Method#call you can get access to the method object. So to keep things secure we’d have to prevent someone from modifying the methods of the Method class. This might be possible using something like http://scie.nti.st/2008/9/17/making-methods-immutable-in-ruby. I’m not sure if that will prevent all tampering attempts though, I’ll have to look into this.

4 Comments »

  1. class Method
    HIDDEN_METHODS = []

    alias_method :orig_call, :call
    def call(*args, &block)
    HIDDEN_METHODS <> “asdf”.upcase
    => “upcased!: ASDF”
    >> real_upcase = Method::HIDDEN_METHODS.first
    => #
    >> real_upcase.bind(“foobar”).call
    => “FOOBAR”

    Comment by Pat Maddox — October 29, 2008 @ 10:16 pm

  2. hrm, comment fail. You can find it at http://pastie.org/303583

    Comment by Pat Maddox — October 29, 2008 @ 10:18 pm

  3. nice, pwned, lemme think about that…

    Comment by coderrr — October 29, 2008 @ 10:20 pm

  4. [...] method then someone could still access the original and pass it an array. So we have to use a “secure” version of alias method chaining. Essentially, we capture the method object of the original method in the [...]

    Pingback by Ridiculous ruby meta programming hack « coderrr — February 21, 2009 @ 2:55 pm


RSS feed for comments on this post. TrackBack URI

Leave a comment

Blog at WordPress.com.