Haunted Mirror

Challenge:

We found a script being used by DEADFACE. One of our informants says that the code contains one of mort1cia's passwords. There must be a way to get it out of the file.

Solution:

The provided Zip file contained an ELF binary:

$ file mirror
mirror: ELF 64-bit LSB executable, x86-64, version 1 (GNU/Linux), statically linked, no section header

When run, the program would simply output the argument passed at the command line, but reversed:

$ ./mirror PingTrip
Hello, stranger. I'm trapped behind your screen. Type any word and I'll write it back to you from the other side. Say the right word, and I'll tell you a secret.
PingTrip
pirTgniP

An interesting behavior I noticed is that the program crashed (SIGSEGV) if a command line argument wasn't included. I'll circle back to that later.

Running strings against the binary produced a portion of the flag, flag{ but not much else of in the way of readable strings. I did notice the binary was UPX packed, which would explain the lack of readable strings:

$ strings mirror | head -n1

UPX!

Luckily, UPX is trivial to unpack:

$ upx -d mirror
                       Ultimate Packer for eXecutables
                          Copyright (C) 1996 - 2018
UPX 3.95        Markus Oberhumer, Laszlo Molnar & John Reiser   Aug 26th 2018

        File size         Ratio      Format      Name
   --------------------   ------   -----------   -----------
    768464 <-    303916   39.55%   linux/amd64   mirror

Circling back to the SIGSEGV behavior, I loaded the binary into Ghidra to determine what might be causing the error, and spotted the same "flag{" string I observed in the strings output earlier.

undefined8 main(undefined8 param_1,long param_2)

{
  long lVar1;
  int local_c;
  
  lVar1 = *(long *)(param_2 + 8);
  puts(
      "Hello, stranger. I\'m trapped behind your screen. Type any word and I\'ll write it back toyou from the other side. Say the right word, and I\'ll tell you a secret."
      );
  printf(*(char **)(param_2 + 8),"flag{","XQwG1PhUqJ9A&5v",&DAT_0047f0ba);
  putchar(10);
  local_c = thunk_FUN_004010d6();
  while (local_c = local_c + -1, -1 < local_c) {
    putchar((int)*(char *)(lVar1 + local_c));
  }
  putchar(10);
  return 0;
}

The printf statement caught my eye since it appeared to be the flag being assembled from three parts:

"flag{","XQwG1PhUqJ9A&5v",&DAT_0047f0ba);

I looked for the data segment referenced as the third section and saw that it was a closing bracket:

`0047f0ba 7d              ??         7Dh    }`

The completed flag was, flag{XQwG1PhUqJ9A&5v}

Note: After poking at the binary further I realized a simper solution was to leverage a Format String attack. Passing %s%s%s as an argument results in the completed flag being read from process memory and outputted:

$ ./mirror %s%s%s
Hello, stranger. I'm trapped behind your screen. Type any word and I'll write it back to you from the other side. Say the right word, and I'll tell you a secret.
flag{XQwG1PhUqJ9A&5v}

s%s%s%

Published:

Updated:

Leave a comment