Depuis le mois de mai, et jusqu’au 14 juin 2019, la Direction Générale de la Sécurité Extérieure propose un CTF, un concours de hacking éthique. Mission, recruter son personnel « geek ». (cf. https://www.zataz.com/concours-dgse-richelieu/)

Le challenge Richelieu commence sur le site https://www.challengecybersec.fr/.

Les éléments de l’article sont sur le Github de Pirates.RE.

Continuer la lecture

Publié le par Sébastien RAMELLA | Commentaires fermés sur Richelieu, à nous 4 Cardinal (Writeup)

Lors de ce second article dédié à Emotet, nous allons construire un PoC (Proof of Concept), en nous basant sur le retour d’expériences de l’article précédent, qui nous permettra d’extraire la configuration (les adresses IP).

Pour la beauté du geste, nous allons également regarder, plus en détail, la fonction d’unpacking pour les besoins de notre outil.

Nous avons observé dans l’article précédent que la portion de codes qui réalise le unpacking, est chargée et exécutée en mémoire. Mais si nous regardons plus en détail, nous comprenons que :

  • le registre ESI prend pour valeur « 3CC159BB » (la première clé XOR) à l’adresse 0x00402550.
  • le call VirtualAllocEx, à l’adresse 0x0040257B, sert à allouer de l’espace mémoire qui contiendra le code d’unpacking.

  • la boucle décrypte et charge les données dans la mémoire.
  • le registre ESI prend la nouvelle clé XOR à l’adresse 0x004025A6.

Nous pouvons représenter ce travail avec le script Python suivant :

#!/usr/bin/env python2.7

from sys import exit

def read_from_hex_offset(file_stream, hex_offset, bytes_to_read) :
  offset = int(hex_offset, 16)
  file_stream.seek(offset)
  return(file_stream.read(bytes_to_read))

def unpack_stage1(path_to_file) :
  from binascii import hexlify, unhexlify
  from struct import pack

  read_file = open(path_to_file,"rb")
  written_file = open("stage1.bin", "wb")

  ## First XOR key
  xor_key = 0x3cc159bb

  ## Decryption
  start_offset = 0x29f0
  max_offset = 1672
  for i in range(0, max_offset, 4) :
    bytes = hexlify(read_from_hex_offset(read_file, str(hex(start_offset)), 4))
    bytes = hexlify(pack('<L', int(bytes, 16)))
    bytes = (int(bytes, 16) + 0xffffffef) & 0xffffffff ## add eax, FFFFFFEF
    bytes = (bytes ^ xor_key) & 0xffffffff             ## xor eax, esi
    bytes = (bytes + 0xffffffff) & 0xffffffff          ## add eax, FFFFFFFF
    bytes = hexlify(pack('<L', bytes))
    xor_key = int(hexlify(pack('<L', int(bytes, 16))), 16)
    written_file.write(unhexlify(bytes))
    start_offset = start_offset + 4

  written_file.close()
  read_file.close()

def main() :
  unpack_stage1("../malware.exe")
  
if(__name__ == '__main__') :
  main()
  exit(0)

Puis utiliser le logiciel IDA pour avoir un aperçu global du résultat.

Nous poursuivons notre analyse, à la recherche d’Emotet.

Pour contrer l’anti-debug (en mode gros bourrin), j’ai utilisé le plugin TitanHide en ayant pris soin d’appliquer à mon système de sandboxing, le patch pour supprimer la protection « PatchGuard ».

J’ai réalisé une bonne partie du travail sans ce plugin. Mais, si je veux me faciliter un peu la vie, …

Pour les étapes suivantes, nous pouvons procéder comme ceci :

  • Nous relançons le programme. Nous activons TitanHide sur le process et nous nous positionnons sur le point d’entrée de celui-ci, à l’adresse 0x00402404.
  • Nous posons un « break point » sur l’adresse 0x004025CB.
  • Nous sollicitons la touche F9 pour nous rendre à l’adresse 0x004025CB puis F7 pour atterrir au point d’entrée mémoire du processus d’unpacking.

C’est à partir d’ici que TitanHide va nous faciliter la vie :

  • Nous positionnons un « Hardware break point » à l’adresse 0x00220028 (dans mon cas), puis F9 pour nous rendre à cette adresse.
  • Nous sollicitons la touche F8 jusqu’à atteindre l’adresse 0x0023009F. Puis nous suivons le « call 230519 » avec la touche F7. La routine de déchiffrement du binaire commence ici.

Nous pouvons constater, dans le registre EAX de la capture précédente, la valeur « MZ » (header de tous les exécutables couramment utilisés par Windows).

C’est le moment de coder un script en nous basant sur nos observations.

#!/usr/bin/env python2.7

from sys import exit

def get_stage1(file_stream, at_offset, size, xor_key) :
  from binascii import hexlify, unhexlify
  from struct import pack

  start_offset = at_offset
  max_offset   = size

  data         = ""
  for i in range(0, max_offset, 4) :
    bytes = hexlify(read_from_hex_offset(file_stream, str(hex(start_offset)), 4))
    bytes = int(hexlify(pack("<L", int(bytes, 16))), 16)
    bytes = xor_with_addition(bytes, xor_key)
    bytes = hexlify(pack("<L", bytes))
    xor_key = int(hexlify(pack("<L", int(bytes, 16))), 16)  
    data = data + unhexlify(bytes)

    start_offset = start_offset + 4

  return(data)

def parse_stage1(stage1) :
  from binascii import hexlify
  from struct import pack

  xor_key         = int(hexlify(pack("<L", int(hexlify(stage1[-284:-280]), 16))), 16)
  sub_vector1     = int(hexlify(pack("<L", int(hexlify(stage1[-354:-350]), 16))), 16)
  sub_fixed_value = int(hexlify(pack(">L", int(hexlify(stage1[-286:-285]), 16))), 16)
  at_offset       = int(hexlify(pack("<L", int(hexlify(stage1[-16:-12]), 16))), 16)
  max_offset      = int(hexlify(pack("<L", int(hexlify(stage1[-12:-8]), 16))), 16)

  return((xor_key, sub_vector1, sub_fixed_value, at_offset, max_offset))

def read_from_hex_offset(file_stream, hex_offset, bytes_to_read) :
  offset = int(hex_offset, 16)
  file_stream.seek(offset)
  return(file_stream.read(bytes_to_read))

def unpack(stage1, file_stream, size_of_raw_data) :
  from binascii import hexlify, unhexlify
  from struct import pack

  xor_key, sub_vector1, sub_fixed_value, at_offset, max_offset = parse_stage1(stage1)

  sub_key      = (int(hexlify(pack("<L", int(hexlify(stage1[-4:]), 16))), 16) + sub_vector1) & 0xffffffff
  start_offset =  at_offset - size_of_raw_data

  data         = ""
  for i in range(0, max_offset, 4) :
    bytes = hexlify(read_from_hex_offset(file_stream, str(hex(start_offset)), 4))
    next_sub_key = int(bytes, 16)
    bytes = int(hexlify(pack("<L", int(bytes, 16))), 16)
    bytes = xor_with_subtraction(bytes, xor_key, sub_key, sub_fixed_value)
    bytes = hexlify(pack("<L", bytes))
    sub_key = int(hexlify(pack("<L", next_sub_key)), 16)
    data = data + unhexlify(bytes)

    start_offset = start_offset + 4

  return(data)

def xor_with_addition(bytes, xor_key) :
  bytes = (bytes + 0xffffffef) & 0xffffffff             ## add eax, FFFFFFEF
  bytes = (bytes ^ xor_key) & 0xffffffff                ## xor eax, esi
  bytes = (bytes + 0xffffffff) & 0xffffffff             ## add eax, FFFFFFFF
  return(bytes)

def xor_with_subtraction(bytes, xor_key, sub_key, sub_fixed_value) :
  bytes = (bytes - sub_fixed_value) & 0xffffffff        ## sub eax, 7
  bytes = (bytes ^ xor_key) & 0xffffffff                ## xor eax, 954132AC
  bytes = (bytes - sub_key) & 0xffffffff                ## sub eax, edx
  return(bytes)

def main() :
  from pefile import PE
  from binascii import hexlify

  input_file = "../malware.exe"
  read_file    = open(input_file, "rb")

  output_file = "unpacked.bin"
  written_file = open(output_file,"wb")

  pe = PE(input_file)

  stage1 = get_stage1(read_file, 0x29f0, 1672, 0x3cc159bb)

  raw_base = int(pe.sections[1].SizeOfRawData & 0xffff) ## TODO: fix that.
  unpacked = unpack(stage1, read_file, raw_base)
  
  written_file.write(unpacked)
  written_file.close()

  read_file.close()
  
if(__name__ == '__main__') :
  main()
  exit(0)

Les règles Yara et Python

Pour nous aider dans notre travail, nous pouvons nous appuyer sur Yara. Un outil très apprécié des chercheurs en sécurité informatique spécialisés dans la traque aux Malwares.

On pourrait utiliser des règles Yara pour :

Dans un premier temps, je vais utiliser très basiquement Yara pour détecter si le binaire d’Emotet est packé || unpacké.

Nous poursuivons en étudiant les signatures du loader et du payload utilisées par CAPE Sandbox. Si la signature du Payload est satisfaisante, celle du Loader ne donne aucun résultat.

Nous gardons donc la signature du payload dont nous pouvons observer l’équivalence dans la capture d’écran ci-dessous.

Nous allons créer ensuite notre propre signature pour le packer. Pour cela, nous nous baserons sur le code suivant :

Nous adoptons la signature de cette fonction qui contient toutes les informations nécessaires au développement de notre PoC (la localisation des données, les informations de décryptage).

Nous pouvons coder quelque chose comme ceci (Je sais … le code est de plus en plus moche.) :

#!/usr/bin/env python2.7

from sys import exit

_R_EMOTET_ = """
rule Emotet_Payload {
  strings :
    // .text:00FC6860 | 33C0                      | xor eax,eax
    // .text:00FC6862 | C705 ???????? ????????    | mov dword ptr ds:[FD24A0],sonicredist.FCF710
    // .text:00FC686C | C705 ???????? ????????    | mov dword ptr ds:[FD24A4],sonicredist.FCF710
    // .text:00FC6876 | A3 ????????               | mov dword ptr ds:[FD24A8],eax
    // .text:00FC687B | A3 ????????               | mov dword ptr ds:[FD24AC],eax
    // .text:00FC6880 | ???? ????????             | cmp dword ptr ds:[FCF710],eax
    // .text:00FC6886 | ?? ??                     | je sonicredist.FC68AA
    // .text:00FC6888 | ?? ??                     | jmp sonicredist.FC6890
    // .text:00FC688A | ???? ??????00             | lea ebx,dword ptr ds:[ebx]
    // .text:00FC6890 | 40                        | inc eax
    // .text:00FC6891 | A3 ????????               | mov dword ptr ds:[FD24A8],eax
    // .text:00FC6896 | 833CC5 ???????? 00        | cmp dword ptr ds:[eax*8+FCF710],0
    // .text:00FC689E | 75 F0                     | jne sonicredist.FC6890
    // .text:00FC68A0 | 51                        | push ecx
    // .text:00FC68A1 | E8 ????????               | call sonicredist.FC1FA0
    // .text:00FC68A6 | 83C4 04                   | add esp,4
    // .text:00FC68A9 | C3                        | ret
    $unpacked = { 33 C0 C7 05 ?? ?? ?? ?? ?? ?? ?? ?? C7 05 ?? ?? ?? ?? ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 40 A3 ?? ?? ?? ?? 83 3C C5 ?? ?? ?? ?? 00 75 F0 51 E8 ?? ?? ?? ?? 83 C4 04 C3 }

  condition :
    // check for MZ Signature at offset 0
    (uint16(0) == 0x5A4D) and $unpacked
}

rule Emotet_Packed {
  strings :
    // .text:00402549 | BA ????????               | mov edx,malware.4037F0
    // .text:0040254E | 29F6                      | sub esi,esi
    // .text:00402550 | 81F6 ????????             | xor esi,3CC159BB
    // .text:00402556 | 52                        | push edx
    // .text:00402557 | B9 ????????               | mov ecx,40
    // .text:0040255C | 51                        | push ecx
    // .text:0040255D | BA ????????               | mov edx,1000
    // .text:00402562 | 52                        | push edx
    // .text:00402563 | BA ????????               | mov edx,688
    // .text:00402568 | 52                        | push edx
    // .text:00402569 | B9 ????????               | mov ecx,0
    // .text:0040256E | 51                        | push ecx
    // .text:0040256F | B9 ????????               | mov ecx,FFFFFFFF
    // .text:00402574 | 51                        | push ecx
    // .text:00402575 | 8D1D ????????             | lea ebx,dword ptr ds:[<&VirtualAllocEx>]
    // .text:0040257B | FF13                      | call dword ptr ds:[ebx]
    // .text:0040257D | 5A                        | pop edx
    // .text:0040257E | 83F8 00                   | cmp eax,0
    // .text:00402581 | 0F84 ????????             | je malware.402A07
    // .text:00402587 | 29DB                      | sub ebx,ebx
    // .text:00402589 | 4B                        | dec ebx
    // .text:0040258A | 21C3                      | and ebx,eax
    // .text:0040258C | 53                        | push ebx
    // .text:0040258D | 81FF ????????             | cmp edi,688
    // .text:00402593 | 74 ??                     | je malware.4025B7
    // .text:00402595 | ????                      | xor eax,eax
    // .text:00402597 | ????                      | sub eax,dword ptr ds:[edx]
    // .text:00402599 | ????                      | neg eax
    // .text:0040259B | 8D52 ??                   | lea edx,dword ptr ds:[edx+4]
    // .text:0040259E | 83C0 ??                   | add eax,FFFFFFEF
    // .text:004025A1 | ????                      | xor eax,esi
    // .text:004025A3 | 83C0 ??                   | add eax,FFFFFFFF
    // .text:004025A6 | ????                      | mov esi,eax
    // .text:004025A8 | 8943 ??                   | mov dword ptr ds:[ebx],eax
    // .text:004025AB | 8D?? ??                   | lea ebx,dword ptr ds:[ebx+4]
    // .text:004025AE | 8D?? ??                   | lea edi,dword ptr ds:[edi+4]
    // .text:004025B1 | 68 ????????               | push malware.40258D
    // .text:004025B6 | C3                        | ret
    $packed = { BA ?? ?? ?? ?? 29 F6 81 F6 ?? ?? ?? ?? 52 B9 ?? ?? ?? ?? 51 BA ?? ?? ?? ?? 52 BA ?? ?? ?? ?? 52 B9 ?? ?? ?? ?? 51 B9 ?? ?? ?? ?? 51 8D 1D ?? ?? ?? ?? FF 13 5A 83 F8 00 0F 84 ?? ?? ?? ?? 29 DB 4B 21 C3 53 81 FF ?? ?? ?? ?? 74 ?? ?? ?? ?? ?? ?? ?? 8D 52 ?? 83 C0 ?? ?? ?? 83 C0 ?? ?? ?? 89 43 ?? 8D ?? ?? 8D ?? ?? 68 ?? ?? ?? ?? C3 }

  condition :
    // check for MZ Signature at offset 0
    (uint16(0) == 0x5A4D) and $packed  
}
"""

def convert_bytes(size) :
  for x in ["Bytes", "Kb", "Mb", "Gb", "Tb"] :
    if size < 1024.0 :
      return("%3.1f %s" % (size, x))
    size /= 1024.0

def digest(algorithm, data) :
  if algorithm == "md5" :
    from hashlib import md5
    return(md5(data).hexdigest())
  elif algorithm == "sha1" :
    from hashlib import sha1
    return(sha1(data).hexdigest())
  elif algorithm == "sha256" :
    from hashlib import sha256
    return(sha256(data).hexdigest())

def file_size(path_to_file) :
  from os import stat
  from os.path import isfile

  if isfile(path_to_file) :
    file_info = stat(path_to_file)
    return(convert_bytes(file_info.st_size))
  return(0)

def get_stage1(file_stream, at_offset, size, xor_key, add_value1, add_value2) :
  from binascii import hexlify, unhexlify
  from struct import pack

  start_offset = at_offset
  max_offset   = size

  data         = ""
  for i in range(0, max_offset, 4) :
    bytes = hexlify(read_from_hex_offset(file_stream, str(hex(start_offset)), 4))
    bytes = int(hexlify(pack("<L", int(bytes, 16))), 16)
    bytes = xor_with_addition(bytes, xor_key, add_value1, add_value2)
    bytes = hexlify(pack("<L", bytes))
    xor_key = int(hexlify(pack("<L", int(bytes, 16))), 16)  
    data = data + unhexlify(bytes)

    start_offset = start_offset + 4

  return(data)

def parse_stage0(signature, image_base, raw_base) :
  from binascii import hexlify
  from struct import pack

  xor_key       = int(hexlify(pack("<L", int(hexlify(signature[0].strings[0][2][9:13]), 16))), 16)
  print_message("xor_key:\t" + str(hex(xor_key)))

  add_value1    = int(hexlify(signature[0].strings[0][2][-23:-22]), 16)
  print_message("add_value1:\t" + str(hex(add_value1)))

  add_value2    = int(hexlify(signature[0].strings[0][2][-18:-17]), 16)
  print_message("add_value2:\t" + str(hex(add_value2)))

  at_offset     = (int(hexlify(pack("<L", int(hexlify(signature[0].strings[0][2][1:5]), 16))), 16) - image_base) - raw_base
  print_message("at_offset:\t" + str(hex(at_offset)))

  max_offset    = int(hexlify(pack("<L", int(hexlify(signature[0].strings[0][2][27:31]), 16))), 16)
  print_message("max_offset:\t" + str(hex(max_offset)))

  return((xor_key, add_value1, add_value2, at_offset, max_offset))

def parse_stage1(stage1) :
  from binascii import hexlify
  from struct import pack

  print("\n[+] parse_stage1")
  xor_key         = int(hexlify(pack("<L", int(hexlify(stage1[-284:-280]), 16))), 16)
  print_message("xor_key:\t" + str(hex(xor_key)))

  sub_vector1     = int(hexlify(pack("<L", int(hexlify(stage1[-354:-350]), 16))), 16)
  print_message("sub_vector1:\t" + str(hex(sub_vector1)))

  sub_fixed_value = int(hexlify(pack(">L", int(hexlify(stage1[-286:-285]), 16))), 16)
  print_message("sub_vector2:\t" + str(hex(sub_fixed_value)))

  at_offset       = int(hexlify(pack("<L", int(hexlify(stage1[-16:-12]), 16))), 16)
  print_message("at_offset:\t" + str(hex(at_offset)))

  max_offset      = int(hexlify(pack("<L", int(hexlify(stage1[-12:-8]), 16))), 16)
  print_message("max_offset:\t" + str(hex(max_offset)))

  return((xor_key, sub_vector1, sub_fixed_value, at_offset, max_offset))

def print_message(message) :
  print("    --> %s" % (message))

def read_file_to_buffer(path_to_file, mode) :
  from os.path import isfile

  if not(isfile(path_to_file)) : return(False)
  try :
    with open(path_to_file, mode) as file_stream :
      file_content = file_stream.read()
  except Exception as Exception_Error :
    print("%s", (Exception_Error))
    return(False)
  return(file_content)

def read_from_hex_offset(file_stream, hex_offset, bytes_to_read) :
  offset = int(hex_offset, 16)
  file_stream.seek(offset)
  return(file_stream.read(bytes_to_read))

def run_yara(rule_code, buffer_to_sample) :
  from yara import compile

  try :
    yar_object = compile(source = rule_code)

    matches = yar_object.match(data = buffer_to_sample)
  except Exception as Exception_Error :
    print("%s", (Exception_Error))
    return(False)

  if len(matches) != 0 :
    return(matches)
  return(False)

def show_pe_info(path_to_file) :
  from pefile import PE
  
  print("filename:\t%s\nSize:\t\t%s\nArchitecture\t%s\n\nMD5:\t\t%s\nSHA1:\t\t%s\nSHA256:\t\t%s\n" % (
    path_to_file.split('/')[::-1][0],
    file_size(path_to_file),
    hex(PE(path_to_file).FILE_HEADER.Machine),
    digest("md5", read_file_to_buffer(path_to_file, "rb")),
    digest("sha1", read_file_to_buffer(path_to_file, "rb")),
    digest("sha256", read_file_to_buffer(path_to_file, "rb"))
  ))

def unpack(stage1, file_stream, size_of_raw_data) :
  from binascii import hexlify, unhexlify
  from struct import pack

  xor_key, sub_vector1, sub_fixed_value, at_offset, max_offset = parse_stage1(stage1)

  sub_key      = (int(hexlify(pack("<L", int(hexlify(stage1[-4:]), 16))), 16) + sub_vector1) & 0xffffffff
  start_offset =  at_offset - size_of_raw_data

  data         = ""
  for i in range(0, max_offset, 4) :
    bytes = hexlify(read_from_hex_offset(file_stream, str(hex(start_offset)), 4))
    next_sub_key = int(bytes, 16)
    bytes = int(hexlify(pack("<L", int(bytes, 16))), 16)
    bytes = xor_with_subtraction(bytes, xor_key, sub_key, sub_fixed_value)
    bytes = hexlify(pack("<L", bytes))
    sub_key = int(hexlify(pack("<L", next_sub_key)), 16)
    data = data + unhexlify(bytes)

    start_offset = start_offset + 4

  return(data)

def xor_with_addition(bytes, xor_key, add_value1, add_value2) :
  bytes = (bytes + 0xffffffef) & 0xffffffff                    ## add eax, FFFFFFEF
  bytes = (bytes ^ xor_key) & 0xffffffff                       ## xor eax, esi
  bytes = (bytes + 0xffffffff) & 0xffffffff                    ## add eax, FFFFFFFF
  return(bytes)

def xor_with_subtraction(bytes, xor_key, sub_key, sub_fixed_value) :
  bytes = (bytes - sub_fixed_value) & 0xffffffff               ## sub eax, 7
  bytes = (bytes ^ xor_key) & 0xffffffff                       ## xor eax, 954132AC
  bytes = (bytes - sub_key) & 0xffffffff                       ## sub eax, edx
  return(bytes)

def main() :
  from binascii import hexlify
  from pefile import PE
  from struct import pack

  path_to_sample = "../malware.exe"
  output_file = "unpacked.bin"
  
  read_file = open(path_to_sample, "rb")

  print("Emotet 2019 - Get Configuration Tools\n\t   by mekhalleh [www.pirates.re]\n")
  print("%s" % ("=" * 40))

  show_pe_info(path_to_sample)

  pe = PE(path_to_sample)
  raw_base = int(pe.sections[1].SizeOfRawData & 0xffff)        ## TODO: fix that?
  image_base = int(pe.OPTIONAL_HEADER.ImageBase)

  signature = run_yara(_R_EMOTET_, read_file_to_buffer(path_to_sample, "rb"))
  if (signature != False) :

    if (signature[0].strings[0][1] == "$packed") :
      print("emotet:\t\tPACKED\nsignature:\t%s (at offset)\n" % (hex(signature[0].strings[0][0])[:-1]))
      print("%s" % ("=" * 40))
      print("[+] Start unpacking Emotet")

      xor_key, add_value1, add_value2, at_offset, size = parse_stage0(signature, image_base, raw_base)

      stage1 = get_stage1(read_file, at_offset, size, xor_key, add_value1, add_value2) ## TODO: add xor vectors!
      unpacked = unpack(stage1, read_file, raw_base)

      s = run_yara(_R_EMOTET_, unpacked)
      if (s[0].strings[0][1] == "$unpacked") :
        written_file = open(output_file, "wb")
        print("\n%s" % ("=" * 40))
        print("[+] Write extracted file to <%s>" % (output_file))
        print("%s" % ("=" * 40))
        written_file.write(unpacked)
        written_file.close()

        show_pe_info(output_file)
        print("emotet:\t\tUNPACKED\nsignature:\t%s (at offset)\n" % (hex(s[0].strings[0][0])[:-1]))
      else :
        print("[!] No Emotet signature found!")
        exit(0)

    elif (signature[0].strings[0][1] == "$unpacked") :
      print("emotet:\t\tUNPACKED\nsignature:\t%s (at offset)\n" % (hex(signature[0].strings[0][0])[:-1]))

  else :
    print("[!] No Emotet signature found!")
    exit(0)

  ## Read Emotet configuration:
  print("%s" % ("=" * 40))

  ## TODO

  read_file.close()
  
if(__name__ == '__main__') :
  main()
  exit(0)

La génération des noms (exécutable et service)

Dans l’article précédent j’avais émis des hypothèses sur la génération du nom de l’exécutable (et du service). Voyons comment ceci fonctionne réellement.

Pour générer son nom d’exécutable (et de service), Emotet n’utilise qu’une seule et unique liste de noms et applique un algorithme pour choisir une première valeur dans cette liste. Puis un second tour permet de choisir une autre valeur qui est concaténée avec la première.

Le vecteur de taille fixe utilisé est la valeur de retour de la fonction GetVolumeInformationW.

Nous pouvons réaliser le script suivant pour illustrer ce travail.

#!/usr/bin/env python2.7

def get_name(pos, candidates) :
  if candidates[pos] == ',' :
    pos = pos + 1
  else :  
    for i in reversed(range(pos)) :
      if candidates[i] == ',' :
        break
      pos = pos - 1
  
  name = ""
  for i in range(len(candidates)) :
    if i >= pos :
      name = name + candidates[i]
      if candidates[i] == ',' :
        break
  return(name)

def main() :
  candidates = "rel,tables,glue,impl,texture,related,key,nis,langs,iprop,exec,wrap,matrix,dump,phoenix,ribbon,sorting,pinned,lics,bit,unpack,adt,rep,jobs,acl,title,sound,events,targets,scrn,mheg,lines,prompt,adjust,xian,ser,cycle,redist,its,boxes,dma,small,cloud,flow,guiddef,whole,parent,bears,random,bulk,idebug,viewer,starta,comment,sel,source,hotspot,pnf,portal,sitka,iell,slide,typ,sonic"
  VolumeInfo = 0xe42f18a6

  name = ""
  for i in range(2) :
    pos = VolumeInfo % len(candidates)
    VolumeInfo = VolumeInfo / len(candidates)
    VolumeInfo = ~VolumeInfo & 0xffffffff
  
    name = name + get_name(pos, candidates)

  print name[:-1]

main()

Dans mon cas, le nom du service s’appelle « sonicredist ».

Au cours du processus d’installation, Emotet génère un autre nom, depuis une liste différente, pour supprimer peut-être une ancienne version.

La fonction de suppression commence à l’adresse mémoire 0x107CCE0 (ASLR) qui fait appel à DeleteFileW.

Dans mon cas, le fichier à supprimer est « C:\\Windows\\SysWOW64\\satavg.exe ».

#!/usr/bin/env python2.7

def get_name(pos, candidates) :
  if candidates[pos] == ',' :
    pos = pos + 1
  else :  
    for i in reversed(range(pos)) :
      if candidates[i] == ',' :
        break
      pos = pos - 1
  
  name = ""
  for i in range(len(candidates)) :
    if i >= pos :
      name = name + candidates[i]
      if candidates[i] == ',' :
        break
  return(name)

def main() :
  candidates = "not,ripple,svcs,serv,wab,shader,single,without,wcs,define,eap,culture,slide,zip,tmpl,mini,polic,panes,earcon,menus,detect,form,uuidgen,pnp,admin,tuip,avatar,started,dasmrc,alaska,guids,wfp,adam,wgx,lime,indexer,repl,dev,mapi,resw,daf,diag,iss,vsc,turned,neutral,sat,source,enroll,mfidl,idl,based,right,cbs,radar,avg,wordpad,metagen,mouse,iprop,mdmmcd,jersey,thunk,subs"
  VolumeInfo = 0xe42f18a6

  name = ""
  for i in range(2) :
    pos = VolumeInfo % len(candidates)
    VolumeInfo = VolumeInfo / len(candidates)
    VolumeInfo = ~VolumeInfo & 0xffffffff
  
    name = name + get_name(pos, candidates).replace(',', '')

  print name

main()

Voici donc pour ce qui est de cette subtilité.

Obtenir la configuration (les adresses IP)

Cette opération va être rapide car nous avons effectué ce travail lors de notre première analyse. Il ne reste donc plus qu’à vérifier la condition de sortie de la boucle et coder …

#!/usr/bin/env python2.7

from sys import argv, exit

_R_EMOTET_ = """
rule Emotet_Payload {
  strings :
    // .text:00FC6860 | 33C0                      | xor eax,eax
    // .text:00FC6862 | C705 ???????? ????????    | mov dword ptr ds:[FD24A0],sonicredist.FCF710
    // .text:00FC686C | C705 ???????? ????????    | mov dword ptr ds:[FD24A4],sonicredist.FCF710
    // .text:00FC6876 | A3 ????????               | mov dword ptr ds:[FD24A8],eax
    // .text:00FC687B | A3 ????????               | mov dword ptr ds:[FD24AC],eax
    // .text:00FC6880 | ???? ????????             | cmp dword ptr ds:[FCF710],eax
    // .text:00FC6886 | ?? ??                     | je sonicredist.FC68AA
    // .text:00FC6888 | ?? ??                     | jmp sonicredist.FC6890
    // .text:00FC688A | ???? ??????00             | lea ebx,dword ptr ds:[ebx]
    // .text:00FC6890 | 40                        | inc eax
    // .text:00FC6891 | A3 ????????               | mov dword ptr ds:[FD24A8],eax
    // .text:00FC6896 | 833CC5 ???????? 00        | cmp dword ptr ds:[eax*8+FCF710],0
    // .text:00FC689E | 75 F0                     | jne sonicredist.FC6890
    // .text:00FC68A0 | 51                        | push ecx
    // .text:00FC68A1 | E8 ????????               | call sonicredist.FC1FA0
    // .text:00FC68A6 | 83C4 04                   | add esp,4
    // .text:00FC68A9 | C3                        | ret
    $unpacked = { 33 C0 C7 05 ?? ?? ?? ?? ?? ?? ?? ?? C7 05 ?? ?? ?? ?? ?? ?? ?? ?? A3 ?? ?? ?? ?? A3 ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? ?? 00 40 A3 ?? ?? ?? ?? 83 3C C5 ?? ?? ?? ?? 00 75 F0 51 E8 ?? ?? ?? ?? 83 C4 04 C3 }

  condition :
    // check for MZ Signature at offset 0
    (uint16(0) == 0x5A4D) and $unpacked
}

rule Emotet_Packed {
  strings :
    // .text:00402549 | BA ????????               | mov edx,malware.4037F0
    // .text:0040254E | 29F6                      | sub esi,esi
    // .text:00402550 | 81F6 ????????             | xor esi,3CC159BB
    // .text:00402556 | 52                        | push edx
    // .text:00402557 | B9 ????????               | mov ecx,40
    // .text:0040255C | 51                        | push ecx
    // .text:0040255D | BA ????????               | mov edx,1000
    // .text:00402562 | 52                        | push edx
    // .text:00402563 | BA ????????               | mov edx,688
    // .text:00402568 | 52                        | push edx
    // .text:00402569 | B9 ????????               | mov ecx,0
    // .text:0040256E | 51                        | push ecx
    // .text:0040256F | B9 ????????               | mov ecx,FFFFFFFF
    // .text:00402574 | 51                        | push ecx
    // .text:00402575 | 8D1D ????????             | lea ebx,dword ptr ds:[<&VirtualAllocEx>]
    // .text:0040257B | FF13                      | call dword ptr ds:[ebx]
    // .text:0040257D | 5A                        | pop edx
    // .text:0040257E | 83F8 00                   | cmp eax,0
    // .text:00402581 | 0F84 ????????             | je malware.402A07
    // .text:00402587 | 29DB                      | sub ebx,ebx
    // .text:00402589 | 4B                        | dec ebx
    // .text:0040258A | 21C3                      | and ebx,eax
    // .text:0040258C | 53                        | push ebx
    // .text:0040258D | 81FF ????????             | cmp edi,688
    // .text:00402593 | 74 ??                     | je malware.4025B7
    // .text:00402595 | ????                      | xor eax,eax
    // .text:00402597 | ????                      | sub eax,dword ptr ds:[edx]
    // .text:00402599 | ????                      | neg eax
    // .text:0040259B | 8D52 ??                   | lea edx,dword ptr ds:[edx+4]
    // .text:0040259E | 83C0 ??                   | add eax,FFFFFFEF
    // .text:004025A1 | ????                      | xor eax,esi
    // .text:004025A3 | 83C0 ??                   | add eax,FFFFFFFF
    // .text:004025A6 | ????                      | mov esi,eax
    // .text:004025A8 | 8943 ??                   | mov dword ptr ds:[ebx],eax
    // .text:004025AB | 8D?? ??                   | lea ebx,dword ptr ds:[ebx+4]
    // .text:004025AE | 8D?? ??                   | lea edi,dword ptr ds:[edi+4]
    // .text:004025B1 | 68 ????????               | push malware.40258D
    // .text:004025B6 | C3                        | ret
    $packed = { BA ?? ?? ?? ?? 29 F6 81 F6 ?? ?? ?? ?? 52 B9 ?? ?? ?? ?? 51 BA ?? ?? ?? ?? 52 BA ?? ?? ?? ?? 52 B9 ?? ?? ?? ?? 51 B9 ?? ?? ?? ?? 51 8D 1D ?? ?? ?? ?? FF 13 5A 83 F8 00 0F 84 ?? ?? ?? ?? 29 DB 4B 21 C3 53 81 FF ?? ?? ?? ?? 74 ?? ?? ?? ?? ?? ?? ?? 8D 52 ?? 83 C0 ?? ?? ?? 83 C0 ?? ?? ?? 89 43 ?? 8D ?? ?? 8D ?? ?? 68 ?? ?? ?? ?? C3 }

  condition :
    // check for MZ Signature at offset 0
    (uint16(0) == 0x5A4D) and $packed  
}
"""

def cli_args() :
  import argparse
  parser   = argparse.ArgumentParser(
    add_help    = False,
    description = "Emotet 2019 - Get Configuration Tools"
  )

  optional = parser._action_groups.pop()
  required = parser.add_argument_group("required arguments")

  required.add_argument(
    "--sample", "-s", action = "store",
    help = "Path to sample file to input."
  )

  required.add_argument(
    "--out-file", "-o", action = "store",
    help = "Path to unpacked file out."
  )

  optional.add_argument(
    "--in-stage1", "-i1", action = "store",
    help = "Path to stage1 file to input."
  )

  optional.add_argument(
    "--help", "-h", action = "store_true",
    help = argparse.SUPPRESS
  )

  parser._action_groups.append(optional)
  return(cli_args_helper(parser.parse_args(), parser))

def cli_args_helper(arguments, parser) :
  # Help message and exit.
  if((arguments.help) or (len(argv) <= 1)) :
    parser.print_help()
    exit(0)

  return(arguments)

def convert_bytes(size) :
  for x in ["Bytes", "Kb", "Mb", "Gb", "Tb"] :
    if size < 1024.0 :
      return("%3.1f %s" % (size, x))
    size /= 1024.0

def digest(algorithm, data) :
  if algorithm == "md5" :
    from hashlib import md5
    return(md5(data).hexdigest())
  elif algorithm == "sha1" :
    from hashlib import sha1
    return(sha1(data).hexdigest())
  elif algorithm == "sha256" :
    from hashlib import sha256
    return(sha256(data).hexdigest())

def exiting(message, ret_code) :
  print("[!!] %s\nExiting..." % (message))
  exit(ret_code)

def file_size(path_to_file) :
  from os import stat
  from os.path import isfile

  if isfile(path_to_file) :
    file_info = stat(path_to_file)
    return(convert_bytes(file_info.st_size))
  return(0)

def get_stage1(file_stream, at_offset, size, xor_key, add_value1, add_value2) :
  from binascii import hexlify, unhexlify
  from struct import pack

  start_offset = at_offset
  max_offset = size

  data = ""
  for i in range(0, max_offset, 4) :
    bytes = hexlify(read_from_hex_offset(file_stream, str(hex(start_offset)), 4))
    bytes = int(hexlify(pack("<L", int(bytes, 16))), 16)
    bytes = xor_with_addition(bytes, xor_key, add_value1, add_value2)
    bytes = hexlify(pack("<L", bytes))
    xor_key = int(hexlify(pack("<L", int(bytes, 16))), 16)  
    data = data + unhexlify(bytes)

    start_offset = start_offset + 4

  return(data)

def hex_to_ip(ip) :
  from socket import inet_ntoa
  from struct import pack

  return(inet_ntoa(pack("<L", ip)))

def parse_stage0(signature, image_base, raw_base) :
  from binascii import hexlify
  from struct import pack

  print("\n ++ [ parse_stage0 ] ++")

  xor_key = int(hexlify(pack("<L", int(hexlify(signature[0].strings[0][2][9:13]), 16))), 16)
  v1_add = int(hexlify(signature[0].strings[0][2][-23:-22]), 16)
  v2_add = int(hexlify(signature[0].strings[0][2][-18:-17]), 16)
  at_offset = (int(hexlify(pack("<L", int(hexlify(signature[0].strings[0][2][1:5]), 16))), 16) - image_base) - raw_base
  max_offset = int(hexlify(pack("<L", int(hexlify(signature[0].strings[0][2][27:31]), 16))), 16)

  print(" xor_key:\t%s\n v1_add:\t%s\n v2_add:\t%s\n at_offset:\t%s\n max_offset:\t%s" % (
    str(hex(xor_key)),
    str(hex(v1_add)),
    str(hex(v2_add)),
    str(hex(at_offset)),
    str(hex(max_offset)),
  ))

  return((xor_key, v1_add, v2_add, at_offset, max_offset))

def parse_stage1(stage1) :
  from binascii import hexlify
  from struct import pack

  print("\n ++ [ parse_stage1 ] ++")

  xor_key = int(hexlify(pack("<L", int(hexlify(stage1[-284:-280]), 16))), 16)
  v1_sub = int(hexlify(pack("<L", int(hexlify(stage1[-354:-350]), 16))), 16)
  v2_sub = int(hexlify(pack(">L", int(hexlify(stage1[-286:-285]), 16))), 16)
  at_offset = int(hexlify(pack("<L", int(hexlify(stage1[-16:-12]), 16))), 16)
  max_offset = int(hexlify(pack("<L", int(hexlify(stage1[-12:-8]), 16))), 16)
  
  print(" xor_key:\t%s\n v1_sub:\t%s\n v2_sub:\t%s\n at_offset:\t%s\n max_offset:\t%s" % (
    str(hex(xor_key)),
    str(hex(v1_sub)),
    str(hex(v2_sub)),
    str(hex(at_offset)),
    str(hex(max_offset)),
  ))

  return((xor_key, v1_sub, v2_sub, at_offset, max_offset))

def pe_info(path_to_file) :
  from pefile import PE

  pe = PE(path_to_file)
  image_base = int(pe.OPTIONAL_HEADER.ImageBase)

  data = read_file_to_buffer(path_to_file, "rb")
  print("filename:\t%s\nSize:\t\t%s\nArchitecture\t%s\n\nMD5:\t\t%s\nSHA1:\t\t%s\nSHA256:\t\t%s\n" % (
    path_to_file.split('/')[::-1][0],
    file_size(path_to_file),
    hex(pe.FILE_HEADER.Machine),
    digest("md5", data),
    digest("sha1", data),
    digest("sha256", data)
  ))
  pe.close()

  signature = run_yara(_R_EMOTET_, data)
  if (signature) :
    print("emotet:\t\t%s\nsignature:\t%s (at offset)\n" % (signature[0].strings[0][1], hex(signature[0].strings[0][0])[:-1]))
  return((image_base, signature))

def read_file_to_buffer(path_to_file, mode) :
  from os.path import isfile

  if not(isfile(path_to_file)) : return(False)
  try :
    with open(path_to_file, mode) as file_stream :
      file_content = file_stream.read()
  except Exception as Exception_Error :
    print("%s", (Exception_Error))
    return(False)
  return(file_content)

def read_from_hex_offset(file_stream, hex_offset, bytes_to_read) :
  offset = int(hex_offset, 16)
  file_stream.seek(offset)
  return(file_stream.read(bytes_to_read))

def run_yara(rule_code, buffer_to_sample) :
  from yara import compile

  try :
    yar_object = compile(source = rule_code)
    matches = yar_object.match(data = buffer_to_sample)

  except Exception as Exception_Error :
    print("%s", (Exception_Error))
    return(False)

  if len(matches) != 0 :
    return(matches)
  return(False)

def unpack(stage1, file_stream, size_of_raw_data) :
  from binascii import hexlify, unhexlify
  from struct import pack

  xor_key, sub_vector1, sub_fixed_value, at_offset, max_offset = parse_stage1(stage1)

  sub_key = (int(hexlify(pack("<L", int(hexlify(stage1[-4:]), 16))), 16) + sub_vector1) & 0xffffffff
  start_offset =  at_offset - size_of_raw_data

  data = ""
  for i in range(0, max_offset, 4) :
    bytes = hexlify(read_from_hex_offset(file_stream, str(hex(start_offset)), 4))
    next_sub_key = int(bytes, 16)
    bytes = int(hexlify(pack("<L", int(bytes, 16))), 16)
    bytes = xor_with_subtraction(bytes, xor_key, sub_key, sub_fixed_value)
    bytes = hexlify(pack("<L", bytes))
    sub_key = int(hexlify(pack("<L", next_sub_key)), 16)
    data = data + unhexlify(bytes)

    start_offset = start_offset + 4

  return(data)

def write_file(path_to_file, mode, data) :
  try :
    with open(path_to_file, mode) as written_file :
      written_file.write(data)
  except IOError :
    return(False)
  return(True)

def xor_with_addition(bytes, xor_key, v1_add, v2_add) :

  v1_add = int("0xffffff" + str(hex(v1_add)).replace("0x", ''), 16)
  v2_add = int("0xffffff" + str(hex(v2_add)).replace("0x", ''), 16)

  bytes = (bytes + v1_add) & 0xffffffff                        ## add eax, FFFFFFEF
  bytes = (bytes ^ xor_key) & 0xffffffff                       ## xor eax, esi
  bytes = (bytes + v2_add) & 0xffffffff                        ## add eax, FFFFFFFF

  return(bytes)

def xor_with_subtraction(bytes, xor_key, v1_sub, v2_sub) :
  bytes = (bytes - v2_sub) & 0xffffffff                        ## sub eax, 7
  bytes = (bytes ^ xor_key) & 0xffffffff                       ## xor eax, 954132AC
  bytes = (bytes - v1_sub) & 0xffffffff                        ## sub eax, edx
  return(bytes)

def get_config(data, signature, image_base, raw_base) :
  from binascii import hexlify
  from struct import pack

  print("[+] IP adresses list")
  pos_to_ip = (int(hexlify(pack("<L", int(hexlify(signature[0].strings[0][2][8:12]), 16))), 16) - image_base) - raw_base

  ip = -1
  while ip != 0 :
    ip = int(hexlify(read_from_hex_offset(data, str(hex(pos_to_ip)), 4)), 16)
    if ip != 0 :
      print(" 0x%X\t<->\t%s" % (ip, hex_to_ip(ip)))
    pos_to_ip = pos_to_ip + 8

def main() :
  from pefile import PE

  params = cli_args()
  print("Emotet 2019 - Get Configuration Tools\n\t   by mekhalleh [www.pirates.re]\n")

  read_file = open(params.sample, "rb")

  ## Get image base address, Yara signature and show PE informations:
  print("%s" % ("=" * 40))
  image_base, signature = pe_info(params.sample)

  if (not(signature) and not(params.in_stage1)) : exiting("No supported version of Emotet or signature no found!", -1)

  if ((signature != False) and (str(signature[0]) == "Emotet_Packed")) or ((signature != False) and (str(signature[0]) == "Emotet_Packed") and params.in_stage1) or ((signature == False) and (params.in_stage1)) :
    print("%s" % ("=" * 40))
    print("[+] Start unpacking Emotet")

    pe = PE(params.sample)
    raw_base = int(pe.sections[1].SizeOfRawData & 0xffff)    ## section:.joi -> TODO: fix that?
    pe.close()

    if(params.in_stage1) :
      print("\n ++ [ load from exported stage1 ] ++")
      stage1 = read_file_to_buffer(params.in_stage1, "rb")
    else :
      if ((signature != False) and (signature[0].strings[0][1] == "$packed")) :
        xor_key, v1_add, v2_add, at_offset, max_offset = parse_stage0(signature, image_base, raw_base)
        stage1 = get_stage1(read_file, at_offset, max_offset, xor_key, v1_add, v2_add)

    unpacked = unpack(stage1, read_file, raw_base)
    
    if not(write_file(params.out_file, "wb", unpacked)) :
      exiting("Unable to create/write file on disk!", -1)
    print("\n[+] Write extracted file to <%s>\n" % (params.out_file))
    
    image_base, signature = pe_info(params.out_file)
    if ((signature) and (signature[0].strings[0][1] == "$unpacked")) :
      read_file = open(params.out_file, "rb")
    else : exiting("No supported version of Emotet or signature no found!", -1)

  ## Read Emotet configuration:
  if (signature[0].strings[0][1] == "$unpacked") :
    print("%s" % ("=" * 40))

    pe = PE(params.out_file)
    raw_base = int(pe.sections[2].SizeOfRawData & 0xffff)    ## section:.data -> TODO: fix that?
    pe.close()

    get_config(read_file, signature, image_base, raw_base)

if(__name__ == '__main__') :
  main()
  exit(0)

Conclusions

Le mérite en revient aux éditeurs d’antivirus qui doivent avoir un travail d’analyse énorme. J’ai testé mon script sur plusieurs souches téléchargées sur Hybrid Analysis avec un résultat plutôt satisfaisant, jusqu’à ce que … je trouve une autre variante d’Emotet.

En analysant un peu rapidement, nous pouvons supposer que cette variante est plus récente que la précédente. Elle est basée sur le même schéma d’exécution mais avec un algorithme un peu différent (pour rendre encore plus compliquer le travail de l’analyste).

Ceci dit, avant de finir, j’ai quand même eu très envie de savoir si le payload (le binaire unpacké) avait changé! Et c’est bien une nouvelle souche d’Emotet.

Nous pouvons le vérifier avec le plugin diaphora et IDA…

Pour réaliser la signature, nous pouvons (sans doute) nous baser sur la même méthode que la version 5 et en cherchant un peu …

Nous pouvons utiliser l’outil mkYARA et extraire la signature depuis IDA (en tant que plugin) ou directement en ligne de commande.

Les scripts finaux sont disponibles sur Github (https://github.com/PiratesRE/emoteted).

Dans le prochain article dédié à Emotet, nous étudierons le protocole de communication pour essayer de comprendre comment tout ceci fonctionne.

Publié le par Sébastien RAMELLA | Commentaires fermés sur Emotet v5 Reversing (unpacking, basic analysis and Indiana Jones) [part. II]

Emotet est un cheval de Troie bancaire qui a été identifié pour la première fois en 2014 par des chercheurs en sécurité. Il était conçu à l’origine comme un malware bancaire qui tentait de pénétrer de manière sournoise dans votre ordinateur pour voler des informations sensibles et privées. Les versions suivantes du logiciel ont vu l’ajout de services de diffusion de spam et de malware, y compris d’autres chevaux de Troie bancaires.

Emotet a ciblé des individus, des entreprises et des entités gouvernementales d’un bout à l’autre des États-Unis et de l’Europe, en volant des identifiants bancaires, des données financières et même des portefeuilles Bitcoin.

Une attaque notable d’Emotet sur la ville d’Allentown, en Pennsylvanie, a nécessité une aide directe de l’équipe de réponse aux incidents de Microsoft. Le nettoyage et la maintenance auraient coûté plus de 1 million de dollars.

Maintenant qu’Emotet sert à télécharger et à diffuser d’autres chevaux de Troie bancaires, la liste des cibles est potentiellement encore plus importante. Les premières versions d’Emotet étaient utilisées pour attaquer les clients bancaires en Allemagne. Les versions suivantes ciblaient des organisations au Canada, au Royaume-Uni et aux États-Unis (source: Malwarebytes).

Continuer la lecture

Publié le par Sébastien RAMELLA | Commentaires fermés sur Emotet v5 Reversing (unpacking, basic analysis and Indiana Jones) [part. I]

NES R&D Challenge (PCAP) || Special Cypherpunk Reverser [part. I]

 

Je voulais justement faire un sujet sur les « packers » et il se trouve qu’étant d’un naturel un peu curieux j’ai mis la main sur un autre CTF qui se cache sur le site de la société NES et dont l’une des épreuves consiste justement à « unpacker » un binaire Windows (Portable Executable) 32-bits classique, enfin … presque.

Mais avant de commencer, un petit intermède musicale (parce que … vives les Pirates!) 😛

Donc … pour votre challenge de sécurité habitants des strates, rendez-vous sur le site de la société NES. Continuer la lecture

Publié dans Write-Ups | Commentaires fermés sur NES R&D Challenge (PCAP) || Special Cypherpunk Reverser [part. I]

Misc. N°91 (NES Challenge-2) || Passw0rd Cr4ck1ng

 

Je commence un nouveau « post », mais à peine entamé, celui-ci a un goût de réchauffé 🙁

Alors, je ne pense pas passer trop de temps sur ce sujet. C’est le second « challenge » de sécurité de la société NES pour Misc Magazine. Si le premier était orienté « cryptographie », celui-ci, commence « tout pareil » mais avec des épreuves bien différentes.

En introduction, je vous invite à suivre et à écouter Le Comptoir Sécu, notamment l’épisode 36 sur les mots de passe.

 

Autrement, voici la publicité en question. Continuer la lecture

Publié dans Write-Ups | Commentaires fermés sur Misc. N°91 (NES Challenge-2) || Passw0rd Cr4ck1ng

Misc. N°89 (NES Challenge-1) || Breaking RSA Cryptosystem

 

Il y a quelques mois est paru dans le magazine Misc. numéro 89 un « challenge » de sécurité. Une photo de l’annonce ci-dessous.

Le « challenge » commence par la lecture d’un « QR Code ». J’ai modifié l’image précédente pour ne garder que la partie corespondante à l’épreuve. Continuer la lecture

Publié dans Write-Ups | Commentaires fermés sur Misc. N°89 (NES Challenge-1) || Breaking RSA Cryptosystem

Dealing with a RAT

 

Depuis quelques temps j’accordais une grande attention à l’idée de faire un blog, et les sujets ne me manquent pas…

Je dois avouer que je ne suis pas très à l’aise lorsqu’il s’agit de faire de la rédaction, et certains sujets sont visiblement censurés par notre chère démocratie (cf. Condamné en justice pour un tuto Aircrack).

Et, une obsession en chasse une autre …

Alors, en rangeant un peu de contenu numérique, je suis tombé par hasard sur une version de « Bozok-RAT ». Quoi ? un rat ? …

Commençons par le commencement … Continuer la lecture

Publié dans Reverse Engineering | Commentaires fermés sur Dealing with a RAT