MISJA 002 DIFFICULTY: ███░░░░░░░ [3/10] Przechwyciliśmy fragment komunikacji pomiędzy dwoma podejrzanymi. Sądzimy, że to hasło administratora do jednego z systemów, na których mogą znajdować się kluczowe dla sprawy dane. ╭┈┈ ┈┈╮ ┊ QW== QT== QT== QQ== QU== Qd== QU== Qd== ┊ QX== QV== QW== Qe== QT== QR== QU== QT== QT== QU== QX== QU== QT== QR== QT== QQ== ┊ QW== Qe== »goo.gl/4Iuxdi« ┊ ╰┈┈ ┈┈╯ Niestety, pomimo, iż wiemy, że użyte zostało kodowanie Base64, nie jesteśmy w stanie zdekodować ukrytej wiadomości. Nasi technicy uparcie twierdzą, że po zdekodowaniu wychodzi „AAAAAAAAAAAAAAAAAAAAAAAAAA”, ale sądzimy, że nie mają racji. Zwracamy się więc do Ciebie z prośbą o pomoc - zrzuć okiem na powyższą wiadomość i sprawdź czy nie ma czasem drugiego dna.
Jak widać, zakodowane części wiadomości nie są identyczne. Jednak przy próbie zdekodowania faktycznie wychodzą same litery A:
TEXT = <<COMM QW== QT== QT== QQ== QU== Qd== QU== Qd== QX== QV== QW== Qe== QT== QR== QU== QT== QT== QU== QX== QU== QT== QR== QT== QQ== QW== Qe== COMM require 'base64' p TEXT.split.map{ |n| Base64.decode64 n }.join |
"AAAAAAAAAAAAAAAAAAAAAAAAAA" |
Po zmianie funkcji decode64 na strict_decode64 wywołanie kończy się wyjątkiem `unpack’: invalid base64 (ArgumentError). Dzieje się tak, ponieważ zakodowane wiadomości nie są zgodne ze specyfikacją: każdy znak (poza =) enkoduje 6 bitów, standardowo bajt ma 8 bitów, więc 4 bity są nadmiarowe. Specyfikacja oczekuje, że będą wyzerowane, ale tutaj wyraźnie są różne. Zapewne w nich ukryta jest “steganograficzna” wiadomość.
Można to sprawdzić w dość prosty sposób:
def decode_letter(l) case l when /[A-Z]/ l.ord - 'A'.ord when /[a-z]/ l.ord - 'a'.ord + 26 when /\d/ l.ord - '0'.ord + 52 when '+' 62 when '/' 63 else nil end end def decode(str) first = (decode_letter(str[0]) << 2) + (decode_letter(str[1]) >> 4) second = decode_letter(str[1]) & 0xF second end p TEXT.split.map{ |s| decode s } |
[6, 3, 3, 0, 4, 13, 4, 13, 7, 5, 6, 14, 3, 1, 4, 3, 3, 4, 7, 4, 3, 1, 3, 0, 6, 14] |
Tak jest. Pierwsze co sprawdziłem to bezpośrednie mapowanie na indeksy liter alfabetu:
p TEXT.split.map{ |s| decode s }.map{ |n| (n + 'A'.ord).chr }.join |
"GDDAENENHFGODBEDDEHEDBDAGO" |
Nope.
A co jeśli kolejne 4-bitowe wartości są kolejnymi nibblami ukrytej wiadomości?
p TEXT.split.map{ |s| decode s }.each_slice(2).map{|a,b| (a << 4) + b }.map(&:chr).join |
"c0MMun1C4t10n" |
Yay! Udało się rozpykać zadanie o trudności 3/10 (pytanie tylko czy to nie jest przypadkiem 1064)
Pełen kod można znaleźć tutaj.
Tylko czemu zmienna “Text” wielką literą? 😛
Bo to stała? 😛
edit: Ok, poprawione. Thx.