Skip to main content

0x06 - crackme02 by noracodes

binary crackme02.64
sha256 7bfdbae26d4295bd934a0bc7ddc55a1bfa62fd4e08e43c4fc2cf106480c7fa78

This short crackme is another from noracodes collection. I built it on my own machine, so the SHA256 above will likely be different for you. Running file reveals that this is a 64-bit, non-stripped binary like we’ve seen before:

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

Looking at strings with rabin2shows us something that looks suspicious:

❯ rabin2 -z ./crackme02.64
[Strings]
nth paddr      vaddr      len size section type  string
―――――――――――――――――――――――――――――――――――――――――――――――――――――――
0   0x00002004 0x00002004 26  27   .rodata ascii Need exactly one argument.
1   0x0000201f 0x0000201f 23  24   .rodata ascii No, %s is not correct.\n
2   0x00002037 0x00002037 9   10   .rodata ascii password1
3   0x00002041 0x00002041 20  21   .rodata ascii Yes, %s is correct!\n

Unfortunately, that’s not what we’re looking for:

❯ ./crackme02.64 password1
No, password1 is not correct.

I then run nm to see what symbols we have:


❯ nm ./crackme02.64
000000000000037c r __abi_tag
0000000000004020 B __bss_start
0000000000004020 b completed.0
                 w __cxa_finalize@GLIBC_2.2.5
0000000000004010 D __data_start
0000000000004010 W data_start
0000000000001090 t deregister_tm_clones
0000000000001100 t __do_global_dtors_aux
0000000000003dd8 d __do_global_dtors_aux_fini_array_entry
0000000000004018 D __dso_handle
0000000000003de0 d _DYNAMIC
0000000000004020 D _edata
0000000000004028 B _end
00000000000011d0 T _fini
0000000000001140 t frame_dummy
0000000000003dd0 d __frame_dummy_init_array_entry
000000000000212c r __FRAME_END__
0000000000003fe8 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000002058 r __GNU_EH_FRAME_HDR
0000000000001000 T _init
0000000000002000 R _IO_stdin_used
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
                 U __libc_start_main@GLIBC_2.34
0000000000001149 T main
                 U printf@GLIBC_2.2.5
                 U puts@GLIBC_2.2.5
00000000000010c0 t register_tm_clones
0000000000001060 T _start
0000000000004020 D __TMC_END__

There is an exported main function, so lets see what its doing:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
undefined8 main(int argc,long argv)

{
  char pw_index;
  undefined8 ret_val;
  long i;
  char current_character;
  
  if (argc == 2) {
    pw_index = 'p';
    i = 0;
    do {
      current_character = *(char *)(*(long *)(argv + 8) + i);
      if (current_character == '\0') break;
      if (pw_index + -1 != (int)current_character) {
        printf("No, %s is not correct.\n");
        return 1;
      }
      pw_index = "password1"[i + 1];
      i = i + 1;
    } while (pw_index != '\0');
    printf("Yes, %s is correct!\n");
    ret_val = 0;
  }
  else {
    puts("Need exactly one argument.");
    ret_val = 0xffffffff;
  }
  return ret_val;
}

I’ve changed the variable names in the listing above to make sense. This is a normal looking function at first. It makes sure that we at least pass one argument, and then it takes that argument and does some validation to apparently compare it to a hard-coded string.

If you look closer, it seems like it’s comparing each character to itself - 1. So the character p for example, would need to be o in our argument, and so on. There’s a problem though, which is that the program doesn’t enforce any kind of length requirement on the key. So we can just give it the character o, and it passes:

❯ ./crackme02.64 o
Yes, o is correct!

That’s all for now!