Xylitol

hack.lu CTF 2025 - Rev: INSTRUCTIONS UNCLEAR

Oct 19th, 2025
1,902
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
Python 10.73 KB | None | 0 0
  1. # ================================================================
  2. # Solveur pour le validateur ASM "IKEA" hack.lu CTF 2025 - Rev: INSTRUCTIONS UNCLEAR
  3. # Solution by Hackgyver 2.0 & Claude.ai
  4. # Ce que fait le code ASM:
  5. # IN -> masquage -> substitution -> rolling-XOR -> permutation -> CAND
  6. # Ensuite il vérifie si (CAND == c5d) alors PASS = 1 sinon PASS = 0
  7. # Donc ce que nous devons faire:
  8. # CAND -> dé-permutation -> annuler le rolling-XOR -> annuler la substitution -> dé-masquer -> IN
  9. # ================================================================
  10.  
  11. # ASM: .ikea : constantes
  12. FLEN  = 65  # Longueur du fichier / nombre total de bytes attendus en entrée
  13. paX   = 23  # seed principale utilisée pour générer les clés
  14. BLKSZ = 12  # Taille d'un bloc (les données sont traitées par paquets de 12 bytes)
  15.  
  16. # ASM: c5d, le flag encodé (la sortie que le programme attend)
  17. c5d = [
  18. 225,204,82,249,67,214,139,164,154,116,172,47,62,84,45,3,47,104,35,84,93,44,
  19. 34,6,25,163,30,206,78,117,5,225,233,23,152,55,146,238,226,49,74,173,199,34,
  20. 15,78,84,81,161,96,220,110,128,201,46,27,123,41,191,6,123,58,89,119,69
  21. ]
  22.  
  23. # Petite table de correspondance (32 bytes) indiquant quel sous-tableau utiliser de la grosse table de substitution.
  24. kallax = [3,0,0,2,0,0,4,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,0,5,0,0,1,0,0]
  25.  
  26. # La grosse table de substitution (8192 bytes)
  27. billy = [
  28. 166,241,180,132,190,47,251,88,10,46,127,195,92,216,151,226,
  29. 103,173,3,218,0,14,199,119,228,111,214,104,131,252,134,152,
  30. 225,52,39,60,56,179,4,144,24,203,233,156,167,91,146,254,
  31. 73,27,187,123,113,40,83,41,1,81,22,202,201,176,100,158,
  32. 87,50,128,182,84,209,55,240,20,62,51,184,75,220,175,192,
  33. 12,172,5,204,61,186,30,154,255,101,213,208,67,145,23,36,
  34. 170,141,108,212,181,193,82,37,74,211,109,155,168,221,138,207,
  35. 121,130,198,9,188,164,124,249,217,157,110,139,210,229,8,116,
  36. 93,6,178,29,70,2,129,53,183,197,99,95,38,244,65,125,
  37. 85,49,194,94,11,219,148,215,115,223,26,140,44,245,19,15,
  38. 248,86,7,205,253,171,160,169,246,105,191,54,243,239,238,79,
  39. 13,242,143,72,68,25,227,97,34,31,161,196,32,147,126,78,
  40. 71,57,159,137,114,42,230,250,117,106,59,17,133,98,16,177,
  41. 136,90,232,174,80,206,185,234,107,76,35,162,237,58,200,163,
  42. 89,77,222,142,102,21,189,122,48,224,165,69,64,247,149,66,
  43. 18,236,153,135,96,33,28,235,231,150,120,118,112,63,45,43,
  44. 177,88,7,134,38,179,127,126,41,20,25,233,37,141,82,124,
  45. 215,206,128,237,239,19,227,180,83,248,249,165,57,228,232,161,
  46. 94,6,192,142,64,200,77,42,132,4,221,153,65,48,238,135,
  47. 89,27,70,122,13,210,16,155,160,119,17,240,242,188,251,51,
  48. 107,22,208,162,197,230,39,229,146,157,168,76,67,105,49,213,
  49. 96,241,109,104,87,125,68,44,66,140,26,136,143,203,245,211,
  50. 183,10,193,43,100,172,171,28,85,98,226,151,73,236,91,40,
  51. 198,110,219,114,117,111,23,60,101,244,62,0,47,186,175,174,
  52. 191,167,52,148,189,173,61,194,250,231,130,54,50,187,93,185,
  53. 9,218,255,214,149,106,32,145,71,31,209,202,29,154,118,190,
  54. 201,56,12,204,84,5,115,246,58,14,166,103,235,170,147,247,
  55. 53,196,2,99,3,92,199,150,97,46,30,131,35,176,81,78,
  56. 222,133,59,18,178,253,79,182,123,75,217,163,137,113,223,95,
  57. 21,252,63,195,158,121,36,34,205,184,224,129,102,11,8,243,
  58. 225,159,254,33,181,112,90,74,216,138,120,80,24,1,220,156,
  59. 139,72,55,207,164,86,69,234,169,144,108,15,212,152,116,45,
  60. 58,14,250,180,0,171,247,215,46,166,50,118,73,231,33,7,
  61. 223,79,242,113,61,88,177,2,147,202,18,15,167,152,101,27,
  62. 183,220,158,30,224,128,153,198,187,6,178,22,34,232,210,5,
  63. 191,219,110,226,244,9,117,45,248,245,8,71,161,124,205,105,
  64. 69,92,241,132,246,81,51,142,20,37,172,208,160,75,62,53,
  65. 42,13,236,193,80,144,181,151,155,235,170,141,59,189,41,148,
  66. 87,233,197,109,123,253,206,19,86,126,188,254,195,182,133,100,
  67. 130,55,116,201,4,112,131,225,96,125,114,60,176,194,149,21,
  68. 211,156,200,134,216,240,111,28,243,140,68,255,237,3,102,10,
  69. 64,207,238,12,67,32,29,99,239,162,164,174,120,115,90,190,
  70. 230,184,89,228,78,221,36,107,52,157,95,17,108,74,234,145,
  71. 185,54,154,229,76,251,94,23,218,137,204,26,136,70,222,186,
  72. 165,91,39,179,173,163,85,66,24,168,150,72,47,199,139,119,
  73. 217,169,122,82,40,214,213,175,252,56,209,146,63,57,11,138,
  74. 127,104,31,1,159,97,83,249,196,106,48,43,227,143,98,16,
  75. 212,135,121,84,44,35,203,129,93,38,25,192,103,77,65,49,
  76. 255,148,204,174,252,10,233,164,253,76,113,9,187,33,51,20,
  77. 87,196,59,193,27,221,116,40,107,37,69,38,172,129,56,72,
  78. 194,119,142,203,168,224,120,133,156,247,195,166,143,70,241,126,
  79. 118,90,191,186,235,234,104,100,210,34,207,152,246,208,229,109,
  80. 64,57,190,160,44,140,132,139,28,62,79,92,182,167,19,202,
  81. 238,60,111,228,213,163,189,237,14,135,13,58,15,150,81,48,
  82. 42,226,93,211,236,21,157,217,250,231,24,185,68,7,198,225,
  83. 138,219,192,249,171,105,242,26,106,77,197,95,200,245,180,201,
  84. 179,43,88,244,136,147,6,121,39,153,223,36,146,232,144,89,
  85. 161,251,218,178,181,215,12,78,22,230,176,159,169,99,80,17,
  86. 206,35,114,66,212,173,85,53,52,222,127,31,125,103,82,243,
  87. 71,8,155,83,29,0,108,254,18,154,84,177,115,94,214,101,
  88. 46,188,102,73,1,170,97,5,151,134,98,45,4,131,91,65,
  89. 54,32,16,205,141,49,220,158,137,124,112,23,2,209,130,67,
  90. 199,122,227,41,11,184,96,47,183,149,239,110,25,175,145,123,
  91. 50,30,240,165,74,55,3,216,128,117,63,248,162,86,75,61,
  92. 144,192,167,72,80,203,160,78,18,82,208,212,222,235,228,84,
  93. 161,12,5,73,132,239,89,252,56,21,233,102,187,231,36,2,
  94. 74,246,39,85,1,130,110,0,97,226,240,156,254,151,47,31,
  95. 205,137,150,224,64,79,206,123,75,159,202,111,214,183,193,220,
  96. 67,140,55,3,182,93,136,200,135,127,117,145,17,8,204,34,
  97. 38,115,100,255,114,26,43,249,68,46,25,186,44,19,91,210,
  98. 42,32,185,81,7,52,165,90,71,178,147,27,195,98,124,134,
  99. 216,163,177,45,103,62,170,173,108,139,146,232,10,229,121,131,
  100. 16,191,94,237,190,181,245,171,230,57,172,247,122,107,154,168,
  101. 169,213,217,209,238,241,201,194,225,95,158,20,133,86,40,128,
  102. 125,179,50,157,242,142,243,129,174,218,223,116,54,37,175,118,
  103. 99,24,13,104,23,164,120,184,119,66,180,87,77,253,244,251,
  104. 101,88,83,215,29,248,152,59,198,166,92,69,48,4,143,138,
  105. 76,30,176,250,65,49,211,199,221,109,227,22,162,113,41,11,
  106. 219,148,96,58,197,149,61,15,196,141,70,35,9,188,153,105,
  107. 33,6,234,207,155,126,51,28,236,189,112,106,63,60,53,14,
  108. 133,211,196,173,138,240,254,105,34,251,116,151,242,225,122,106,
  109. 127,120,33,36,40,56,130,156,252,207,29,219,227,100,149,46,
  110. 186,176,70,51,200,101,230,136,109,67,243,21,234,134,93,50,
  111. 150,64,74,19,24,141,155,168,241,25,18,16,171,175,121,201,
  112. 245,255,187,115,82,3,226,107,43,154,124,42,145,52,32,182,
  113. 91,157,62,237,76,75,220,1,45,85,218,193,54,2,35,253,
  114. 90,163,58,113,28,203,167,98,4,162,152,189,87,217,146,123,
  115. 49,89,0,41,192,13,214,61,9,47,205,222,147,38,231,246,
  116. 199,174,39,129,232,114,135,92,83,55,14,185,238,224,102,161,
  117. 78,53,216,159,190,65,131,165,139,80,158,81,212,63,140,79,
  118. 30,26,143,233,248,71,249,22,247,77,180,166,179,197,236,223,
  119. 244,66,6,125,183,170,137,213,160,86,250,195,119,27,12,208,
  120. 210,37,177,169,97,48,228,215,112,110,84,59,5,209,221,10,
  121. 229,144,103,11,153,72,198,164,118,69,7,191,132,104,23,172,
  122. 148,96,17,202,194,184,88,20,188,126,73,235,204,108,60,31,
  123. 15,8,178,128,94,44,206,111,95,68,239,181,142,117,99,57
  124. ]
  125.  
  126. # ----- Génération de clé et keystream RC4 (mod N)
  127. # ASM: init_SA/ksa_a + init_SB/cult + prga_*
  128. # KSA modifié, initialise un tableau S de taille N.
  129. def ksa_mod(N, key):
  130.     S = list(range(N))
  131.     j = 0
  132.     for i in range(N):
  133.         j = (j + S[i] + key[i % len(key)]) % N
  134.         S[i], S[j] = S[j], S[i]
  135.     return S
  136.  
  137. # ASM: PRGA modifié, génère out_len octets de clé.
  138. def prga_mod(S, N, out_len):
  139.     i = 0
  140.     j = 0
  141.     out = []
  142.     for _ in range(out_len):
  143.         i = (i + 1) % N
  144.         j = (j + S[i]) % N
  145.         S[i], S[j] = S[j], S[i]
  146.         t = (S[i] + S[j]) % N
  147.         out.append(S[t])
  148.     return out
  149.  
  150.  
  151. # ----- Fonctions auxiliaires pour les paramètres par bloc
  152. def flags_for_block(bidx):
  153. # ASM: stackfault: Calcule les drapeaux et options (use_billy, use_roll) pour un bloc donné.
  154.     flags = (paX + 3*bidx + 0x5A) & 0xFF
  155.     use_billy = flags % 2
  156.     use_roll = 1 if (flags % 4) > 1 else 0  # cmp 1, t5; jl set_roll
  157.     return flags, use_billy, use_roll
  158.  
  159. def billy_slice_index(bidx):
  160. # ASM: steuerfahndung: Détermine quel sous-tableau de 'billy' utiliser pour ce bloc.
  161.     billy_id = (paX + 3*bidx) % 32
  162.     return kallax[billy_id]  # renvoie une valeur entre 0 et 5
  163.  
  164.  
  165. # ----- Inversion complète d’un bloc de transformation (permute^-1 -> roll^-1 -> billy^-1 -> prga_mask^-1)
  166. def invert_block(block_cand, bidx):
  167.     lenb = len(block_cand)
  168.  
  169.     # 1: Annuler la permutation (Fisher–Yates inversée)
  170.     # ASM: stage_perm -> init_SP/ksa_sp/prga_sp -> permute (BADRING)
  171.     PK0 = (paX + 13*bidx + 57) & 0xFF
  172.     PK1 = (paX + 17*bidx + 91) & 0xFF
  173.     SP = ksa_mod(97, [PK0, PK1])  # ASM: init_SP / ksa_sp
  174.     RND = prga_mod(SP, 97, lenb)  # ASM: prga_sp -> RND
  175.  
  176.     blk = block_cand[:]
  177.     # La permutation directe est descendante -> pour inverser, on parcourt en ordre croissant (BADRING en sens inverse)
  178.     for idx in range(lenb):
  179.         j = RND[idx] % (idx + 1)
  180.         blk[idx], blk[j] = blk[j], blk[idx]
  181.  
  182.     # 2: Annuler le rolling XOR (si activé, l'inverse de do_roll dans tombola si use_roll)
  183.     _, use_billy, use_roll = flags_for_block(bidx)
  184.     if use_roll:
  185.         acc = (paX + 3*bidx + 17) & 0xFF
  186.         step = (paX + 5*bidx + 1) & 0xFF
  187.         for i in range(lenb):
  188.             y = blk[i]
  189.             x = y ^ acc
  190.             blk[i] = x
  191.             acc = (acc + x + step) & 0xFF
  192.  
  193.     # 3: Annuler la substitution (si activée, l'innverse de stage_billy/billy_apply)
  194.     # 3: ASM: stage_billy -> do_billy -> billy_apply : substitution via billy[base+byte]
  195.     if use_billy:
  196.         idx_slice = billy_slice_index(bidx)
  197.         base = 256 * idx_slice
  198.         sbox = billy[base:base + 256]
  199.         inv = [0]*256
  200.         for x, y in enumerate(sbox):
  201.             inv[y] = x
  202.         blk = [inv[val] for val in blk]
  203.  
  204.     # 4: Annuler le masquage (double XOR avec flux RC4, prga_mask)
  205.     # ASM: prga_mask : double XOR RC4 (mod 97 puis mod 89)
  206.     KA0 = (paX + 7*bidx + 11) & 0xFF
  207.     KA1 = (paX ^ 0xA5) & 0xFF
  208.     SA = ksa_mod(97, [KA0, KA1])
  209.     kA = prga_mod(SA, 97, lenb)
  210.  
  211.     KB0 = (paX + 9*bidx + 23) & 0xFF
  212.     KB1 = (5*paX + 0x3D) & 0xFF
  213.     SB = ksa_mod(89, [KB0, KB1])
  214.     kB = prga_mod(SB, 89, lenb)
  215.  
  216.     for i in range(lenb):
  217.         blk[i] ^= kA[i]
  218.         blk[i] ^= kB[i]
  219.  
  220.     return blk
  221.  
  222. # ----- Reconstruit tout l'input (les 65 bytes) bloc par bloc, ASM: checkin / blk_check / do_block / store_block / hotdogs
  223. IN_recovered = [0]*FLEN
  224. offset = 0
  225. bidx = 0
  226. while offset < FLEN:
  227.     lenb = min(BLKSZ, FLEN - offset)
  228.     block_cand = c5d[offset:offset + lenb]
  229.     block_in = invert_block(block_cand, bidx)
  230.     IN_recovered[offset:offset + lenb] = block_in
  231.     offset += lenb
  232.     bidx += 1
  233.  
  234. # ----- Affiche le résultat final.
  235. flag = ''.join(chr(b) for b in IN_recovered)
  236. print("Flag:", flag)
  237. # Flag : flag{br3_unsTuUc7-d4_c3LInG_f4N_y33t--br00_1nStRuCT10N5_n0W_Cl3R}
Advertisement
Add Comment
Please, Sign In to add comment