-2

This is a reverse engineering challenge from a CTF event.

The clue/hint is: Well ol' chap, it seems we have ourselves another puzzle! I just can't picture what this one might be... Well, there's no time to waste shuffling about, we must begin!

It's an ELF file and I've used Ghidra, R2, IDA to look deeper at the assembly code but I'm not having any luck really understanding or interpreting what's going on.

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

My understanding so far, and I could be wrong, is that when running 'customary', it requires an argument in order to successfully extract and create 'sherlock'jpeg' from the data.ana file...I think.

When I grab the assembly for the main function(snippet below), I can see it's doing something with 'sherlock'jpeg' and further down is 'Exiting' but I'm not making too much sense of it.

            ; DATA XREF from entry0 @ 0x229d(r)
┌ 721: int main (int argc, char **argv, char **envp);
│           ; var int64_t var_18h @ rbp-0x18
│           ; var int64_t var_28h @ rbp-0x28
│           ; var signed int64_t var_2ch @ rbp-0x2c
│           ; var signed int64_t var_30h @ rbp-0x30
│           ; var signed int64_t var_34h @ rbp-0x34
│           ; var int64_t var_35h @ rbp-0x35
│           ; var int64_t var_60h @ rbp-0x60
│           ; var int64_t var_80h @ rbp-0x80
│           ; var int64_t var_88h @ rbp-0x88
│           ; var int64_t var_89h @ rbp-0x89
│           ; var int64_t var_8ah @ rbp-0x8a
│           ; var int64_t var_b0h @ rbp-0xb0
│           ; var int64_t var_b8h @ rbp-0xb8
│           ; var int64_t var_d0h @ rbp-0xd0
│           ; var int64_t var_100h @ rbp-0x100
│           0x00002739      55             push rbp
│           0x0000273a      4889e5         mov rbp, rsp
│           0x0000273d      4155           push r13
│           0x0000273f      4154           push r12
│           0x00002741      53             push rbx
│           0x00002742      4881ece80000.  sub rsp, 0xe8
│           0x00002749      488d8576ffff.  lea rax, [var_8ah]
│           0x00002750      4889c7         mov rdi, rax
│           0x00002753      e838f9ffff     call fcn.00002090
│           0x00002758      488d9576ffff.  lea rdx, [var_8ah]
│           0x0000275f      488d8550ffff.  lea rax, [var_b0h]
│           0x00002766      488d35115a00.  lea rsi, str.sherlock.jpeg  ; 0x817e ; "sherlock.jpeg"
│           0x0000276d      4889c7         mov rdi, rax
│           0x00002770      e82bfaffff     call method std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >::basic_string(char const*, std::allocator<char> const&) ; method.std::__cxx11::basic_string_char__std::char_traits_char___std::allocator_char___.basic_string_char_const__std::allocator_char__const_
│           0x00002775      488d8576ffff.  lea rax, [var_8ah]
│           0x0000277c      4889c7         mov rdi, rax
│           0x0000277f      e8dcfaffff     call sym std::allocator<char>::~allocator() ; sym.imp.std::allocator_char_::allocator__
│                                                                      ; std::allocator<char>::~allocator()
│           0x00002784      488d8548ffff.  lea rax, [var_b8h]
│           0x0000278b      488db550ffff.  lea rsi, [var_b0h]
│           0x00002792      b900000000     mov ecx, 0
│           0x00002797      ba00000000     mov edx, 0
│           0x0000279c      4889c7         mov rdi, rax
│           0x0000279f      e84cfaffff     call method OpenImageIO_v2_2::ImageInput::open(std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> > const&, OpenImageIO_v2_2::ImageSpec const*, OpenImageIO_v2_2::Filesystem::IOProxy*) ; method.OpenImageIO_v2_2::ImageInput.open_std::__cxx11::basic_string_char__std::char_traits_char___std::allocator_char____const__OpenImageIO_v2_2::ImageSpec_const__OpenImageIO_v2_2::Filesystem::IOProxy_
│           0x000027a4      488d8548ffff.  lea rax, [var_b8h]
│           0x000027ab      4889c7         mov rdi, rax                ; int64_t arg1
│           0x000027ae      e8031d0000     call fcn.000044b6
│           0x000027b3      83f001         xor eax, 1
│           0x000027b6      84c0           test al, al
│       ┌─< 0x000027b8      7416           je 0x27d0
│       │   0x000027ba      488d3dcb5900.  lea rdi, str.Exiting...     ; 0x818c ; "Exiting..." ; const char *s
│       │   0x000027c1      e85af9ffff     call sym.imp.puts           ; int puts(const char *s)
│       │   0x000027c6      bb01000000     mov ebx, 1
│      ┌──< 0x000027cb      e90a020000     jmp 0x29da
│      ││   ; CODE XREF from main @ 0x27b8(x)

Any assistance to understand it more, or even actually execute it would be great.

I've included a copy of the binary (zipped) here https://github.com/gnarkill78/watson

This is function 44b6 that was suggested to look at:

bool FUN_00102739(void)

{
  code *pcVar1;
  char cVar2;
  long *plVar3;
  undefined8 uVar4;
  undefined local_108 [48];
  vector<> local_d8 [24];
  basic_string local_c0 [8];
  ImageSpec local_b8 [38];
  allocator<char> local_92;
  allocator<char> local_91;
  undefined local_90 [8];
  vector<> local_88 [32];
  basic_string<> local_68 [43];
  allocator<char> local_3d;
  int local_3c;
  int local_38;
  int local_34;
  long local_30;
  
  std::allocator<char>::allocator();
                    /* try { // try from 00102770 to 00102774 has its CatchHandler @ 001029ff */
  std::__cxx11::basic_string<>::basic_string((char *)local_b8,(allocator *)"sherlock.jpeg");
  std::allocator<char>::~allocator(&local_92);
                    /* try { // try from 0010279f to 001027a3 has its CatchHandler @ 00102a9f */
  OpenImageIO_v2_2::ImageInput::open(local_c0,local_b8,(IOProxy *)0x0);
  cVar2 = FUN_001044b6(local_c0);
  if (cVar2 == '\x01') {
    plVar3 = (long *)FUN_001044d6(local_c0);
    local_30 = (**(code **)(*plVar3 + 0x38))(plVar3);
    local_34 = *(int *)(local_30 + 0xc);
    local_38 = *(int *)(local_30 + 0x10);
    local_3c = *(int *)(local_30 + 0x3c);
    std::allocator<char>::allocator();
                    /* try { // try from 00102841 to 00102845 has its CatchHandler @ 00102a1c */
    FUN_001044f0(local_d8,(long)(local_34 * local_38 * local_3c),&local_91);
    std::allocator<char>::~allocator(&local_91);
    plVar3 = (long *)FUN_001044d6(local_c0);
    pcVar1 = *(code **)(*plVar3 + 0x98);
    uVar4 = FUN_00103da4(local_d8,0);
    FUN_001030f8(local_90,2,1,0,0);
                    /* try { // try from 001028e0 to 00102904 has its CatchHandler @ 00102a77 */
    (*pcVar1)(plVar3,local_90,uVar4,0x8000000000000000,0x8000000000000000,0x8000000000000000,0,0);
    plVar3 = (long *)FUN_001044d6(local_c0);
    (**(code **)(*plVar3 + 0x50))(plVar3);
    FUN_0010319a(local_108);
    FUN_001031da(local_108,local_34,local_38,0x578);
                    /* try { // try from 0010293f to 00102943 has its CatchHandler @ 00102a63 */
    FUN_00103c62(local_88,local_d8);
                    /* try { // try from 00102955 to 00102959 has its CatchHandler @ 00102a30 */
    FUN_00103240(local_108,local_88);
    std::vector<>::~vector(local_88);
    std::allocator<char>::allocator();
                    /* try { // try from 00102984 to 00102988 has its CatchHandler @ 00102a52 */
    std::__cxx11::basic_string<>::basic_string((char *)local_68,(allocator *)"data.ana");
                    /* try { // try from 0010299a to 0010299e has its CatchHandler @ 00102a41 */
    FUN_001032b6(local_108,local_68);
    std::__cxx11::basic_string<>::~basic_string(local_68);
    std::allocator<char>::~allocator(&local_3d);
    FUN_00103768(local_108);
    std::vector<>::~vector(local_d8);
  }
  else {
                    /* try { // try from 001027c1 to 001027ed has its CatchHandler @ 00102a8b */
    puts("Exiting...");
  }
  std::unique_ptr<>::~unique_ptr((unique_ptr<> *)local_c0);
  std::__cxx11::basic_string<>::~basic_string((basic_string<> *)local_b8);
  return cVar2 != '\x01';
}

Unfortunately it's just not making and sense to me.

Keiran
  • 31
  • 6
  • What happens when you execute it? – ArSeN Aug 21 '23 at 05:03
  • It writes 'Executing...' then exits. Run in gdb: `[Thread debugging using libthread_db enabled] Using host libthread_db library "/lib/x86_64-linux-gnu/libthread_db.so.1". [New Thread 0x7fffe35ff640 (LWP 40557)] [New.. 0x7fffdad9e640 (..40558)] [New.. 0x7fffda59d640 (..40559)] [New.. 0x7fffd9d9c640 (..40560)] [New.. 0x7fffd959b640 (..40561)] Exiting... [Thread 0x7fffd959b640 (LWP 40561) exited] [..0x7fffda59d640 (..40559) ..] [..0x7fffd9d9c640 (..40560) ..] [..0x7fffdad9e640 (..40558) ..] [..0x7fffe35ff640 (..40557) ..] [Inferior 1 (process 40554) exited with code 01]` – Keiran Aug 21 '23 at 05:07
  • What happens when you create a (read and writeable) `sherlock.jpeg` in the same directory as the executable first, and then execute it? :) – ArSeN Aug 21 '23 at 05:11
  • Just a blank 'sherlock.jpeg'? I hadn't tried making it executable before but just did and still nothing. – Keiran Aug 21 '23 at 05:14
  • the important function seems to be `fcn.000044b6` - look there – Paweł Łukasik Aug 23 '23 at 03:49
  • I must be misunderstanding something somewhere. My understanding was that 'customary' is writing an image called 'sherlock.jpeg' from data contained in 'data.ana' but it looks like it's trying to open an image called 'sherlock.jpeg' instead which doesn't exist. – Keiran Aug 23 '23 at 07:36

0 Answers0