Post

norzhctf2021 - Secure Auth v0

One of NorzhNuclea’s developers joined the team last quarter, specialized in authentication systems he found one he developed a few years ago with a innovative obfuscation method. Find the correct password to validate the checks.


Análisis estático básico

Al descargar el archivo que nos adjuntan en el reto, lo primero que me sorprende es su peso:

1
2
$ ls -lh
-rwxr-xr-x 1 x    x     70M may 22 04:58 chall

Compruebo qué clase de archivo es:

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

ELF de 64 bits, linkado dinámicmanete. Buscando cadenas de caracteres imprimibles en él, no encuentro gran cosa:

1
2
3
4
5
6
$ strings chall
...
c-n|TD^zJFp|I'q"VCj7.mNj4
f4|<eN3w$
Failed !
...

Análisis dinámico básico

Compruebo las llamadas a librerias que hace el binario:

1
2
3
4
5
6
7
8
$ltrace ./chall
__isoc99_scanf(0x49c8031, 0x7ffdf6599610, 0x7ffdf6599768, 0x7f343dac2598AAAA
)                                             = 1
strlen("AAAA")                                                                                                        = 4
strcmp("'T=\\", "c-n|TD^zJFp|I'q"VCj7.mNj4")                                                                          = -60
puts("Failed !"Failed !
)                                                                                                      = 9
+++ exited (status 1) +++
Parece que le hace algún tipo de transformación a la cadena que le pasamos al binario antes de compararla con la cadena “c-nTD^zJFpI’q”VCj7.mNj4”

Análisis estático avanzado

Al intentar desensamblarlo, radare2 tarda basatante más de lo normal. Intento depurarlo con gdb y me doy cuenta de que entre cada instrucción hay miles de NOPs, echo un ojo a los bytes del binario para confirmarlo:

1
2
3
4
5
$ xxd chall
...
001c5900: 9090 9090 9090 9090 9090 9090 9090 9090  ................
...
001deb60: 9090 9090 9090 9090 9090 9090 9090 9090  ................

Depurar este binario es algo enormemente ineficiente debido a su nivel de ofuscación. Para tratar de obtener las instrucciones “útiles” desensamblo el binario:

1
2
$ objdump -d -M intel chall | grep -v nop | tee disass.txt
...

Tarda un rato pero porfín obtendo las instrucciones que me interesan, menos de 270 en un binario de 70MB.

A continuación adjunto la parte interesante del binario, que es la que transforma la cadena que le pasamos antes de compararla con la extraña cadedna hardcodeada.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
 1f46549:       0f be 04 08             movsx  eax,BYTE PTR [rax+rcx*1]
 1facfb9:       83 f8 20                cmp    eax,0x20
 2013bfb:       0f 8e cf e6 c6 00       jle    2c822d0 <__isoc99_scanf@plt+0x2881270>
 20e0f08:       48 8b 45 f8             mov    rax,QWORD PTR [rbp-0x8]
 2147b91:       48 63 4d f0             movsxd rcx,DWORD PTR [rbp-0x10]
 21ae582:       0f be 04 08             movsx  eax,BYTE PTR [rax+rcx*1]
 221505c:       83 f8 7f                cmp    eax,0x7f
 227b94c:       0f 8d 7e 69 a0 00       jge    2c822d0 <__isoc99_scanf@plt+0x2881270>
 2348cdc:       48 8b 45 f8             mov    rax,QWORD PTR [rbp-0x8]
 23af83d:       48 63 4d f0             movsxd rcx,DWORD PTR [rbp-0x10]
 2416471:       0f be 04 08             movsx  eax,BYTE PTR [rax+rcx*1]
 247cf90:       83 e8 21                sub    eax,0x21
 24e383b:       89 45 ec                mov    DWORD PTR [rbp-0x14],eax
 254a273:       48 8b 0c 25 50 a0 9c    mov    rcx,QWORD PTR ds:0x49ca050
 254a27a:       04
 25b0d8b:       8b 45 f0                mov    eax,DWORD PTR [rbp-0x10]
 26177e7:       be 04 00 00 00          mov    esi,0x4
 267e31d:       99                      cdq
 26e4cfb:       f7 fe                   idiv   esi
 274b6ff:       8b 45 ec                mov    eax,DWORD PTR [rbp-0x14]
 27b2235:       48 63 d2                movsxd rdx,edx
 2818afa:       0f be 0c 11             movsx  ecx,BYTE PTR [rcx+rdx*1]
 287f631:       83 e9 21                sub    ecx,0x21
 28e6255:       01 c8                   add    eax,ecx
 294cbd5:       b9 5f 00 00 00          mov    ecx,0x5f
 29b3497:       99                      cdq
 2a19f4e:       f7 f9                   idiv   ecx
 2a80a2b:       83 c2 21                add    edx,0x21
 2b4e187:       48 8b 45 f8             mov    rax,QWORD PTR [rbp-0x8]
 2bb4c75:       48 63 4d f0             movsxd rcx,DWORD PTR [rbp-0x10]
 2c1b7b3:       88 14 08                mov    BYTE PTR [rax+rcx*1],dl
 2ce8f20:       e9 74 6a 06 00          jmp    2d4f999 <__isoc99_scanf@plt+0x294e939>
 2db64b0:       8b 45 f0                mov    eax,DWORD PTR [rbp-0x10]
 2e1ce68:       83 c0 01                add    eax,0x1
 2e8372d:       89 45 f0                mov    DWORD PTR [rbp-0x10],eax
 2eea00d:       e9 18 db d8 fe          jmp    1c77b2a <__isoc99_scanf@plt+0x1876aca>
 2fb7634:       5d                      pop    rbp
 3084d2e:       c3                      ret

Y a continuación muestro el funcionamiento de este algoritmo en python3:

pseudo.py

str1 = "c-n|TD^zJFp|I'q\"VCj7.mNj4"
str2 = "f4|<"
out = ""

flag = input()

for i in range(0, len(input)):
    if ord(c) > 0x1f and ord(c) < 0x80:
        n = ord(c) - 0x21
        n += (ord(str2[i%4]) - 0x21)
        n %= 0x5f
        n += 0x21
        out = ''.join((chr(n),out))
    if out != str1:
	print("Failed !")

Para escribir el solver, no puedo invertir el algoritmo por la operación “% 0x5f”, paso cada caracter por el algoritmo y si coincide con el caracter correspondiente de la cadena hardcodeada, lo antepongo a la flag.

solver.py

#!/usr/bin/python3

import string

ct = "c-n|TD^zJFp|I'q\"VCj7.mNj4"
rara = "f4|<"
flag = ""

for i in range(0, len(ct)):
    for c in string.printable:
        if ord(c) > 0x1f and ord(c) < 0x80:
            n = ord(c) - 0x21
            n += (ord(rara[i%4]) - 0x21)
            n %= 0x5f
            n += 0x21
            if n == ord(ct[i]):
                flag = ''.join((c,flag))
                pass

print(flag)

Flag: NORZH{n0pfuscat3d_b1nary}

This post is licensed under CC BY 4.0 by the author.