1

This is my first post here. I was recently involved in a capture the flag preparation test which involved decompiling an ELF 32-bit LSB executable, Intel 80386 file for Linux compiled with GCC. The binary is supposed to contain a password for the zip file provided. This is just for preparation to the actual capture the flag challenge that will take place on October 5. I was able to solve the other challenges but this seems impossible.

I uploaded the files using base64 down there.

At first I had to install GCC-multilib because I wasn't able to run the binary on a 32 bits system.

I used strings, objdump and radare2 to get an idea of what was inside the binary. I eventually found the main function with a strange behaviour: It calls strcompare with the input string and "s3cR3t_p4sSw0rD" but if the two strings match it prints out "This is not the solution you are looking for :)", on the other hand, if they don't match, it calls a stringlength function over your input and if it is not 34 characters long it prints out "Try again :(".

There's no success string, in fact in every other case the program stops.

I tried using radare debug to run the code but if I run it, prints out "No debugger please!". I eventually found the instruction where this check seems to happen and added a jmp instruction to bypass the check. I was not able to add any breakpoint and run the program through the debugger. It just ends without printing anything.

There are some functions that contain character data but I didn't find anything useful.

Please help me find the password, I'm getting crazy over this.

Base 64 Data (Note, this is a zip file not a url...): here

  • 1
    Hint: The program does other things besides calling the main function. – user202729 Sep 25 '18 at 15:45
  • 2
    iS, aa and afl are your friends. Additionally trace an execution using pintool or r2's debugger to see what "hidden" code is executing. – sudhackar Sep 25 '18 at 18:06
3

Since another question was based on the same binary and the accepted answer doesn't go into detail on how to find the function responsible, here's a writeup. Usual stuff $ r2 wysiNwyg; aaa

have a look at the list of functions(afl)

[0x080484a0]> afl
0x080483bc    3 35           fcn.080483bc
0x080483f0    1 6            sym.imp.strcmp
0x08048400    1 6            sym.imp.printf
0x08048410    1 6            sym.imp.fgets
0x08048420    1 6            sym.imp.puts
0x08048430    1 6            loc.imp.__gmon_start
0x08048440    1 6            sym.imp.exit
0x08048450    1 6            sym.imp.strlen
0x08048460    1 6            sym.imp.__libc_start_main
0x08048470    1 6            sym.imp.memset
0x08048480    1 6            sym.imp.putchar
0x08048490    1 6            sym.imp.ptrace
0x080484a0    1 33           entry0
0x080484d0    1 4            fcn.080484d0
0x080484e0    4 43           fcn.080484e0
0x08048550    3 30           entry3.fini
0x08048570    8 43   -> 93   entry1.init
0x0804859b    3 55           entry2.init
0x080485d2   12 446          entry4.fini
0x08048790    8 250          main

Other than main, entry4.fini function is quite large. Have a look at .fini and .fini_array sections from the binary(iS). Functions from .fini_array are called when the program is about to terminate(after main).

[0x080484a0]> iS~fini
14 0x00000904    20 0x08048904    20 -r-x .fini
19 0x00000c08     8 0x08049c08     8 -rw- .fini_array

Seek to that address(s). Dump .fini_array(pxw).

[0x080484a0]> s 0x08049c08
[0x08049c08]> pxw 0x10
0x08049c08  0x08048550 0x080485d2 0x00000000 0x00000001  P...............

entry4.fini has been referenced in .fini_array. Go to main and disassemble. Check where the input is getting stored to

│           0x080487d5      a1409d0408     mov eax, dword [obj.stdin]  ; [0x8049d40:4]=0
│           0x080487da      83ec04         sub esp, 4
│           0x080487dd      50             push eax                    ; FILE *stream
│           0x080487de      6a23           push 0x23                   ; '#' ; 35 ; int size
│           0x080487e0      68609d0408     push 0x8049d60              ; char *s
│           0x080487e5      e826fcffff     call sym.imp.fgets          ; char *fgets(char *s, int size, FILE *stream)

Input from fgets is going to 0x8049d60. You can also name it f input 35 @ 0x8049d60. Check for xrefs on it

[0x08048790]> axt 0x8049d60
entry4.fini 0x80486ec [DATA] push 0x8049d60
main 0x80487c8 [DATA] push 0x8049d60
main 0x80487e0 [DATA] push 0x8049d60
main 0x80487f8 [DATA] push 0x8049d60
main 0x8048808 [DATA] movzx eax, byte [eax + 0x8049d60]
main 0x8048816 [DATA] push 0x8049d60
main 0x8048826 [DATA] mov byte [eax + 0x8049d60], 0
main 0x8048835 [DATA] push 0x8049d60
main 0x804885b [DATA] push 0x8049d60

entry4.fini also references the input. Disassemble it.

│           0x080486ec      68609d0408     push 0x8049d60              ; const char *s
│           0x080486f1      e85afdffff     call sym.imp.strlen         ; size_t strlen(const char *s)
│           0x080486f6      83c410         add esp, 0x10
│           0x080486f9      83f822         cmp eax, 0x22               ; '"' ; 34

First check is if your input is 34 bytes long.

│           0x080486f9      83f822         cmp eax, 0x22               ; '"' ; 34
│       ┌─< 0x080486fc      7405           je 0x8048703
│      ┌──< 0x080486fe      e988000000     jmp 0x804878b
│      ││   ; CODE XREF from entry4.fini (0x80486fc)
│      │└─> 0x08048703      c745f4000000.  mov dword [local_ch], 0
│      │┌─< 0x0804870a      eb2c           jmp 0x8048738
│      ││   ; CODE XREF from entry4.fini (0x804873c)
│     ┌───> 0x0804870c      8d55d1         lea edx, [local_2fh]
│     ⁝││   0x0804870f      8b45f4         mov eax, dword [local_ch]
│     ⁝││   0x08048712      01d0           add eax, edx
│     ⁝││   0x08048714      0fb600         movzx eax, byte [eax]
│     ⁝││   0x08048717      0fbed0         movsx edx, al
│     ⁝││   0x0804871a      8b45f4         mov eax, dword [local_ch]
│     ⁝││   0x0804871d      05609d0408     add eax, 0x8049d60
│     ⁝││   0x08048722      0fb600         movzx eax, byte [eax]
│     ⁝││   0x08048725      83f033         xor eax, 0x33
│     ⁝││   0x08048728      0fbec0         movsx eax, al
│     ⁝││   0x0804872b      0fb6c0         movzx eax, al
│     ⁝││   0x0804872e      39c2           cmp edx, eax
│    ┌────< 0x08048730      7402           je 0x8048734
│   ┌─────< 0x08048732      eb57           jmp 0x804878b
│   ││⁝││   ; CODE XREF from entry4.fini (0x8048730)
│   │└────> 0x08048734      8345f401       add dword [local_ch], 1
│   │ ⁝││   ; CODE XREF from entry4.fini (0x804870a)
│   │ ⁝│└─> 0x08048738      837df421       cmp dword [local_ch], 0x21  ; [0x21:4]=-1 ; '!' ; 33
│   │ └───< 0x0804873c      7ece           jle 0x804870c
│   │  │    0x0804873e      c745f4000000.  mov dword [local_ch], 0
│   │  │┌─< 0x08048745      eb21           jmp 0x8048768

Second check involves xor'ing each byte from local_2fh with 0x33 and the comparing with the input byte by byte. Start the VM to dump the memory(0x22 bytes from local_2fh) and perform the xor operation.

[0x080484a0]> s entry4.fini
[0x080485d2]> aei
[0x080485d2]> aeim
[0x080485d2]> aeip

Local variables are assigned up to instruction 0x08048665 for the check. Emulate and dump the values.

[0x080485d2]> aesu 0x08048665
[0x08048659]> pcp 0x22  @ebp-0x2f 
import struct
buf = struct.pack ("34B", *[
0x02,0x5d,0x02,0x67,0x6c,0x07,0x5d,0x77,0x6c,0x75,0x02,
0x5d,0x02,0x6c,0x07,0x41,0x61,0x07,0x6a,0x40,0x6c,0x07,
0x41,0x00,0x6c,0x60,0x03,0x6c,0x00,0x07,0x40,0x6a,0x12,
0x12])
[0x00000041]> !python
Python 2.7.15rc1 (default, Apr 15 2018, 21:51:34) 
[GCC 7.3.0] on linux2
Type "help", "copyright", "credits" or "license" for more information.
>>> import struct
>>> buf = struct.pack ("34B", *[
... 0x02,0x5d,0x02,0x67,0x6c,0x07,0x5d,0x77,0x6c,0x75,0x02,
... 0x5d,0x02,0x6c,0x07,0x41,0x61,0x07,0x6a,0x40,0x6c,0x07,
... 0x41,0x00,0x6c,0x60,0x03,0x6c,0x00,0x07,0x40,0x6a,0x12,
... 0x12])
>>> print ''.join(map(lambda x:chr(ord(x)^0x33),buf))
1n1T_4nD_F1n1_4rR4Ys_4r3_S0_34sY!!

This works

./wysiNwyg 

#########################################################
### Welcome to the "wysiNwyg" challenge!
###     Your task is to find out the password to be able
###     to decrypt the password-protected zip and read
###     the secret flag. Good Luck!!
#########################################################

Password: 1n1T_4nD_F1n1_4rR4Ys_4r3_S0_34sY!!
Congratulations! You just won :p
4

you're looking the wrong way. look in the functions before running main and you will find the decryption function.

when you're using the decrypt function, you get a key for the archive: 1n1T_4nD_F1n1_4rR4Ys_4r3_S0_34sY!!

and flag: {FLG:4#hfoU98Y5(ButYou'llNeverKnowIt)}

EZ

part 1 = [2, 93, 2, 103, 108, 7, 93, 119, 108, 117, 2, 93, 2, 108, 7, 65, 97, 7, 106, 64, 108, 7, 65, 0, 108, 96, 3, 108, 0, 7, 64, 106, 18, 18, 0]     
for i in range(0, len(part_1) - 1):
      print(chr(part_1[i] ^ 0x33))

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service, privacy policy and cookie policy

Not the answer you're looking for? Browse other questions tagged or ask your own question.