Skip to main content

0x03 - rev by helithumper

binary rev
sha256 2b704c51b17fe6ed1461f4068eb7d8824a7a66d37eae56dc9adcf0d2cbd0cce2

This is a very straightforward one by helithumper, called rev. It’s another 64-bit, non-stripped ELF:

❯ file rev
rev: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=e4dbcb1281821db359d566c68fea7380aeb27378, for GNU/Linux 3.2.0, not stripped
❯ nm rev
0000000000004010 B __bss_start
                 U calloc@@GLIBC_2.2.5
0000000000004010 b completed.7963
                 w __cxa_finalize@@GLIBC_2.2.5
0000000000004000 D __data_start
0000000000004000 W data_start
00000000000010c0 t deregister_tm_clones
0000000000001130 t __do_global_dtors_aux
0000000000003da0 d __do_global_dtors_aux_fini_array_entry
0000000000004008 D __dso_handle
0000000000003da8 d _DYNAMIC
0000000000004010 D _edata
0000000000004018 B _end
0000000000001334 T _fini
0000000000001170 t frame_dummy
0000000000003d98 d __frame_dummy_init_array_entry
00000000000021ec r __FRAME_END__
0000000000003f98 d _GLOBAL_OFFSET_TABLE_
                 w __gmon_start__
0000000000002080 r __GNU_EH_FRAME_HDR
0000000000001000 t _init
0000000000003da0 d __init_array_end
0000000000003d98 d __init_array_start
0000000000002000 R _IO_stdin_used
                 U __isoc99_scanf@@GLIBC_2.7
                 w _ITM_deregisterTMCloneTable
                 w _ITM_registerTMCloneTable
0000000000001330 T __libc_csu_fini
00000000000012d0 T __libc_csu_init
                 U __libc_start_main@@GLIBC_2.2.5
0000000000001175 T main
                 U puts@@GLIBC_2.2.5
00000000000010f0 t register_tm_clones
                 U __stack_chk_fail@@GLIBC_2.4
0000000000001090 T _start
                 U strlen@@GLIBC_2.2.5
0000000000004010 D __TMC_END__
00000000000011ea T validate

Since this binary isn’t stripped, we of course have some symbols available to us. There is a function called validatewhich sounds interesting, and we also have main:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
bool main(void)

{
  int bool_result;
  void *input_buffer;
  
  input_buffer = calloc(0x32,1);
  puts(&DAT_00102008);
  __isoc99_scanf(&format_string,input_buffer);
  bool_result = validate(input_buffer);
  if (bool_result == 0) {
    puts(&fail_message);
  }
  else {
    puts("Right this way...");
  }
  return bool_result == 0;
}

Note that I’ve already gone ahead and changed variable names to make things clearer. The validatefunction is where the key validation happens. It simply takes the input buffer and checks each byte against a static set of bytes:

 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
31
32
33
34
35
36
37
38
39
40
41
42
43
44
undefined8 validate(char *param_1)

{
  size_t input_length;
  undefined8 bool_result;
  long in_FS_OFFSET;
  int i;
  int expected_values [14];
  long stack_canary;
  
  stack_canary = *(long *)(in_FS_OFFSET + 0x28);
  expected_values[0] = 0x66;
  expected_values[1] = 0x6c;
  expected_values[2] = 0x61;
  expected_values[3] = 0x67;
  expected_values[4] = 0x7b;
  expected_values[5] = 0x48;
  expected_values[6] = 0x75;
  expected_values[7] = 0x43;
  expected_values[8] = 0x66;
  expected_values[9] = 0x5f;
  expected_values[10] = 0x6c;
  expected_values[0xb] = 0x41;
  expected_values[0xc] = 0x62;
  expected_values[0xd] = 0x7d;
  input_length = strlen(param_1);
  i = 0;
  do {
    if ((int)input_length <= i) {
      bool_result = 1;
LAB_001012b7:
      if (stack_canary != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
        __stack_chk_fail();
      }
      return bool_result;
    }
    if ((int)param_1[i] != expected_values[i]) {
      bool_result = 0;
      goto LAB_001012b7;
    }
    i = i + 1;
  } while( true );
}

I copied these bytes into a Go program to print out the flag:

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
package solver

import (
	"fmt"
)

var flag_bytes = []byte{
	0x66, 0x6c, 0x61, 0x67, 0x7b, 0x48, 0x75,
	0x43, 0x66, 0x5f, 0x6c, 0x41, 0x62, 0x7d}

func Solve() {
	for _, v := range flag_bytes {
			fmt.Printf("%c", v)
	}
	fmt.Println()
}

When we run the solver, the flag is returned:

❯ go run ./cmd/main.go
flag{HuCf_lAb}

And finally, test it against the actual binary:

❯ ./rev
Welcome to the Salty Spitoon™, How tough are ya?
flag{HuCf_lAb}
Right this way...