Magic.mjs 6.9 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156
  1. /**
  2. * @author n1474335 [n1474335@gmail.com]
  3. * @copyright Crown Copyright 2016
  4. * @license Apache-2.0
  5. */
  6. import Operation from "../Operation";
  7. import Utils from "../Utils";
  8. import Dish from "../Dish";
  9. import MagicLib from "../lib/Magic";
  10. /**
  11. * Magic operation
  12. */
  13. class Magic extends Operation {
  14. /**
  15. * Magic constructor
  16. */
  17. constructor() {
  18. super();
  19. this.name = "Magic";
  20. this.flowControl = true;
  21. this.module = "Default";
  22. this.description = "The Magic operation attempts to detect various properties of the input data and suggests which operations could help to make more sense of it.<br><br><b>Options</b><br><u>Depth:</u> If an operation appears to match the data, it will be run and the result will be analysed further. This argument controls the maximum number of levels of recursion.<br><br><u>Intensive mode:</u> When this is turned on, various operations like XOR, bit rotates, and character encodings are brute-forced to attempt to detect valid data underneath. To improve performance, only the first 100 bytes of the data is brute-forced.<br><br><u>Extensive language support:</u> At each stage, the relative byte frequencies of the data will be compared to average frequencies for a number of languages. The default set consists of ~40 of the most commonly used languages on the Internet. The extensive list consists of 284 languages and can result in many languages matching the data if their byte frequencies are similar.<br><br>A technical explanation of the Magic operation can be found <a href='https://github.com/gchq/CyberChef/wiki/Automatic-detection-of-encoded-data-using-CyberChef-Magic'>here</a>.";
  23. this.inputType = "ArrayBuffer";
  24. this.outputType = "JSON";
  25. this.presentType = "html";
  26. this.args = [
  27. {
  28. "name": "Depth",
  29. "type": "number",
  30. "value": 3
  31. },
  32. {
  33. "name": "Intensive mode",
  34. "type": "boolean",
  35. "value": false
  36. },
  37. {
  38. "name": "Extensive language support",
  39. "type": "boolean",
  40. "value": false
  41. }
  42. ];
  43. }
  44. /**
  45. * @param {Object} state - The current state of the recipe.
  46. * @param {number} state.progress - The current position in the recipe.
  47. * @param {Dish} state.dish - The Dish being operated on.
  48. * @param {Operation[]} state.opList - The list of operations in the recipe.
  49. * @returns {Object} The updated state of the recipe.
  50. */
  51. async run(state) {
  52. const ings = state.opList[state.progress].ingValues,
  53. [depth, intensive, extLang] = ings,
  54. dish = state.dish,
  55. magic = new MagicLib(await dish.get(Dish.ARRAY_BUFFER)),
  56. options = await magic.speculativeExecution(depth, extLang, intensive);
  57. // Record the current state for use when presenting
  58. this.state = state;
  59. dish.set(options, Dish.JSON);
  60. return state;
  61. }
  62. /**
  63. * Displays Magic results in HTML for web apps.
  64. *
  65. * @param {JSON} options
  66. * @returns {html}
  67. */
  68. present(options) {
  69. const currentRecipeConfig = this.state.opList.map(op => op.config);
  70. let output = `<table
  71. class='table table-hover table-sm table-bordered'
  72. style='table-layout: fixed;'>
  73. <tr>
  74. <th>Recipe (click to load)</th>
  75. <th>Result snippet</th>
  76. <th>Properties</th>
  77. </tr>`;
  78. /**
  79. * Returns a CSS colour value based on an integer input.
  80. *
  81. * @param {number} val
  82. * @returns {string}
  83. */
  84. function chooseColour(val) {
  85. if (val < 3) return "green";
  86. if (val < 5) return "goldenrod";
  87. return "red";
  88. }
  89. options.forEach(option => {
  90. // Construct recipe URL
  91. // Replace this Magic op with the generated recipe
  92. const recipeConfig = currentRecipeConfig.slice(0, this.state.progress)
  93. .concat(option.recipe)
  94. .concat(currentRecipeConfig.slice(this.state.progress + 1)),
  95. recipeURL = "recipe=" + Utils.encodeURIFragment(Utils.generatePrettyRecipe(recipeConfig));
  96. let language = "",
  97. fileType = "",
  98. matchingOps = "",
  99. useful = "";
  100. const entropy = `<span data-toggle="tooltip" data-container="body" title="Shannon Entropy is measured from 0 to 8. High entropy suggests encrypted or compressed data. Normal text is usually around 3.5 to 5.">Entropy: <span style="color: ${chooseColour(option.entropy)}">${option.entropy.toFixed(2)}</span></span>`,
  101. validUTF8 = option.isUTF8 ? "<span data-toggle='tooltip' data-container='body' title='The data could be a valid UTF8 string based on its encoding.'>Valid UTF8</span>\n" : "";
  102. if (option.languageScores[0].probability > 0) {
  103. let likelyLangs = option.languageScores.filter(l => l.probability > 0);
  104. if (likelyLangs.length < 1) likelyLangs = [option.languageScores[0]];
  105. language = "<span data-toggle='tooltip' data-container='body' title='Based on a statistical comparison of the frequency of bytes in various languages. Ordered by likelihood.'>" +
  106. "Possible languages:\n " +
  107. likelyLangs.map(lang => {
  108. return MagicLib.codeToLanguage(lang.lang);
  109. }).join("\n ") +
  110. "</span>\n";
  111. }
  112. if (option.fileType) {
  113. fileType = `<span data-toggle="tooltip" data-container="body" title="Based on the presence of magic bytes.">File type: ${option.fileType.mime} (${option.fileType.ext})</span>\n`;
  114. }
  115. if (option.matchingOps.length) {
  116. matchingOps = `Matching ops: ${[...new Set(option.matchingOps.map(op => op.op))].join(", ")}\n`;
  117. }
  118. if (option.useful) {
  119. useful = "<span data-toggle='tooltip' data-container='body' title='This could be an operation that displays data in a useful way, such as rendering an image.'>Useful op detected</span>\n";
  120. }
  121. output += `<tr>
  122. <td><a href="#${recipeURL}">${Utils.generatePrettyRecipe(option.recipe, true)}</a></td>
  123. <td>${Utils.escapeHtml(Utils.printable(Utils.truncate(option.data, 99)))}</td>
  124. <td>${language}${fileType}${matchingOps}${useful}${validUTF8}${entropy}</td>
  125. </tr>`;
  126. });
  127. output += "</table><script type='application/javascript'>$('[data-toggle=\"tooltip\"]').tooltip()</script>";
  128. if (!options.length) {
  129. output = "Nothing of interest could be detected about the input data.\nHave you tried modifying the operation arguments?";
  130. }
  131. return output;
  132. }
  133. }
  134. export default Magic;