gchq / CyberChef

The Cyber Swiss Army Knife - a web app for encryption, encoding, compression and data analysis
https://gchq.github.io/CyberChef
Apache License 2.0
28.57k stars 3.22k forks source link

Feature request: Have 'Magic' detect ROT13 ciphertext #1095

Open jdgregson opened 4 years ago

jdgregson commented 4 years ago

Is your feature request related to a problem? Please describe. The Magic function is unable to identify ROT13 ciphertext.

Describe the solution you'd like ROT13 is deterministic. The Magic function should look for common text patterns in the input and attempt to apply the ROT13 transformation if it sees some of them. For example, "the" will always be transformed to "gur", so if / gur / is encountered, there is a good chance it just needs ROT13 to be applied again.

Describe alternatives you've considered A good solution for me going forward is to think of ROT13 whenever I see "gur" or "sbe". But that doesn't help anyone who isn't familiar with ROT13.

image

Additional context Searching for / gur / or / sbe / would only be helpful for ciphertexts where the plaintext was in English and has been shifted exactly 13 places. Perhaps the 10 most common words from each language could be searched for, and attempt 10-16 shifts. Having Magic come back and tell me "this is Spanish text shifted 11 places with ROT13" would be 🔥.

jdgregson commented 4 years ago

I put together a proof-of-concept which can detect the 10 most common English words (the, of, and, a, to, in, is, you, that, it) for any number of rotations:

const lookupTable = {
  'en': [
    ['the', 'uif', 'vjg', 'wkh', 'xli', 'ymj', 'znk', 'aol', 'bpm', 'cqn', 'dro', 'esp', 'ftq', 'gur', 'hvs', 'iwt', 'jxu', 'kyv', 'lzw', 'max', 'nby', 'ocz', 'pda', 'qeb', 'rfc', 'sgd'],
    ['of', 'pg', 'qh', 'ri', 'sj', 'tk', 'ul', 'vm', 'wn', 'xo', 'yp', 'zq', 'ar', 'bs', 'ct', 'du', 'ev', 'fw', 'gx', 'hy', 'iz', 'ja', 'kb', 'lc', 'md', 'ne'],
    ['and', 'boe', 'cpf', 'dqg', 'erh', 'fsi', 'gtj', 'huk', 'ivl', 'jwm', 'kxn', 'lyo', 'mzp', 'naq', 'obr', 'pcs', 'qdt', 'reu', 'sfv', 'tgw', 'uhx', 'viy', 'wjz', 'xka', 'ylb', 'zmc'],
    ['a', 'b', 'c', 'd', 'e', 'f', 'g', 'h', 'i', 'j', 'k', 'l', 'm', 'n', 'o', 'p', 'q', 'r', 's', 't', 'u', 'v', 'w', 'x', 'y', 'z'],
    ['to', 'up', 'vq', 'wr', 'xs', 'yt', 'zu', 'av', 'bw', 'cx', 'dy', 'ez', 'fa', 'gb', 'hc', 'id', 'je', 'kf', 'lg', 'mh', 'ni', 'oj', 'pk', 'ql', 'rm', 'sn'],
    ['in', 'jo', 'kp', 'lq', 'mr', 'ns', 'ot', 'pu', 'qv', 'rw', 'sx', 'ty', 'uz', 'va', 'wb', 'xc', 'yd', 'ze', 'af', 'bg', 'ch', 'di', 'ej', 'fk', 'gl', 'hm'],
    ['is', 'jt', 'ku', 'lv', 'mw', 'nx', 'oy', 'pz', 'qa', 'rb', 'sc', 'td', 'ue', 'vf', 'wg', 'xh', 'yi', 'zj', 'ak', 'bl', 'cm', 'dn', 'eo', 'fp', 'gq', 'hr'],
    ['you', 'zpv', 'aqw', 'brx', 'csy', 'dtz', 'eua', 'fvb', 'gwc', 'hxd', 'iye', 'jzf', 'kag', 'lbh', 'mci', 'ndj', 'oek', 'pfl', 'qgm', 'rhn', 'sio', 'tjp', 'ukq', 'vlr', 'wms', 'xnt'],
    ['that', 'uibu', 'vjcv', 'wkdw', 'xlex', 'ymfy', 'zngz', 'aoha', 'bpib', 'cqjc', 'drkd', 'esle', 'ftmf', 'gung', 'hvoh', 'iwpi', 'jxqj', 'kyrk', 'lzsl', 'matm', 'nbun', 'ocvo', 'pdwp', 'qexq', 'rfyr', 'sgzs'],
    ['it', 'ju', 'kv', 'lw', 'mx', 'ny', 'oz', 'pa', 'qb', 'rc', 'sd', 'te', 'uf', 'vg', 'wh', 'xi', 'yj', 'zk', 'al', 'bm', 'cn', 'do', 'ep', 'fq', 'gr', 'hs'],
  ]
};

const detectRotationMaybeGetInfosecJob = (ciphertext, lookupTable) => {
  if (!lookupTable) {
    throw 'No lookup table provided.';
  }
  ciphertext = ciphertext.toLowerCase();
  let output = [];
  const languages = Object.keys(lookupTable);
  languages.forEach((language, i) => {
    const languageTable = lookupTable[language];
    languageTable.forEach((wordTable, j) => {
      const plaintextWord = wordTable[0];
      wordTable.forEach((ciphertextWord, k) => {
        if (k > 0) {
          const exp = new RegExp(`(^| )${ciphertextWord}( |\\.|$)`);
          if (ciphertext.match(exp)) {
            output.push(`Found '${ciphertextWord}' which may be ` +
                `'${plaintextWord}' in '${language}' rotated by ${k} places.`);
          }
        }
      });
    });
  });
  return output.join('\n');
};

Usage:

>> detectRotationMaybeGetInfosecJob('Guvf vf n frperg zrffntr va hapunegrq fcnpr.', lookupTable)
<< "Found 'n' which may be 'a' in 'en' rotated by 13 places.
   Found 'va' which may be 'in' in 'en' rotated by 13 places.
   Found 'vf' which may be 'is' in 'en' rotated by 13 places."

>> detectRotationMaybeGetInfosecJob('Lzak ak s kwujwl ewkksyw af mfuzsjlwv khsuw.', lookupTable)
<< "Found 's' which may be 'a' in 'en' rotated by 18 places.
   Found 'af' which may be 'in' in 'en' rotated by 18 places.
   Found 'ak' which may be 'is' in 'en' rotated by 18 places."