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
2

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
|improve this answer|||||
3

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))
|improve this answer|||||

Your Answer

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