mara's blog

mara mara@localhost.local

Presentation

Welcome, I'm mara, during my spare time, I'm contributing to develop intros/demos for the Flush group. You can find some Flush information on the offical website or on demozoo or pouet and our youtube channel

Crackmes.one's solutions

Solution for destructeur's ShAPK1

mara mara@localhost.local

Analysis

The program is an apk file, so probably an Android program. We used jad1, to analyze the binary.

Three classes are present :

  • BuildConfig
  • MainActivity
  • R

The MainActivity classe is insteresting for us. It contains, three methods :

  • onCreate
  • encrypt
  • check

onCreate called by the Android runtime to create the GUI controls, encrypt and check are used by the application logic. The check aims to validate if the code entered are correct or not.

   public void check() {
        Button button = (Button) findViewById(R.id.check);
        this.checkButton = button;
        button.setOnClickListener(new View.OnClickListener() {
            public void onClick(View view) {
                TextView textView = (TextView) MainActivity.this.findViewById(R.id.messageText);
                String obj = ((EditText) MainActivity.this.findViewById(R.id.passwordInput)).getText().toString();
                if (Arrays.equals(Base64.encode(MainActivity.this.encrypt(obj.getBytes(StandardCharsets.UTF_8)), 2), MainActivity.this.getResources().getString(R.string.secret).getBytes(StandardCharsets.UTF_8))) {
                    textView.setText("YES! PASSWORD IS CORRECT!!");
                } else {
                    textView.setText("PASSWORD IS WRONG!!");
                }
            }
        });
    }

This line is interesing :

if (Arrays.equals(Base64.encode(MainActivity.this.encrypt(obj.getBytes(StandardCharsets.UTF_8)), 2), MainActivity.this.getResources().getString(R.string.secret).getBytes(StandardCharsets.UTF_8))) {

To validate the challenge, we need to have a code which equals to R.string.secrets. R.string.secrets is a string located in Resource : Resources->resource.arsc->string.xml. It's value is :

    <string name="secret">NQALCgEDDDEzUjpTBwocBgcDPTIIGwIK</string>

The conditional test use the encrypt function to encode the code entered before compare it with the right hardcoded code.

    public byte[] encrypt(byte[] bArr) {
        byte[] bytes = getResources().getString(R.string.key).getBytes(StandardCharsets.UTF_8);
        int length = bytes.length;
        byte[] bArr2 = new byte[bArr.length];
        for (int i = 0; i < bArr.length; i++) {
            bArr2[i] = (byte) (bArr[i] ^ bytes[i % length]);
        }
        return bArr2;
    }

The function encrypt take a parameter, our code entered, the bytes array is a key pointed by R.string.key and available in Resources->resource.arsc->string.xml.

It's value is :

    <string name="key">beginning</string>

The encrypt function xored each characters of bArr with bArr2 for each iterations.

WARNING: the bArr2 can be short than bArr so a modulo with the key length (bArr2) is applied to avoid the bound array overflow

Keygen

You will find an unencrypt script in python language to find the original code :

#!/usr/bin/env python3
# -*- encoding: utf-8 -*-

import base64

BEGINNING = "beginning"
SERIAL = "NQALCgEDDDEzUjpTBwocBgcDPTIIGwIK"

serial_b64decoded = base64.b64decode(SERIAL)
serial_b64decoded_length = len(serial_b64decoded)

beginning_length = len(BEGINNING)
original_serial = ""

for i in range(serial_b64decoded_length):
    xored_value = (serial_b64decoded[i] ^ 
        ord(BEGINNING[i % beginning_length]))
    original_serial += chr(xored_value)

# the original code 
print(original_serial)
1

https://github.com/skylot/jadx/

Solution for nutcake's WhiteRabbit

mara mara@localhost.local

Analysis

The description suggest a hidden function...

Take IDA 7 Freeware 1, to analyze the binary.

Type CTRL+E keys and go to _start function, you will see the startup stub which trigger the main function.

The main function printing only two uninteresting string.

If you press another time the CTRL+E keys, you will see a function named secret. This function print a flag but it haven't got any cross references. How we can go to the secret's function ?

The first idea will be to patch the EntryPoint but the program will crashed.

Another option will to patch the instruction lea rdi, main to lea rdi, secret in the _start function.

WARNING: Beware the nmemonic is position relative.

public _start
_start proc near
xor     ebp, ebp
mov     r9, rdx         ; rtld_fini
pop     rsi             ; argc
mov     rdx, rsp        ; ubp_av
and     rsp, 0FFFFFFFFFFFFFFF0h
push    rax
push    rsp             ; stack_end
lea     r8, __libc_csu_fini ; fini
lea     rcx, __libc_csu_init ; init
lea     rdi, main       ; main  <--------- here
call    cs:__libc_start_main_ptr
hlt
_start endp

Pick the hexadecimal values of the lea : 0x48, 0x8D, 0x3D, 0x51, 0x01, 0x00, 0x00 .

Often in the 64 bits programming the address are relative. To verify this argue, we help us of the online encoder/decoder of Jonathan Salwan's website.2

Copy the previous hexadecimal values without the 0x and comma in this website, select x86(64) in little endian mode and click on the disassemble button. The result will be :

0x0000000000000000: lea rdi, qword ptr [rip + 0x151]

Why it's 0x151 ? It's quite simple, look the next formula :

address of the main function - address of the lea - size of the instuction of
the lea opcode

0x11D5 - 0x107D - 7 => 0x151

TIP: we can type this previous operation at the left of IDC at the bottom of IDA window, to compute the operation.

Our goal will change the value by the correct position , the position of secret function. The formula :

address of the secret function - address of the lea - size of the instuction of
the lea opcode

0x1145 - 0x107D - 7 => 0xC1

Now, via the Jonathan's website 2, you filling out the form with :

0x0000000000000000: lea rdi, qword ptr [rip + 0xc1]

Select x86(64) in little endian mode and click on the assemble button. The result will be :

488D3D51010000

Fire up a hexadecimal editor and replace the old values by the new one :

48 8D 3D 51 01 00 00

by

48 8D 3D C1 00 00 00

Now, you can run the program and grab the flag !

Content in this archive

  • hidden.i64 : the hidden analysis
  • hidden_mod : the hidden file patched
  • hidden_mod.i64 : the hidden_mod analysis
  • solution.html : the present file.
1

https://www.hex-rays.com/products/ida/support/download_freeware.shtml

2

http://shell-storm.org/online/Online-Assembler-and-Disassembler/

Solution for zed-zahir's ZED-Crackme

mara mara@localhost.local

Analysis

Before all, unpack the binary via upx1.

$ upx -d ZED-Crackme-x64.bin

The goal will find the good serial.

Take Cutter2, to analyze the binary.

Don't forget, use aaa option to disassemble the crackme. The entry point with Cutter is named by a flag entry0

NOTE: a flag is a litteral or a identifier in a Cutter jargon

  ;-- section..text:
  ;-- .text:
  ;-- _start:
42: entry0 (int64_t arg3);
; arg int64_t arg3 @ rdx
0x000008c0      xor     ebp, ebp   ; [14] -r-x section size 3410 named .text
0x000008c2      mov     r9, rdx    ; arg3
0x000008c5      pop     rsi
0x000008c6      mov     rdx, rsp
0x000008c9      and     rsp, 0xfffffffffffffff0
0x000008cd      push    rax
0x000008ce      push    rsp
0x000008cf      lea     r8, [sym.__libc_csu_fini] ; 0x1610
0x000008d6      lea     rcx, [sym.__libc_csu_init] ; 0x15a0 ; "AWAVI\x89\xd7AUATL\x8d%\xae\a "
0x000008dd      lea     rdi, [main] ; sym.main
                                   ; 0x1343
0x000008e4      call    qword [reloc.__libc_start_main] ; 0x201fe0

It's the classical startup sequence for a Linux program. Look the assembly at sym.main :

int main (int argc, char **argv, char **envp);
; var int64_t var_b0h @ rbp-0xb0
; var int64_t var_a4h @ rbp-0xa4
; var int64_t var_9ch @ rbp-0x9c
; var int64_t var_91h @ rbp-0x91
; var int64_t var_90h @ rbp-0x90
; var int64_t var_8fh @ rbp-0x8f
; var int64_t var_8eh @ rbp-0x8e
; var int64_t var_8dh @ rbp-0x8d
; var int64_t var_88h @ rbp-0x88
; var int64_t var_80h @ rbp-0x80
; var int64_t var_7eh @ rbp-0x7e
; var int64_t var_70h @ rbp-0x70
; var int64_t var_50h @ rbp-0x50
; var int64_t var_4fh @ rbp-0x4f
; var int64_t var_4eh @ rbp-0x4e
; var int64_t var_4dh @ rbp-0x4d
; var int64_t var_4ch @ rbp-0x4c
; var int64_t var_30h @ rbp-0x30
; var int64_t var_28h @ rbp-0x28
; var int64_t var_20h @ rbp-0x20
; var int64_t var_18h @ rbp-0x18
; var int64_t var_10h @ rbp-0x10
; var int64_t var_eh @ rbp-0xe
; var int64_t var_8h @ rbp-0x8
; arg int argc @ rdi
; arg char **argv @ rsi
0x00001343      push    rbp
0x00001344      mov     rbp, rsp
0x00001347      sub     rsp, 0xb0
0x0000134e      mov     dword [var_a4h], edi ; argc
0x00001354      mov     qword [var_b0h], rsi ; argv
0x0000135b      mov     rax, qword fs:[0x28]
0x00001364      mov     qword [var_8h], rax
0x00001368      xor     eax, eax
0x0000136a      lea     rdi, str.  ; 0x17dd ; "***********************" ; const char *s
0x00001371      call    sym.imp.puts ; int puts(const char *s)
0x00001376      lea     rdi, str.rules: ; 0x17f5 ; "**      rules:       **" ; const char *s
0x0000137d      call    sym.imp.puts ; int puts(const char *s)
0x00001382      lea     rdi, str.  ; 0x17dd ; "***********************" ; const char *s
0x00001389      call    sym.imp.puts ; int puts(const char *s)
0x0000138e      mov     edi, 0xa   ; int c
0x00001393      call    sym.imp.putchar ; int putchar(int c)
0x00001398      lea     rdi, str.do_not_bruteforce ; 0x180d ; "* do not bruteforce" ; const char *s
0x0000139f      call    sym.imp.puts ; int puts(const char *s)
0x000013a4      lea     rdi, str.do_not_patch__find_instead_the_serial. ; 0x1828 ; "* do not patch, find instead the serial." ; const char *s
0x000013ab      call    sym.imp.puts ; int puts(const char *s)
0x000013b0      mov     edi, 0xa   ; int c
0x000013b5      call    sym.imp.putchar ; int putchar(int c)

Here, zed-zahir print the crackme banner, but later in the code some interesting stuff will be discovered.

0x000013fc      sidt    [rbp - 0x96]
0x00001403      movzx   eax, byte [var_91h]
0x0000140a      cmp     al, 0xff
0x0000140c      jne     0x1424
0x0000140e      lea     rdi, str.VMware_detected ; 0x1851 ; "VMware detected" ; const char *s
0x00001415      call    sym.imp.puts ; int puts(const char *s)
0x0000141a      mov     edi, 1     ; int status
0x0000141f      call    sym.imp.exit ; void exit(int status)

The first one is an anti vmware by sidt nmemonic... well-known unless if I mistaken red-pill3 . Another interesting article that threat about of protection ultimate-anti-reversing4.

0x0000149b      mov     esi, 0     ; pid_t pid
0x000014a0      mov     edi, 0     ; __ptrace_request request
0x000014a5      mov     eax, 0
0x000014aa      call    sym.imp.ptrace ; long ptrace(__ptrace_request request, pid_t pid, void*addr, void*data)
0x000014af      test    rax, rax
0x000014b2      jns     0x14ca
0x000014b4      lea     rdi, str.This_process_is_being_debugged ; 0x1878 ; "This process is being debugged!!!" ; const char *s
0x000014bb      call    sym.imp.puts ; int puts(const char *s)
0x000014c0      mov     edi, 1     ; int status
0x000014c5      call    sym.imp.exit ; void exit(int status)

Voila, the second one detect if an debugger is attached via ptrace function.

0x00001545      lea     rdx, [var_70h]
0x00001549      lea     rax, [var_50h]
0x0000154d      mov     rsi, rdx   ; const char *s2
0x00001550      mov     rdi, rax   ; const char *s1
0x00001553      call    sym.imp.strcmp ; int strcmp(const char *s1, const char *s2)
0x00001558      test    eax, eax
0x0000155a      jne     0x156a
0x0000155c      lea     rdi, str.you_succeed ; 0x189a ; "you succeed!!" ; const char *s
0x00001563      call    sym.imp.puts ; int puts(const char *s)
0x00001568      jmp     0x1576
0x0000156a      lea     rdi, str.try_again ; 0x18a8 ; "try again" ; const char *s
0x00001571      call    sym.imp.puts ; int puts(const char *s)
0x00001576      mov     eax, 0

The strcmp compare two values from [var_70h] and [var_50h]. Cutter is able to highlight the values usage. Here, click on the value [var_70h].

[var_70h] seems used twice at 0x00001545 and 0x00001483 directly.

At 0x00001483, [var_70h] moved in rax then rsi.

To conclude, the strcmp function take the contents without alteration. [var_70h] is the user value.

So [var_50h], by deduction is the correct value to find.

I don't want trace the code step by step. The first strategy will use the Cutter code emulation. But the quick and the good strategy will be to inspect the content of [var_70h] and [var_50h] via LD_PRELOAD tricks.

int strcmp(const char *s, const char *s2) {
	printf("s1 %s\ns2 %s", s, s2);
	return 0;
}

The idea behind LD_PRELOAD tricks is hooking the real function by our library. strcmp hooked but before, the c code must be compiled. Here, the program is m.c

$ gcc -shared -fPIC m.c -o m.so

To inject m.so at the execution of ZED Crackme, type the following command :

$ LD_PRELOAD=./m.so ./ZED-Crackme-x64.bin

Enter a dummy password okokok.

***********************
**      rules:       **
***********************

* do not bruteforce
* do not patch, find instead the serial.

enter the passphrase: s1 C(uiICD@CADDEBNEEDD
s2 okokokyou succeed!!

The password printed. It's C(uiICD@CADDEBNEEDD Now, you can run the program and check the flag !

***********************
**      rules:       **
***********************

* do not bruteforce
* do not patch, find instead the serial.

enter the passphrase: you succeed!!

Great !

1

https://upx.github.io

2

https://cutter.re

3

https://www.lions.odu.edu/~c1wang/course/cs495/lecture/10_2_Anti-VM_Techniques.pdf

4

https://anti-reversing.com/Downloads/Anti-Reversing/The_Ultimate_Anti-Reversing_Reference.pdf

Solution for BitFriends' nasm crack

mara mara@localhost.local

Analysis

The goal will find the "correct" message and it's a 64 bits program..

Take radare21, to analyze the binary.

NOTE: please use the git version of radare2 to be up to date

$ r2 nasm_crack
 -- git blind --hard
[0x00401028]>

Don't forget, use aaa option to disassemble the crackme. The entry point with Cutter is named by a flag entry0

NOTE: a flag is a litteral or a identifier in a radare jargon

[0x00401028]> aaa
[x] Analyze all flags starting with sym. and entry0 (aa)
[x] Analyze function calls (aac)
[x] Analyze len bytes of instructions for references (aar)
[x] Check for objc references
[x] Check for vtables
[x] Type matching analysis for all functions (aaft)
[x] Propagate noreturn information
[x] Use -AA or aaaa to perform additional experimental analysis.
[0x00401028]>

To navigate efficiently in the code, we can use Vp command.

[0x00401028]> Vp

TIP: we can navigate with arrow keys or h, j, k, l keys like in VIM text editor.

According to linux-x64-syscall-table2, the code between 0x401028 and 0x401041, print the message "Enter your password: ".

Add a comment at 0x401041, by pressing ; key an validate it by enter/return key.

The code between 0x401043 and 0x40105c, grab the user entry.

The user entry string will be a length of 16 characters.

The code between 0x40105e and 0x401077 compare the content of address pointed by rsi and rdi until ecx will be equals to zero or a characters is different during the loop iteration. If all characters are equals the je nmemonic at 0x401079 will be taken.

To jump, on this line, press enter/return key to jump at loc.correct_fun

TIP: to return before the jump press the u key.

Great, if so the answer is the value pointed by rdi, so loc.passwd.

The right value is supersecret.

1

https://rada.re

2

https://blog.rchapman.org/posts/Linux_System_Call_Table_for_x86_64/