A l'occasion du IFSF CTF j'ai participé aux qualifs avec la team de Zenk-Security, qui a fini 10ème. http://ctf.forbiddenbits.net/?scoreboard.

Je n'ai pas eu trop le temps de participer mais le peu que j'ai fait a été fun (je n'ai pas eu affaire à du guess).
Je présente donc ci-dessous ma solution pour le challenge 10, un binaire DOS, 16 bits. Le binaire: 10.exe

Il s'exécute sans souci sur un Windows XP 32 bits.

Pour le désassembler, IDA bug, et les autres habituels tels que Olly ne prennent pas le 16-bit.
On utilise « Turbo Debugger for DOS », que l'on retrouve ici:

http://www.woodmann.com/collaborative/tools/images/Bin_Turbo_Debugger_2009-6-17_13.16_tdbg55.zip

Le binaire n'a aucun symboles, pas d'appels vers des libs Win etc, juste des interruptions.
On va donc chercher à quel moment il lit les données, pour commencer.
On lit http://en.wikipedia.org/wiki/BIOS_interrupt_call, on se rend compte qu'il nous faut une interruption 16h, qui est celle communiquant avec le clavier. On s'attend à avoir AH set à 0 avant l'interruption.

On trouve alors ce que l'on cherche, en 0x025D:

xor ax, ax
int 16
ret

On va donc break sur le ret, en 0x0261, pour revenir dans la fonction qui a demandé à lire le char, pour étudier celle-ci.
Après un run, avoir entré un char (on n'entre que des 'a'), et step, on retombe juste en dessous, en 0x026F:

0x026F:   
       mov si, [0114]
       mov bl, 09
 +> call [0104]                 ; call 0x25D / read char
 |     test al, al
 |     je 028B    ----+
 |     cmp al, 0D     |
 |     je 028B     ---+
 |     rol al, 03        |
 |     xor al, 19       |
 |     mov [si], al    |
 |     inc si              |
 |     ...                   |
 |     test bl, bl       |
 |     je 028B   ----+
 +-   jmp 026B      |
       ret         <---+

Cette fonction lit 9 char, sauf si elle reçoit un 0x0 ou 0xD ( '\r' ).
Cela nous donne une fonction similaire à la suivante en C:

void get_input(void)
{
    char s[10] = 0; // &s = 0x138 ( si )
    char c;
    int bl = 9;
    int i = 0;
 
    while (1)
    {
         c = getchar(); // al = c
 
         if (c == 0x00 || c == '\r')
             return s;
 
         if (bl-- == 0)
             return s;
 
          asm volatile("rol $0x03, %%al\r\n
                               xor $0x19, %%al\r\n"
                               : "=a"(c)
                               : "a"(c)
                               : "ax"); // Probablement n'importe quoi niveau input / output / clobber :D
 
          s[i++] = c; // mov [si], al ; inc si
    }
}

On retient surtout que notre chaîne est stockée « cryptée » en mémoire, avec un rol / xor appliqué sur le char reçu.
Entrer 'a' écrit 0x12 en mémoire.

On break sur le ret, en 0x28b, puis on step pour arriver sur:

call [010A] ; Affiche "Authorization code :"
call [0106] ; Lit le code
call [010E] ; ? Check code ?
ret

On suit donc l'exécution, on tombe sur:

mov bx, [0x114]
mov si, [0x116]
mov cx, 0x9
xor dx, dx
xor ax, ax
mov al, [bx]
xor al, [si]
test al, al
jz 0x145

Cette partie compare 2 chaînes, et on retrouve notre chaîne « cryptée » 0x12 * 9.


debug_dos

Elle est comparée à celle pointée par si (0148): 0xA0 0x8B 0x98 0x7B 0x63 0x90 0x98 0x90 0x63

On sort python pour inverser le xor / rol et:

>>> def ror(byte, count):
...     while count > 0:
...         byte = (byte >> 1 | byte << 7) & 0xFF
...         count -= 1
...     return byte
... 
>>> l = [0xA0, 0x8B, 0x98, 0x7B, 0x63, 0x90, 0x98, 0x90, 0x63]
>>> ''.join(chr(ror(0x19 ^ i, 3)) for i in l)
'7R0LO101O'

On a notre flag: 7R0LO101O.