Reverse - JeanLouis

Analyse

En ouvrant le binaire dans Ghidra et après avoir renommĂ© les variables, on tombe sur une sorte de memcpy d’un shellcode qui sera xorĂ© avec une clĂ© statique ensuite par le programme.

Figure 1

Figure 1: Copie du shellcode et XOR de celui-ci.

SHELLCODE = "4F274DEB5442071D4F075746544F530743424F42074A484F425D074B4F624953559F232727279C26272727AEC69D3F272727EAA79F242727279C27272727AA6B03C79D37272727EAA7AEE8AD30A7D56AA7DD3928A2CC27272760AD30A7D548A7DD1C28A2FB27272760AD30A7D553A7DD1B28A2EA27272760AD30A7D543A7DD0228A29927272760AD30A7D542A7DD0128A28827272760AD30A7D557A7DD1C28A28727272760AD30A7D546A7DD3D28A2B627272760AD30A7D554A7DD0D28A2A527272760AD30A7D554A7DD31525060AD30A7D542A7DD3B524B60AD30A7D514A7DD71524660AD30A7D514A7DD6D527160AD30A7D545A7DD20526C60AD30A7D546A7DD75526760AD30A7D543A7DD70521260AD30A7D543A7DD3E520D604F0E0E2D274F0042071D4F074D48524F654E42499F232727279C26272727AEC69D28272727EAA7CC1D4D2D4F090909094F544209094F075746544F530743424F54074A484F5251464E4F6A4627279F232727279C26272727AEC69D07272727EAA7CC279F262727279C27272727EAA700"
SHELLCODE = bytes.fromhex(SHELLCODE)

final_shellcode = bytearray(len(SHELLCODE))


for k in range(0, 0x18b):
    final_shellcode[k] = SHELLCODE[k] ^ 0x27

print(bytes(final_shellcode))

Il suffit de prendre le shellcode en mémoire de le copier et de placer la valeur hexa dans un site comme defuse.ca pour retrouver les instructions.

Une fois les instructions en notre possesion, on peut les mettre dans un fichier afin de les compiler :

BITS 32

section .text
global _start

_start:
    push   0x73cc6a00
    and    [edx],bh
    push   0x73617020
    push   0x65642074
    push   0x6f6d2065
    push   0x6c207a65
    push   0x72746e45
    mov    eax,0x4
    mov    ebx,0x1
    mov    ecx,esp
    mov    edx,0x18
    int    0x80
    mov    eax,0x3
    mov    ebx,0x0
    lea    ecx,[esp-0x20]
    mov    edx,0x10
    int    0x80
    mov    edi,ecx
    mov    dl,[edi]
    xor    dl,0x4d
    cmp    dl,0x1e
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x6f
    cmp    dl,0x3b
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x74
    cmp    dl,0x3c
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x64
    cmp    dl,0x25
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x65
    cmp    dl,0x26
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x70
    cmp    dl,0x3b
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x61
    cmp    dl,0x1a
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x73
    cmp    dl,0x2a
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x73
    cmp    dl,0x16
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x65
    cmp    dl,0x1c
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x33
    cmp    dl,0x56
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x33
    cmp    dl,0x4a
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x62
    cmp    dl,0x7
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x61
    cmp    dl,0x52
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x64
    cmp    dl,0x57
    jne    0x144
    inc    edi
    mov    dl,[edi]
    xor    dl,0x64
    cmp    dl,0x19
    jne    0x144
    inc    edi
    push   0xa2929
    push   0x3a206527
    push   0x756f6a20
    push   0x6e656942
    mov    eax,0x4
    mov    ebx,0x1
    mov    ecx,esp
    mov    edx,0xf
    int    0x80
    jmp    0x17e
    push   0xa
    push   0x2e2e2e2e
    push   0x2e2e6573
    push   0x73617020
    push   0x65642074
    push   0x6f6d2073
    push   0x69617675
    push   0x614d
    mov    eax,0x4
    mov    ebx,0x1
    mov    ecx,esp
    mov    edx,0x20
    int    0x80
    jmp    0x17e
    mov    eax,0x1
    mov    ebx,0x0
    int    0x80
    daa

Une fois compilĂ© Ă  l’aide de la commande nasm -f elf32 shellcode.asm -o shellcode.o && ld -i elf_i386 shellcode.o -o shellcode.

On peut de nouveau l’envoyer dans Ghidra pour comprendre ce qu’il fait.

Bien évidemment la condition saute au yeux et permet de récupérer le flag.

Figure 2

Figure 2: Condition de vérification du flag.

flag: STHACK{Yeyeye33}

Reverse - ghozt

Analyse

L’ARM est un langage avec lequel je suis beaucoup moins bon.

Pour ce challenge après avoir dĂ©couvert la fonction qui permet de faire la vĂ©rification du flag, j’ai dĂ©cidĂ© de le bruteforcer car je ne voulais pas reverse la fonction permettant de faire le calcul sur la chaĂ®ne passĂ© en argument car trop longue et chiante.

Dans un premier temps, le binaire récupère notre entré utilisateur print quelques messages et encode notre input.

Figure 1

Figure 1: Read, Puts, Encode.

Une fois ces actions effectués, le flag encodé est placé sur la stack et comparé avec notre input caractère par caractère.

Figure 2

Figure 2: Set encoded flag et check du flag avec notre input.

Si on regarde le code assembleur de cette fonction on remarque l’instruction cmp r2,r3 qui compare deux registres entre eux. Je vais pouvoir lancer un gdb-multiarch et placer un breakpoint sur cette instruction pour checker Ă  chaque fois mon input avec celui du flag.

Figure 3

Figure 3: Code assembleur de la boucle de vérification.

Bruteforce goes brrrrrrrrrrrrrrru

Pour le bruteforce, je vais lancer dans un chall le programme avec qemu et je vais lui mettre le flag -g qui permet de debug le binaire Ă  l’aide de gdb en remote.

Maintenant, il ne reste plus qu’Ă  boucler caractère par caractère et vĂ©rifier la sortie de gdb pour s’assurer que les registres sont Ă©gaux.

Voici le code python:


import subprocess

FLAG_LENGTH = 32
FLAG = ""

OLD_R3 = ""

char = -1

while char != FLAG_LENGTH:
    guess = 32
    char += 1
    while guess != 126:

        with open("/tmp/armflag", "w") as f:
            f.write(FLAG + chr(guess))

        CMD = [
            "gdb-multiarch",
            "--ex", "target remote:8090",
            "--ex", "b *0x000107cc",
        ]

        for k in range(len(FLAG) + 1):
            CMD.append("--ex")
            CMD.append("c")

        CMD.extend(["--ex", "i r", "--ex", "q"])

        process = subprocess.Popen(CMD, stdout=subprocess.PIPE, stderr=subprocess.PIPE)
        
        output = process.stdout.read()

        for line in output.splitlines():
            if line.startswith(b"r2"):
                r2 = line
            if line.startswith(b"r3"):
                r3 = line
        

        if OLD_R3 == r3:
            continue
        elif r2[2:] == r3[2:]:
            print(r2)
            print(r3)
            FLAG += (chr(guess) if len(FLAG) == 0 else chr(guess-1))
            print(f"Flag: {FLAG}")
            OLD_R3 = ""
            break
        else:
            OLD_R3 = r3
        
        guess += 1

# while true; do cat /tmp/armflag | qemu-arm-static -g 8090 ./armfull; done

Pour chaque caractère, il va lancer la commande gdb-multiarch, se connecter sur le binaire target remote:8090 placer un breakpoint sur 0x000107cc continuer l’exĂ©cution du programme tant qu’on connait le caractère et lorsque qu’on ne le connait pas on regarde les registres i r pour faire notre comparaison ensuite et on quitte q.

Petit Ă  petit le flag se dessine sur notre terminal.

Pour obtenir:

flag: STHARMK{th1s_1s_t00_3asy_F0R_M3}

Pwn

Analyse

Ce chall est typiquement le genre de chall qui n’est pas regardĂ© car il vaut beaucoup de point et pourtant qui est simple au point ou tout le monde pourrait le faire.

Une fois le binaire rĂ©cupĂ©rĂ© sur le système distant, on peut l’ouvrir dans ghidra pour comprendre son fonctionnement.

Dans un premier temps, le binaire va récupérer notre choix puis va exécuter une fonction dynamique en fonction de cela.

Figure 1

Figure 1: Choix de l’utilisateur.

La liste des fonctions est la suivante:

Figure 2

Figure 2: Liste des fonctions possible en fonction du choix.

Path to flag

En regardant de plus près la fonction choice_auth, on remarque qu’il va regarder si des utilisateurs ont Ă©tĂ© enregistrĂ© dans le programme, si c’est le cas, alors le programme demande un nom d’utilisateur et un mot de passe et les vĂ©rifie avec le compte admin. Si les identifiants sont bon alors le flag est affichĂ© sinon un message d’erreur est affichĂ©.

Figure 3

Figure 3: Code de la fonction choice_auth.

Le fichier contenant le mot de passe du compte admin et guest Ă©tant lisible pour arriver jusqu’au flag, rien de plus simple.

Il suffit de se laisser guider par le programme.

pleutre@dog:~$ cat admin.pw 
flag{mA1l0c_1s_e4syY}
pleutre@dog:~$ ./dog
[1] - Add user
[2] - List available users
[3] - Delete user
[4] - Log in
[5] - Exit
.····[?] Choice
ˇ·-> 1

.····[+] Username
ˇ·-> admin
Successfully added user 'admin'.

[1] - Add user
[2] - List available users
[3] - Delete user
[4] - Log in
[5] - Exit
.····[?] Choice
ˇ·-> 1

.····[+] Username
ˇ·-> guest
Successfully added user 'guest'.

[1] - Add user
[2] - List available users
[3] - Delete user
[4] - Log in
[5] - Exit
.····[?] Choice
ˇ·-> 2

[ADDED] admin: Unbreakable password.
[ADDED] guest: Weak password.

[1] - Add user
[2] - List available users
[3] - Delete user
[4] - Log in
[5] - Exit
.····[?] Choice
ˇ·-> 4

.····[!] Username
ˇ·-> admin
.····[!] Password
ˇ·-> flag{mA1l0c_1s_e4syY}
Successfully logged in as 'admin'.

ri3n_n3_Se_P4s5e_C0mm3_pRevU

Je ne pense pas que ce soit comme ça qu’il fallait rĂ©soudre ce challenge. Typiquement au vu du nom du mot de passe admin cela aurait du ressembler Ă  un double free mais tant pis pour le crĂ©ateur du challenge (Hackday devrait prendre de la graine).

flag: ri3n_n3_Se_P4s5e_C0mm3_pRevU