Casino King On Line, an illegal gambling site Analysis of their poker games showed that the games were all rigged in favour of the house We decided to get his money back. And then some.
there’s a middleware called ActionDispatch::Middleware::ParamsParser It uses the Content-Type header to determine how it should parse the request body Defaults to parsing JSON and XML via text/json and text/xml Content-Type headers.
called init_with or yaml_initialize on the class that it’s unserialising and calls that method if it exists Otherwise it sets the instance variables with instance_variable_set We found a class called ActiveSupport::Deprecation::DeprecatedI nstanceVariableProxy which was designed to emit a deprecation warning before forwarding the method to the original object
m unless m =~ /^__|^object_id$/ end def method_missing(called, *args, &block) warn caller, called, args target.__send__(called, *args, &block) end end class DeprecatedInstanceVariableProxy < DeprecationProxy def target @instance.__send__(@method) end end end end
m unless m =~ /^__|^object_id$/ end def method_missing(called, *args, &block) warn caller, called, args target.__send__(called, *args, &block) end end class DeprecatedInstanceVariableProxy < DeprecationProxy def target @instance.__send__(@method) end end end end
m unless m =~ /^__|^object_id$/ end def method_missing(called, *args, &block) warn caller, called, args target.__send__(called, *args, &block) end end class DeprecatedInstanceVariableProxy < DeprecationProxy def target @instance.__send__(@method) end end end end
instance_variable_set Psych uses instance_variable_set when deserializing YAML into objects So we can’t create a DeprecatedInstanceVariableProxy from YAML :(
it can deserialize DeprecatedInstanceVariableProxy, even though instance_variable_set is missing. If we can get arbitrary Marshal.load, we can trivially achieve remote code execution.
of Rails by default that calls Marshal.load on any old data. This class also needs to be YAML deserializable. This is where Rack::Session::Abstract::SessionHash comes in. Since Rails depends on Rack, this class is loaded in every Rails app :) Let’s take a look
def has_key?(key) load_for_read! # ... end alias :key? :has_key? alias :include? :has_key? def load_for_read! load! if !loaded? && exists? end def load! @id, session = @by.send(:load_session, @env) end end
method to Marshal.load We control all the instance variables on the Rack session classes (we YAML’ed them after all) Therefore, we control the data being unmarshaled.
instance into params We can execute our own code when any Enumerable method is called. We can’t guarantee the user will call one of those methods. We’ve gotten this far, surely we can finish it off with an automatic RCE?
“@#{ivar}”, val end Gem.load_yaml fix_syck_default_key_in_requirements end def fix_syck_default_key_in_requirements Gem.load_yaml @requirements.each do |r| # ... end end end
“@#{ivar}”, val end Gem.load_yaml fix_syck_default_key_in_requirements end def fix_syck_default_key_in_requirements Gem.load_yaml @requirements.each do |r| # ... end end end Rack::Session::Abstract::SessionHash pwned
against the application We use the remote code execution exploit to prevent our withdrawal from being flagged for a high amount We hire Walter, amateur money launderer, third and final of the team The ’11’ in chendo’s 11 is in binary
data into smaller packets Packets get sent across the internets Sent packets must be ACKnowledged by the receiver before it’s considered sent. Think registered mail.
does its thing and tries to send back data Attacker’s network stack has fingers in its ears going “LALALALALALALALALA” and doesn’t respond Server retries sending the packets until connection times out
socket would block until the client reads from their end of the socket TCP buffers act as a staging area so your application doesn’t block when it writes (unless its full) Only gets drained when client sends an ACKnowledgement that it has received the data Used for retransmitting dropped packets
payload until it’s complete Read Buffer Write Buffer Read Buffer Write Buffer Read Buffer Write Buffer When complete, Nginx sends the request to unicorn Once request has been completed, the response is sent back to Nginx Nginx sends response back to the client
payload until it’s complete Read Buffer Write Buffer Read Buffer Write Buffer Read Buffer Write Buffer When complete, Nginx sends the request to unicorn Nginx’s read buffer fills and Unicorn’s response gets blocked and stalls. Soon gets killed by master Nginx tries to send response to client but no ACKs means write buffer doesn’t drain
= 32K/64K - upstream traffic read into this proxy_max_temp_file_size: 1024MB - upstream traffic written to disk if it doesn’t fit in memory buffers You’re probably safe. Unless turned proxy_buffering off for streaming requests/Comet/etc...
payload until it’s complete Read Buffer Read Buffer Write Buffer When complete, Nginx sends the request to unicorn The response data is streamed back to the client; Nginx does not buffer
to use Comet for games/etc Quick fix was to only disable buffering for the Comet URIs only Tired admins go home for the day Only to find out the next day that it was a distraction for a heist.
TCP stack: sudo ipfw add deny tcp from <target IP> 80 to me iplen 300-2000 Prevent our stack from telling server that we’ve closed the connection: sudo ipfw add deny tcp from me to <target IP> 80 tcpflags fin sudo ipfw add deny tcp from me to <target IP> 80 tcpflags rst