Skip to content

Instantly share code, notes, and snippets.

@dissolved
Last active August 29, 2015 14:01
Show Gist options
  • Save dissolved/09e9cc5e774985b33366 to your computer and use it in GitHub Desktop.
Save dissolved/09e9cc5e774985b33366 to your computer and use it in GitHub Desktop.
What is the most destructive thing you could assign to params?
def string_to_camelcase(string)
string.gsub(/\s+/, "").camelize # camelize defined in Rails API
end
the_class = eval string_to_camelcase(params[:user_input])
the_object = the_class.new(params[:more_user_input],params[:even_more_user_input])
@dissolved
Copy link
Author

This seems like very dangerous code. Yet calling string_to_camelcase prior to eval'ing the string is making it hard for me to dream up something destructive. I'm sure I'm missing all kinds of devious things, help me think of one to win my case.

@cupakromer
Copy link

# decodes to `ls`, but imagine `rm -rf /`
input = "Kernel.eval(Base64.decode64('YGxzYA==\n'))"
string_to_camelcase(input)

@dissolved
Copy link
Author

Great! I knew it was dangerous. I guess I'm confused at why String#camelize leaves the .eval and .decode64 as lowercase... but it obviously does! Thanks again!

@cupakromer
Copy link

@dissolved I believe it's because it's not an underscore

@dissolved
Copy link
Author

And now that you've opened my eyes. I'm thinking of much worse that just erasing the app server!

@cupakromer
Copy link

Well yeah, you've let remote code execute on the server. They can do anything and everything.

@cupakromer
Copy link

My question would be what's the intent? Are you looking for say a constant or class name? If so eval is not what you want.

@cupakromer
Copy link

Ruby is very whitespace forgiving too: ;puts'foobar' is valid Ruby.

So combine all that with base64 encoding/decoding, URL encoding/decoding, YAML, etc. you're in for a world of pain.

@dissolved
Copy link
Author

Yes, the code wanted to call new on a class name. I agree it isn't the way to do it, I didn't write it! I don't have time to refactor this bit now either, but I did put a sanitizer in: render_404 and return unless TheClass.names.include?(params[:user_input]). So at least arbitrary code can't be executed.

@cupakromer
Copy link

I suggest adding constantize to that code. This should protect you a bit better, though I'm not 100% sure you still aren't vulnerable to something.

Something like:

def string_to_class(string)
  # .constantize will raise a NameError if the class is not defined
  string.gsub(/\s+/, "").camelize.constantize
end

the_class = string_to_class(params[:user_input])
the_object = the_class.new(params[:more_user_input],params[:even_more_user_input])

@cupakromer
Copy link

Also, I may try to impose a whitelist approach of what I want to allow:

string.gsub(/[^\w:]+/, '').camelize.constantize)

@dissolved
Copy link
Author

Agreed, no need for eval at all here... constantize gets the job done with less risk. Great comments. Thanks again!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment