c0r3dump CTF writeups

CTF writeups by c0r3dump.

View on GitHub

Faust CTF 2022 - Digital Seconds Ago

Description

The challenge consists of a single ELF binary called digital-seconds-ago, which runs as a systemd service.

Analysis of the binary

After analyzing the binary, you can see that it uses a database, which is created via the following SQL command:

CREATE TABLE IF NOT EXISTS users(uid INTEGER PRIMARY KEY, name TEXT, pubkey  TEXT, profile TEXT);

After the database is created, the service provides the following actions:

Where are the flags?

The flags are in the profile field of the database. In each round, the organizers register a new user with the profile being the flag.

Getting the flags

If you successfully login, then the profile of that user is printed; but in order to log in, you need to find a vulnerability in the cryptographic system that checks the signature. This was the harder way to solve a challenge, but there was a shortcut hidden in the binary.

The main(0x22d0) function reads a line, checks if it matches one of the actions mentioned before, and if it does, it calls the handler function for that action. However, there is an extra check, which is in a function called by main, let’s call it check_backdoor(0x3810). Here is the decompiled version of the function:

undefined8 check_backdoor(char *command)

{
  long lVar1;
  size_t command_len;
  undefined8 uVar2;
  undefined8 uVar3;
  long in_FS_OFFSET;
  
  lVar1 = *(long *)(in_FS_OFFSET + 0x28);
  command_len = strlen(command);
  uVar3 = 0;
  uVar2 = 0;
  if ((((((command_len == 0xc) && (*command != command[1])) && (command[2] != command[5])) &&
       ((uVar3 = uVar2, command[3] != command[4] && (*command == command[0xb])))) &&
      ((command[1] == command[10] && ((command[2] == command[9] && (command[3] == command[8]))))))
     && (command[4] == command[7])) {
    if (command[5] == command[6]) {
      puts("... ok, but only 2 past seconds");
      uVar3 = 1;
    }
    else {
      uVar3 = 0;
    }
  }
  if (lVar1 == *(long *)(in_FS_OFFSET + 0x28)) {
    return uVar3;
  }
                    /* WARNING: Subroutine does not return */
  __stack_chk_fail();
}

We can see that this function returns 1 if the following conditions are met:

It is really easy to come up with a solution, for example: "abcdeffedcba".

Looking back at the main function, we see that if we pass the check, it will call another function:

      iVar1 = check_backdoor(input);
      if (iVar1 != 0) {
        do_backdoor();
        goto LAB_001023b0;
      }

The do_backdoor(0x38c0) function runs the following SQL query:

"SELECT name, profile FROM users ORDER BY uid DESC LIMIT 2"

It selects the last two entries in the database (ordered by uid), and prints their name and profile. Since the profile contains the flag, we can just read it.

Summary

The solution is really simple: you just have to send "abcdeffedcba", and then you get two flags.

Patching the backdoor

Here is the relevant part of the assembly code of the main function:

            0x000023ff      4889ef         mov rdi, rbp
            0x00002402      e809140000     call check_backdoor
            0x00002407      85c0           test eax, eax
        ┌─< 0x00002409      7545           jne 0x2450                   ; here we jump to the backdoor, if the check succeeded
        │   0x0000240b      83c301         add ebx, 1
        │   0x0000240e      4983c710       add r15, 0x10
        │   0x00002412      83fb06         cmp ebx, 6
        │   0x00002415      75d4           jne 0x23eb
        │   0x00002417      488d3d511f00.  lea rdi, str.unknown_command
        │   0x0000241e      e85dfcffff     call sym.imp.puts
        │   0x00002423      eb8b           jmp 0x23b0
        │   0x00002425      0f1f00         nop dword [rax]
        │   0x00002428      488d3d2c1f00.  lea rdi, str.fail_to_get_command
        │   0x0000242f      e84c030000     call 0x2780
        │   0x00002434      eba8           jmp 0x23de
        │   0x00002436      662e0f1f8400.  nop word cs:[rax + rax]
        │   0x00002440      4863db         movsxd rbx, ebx
        │   0x00002443      48c1e304       shl rbx, 4
        │   0x00002447      ff541c08       call qword [rsp + rbx + 8]
        │   0x0000244b      e960ffffff     jmp 0x23b0
        └─> 0x00002450      31c0           xor eax, eax
            0x00002452      e869140000     call do_backdoor
            0x00002457      e954ffffff     jmp 0x23b0`

There are many ways to fix it, e.g. replace the call to check_backdoor with xor eax, eax (and some nop-s), or you can modify the check_backdoor and do_backdoor functions, too. I chose to replace the conditinonal jump with two nop-s so that we never jump to call do_backdoor. Here is the modified code:

            0x000023ff      4889ef         mov rdi, rbp
            0x00002402      e809140000     call check_backdoor
            0x00002407      85c0           test eax, eax
            0x00002409      90             nop                          ; conditional jump replaced with nops, hence we never jump to `call do_backdoor`
            0x0000240a      90             nop
            0x0000240b      83c301         add ebx, 1
            0x0000240e      4983c710       add r15, 0x10
            0x00002412      83fb06         cmp ebx, 6
            0x00002415      75d4           jne 0x23eb
            0x00002417      488d3d511f00.  lea rdi, str.unknown_command
            0x0000241e      e85dfcffff     call sym.imp.puts
            0x00002423      eb8b           jmp 0x23b0
            0x00002425      0f1f00         nop dword [rax]
            0x00002428      488d3d2c1f00.  lea rdi, str.fail_to_get_command
            0x0000242f      e84c030000     call 0x2780
            0x00002434      eba8           jmp 0x23de
            0x00002436      662e0f1f8400.  nop word cs:[rax + rax]
            0x00002440      4863db         movsxd rbx, ebx
            0x00002443      48c1e304       shl rbx, 4
            0x00002447      ff541c08       call qword [rsp + rbx + 8]
            0x0000244b      e960ffffff     jmp 0x23b0
            0x00002450      31c0           xor eax, eax
            0x00002452      e869140000     call do_backdoor
            0x00002457      e954ffffff     jmp 0x23b0