Revving Up

Description:
Clam wrote a program for his school's cybersecurity club's first rev lecture! Can you get it to give you the flag?
You can find it at /problems/2020/revving_up on the shell server,
which you can access via the "shell" link at the top of the site.

Hint : 
Try some google searches for "how to run a file in linux" or "bash for beginners".

File:
revving_up

Author: 
aplet123

In this challenge we are given the revving_up binary file.

❯ file revving_up
revving_up: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=e9a5285cc0d71320a73e27870a35a98efbe01a87, not stripped

❯ ./revving_up
Congratulations on running the binary!
Now there are a few more things to tend to.
Please type "give flag" (without the quotes).
give flag
Good job!
Now run the program with a command line argument of "banana" and you'll be done!
❯ 

When we run the program we are asked to enter the input ‘give flag’. But in the next instruction we are asked to add the ‘banana’ argument when executing the program. The following is the pseudocode, I use radare2 with r2ghidra-dec.

iVar2 = sym.imp.strcmp(&s1, "give flag");
    if (iVar2 == 0) {
        sym.imp.puts("Good job!");
        if ((int32_t)argc < 2) {
            sym.imp.puts("Now run the program with a command line argument of \"banana\" and you\'ll be done!");
            uVar4 = 1;
        } else {
            iVar2 = sym.imp.strcmp(argv[1], "banana");
            if (iVar2 == 0) {
                sym.imp.puts("Well I think it\'s about time you got the flag!");
                sym.print_flag();
                uVar4 = 0;
            } else {
                sym.imp.printf("You provided \"%s\", not \"banana\". Please try again.\n", argv[1]);
                uVar4 = 1;
            }
        }

As you can see, on the iVar2 variable there is strcmp (argv [1], 'banana'); As we mentioned earlier we need to add an argument to the argument when executing the program. Now run the program on the given server ssh.

team5385@actf:/problems/2020/revving_up$ ./revving_up banana
Congratulations on running the binary!
Now there are a few more things to tend to.
Please type "give flag" (without the quotes).
give flag
Good job!
Well I think it's about time you got the flag!
actf{g3tting_4_h4ng_0f_l1nux_4nd_b4sh}

FLAG : actf{g3tting_4_h4ng_0f_l1nux_4nd_b4sh}


Windows of Opportunity

Description:
Clam's a windows elitist and he just can't stand seeing all of these linux challenges!
So, he decided to step in and create his own rev challenge with the "superior" operating system.

Hint : 
You can probably solve it just by looking at the disassembly.

File:
windows_of_opportunity.exe

Author: 
aplet123

In the following challenge we are given the file windows_of_opportunity.exe.

❯ file windows_of_opportunity.exe
windows_of_opportunity.exe: PE32+ executable (console) x86-64, for MS Windows

❯ wine windows_of_opportunity.exe
Welcome to the superior rev challenge compiled for the superior operating system!
What's the superior flag for this superior rev challenge?
give flag
Your flag is way too different from my superior flag!

As Hint said, let’s look at the disassembly code. Following is the disassembly code of radare2.

...
│           0x00401603      ffd0           call rax
│           0x00401605      4989c0         mov r8, rax
│           0x00401608      ba80000000     mov edx, 0x80               ; 128 ; FILE *stream
│           0x0040160d      488d0d8c1a00.  lea rcx, [0x004030a0]       ; "actf{ok4y_m4yb3_linux_is_s7ill_b3tt3r}"
│           0x00401614      e8c7160000     call sym.fgets              ; char *fgets(char *s, int size, FILE *stream)
│           0x00401619      488d0d801a00.  lea rcx, [0x004030a0]       ; "actf{ok4y_m4yb3_linux_is_s7ill_b3tt3r}"
│           0x00401620      e87b160000     call sym.strlen             ; size_t strlen(const char *s)
│           0x00401625      83e801         sub eax, 1
│           0x00401628      8945f8         mov dword [var_8h], eax
│           0x0040162b      8b45f8         mov eax, dword [var_8h]
│           0x0040162e      4863d0         movsxd rdx, eax
│           0x00401631      488d05681a00.  lea rax, [0x004030a0]       ; "actf{ok4y_m4yb3_linux_is_s7ill_b3tt3r}"
│           0x00401638      c6040200       mov byte [rdx + rax], 0
│           0x0040163c      837df826       cmp dword [var_8h], 0x26
│       ┌─< 0x00401640      7416           je 0x401658
│       │   0x00401642      488d0d4f2a00.  lea rcx, str.Your_flag_is_way_too_different_from_my_superior_flag ; 0x404098 ; "Your flag is way too different from my superior flag!"
│       │   0x00401649      e862160000     call sym.puts               ; int puts(const char *s)
│       │   0x0040164e      b901000000     mov ecx, 1
│       │   0x00401653      e890160000     call sym.exit
│       │   ; CODE XREF from sym.main @ 0x401640
...

[0x00401500]> px 0x26 @ 0x004030a0
- offset -   0 1  2 3  4 5  6 7  8 9  A B  C D  E F  0123456789ABCDEF
0x004030a0  6163 7466 7b6f 6b34 795f 6d34 7962 335f  actf{ok4y_m4yb3_
0x004030b0  6c69 6e75 785f 6973 5f73 3769 6c6c 5f62  linux_is_s7ill_b
0x004030c0  3374 7433 727d                           3tt3r}

We can immediately see the flag very clearly. The flag is located at memory address 0x004030a0.

❯ wine windows_of_opportunity.exe
Welcome to the superior rev challenge compiled for the superior operating system!
What's the superior flag for this superior rev challenge?
actf{ok4y_m4yb3_linux_is_s7ill_b3tt3r}
Oh wow a fellow windows user!

FLAG : actf{ok4y_m4yb3_linux_is_s7ill_b3tt3r}


Taking Off

Description:
So you started revving up, but is it enough to take off? Find the problem in /problems/2020/taking_off/ in the shell server.

Hint : 
You should look into tools like GHIDRA, *gdb*, and *objdump*.

File:
taking_off

Author: 
aplet123

In the following challenge we are given the binary taking_off file.

❯ file taking_off
taking_off: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=fc4deaf2c2da6fdaf4cb7bc1e83d4f1372720832, not stripped

❯ ./taking_off
So you figured out how to provide input and command line arguments.
But can you figure out what input to provide?
Make sure you have the correct amount of command line arguments!
...
...
❯ ./taking_off aa bb cc dd
So you figured out how to provide input and command line arguments.
But can you figure out what input to provide?
Don't try to guess the arguments, it won't work.

Almost the same as the previous challenge, we are asked to execute the program with arguments. As you can see after we add as many as four arguments the program displays different outputs. Let’s look at the pseudocode.

[0x00400790]> pdg@main
...
	if (argc == 5) {
		sym.string_to_int(argv[1], (int64_t)&var_ach);
		sym.string_to_int(argv[2], (int64_t)&var_ach + 4);
		sym.string_to_int(argv[3], (int64_t)&var_a4h);
...

[0x00400790]> pdg@sym.string_to_int

void sym.string_to_int(char *arg1, int64_t arg2)
{
    int64_t var_10h;
    char *s;
    
    sym.imp.sscanf(arg1, 0x400c8d, arg2);
    return;
}

As we can see there is a function string_to_int (char * arg1, int64_t arg2), where it transforms our argument into an integer.

[0x00400790]> pdg@main
...
    iVar2 = sym.is_invalid((uint64_t)(uint32_t)var_ach);	
    if (iVar2 == 0) {
        iVar2 = sym.is_invalid((uint64_t)var_ach._4_4_);
        if (iVar2 == 0) {
            iVar2 = sym.is_invalid((uint64_t)var_a4h);
            if (iVar2 == 0) {
                if (var_a4h + var_ach._4_4_ * 100 + (uint32_t)var_ach * 10 == 0x3a4) {
                    iVar2 = sym.imp.strcmp(argv[4], "chicken");
...
[0x00400790]> pdg@sym.is_invalid

undefined8 sym.is_invalid(undefined8 arg1)
{
    undefined8 uVar1;
    undefined8 var_4h;
    
    if (((int32_t)arg1 < 0) || (9 < (int32_t)arg1)) {
        uVar1 = 1;
    } else {
        uVar1 = 0;
    }
    return uVar1;
}

Next after changing our argument there is also the sym.is_invalid function (undefined8 arg1). The function aims to check whether our argument is smaller than 0 or greater than 9 then returns the value of uVar1.

...
    if (var_a4h + var_ach._4_4_ * 100 + (uint32_t)var_ach * 10 == 0x3a4) {
        iVar2 = sym.imp.strcmp(argv[4], "chicken");
        if (iVar2 == 0) {
            sym.imp.puts("Well, you found the arguments, but what\'s the password?");
...

Next there is the condition if (var_a4h + var_ach._4_4_ * 100 + (uint32_t)var_ach * 10 == 0x3a4). our three arguments after calculated the result must be equal to 932. And in the next line our fourth argument is chicken.

Here is a simple code to get to our three arguments.

>>>  for i in range(0, 10):
...		for j in range(0, 10):
...		     for k in range(0, 10):
...			  if 100 * j + 10 * i + k == 932:
...			       print (i, j ,k)
...			       break
... 
3 9 2

So our argument is 3 9 2 chicken. Let’s look at the following lines of code.

...
    sym.imp.puts("Well, you found the arguments, but what\'s the password?");
    sym.imp.fgets(&s, 0x80, _reloc.stdin);
    var_98h = (char *)sym.imp.strchr(&s, 10);
    if (var_98h != (char *)0x0) {
        *var_98h = '\0';
    }
    var_9ch = sym.imp.strlen(&s);
    var_a0h = 0;
    while (var_a0h <= var_9ch) {
        if ((*(uint8_t *)((int64_t)&s + (int64_t)var_a0h) ^ 0x2a) != "ZFOKYO\nMC\\O\nLFKM*"[var_a0h]) {
            sym.imp.puts("I\'m sure it\'s just a typo. Try again.");
            uVar3 = 1;
            goto code_r0x00400bc7;
        }
        var_a0h = var_a0h + 1;
    }
    sym.imp.puts("Good job! You\'re ready to move on to bigger and badder rev!");
    sym.print_flag();
    uVar3 = 0;
    goto code_r0x00400bc7;
...

In this section we are asked to enter a password, each character will be xor with 0x2a and the result must be the same as ZFOKYO\nMC\\O\nLFKM*. Let’s make a simple code to guess the password.

>>> from string import printable
>>> password = ''
>>> res = "ZFOKYO\nMC\\O\nLFKM*"
>>> for i in res:
...     for ch in printable:
...             if chr(ord(ch)^0x2a) == i:
...                     password += ch
... 
>>> password
'please give flag'

Now that we know the password, it’s time to prove. Let’s run the program from the ssh server.

team5385@actf:~$ cd /problems/2020/taking_off/
team5385@actf:/problems/2020/taking_off$ ./taking_off 3 9 2 chicken
So you figured out how to provide input and command line arguments.
But can you figure out what input to provide?
Well, you found the arguments, but what's the password?
please give flag
Good job! You're ready to move on to bigger and badder rev!
actf{th3y_gr0w_up_s0_f4st}
team5385@actf:/problems/2020/taking_off$ 

FLAG : actf{th3y_gr0w_up_s0_f4st}


Masochistic Sudoku

Description:
Clam's tired of the ease and boredom of traditional sudoku. Having just one solution that can be determined via a simple online sudoku solver isn't good enough for him.
So, he made masochistic sudoku! Since there are no hints, there are around 6*10^21 possible solutions but only one is actually accepted!
Find it on the shell server at /problems/2020/masochistic_sudoku/.

File:
masochistic_sudoku

Author: 
aplet123

In the following problem we are given the masochistic_sudoku binary file.

❯ file masochistic_sudoku
masochistic_sudoku: ELF 64-bit LSB executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, for GNU/Linux 3.2.0, BuildID[sha1]=d812dc3b76f3241ad03e6018d45d1d18665ce1dd, not stripped
❯ ./masochistic_sudoku
+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+
|   |   |   |   |   |   |   |   |   |
+---+---+---+---+---+---+---+---+---+

This challenge is very interesting, so I need IDA to debug. When after opening it with IDA there are very many instructions, but there is a function that interests me, the check_flag function.

check_flag

Following is the pseudocode of the check_flag function.

...
...
  v0 = gen_value(0LL, 0LL, (unsigned int)board[0]);
  assert(v0 == 1754831936);
  v1 = gen_value(0LL, 4LL, (unsigned int)dword_603170);
  assert(v1 == 1322670498);
  v2 = gen_value(0LL, 6LL, (unsigned int)dword_603178);
  assert(v2 == 2075469024);
  v3 = gen_value(0LL, 7LL, (unsigned int)dword_60317C);
  assert(v3 == 1924349448);
  v4 = gen_value(1LL, 2LL, (unsigned int)dword_60318C);
  assert(v4 == 1737338032);
  v5 = gen_value(1LL, 4LL, (unsigned int)dword_603194);
  assert(v5 == 382094521);
  v6 = gen_value(1LL, 5LL, (unsigned int)dword_603198);
  assert(v6 == 2003484635);
  v7 = gen_value(1LL, 6LL, (unsigned int)dword_60319C);
  assert(v7 == 1224890436);
  v8 = gen_value(2LL, 4LL, (unsigned int)dword_6031B8);
  assert(v8 == 613863398);
  v9 = gen_value(2LL, 5LL, (unsigned int)dword_6031BC);
  assert(v9 == 2131248558);
  v10 = gen_value(2LL, 7LL, (unsigned int)dword_6031C4);
  assert(v10 == 1855404474);
...
...
int __fastcall gen_value(int a1, int a2, int a3)
{
  srand(13 * ((100 * a1 + 10 * a2 + a3) ^ 0x2A) % 10067);
  return rand();
}

And there is also the gen_value function. To understand what happens to the check_flag function, let’s place breakpoints in the check_flag function and press the F9 key until we hit breakpoint.

break_check_flag

Following is the technique that I use to find out the value of each box that will go through the gen_value function:

  1. Fill every 6 squares with numbers 1 through 9

  2. View changes that occur in the .bss stack

  3. Repeat the first and second steps, until the last 6 squares

sudoku

Sudoku

Following are my observations, the numbers represent that the values ​​contained in the box will be used as parameters in the gen_value function.

Sudoku

Let’s create a script to find a suitable value to match in each box that we find.

from ctypes import *

libc = cdll.LoadLibrary("/lib/x86_64-linux-gnu/libdl.so.2")

def gen_value(a1,a2,parm):
    for num in range(1,10):
        libc.srand((13 * ((100 * a1 + 10 * a2 + num) ^ 42) % 10067))
        res = libc.rand()
        if res == parm:
            return num
    return None

def main():
    val = [1754831936, 1322670498, 2075469024, 1924349448, 1737338032, 382094521, 2003484635, 1224890436, 613863398, 2131248558, 1855404474, 203716718, 2132752585, 54194304, 548400147, 2040844259, 348846481, 712829567, 198917626, 1999818593, 47214827, 117615071, 1948118465, 345110140, 2113220118, 443730372, 2136198019, 1427855150, 323649682, 1443247958]
    
    guess = [0 for _ in range(30)]
    guess[0] = gen_value(0,0,val[0])
    guess[1] = gen_value(0,4,val[1])
    guess[2] = gen_value(0,6,val[2])
    guess[3] = gen_value(0,7,val[3])
    guess[4] = gen_value(1,2,val[4])
    guess[5] = gen_value(1,4,val[5])
    guess[6] = gen_value(1,5,val[6])
    guess[7] = gen_value(1,6,val[7])
    guess[8] = gen_value(2,4,val[8])
    guess[9] = gen_value(2,5,val[9])
    guess[10] = gen_value(2,7,val[10])
    guess[11] = gen_value(3,0,val[11])
    guess[12] = gen_value(3,2,val[12])
    guess[13] = gen_value(4,0,val[13])
    guess[14] = gen_value(4,1,val[14])
    guess[15] = gen_value(4,7,val[15])
    guess[16] = gen_value(4,8,val[16])
    guess[17] = gen_value(5,6,val[17])
    guess[18] = gen_value(5,8,val[18])
    guess[19] = gen_value(6,1,val[19])
    guess[20] = gen_value(6,3,val[20])
    guess[21] = gen_value(6,4,val[21])
    guess[22] = gen_value(7,2,val[22])
    guess[23] = gen_value(7,3,val[23])
    guess[24] = gen_value(7,4,val[24])
    guess[25] = gen_value(7,6,val[25])
    guess[26] = gen_value(8,1,val[26])
    guess[27] = gen_value(8,2,val[27])
    guess[28] = gen_value(8,4,val[28])
    guess[29] = gen_value(8,8,val[29])
    for i,j in enumerate(guess):
        print(f'Guess: {j} --> Val: {val[i]}')
      
if __name__ == '__main__':
    main()

Output :

Guess: 1 --> Val: 1754831936    // (1,1)
Guess: 6 --> Val: 1322670498    // (2,2)
Guess: 8 --> Val: 2075469024    // (3,1)
Guess: 5 --> Val: 1924349448    // (3,2)
Guess: 5 --> Val: 1737338032    // (1,5)
Guess: 8 --> Val: 382094521     // (3,4)
Guess: 3 --> Val: 2003484635    // (2,6)
Guess: 1 --> Val: 1224890436    // (3,4)
Guess: 1 --> Val: 613863398     // (2,8)
Guess: 2 --> Val: 2131248558    // (2,9)
Guess: 9 --> Val: 1855404474    // (3,8)
Guess: 9 --> Val: 203716718     // (4,1)
Guess: 7 --> Val: 2132752585    // (4,3)
Guess: 5 --> Val: 54194304      // (4,4)
Guess: 3 --> Val: 548400147     // (4,5)
Guess: 8 --> Val: 2040844259    // (6,5)
Guess: 9 --> Val: 348846481     // (6,6)
Guess: 3 --> Val: 712829567     // (6,7)
Guess: 5 --> Val: 198917626     // (6,9)
Guess: 4 --> Val: 1999818593    // (7,2)
Guess: 6 --> Val: 47214827      // (8,1)
Guess: 2 --> Val: 117615071     // (8,2)
Guess: 6 --> Val: 1948118465    // (7,6)
Guess: 1 --> Val: 345110140     // (8,4)
Guess: 9 --> Val: 2113220118    // (8,5)
Guess: 7 --> Val: 443730372     // (9,4)
Guess: 2 --> Val: 2136198019    // (7,8)
Guess: 1 --> Val: 1427855150    // (7,9)
Guess: 3 --> Val: 323649682     // (8,8)
Guess: 4 --> Val: 1443247958    // (9,9)

After we get the value, then enter each value into the box that we found earlier.

Found_value

Now is the time to fill in the empty boxes. There are three ways to solve it.

  1. Using z3-Solver.

  2. Using Sudoku Solver.

  3. Using our brain.

I prefer to use number 2.

final

Then press q.

final

Now we prove it on ssh server.

team5385@actf:~$ cd /problems/2020/masochistic_sudoku/
team5385@actf:/problems/2020/masochistic_sudoku$ ./masochistic_sudoku 
Wow you're good at sudoku!
actf{sud0ku_but_f0r_pe0ple_wh0_h4te_th3mselves}
team5385@actf:/problems/2020/masochistic_sudoku$ 

FLAG : actf{sud0ku_but_f0r_pe0ple_wh0_h4te_th3mselves}