Tinyhack.com

Flare-On 4: Challenge 9 Quick Solution

This is an Arduino (AVR) challenge. You can read the full official solution from FireEye, here I just want to show how we can just find use “grep” to quickly find the decryption function to get the flag.

At first, I was going to try to understand what this binary does, but before going too deep, I had an idea: this binary is so small, what if I can just find the flag string without looking at the program’s logic. Looking at the strings present in the binary, it is obvious The flag is not in cleartext, so it must be encrypted somehow.

Most encryption algorithm will involve the use of XOR (eor in AVR). Looking at the disassembly, all EORs are just to clear a register (e.g: eor r1, r1). There is only one eor in 0xaee that is not clearing a register (eor r25, r24), which is the last one in this grep output.

$ avr-objdump -m avr -D remorse.ino.hex |grep eor
      c4:	11 24       	eor	r1, r1
     1ec:	99 27       	eor	r25, r25
     2e6:	99 27       	eor	r25, r25
     340:	11 27       	eor	r17, r17
     59e:	88 27       	eor	r24, r24
     742:	11 24       	eor	r1, r1
     78e:	11 24       	eor	r1, r1
     7f2:	11 24       	eor	r1, r1
     904:	11 24       	eor	r1, r1
     a16:	11 24       	eor	r1, r1
     aee:	98 27       	eor	r25, r24

Looking at the code around it: it is a single loop, with eor and subi. This must be the decrypt loop.

  ae6:       ldi     r26, 0x6C       ; 108
  ae8:       ldi     r27, 0x05       ; 5
  aea:       ldi     r18, 0x00       ; 0

decrypt:
  aec:       ld      r25, Z+
  aee:       eor     r25, r24
  af0:       add     r25, r18
  af2:       st      X+, r25
  af4:       subi    r18, 0xFF       ; 255
  af6:       cpi     r18, 0x17       ; 23
  af8:       brne    .-14            ; 0xaec 

We just need to find the encrypted data pointed by Z (which is a pair of R31:R30), and r24 (the xor key). Looking a bit up, we found the code that fills in the encrypted data. It sets Z with the value of Y (pair of R29:R28), clears the memory, and fill it with some bytes.

  a80:   movw    r30, r28        ; Z = Y
  a82:   adiw    r30, 0x01       ; Z++
  a84:   movw    r26, r30        ; X = Z
  a86:   ldi     r25, 0xFF       ; 
  a88:   add     r25, r30        ; 

clear:
  a8a:   st      X+, r1
  a8c:   cpse    r25, r26
  a8e:   rjmp    .-6             ; 0xa8a 

  a90:   ldi     r25, 0xB5 
  a92:   std     Y+1, r25  
  a94:   std     Y+2, r25  
  a96:   ldi     r25, 0x86 
  a98:   std     Y+3, r25  
  a9a:   ldi     r25, 0xB4 
  a9c:   std     Y+4, r25  
  a9e:   ldi     r25, 0xF4 
  aa0:   std     Y+5, r25  
  aa2:   ldi     r25, 0xB3 
  aa4:   std     Y+6, r25  
  aa6:   ldi     r25, 0xF1 
  aa8:   std     Y+7, r25  
  aaa:   ldi     r18, 0xB0 
  aac:   std     Y+8, r18  
  aae:   std     Y+9, r18  
  ab0:   std     Y+10, r25 
  ab2:   ldi     r25, 0xED 
  ab4:   std     Y+11, r25 
  ab6:   ldi     r25, 0x80 
  ab8:   std     Y+12, r25 
  aba:   ldi     r25, 0xBB 
  abc:   std     Y+13, r25 
  abe:   ldi     r25, 0x8F 
  ac0:   std     Y+14, r25 
  ac2:   ldi     r25, 0xBF 
  ac4:   std     Y+15, r25 
  ac6:   ldi     r25, 0x8D 
  ac8:   std     Y+16, r25 
  aca:   ldi     r25, 0xC6 
  acc:   std     Y+17, r25 
  ace:   ldi     r25, 0x85 
  ad0:   std     Y+18, r25 
  ad2:   ldi     r25, 0x87 
  ad4:   std     Y+19, r25 
  ad6:   ldi     r25, 0xC0 
  ad8:   std     Y+20, r25 
  ada:   ldi     r25, 0x94 
  adc:   std     Y+21, r25 
  ade:   ldi     r25, 0x81 
  ae0:   std     Y+22, r25 
  ae2:   ldi     r25, 0x8C 
  ae4:   std     Y+23, r25 

Going a bit up again, we found a ret (return), which means its the end of another function/subroutine. It seems that r24 is filled somewhere else by the caller of this decrypt function.

It doesn’t matter, r24 is just an 8 bit register (256 possible values). Translating this to python, with a brute force loop:

a = "b5b586b4f4b3f1b0b0f1ed80bb8fbf8dc68587c094818c".decode("hex")

for key in range(0, 256):
        s = ''
        for i,c in enumerate(a):
                m = ((ord(c)^key) + i)&0xff
                s = s + chr(m)
        print key, hex(key), s

And since all flags always have a flare-on.com suffix, we can just add a grep:

$ python brute.py|strings|grep flare
219 0xdb no_r3m0rs3@flare-on.com

So the flag is no_r3m0rs3@flare-on.com and the key is 219 decimal (0xdb).

Exit mobile version