Quantcast
Channel: coderrr » security
Viewing all articles
Browse latest Browse all 10

Secure alias method chaining

$
0
0

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.



Viewing all articles
Browse latest Browse all 10

Trending Articles