mirror of
https://github.com/gchq/CyberChef.git
synced 2025-04-20 06:55:08 -04:00
Merge 68f69d690f
into 7c8be12d52
This commit is contained in:
commit
0c2f0acfe7
6 changed files with 186 additions and 7 deletions
|
@ -238,6 +238,7 @@
|
||||||
"DNS over HTTPS",
|
"DNS over HTTPS",
|
||||||
"Strip HTTP headers",
|
"Strip HTTP headers",
|
||||||
"Dechunk HTTP response",
|
"Dechunk HTTP response",
|
||||||
|
"Parse Ethernet frame",
|
||||||
"Parse User Agent",
|
"Parse User Agent",
|
||||||
"Parse IP range",
|
"Parse IP range",
|
||||||
"Parse IPv6 address",
|
"Parse IPv6 address",
|
||||||
|
|
115
src/core/operations/ParseEthernetFrame.mjs
Normal file
115
src/core/operations/ParseEthernetFrame.mjs
Normal file
|
@ -0,0 +1,115 @@
|
||||||
|
/**
|
||||||
|
* @author tedk [tedk@ted.do]
|
||||||
|
* @copyright Crown Copyright 2024
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
|
||||||
|
import Operation from "../Operation.mjs";
|
||||||
|
import OperationError from "../errors/OperationError.mjs";
|
||||||
|
import Utils from "../Utils.mjs";
|
||||||
|
import {fromHex, toHex} from "../lib/Hex.mjs";
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Parse Ethernet frame operation
|
||||||
|
*/
|
||||||
|
class ParseEthernetFrame extends Operation {
|
||||||
|
|
||||||
|
/**
|
||||||
|
* ParseEthernetFrame constructor
|
||||||
|
*/
|
||||||
|
constructor() {
|
||||||
|
super();
|
||||||
|
|
||||||
|
this.name = "Parse Ethernet frame";
|
||||||
|
this.module = "Default";
|
||||||
|
this.description = "Parses an Ethernet frame and either shows the deduced values (Source and destination MAC, VLANs) or returns the packet data.<br /><br />Good for use in conjunction with the Parse IPv4, and Parse TCP/UDP recipes.";
|
||||||
|
this.infoURL = "https://en.wikipedia.org/wiki/Ethernet_frame#Frame_%E2%80%93_data_link_layer";
|
||||||
|
this.inputType = "string";
|
||||||
|
this.outputType = "html";
|
||||||
|
this.args = [
|
||||||
|
{
|
||||||
|
name: "Input type",
|
||||||
|
type: "option",
|
||||||
|
value: [
|
||||||
|
"Raw", "Hex"
|
||||||
|
],
|
||||||
|
defaultIndex: 0,
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Return type",
|
||||||
|
type: "option",
|
||||||
|
value: [
|
||||||
|
"Text output", "Packet data", "Packet data (hex)",
|
||||||
|
],
|
||||||
|
defaultIndex: 0,
|
||||||
|
}
|
||||||
|
];
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param {string} input
|
||||||
|
* @param {Object[]} args
|
||||||
|
* @returns {html}
|
||||||
|
*/
|
||||||
|
run(input, args) {
|
||||||
|
const format = args[0];
|
||||||
|
const outputFormat = args[1];
|
||||||
|
|
||||||
|
if (format === "Hex") {
|
||||||
|
input = fromHex(input);
|
||||||
|
} else if (format === "Raw") {
|
||||||
|
input = new Uint8Array(Utils.strToArrayBuffer(input));
|
||||||
|
} else {
|
||||||
|
throw new OperationError("Invalid input format selected.");
|
||||||
|
}
|
||||||
|
|
||||||
|
const destinationMac = input.slice(0, 6);
|
||||||
|
const sourceMac = input.slice(6, 12);
|
||||||
|
|
||||||
|
let offset = 12;
|
||||||
|
const vlans = [];
|
||||||
|
|
||||||
|
while (offset < input.length) {
|
||||||
|
const ethType = Utils.byteArrayToChars(input.slice(offset, offset+2));
|
||||||
|
offset += 2;
|
||||||
|
|
||||||
|
|
||||||
|
if (ethType === "\x08\x00") {
|
||||||
|
break;
|
||||||
|
} else if (ethType === "\x81\x00" || ethType === "\x88\xA8") {
|
||||||
|
// Parse the VLAN tag:
|
||||||
|
// [0000] 0000 0000 0000
|
||||||
|
// ^^^ PRIO - Ignored
|
||||||
|
// ^ DEI - Ignored
|
||||||
|
// ^^^^ ^^^^ ^^^^ VLAN ID
|
||||||
|
const vlanTag = input.slice(offset+2, offset+4);
|
||||||
|
vlans.push((vlanTag[0] & 0b00001111) << 4 | vlanTag[1]);
|
||||||
|
|
||||||
|
offset += 2;
|
||||||
|
} else {
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
const packetData = input.slice(offset);
|
||||||
|
|
||||||
|
if (outputFormat === "Packet data") {
|
||||||
|
return Utils.byteArrayToChars(packetData);
|
||||||
|
} else if (outputFormat === "Packet data (hex)") {
|
||||||
|
return toHex(packetData);
|
||||||
|
} else if (outputFormat === "Text output") {
|
||||||
|
let retval = `Source MAC: ${toHex(sourceMac, ":")}\nDestination MAC: ${toHex(destinationMac, ":")}\n`;
|
||||||
|
if (vlans.length > 0) {
|
||||||
|
retval += `VLAN: ${vlans.join(", ")}\n`;
|
||||||
|
}
|
||||||
|
retval += `Data:\n${toHex(packetData)}`;
|
||||||
|
return retval;
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
export default ParseEthernetFrame;
|
|
@ -33,6 +33,12 @@ class ParseIPv4Header extends Operation {
|
||||||
"name": "Input format",
|
"name": "Input format",
|
||||||
"type": "option",
|
"type": "option",
|
||||||
"value": ["Hex", "Raw"]
|
"value": ["Hex", "Raw"]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
"name": "Output format",
|
||||||
|
"type": "option",
|
||||||
|
"value": ["Table", "Data (hex)", "Data (raw)"],
|
||||||
|
defaultIndex: 0,
|
||||||
}
|
}
|
||||||
];
|
];
|
||||||
}
|
}
|
||||||
|
@ -44,6 +50,8 @@ class ParseIPv4Header extends Operation {
|
||||||
*/
|
*/
|
||||||
run(input, args) {
|
run(input, args) {
|
||||||
const format = args[0];
|
const format = args[0];
|
||||||
|
const outputFormat = args[1];
|
||||||
|
|
||||||
let output;
|
let output;
|
||||||
|
|
||||||
if (format === "Hex") {
|
if (format === "Hex") {
|
||||||
|
@ -98,7 +106,10 @@ class ParseIPv4Header extends Operation {
|
||||||
checksumResult = givenChecksum + " (incorrect, should be " + correctChecksum + ")";
|
checksumResult = givenChecksum + " (incorrect, should be " + correctChecksum + ")";
|
||||||
}
|
}
|
||||||
|
|
||||||
output = `<table class='table table-hover table-sm table-bordered table-nonfluid'><tr><th>Field</th><th>Value</th></tr>
|
const data = input.slice(ihl * 4);
|
||||||
|
|
||||||
|
if (outputFormat === "Table") {
|
||||||
|
output = `<table class='table table-hover table-sm table-bordered table-nonfluid'><tr><th>Field</th><th>Value</th></tr>
|
||||||
<tr><td>Version</td><td>${version}</td></tr>
|
<tr><td>Version</td><td>${version}</td></tr>
|
||||||
<tr><td>Internet Header Length (IHL)</td><td>${ihl} (${ihl * 4} bytes)</td></tr>
|
<tr><td>Internet Header Length (IHL)</td><td>${ihl} (${ihl * 4} bytes)</td></tr>
|
||||||
<tr><td>Differentiated Services Code Point (DSCP)</td><td>${dscp}</td></tr>
|
<tr><td>Differentiated Services Code Point (DSCP)</td><td>${dscp}</td></tr>
|
||||||
|
@ -116,13 +127,19 @@ class ParseIPv4Header extends Operation {
|
||||||
<tr><td>Protocol</td><td>${protocol}, ${protocolInfo.protocol} (${protocolInfo.keyword})</td></tr>
|
<tr><td>Protocol</td><td>${protocol}, ${protocolInfo.protocol} (${protocolInfo.keyword})</td></tr>
|
||||||
<tr><td>Header checksum</td><td>${checksumResult}</td></tr>
|
<tr><td>Header checksum</td><td>${checksumResult}</td></tr>
|
||||||
<tr><td>Source IP address</td><td>${ipv4ToStr(srcIP)}</td></tr>
|
<tr><td>Source IP address</td><td>${ipv4ToStr(srcIP)}</td></tr>
|
||||||
<tr><td>Destination IP address</td><td>${ipv4ToStr(dstIP)}</td></tr>`;
|
<tr><td>Destination IP address</td><td>${ipv4ToStr(dstIP)}</td></tr>
|
||||||
|
<tr><td>Data (hex)</td><td>${toHex(data)}</td></tr>`;
|
||||||
|
|
||||||
if (ihl > 5) {
|
if (ihl > 5) {
|
||||||
output += `<tr><td>Options</td><td>${toHex(options)}</td></tr>`;
|
output += `<tr><td>Options</td><td>${toHex(options)}</td></tr>`;
|
||||||
|
}
|
||||||
|
|
||||||
|
return output + "</table>";
|
||||||
|
} else if (outputFormat === "Data (hex)") {
|
||||||
|
return toHex(data);
|
||||||
|
} else if (outputFormat === "Data (raw)") {
|
||||||
|
return Utils.byteArrayToChars(data);
|
||||||
}
|
}
|
||||||
|
|
||||||
return output + "</table>";
|
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
@ -249,7 +249,7 @@ module.exports = {
|
||||||
testOpHtml(browser, "Parse colour code", "#000", ".colorpicker-preview", "rgb(0, 0, 0)");
|
testOpHtml(browser, "Parse colour code", "#000", ".colorpicker-preview", "rgb(0, 0, 0)");
|
||||||
testOpHtml(browser, "Parse DateTime", "01/12/2000 13:00:00", "", /Date: Friday 1st December 2000/);
|
testOpHtml(browser, "Parse DateTime", "01/12/2000 13:00:00", "", /Date: Friday 1st December 2000/);
|
||||||
// testOp(browser, "Parse IP range", "test input", "test_output");
|
// testOp(browser, "Parse IP range", "test input", "test_output");
|
||||||
testOpHtml(browser, "Parse IPv4 header", "45 c0 00 c4 02 89 00 00 ff 11 1e 8c c0 a8 0c 01 c0 a8 0c 02", "tr:last-child td:last-child", "192.168.12.2");
|
testOpHtml(browser, "Parse IPv4 header", "45 c0 00 c4 02 89 00 00 ff 11 1e 8c c0 a8 0c 01 c0 a8 0c 02", "tr:nth-last-child(2) td:last-child", "192.168.12.2");
|
||||||
// testOp(browser, "Parse IPv6 address", "test input", "test_output");
|
// testOp(browser, "Parse IPv6 address", "test input", "test_output");
|
||||||
// testOp(browser, "Parse ObjectID timestamp", "test input", "test_output");
|
// testOp(browser, "Parse ObjectID timestamp", "test input", "test_output");
|
||||||
// testOp(browser, "Parse QR Code", "test input", "test_output");
|
// testOp(browser, "Parse QR Code", "test input", "test_output");
|
||||||
|
|
|
@ -115,6 +115,7 @@ import "./tests/NetBIOS.mjs";
|
||||||
import "./tests/NormaliseUnicode.mjs";
|
import "./tests/NormaliseUnicode.mjs";
|
||||||
import "./tests/NTLM.mjs";
|
import "./tests/NTLM.mjs";
|
||||||
import "./tests/OTP.mjs";
|
import "./tests/OTP.mjs";
|
||||||
|
import "./tests/ParseEthernetFrame.mjs";
|
||||||
import "./tests/ParseIPRange.mjs";
|
import "./tests/ParseIPRange.mjs";
|
||||||
import "./tests/ParseObjectIDTimestamp.mjs";
|
import "./tests/ParseObjectIDTimestamp.mjs";
|
||||||
import "./tests/ParseQRCode.mjs";
|
import "./tests/ParseQRCode.mjs";
|
||||||
|
|
45
tests/operations/tests/ParseEthernetFrame.mjs
Normal file
45
tests/operations/tests/ParseEthernetFrame.mjs
Normal file
|
@ -0,0 +1,45 @@
|
||||||
|
/**
|
||||||
|
* Parse Ethernet frame tests.
|
||||||
|
*
|
||||||
|
* @author tedk [tedk@ted.do]
|
||||||
|
* @copyright Crown Copyright 2017
|
||||||
|
* @license Apache-2.0
|
||||||
|
*/
|
||||||
|
import TestRegister from "../../lib/TestRegister.mjs";
|
||||||
|
|
||||||
|
TestRegister.addTests([
|
||||||
|
{
|
||||||
|
name: "Parse plain Ethernet frame",
|
||||||
|
input: "000000000000ffffffffffff08004500",
|
||||||
|
expectedOutput: "Source MAC: ff:ff:ff:ff:ff:ff\nDestination MAC: 00:00:00:00:00:00\nData:\n45 00",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Parse Ethernet frame",
|
||||||
|
"args": ["Hex", "Text output"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
// Example PCAP data from: https://packetlife.net/captures/protocol/vlan/
|
||||||
|
{
|
||||||
|
name: "Parse Ethernet frame with one VLAN tag (802.1q)",
|
||||||
|
input: "01000ccdcdd00013c3dfae188100a0760165aaaa",
|
||||||
|
expectedOutput: "Source MAC: 00:13:c3:df:ae:18\nDestination MAC: 01:00:0c:cd:cd:d0\nVLAN: 117\nData:\naa aa",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Parse Ethernet frame",
|
||||||
|
"args": ["Hex", "Text output"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
},
|
||||||
|
{
|
||||||
|
name: "Parse Ethernet frame with two VLAN tags (802.1ad)",
|
||||||
|
input: "0019aa7de688002155c8f13c810000d18100001408004500",
|
||||||
|
expectedOutput: "Source MAC: 00:21:55:c8:f1:3c\nDestination MAC: 00:19:aa:7d:e6:88\nVLAN: 16, 128\nData:\n45 00",
|
||||||
|
recipeConfig: [
|
||||||
|
{
|
||||||
|
"op": "Parse Ethernet frame",
|
||||||
|
"args": ["Hex", "Text output"]
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
|
]);
|
Loading…
Add table
Add a link
Reference in a new issue