CrossCTF 2018 Final RE Write-up Collection

Introduction

This post will contain the 2 RE challenges in CrossCTF 2018 final: perfect and Rochefort_6.

Perfect number

The challenge is a bit interesting, the critical part of the challenge is given below:

  __gmpz_init(&v8, a2, a3);
  __gmpz_set_ui(&v8, 0LL);
  __gmpz_init(&v9, 0LL, v3);
  __gmpz_set_ui(&v9, 0LL);
  __gmpz_init(&v11, 0LL, v4);
  __gmpz_set_ui(&v11, 0LL);
  __gmpz_init(&v13, 0LL, v5);
  __gmpz_set_ui(&v13, 0LL);
  __gmpz_init(&v14, 0LL, v6);
  __gmpz_set_ui(&v14, 2LL);
  __gmpz_mul_2exp(&v14, &v14, 212LL);
  printf("Eschucha? ", &v14);
  __isoc99_scanf("%1023s", &v15);
  if ( (unsigned int)__gmpz_set_str(&v11, &v15, 10LL) )
    __assert_fail("flag == 0", "perfect.c", 0x20u, "main");
  __gmpz_sub_ui(&v11, &v11, 1LL);
  if ( (unsigned int)__gmpz_set_str(&v13, &v15, 10LL) )
    __assert_fail("flag == 0", "perfect.c", 0x23u, "main");
  while ( v12  0 )
  {
    __gmpz_mod(&v9, &v13, &v11);
    if ( v10 >= 0 && v10  0 )
  {
    printf("random.seed(", &v14);
    __gmpz_out_str(_bss_start, 10LL, &v8);
    puts(")");
    puts("k = \"\".join([chr(random.randint(0, 255)) for i in range(35)])");
    puts("xor(k, 754e26ccd4b1bfafb3ffbdaa748780b7f0e0c3ae9acc3c008670f0fafd34f8ffa596db)");
  }

I spend some time trying to understand the meaning of the statement if ( v10 >= 0 && v10 <= 0 ). After I realise that the statement means that v11 is a divisor of v13, I understand that it tries to find the perfect number in mathematics. So this challenge tries to find the perfect number, which is larger than 2^212. Since the perfect number must satisfy 2^(p-1) * (2^p – 1).

The final exploit:

import random

random.seed(14474011154664524427946373126085988481573677491474835889066354349131199152128)

def xor(a, b):
    r = ''
    for i in range(0, 35):
        r += chr( ord(a[i]) ^ ord(b[i]) )
    return r

k = ''.join([chr(random.randint(0, 255)) for i in range(35)])

print xor(k, '754e26ccd4b1bfafb3ffbdaa748780b7f0e0c3ae9acc3c008670f0fafd34f8ffa596db'.decode('hex'))

Rochefort 6

This challenge will first generate a 2-byte random number. Then the challenge will read a string input. For each byte of the string, it will try to use the following function to add. The final result must be equal to the random number.

unsigned int fun(int a1)
{
  return (unsigned int)(0x43726F73 * a1 + 0x73435446);
}

We use the following to the exploit.

from pwn import *

DEBUG = int(sys.argv[1]);

if(DEBUG == 0):
    r = remote("ctf.pwn.sg", 16667);
elif(DEBUG == 1):
    r = process("./tower1");
elif(DEBUG == 2):
    r = process("./tower1");
    gdb.attach(r, '''source ./script''');

def fun(intNum):
    tmp = 0x43726f73 * intNum + 0x73435446;
    ans = tmp & 0xffffffff;
    return ans;

target = 0x1585;
number = 0x100;
retVal = "";

def backtrack(level, curValue, ans, target):
    global retVal;
    if(level == 0x20):
        return False;
    if((curValue & 0xffff) == target):
        log.info(ans);
        #for i in range(0, len(ans)):
        #    print("0x%x" % ord(ans[i]));
        retVal = ans;
        print("FOUND");
        return True;
    for i in range(1, 0xff):
        flag = backtrack(level+1, fun(curValue + i), ans + chr(i), target);
        if(flag):
            return True;

def halt():
    while(True):
        log.info(r.recvline());

def exploit():
    r.recvuntil(";)");
    r.sendline("6");
    for i in range(0, 20):
        r.recvuntil(":P\n");
        number = r.recvline();
        num = int(number);
        log.info("0x%x: %d" % (num, num));
        backtrack(0, 0, "", num);
        r.recvuntil("Your turn:");
        r.sendline(retVal + "\x00");
    halt();

exploit();

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.