initial work on "unroller" (decrypt wtv encrypted pcap)
This commit is contained in:
17
zefie_wtvp_minisrv/package-lock.json
generated
17
zefie_wtvp_minisrv/package-lock.json
generated
@@ -17,10 +17,12 @@
|
||||
"express": "^4.21.2",
|
||||
"follow-redirects": "^1.15.6",
|
||||
"html-entities": "^2.5.2",
|
||||
"http-string-parser": "^0.0.6",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"mime-types": "^2.1.35",
|
||||
"newsie": "1.2.1",
|
||||
"nntp-server-zefie": "^3.1.0",
|
||||
"pcap-parser": "^0.2.1",
|
||||
"php-serialize": "^5.0.1",
|
||||
"proxy-agent": "^6.4.0",
|
||||
"rc4-crypto": "^1.5.0",
|
||||
@@ -1912,6 +1914,12 @@
|
||||
"node": ">= 14"
|
||||
}
|
||||
},
|
||||
"node_modules/http-string-parser": {
|
||||
"version": "0.0.6",
|
||||
"resolved": "https://registry.npmjs.org/http-string-parser/-/http-string-parser-0.0.6.tgz",
|
||||
"integrity": "sha512-sngOeBkIL32kum4Z+FulU+3Ve41B3js1IKfel0WAhwLqDJiUPC1UTqFRBr2/IDw9dbks6B4xSIYgPiJU7ivxww==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/https-proxy-agent": {
|
||||
"version": "7.0.6",
|
||||
"resolved": "https://registry.npmjs.org/https-proxy-agent/-/https-proxy-agent-7.0.6.tgz",
|
||||
@@ -2495,6 +2503,15 @@
|
||||
"integrity": "sha512-RA1GjUVMnvYFxuqovrEqZoxxW5NUZqbwKtYz/Tt7nXerk0LbLblQmrsgdeOxV5SFHf0UDggjS/bSeOZwt1pmEQ==",
|
||||
"license": "MIT"
|
||||
},
|
||||
"node_modules/pcap-parser": {
|
||||
"version": "0.2.1",
|
||||
"resolved": "https://registry.npmjs.org/pcap-parser/-/pcap-parser-0.2.1.tgz",
|
||||
"integrity": "sha512-+1t1GiMpEHI+MFub/mpCmfpyU4oVOyn4h71Zp5GqC/2uv0yteM6MghazKBQMkNXgmmsCPT1JUMfqsF03cYjnyw==",
|
||||
"license": "MIT",
|
||||
"engines": {
|
||||
"node": ">=0.6.0"
|
||||
}
|
||||
},
|
||||
"node_modules/php-serialize": {
|
||||
"version": "5.1.0",
|
||||
"resolved": "https://registry.npmjs.org/php-serialize/-/php-serialize-5.1.0.tgz",
|
||||
|
||||
@@ -35,10 +35,12 @@
|
||||
"express": "^4.21.2",
|
||||
"follow-redirects": "^1.15.6",
|
||||
"html-entities": "^2.5.2",
|
||||
"http-string-parser": "^0.0.6",
|
||||
"iconv-lite": "^0.6.3",
|
||||
"mime-types": "^2.1.35",
|
||||
"newsie": "1.2.1",
|
||||
"nntp-server-zefie": "^3.1.0",
|
||||
"pcap-parser": "^0.2.1",
|
||||
"php-serialize": "^5.0.1",
|
||||
"proxy-agent": "^6.4.0",
|
||||
"rc4-crypto": "^1.5.0",
|
||||
|
||||
18
zefie_wtvp_minisrv/test_lzpf.js
Normal file
18
zefie_wtvp_minisrv/test_lzpf.js
Normal file
@@ -0,0 +1,18 @@
|
||||
const WTVLzpf = require("./includes/classes/WTVLzpf.js")
|
||||
const lzpf = new WTVLzpf();
|
||||
|
||||
// Test with a simple string
|
||||
const testString = "This is a test string to compress and decompress";
|
||||
const compressed = lzpf.Compress(testString);
|
||||
const decompressed = lzpf.Decompress(compressed);
|
||||
|
||||
console.log("Original:", testString);
|
||||
console.log("Decompressed:", decompressed.toString());
|
||||
console.log("Match:", testString === decompressed.toString());
|
||||
|
||||
// Test with HTML-like data (which LZPF was optimized for)
|
||||
const htmlString = "<html><body><h1>Test</h1><p>This is a paragraph.</p></body></html>";
|
||||
const compressedHtml = lzpf.Compress(htmlString);
|
||||
const decompressedHtml = lzpf.Decompress(compressedHtml);
|
||||
|
||||
console.log("HTML match:", htmlString === decompressedHtml.toString());
|
||||
128
zefie_wtvp_minisrv/unroll_rc4.js
Normal file
128
zefie_wtvp_minisrv/unroll_rc4.js
Normal file
@@ -0,0 +1,128 @@
|
||||
const fs = require('fs');
|
||||
const pcap = require('pcap-parser');
|
||||
const WTVSec = require('./includes/classes/WTVSec.js');
|
||||
const WTVLZPF = require('./includes/classes/WTVLzpf.js');
|
||||
const httpHeaderParser = require('http-string-parser');
|
||||
|
||||
const server_ip = '192.168.11.26'; // 🔁 Replace with actual IP
|
||||
const connections = new Map(); // (key: `${srcIP}:${srcPort}<->${dstIP}:${dstPort}`)
|
||||
|
||||
function connectionKey(src, sport, dst, dport) {
|
||||
return `${src}:${sport}<->${dst}:${dport}`;
|
||||
}
|
||||
|
||||
class ConnectionState {
|
||||
constructor() {
|
||||
this.packets = [];
|
||||
this.buffer = Buffer.alloc(0);
|
||||
this.rc4 = null;
|
||||
this.secure = false;
|
||||
this.wtv = null;
|
||||
this.incarnation = null;
|
||||
}
|
||||
|
||||
feed(data) {
|
||||
this.buffer = Buffer.concat([this.buffer, data]);
|
||||
this.packets.push(data);
|
||||
|
||||
// Parse headers if not done
|
||||
const text = this.buffer.toString();
|
||||
if (!this.wtv && text.includes('wtv-initial-key')) {
|
||||
const headers = httpHeaderParser.parseResponse(text).headers;
|
||||
if (headers['wtv-initial-key'] && headers['wtv-challenge']) {
|
||||
const initialKey = headers['wtv-initial-key'].trim();
|
||||
const challenge = headers['wtv-challenge'].trim();
|
||||
|
||||
this.wtv = new WTVSec({
|
||||
config: {
|
||||
keys: {
|
||||
initial_shared_key: initialKey,
|
||||
},
|
||||
debug_flags: {
|
||||
debug: false
|
||||
}
|
||||
}
|
||||
});
|
||||
|
||||
this.wtv.ProcessChallenge(challenge);
|
||||
}
|
||||
}
|
||||
|
||||
if (!this.secure && text.includes('SECURE ON')) {
|
||||
const headers = httpHeaderParser.parseRequest(text).headers;
|
||||
let incarnationHeader = Object.keys(headers).find(k => k.toLowerCase() === 'wtv-incarnation');
|
||||
let incarnationValue = incarnationHeader ? headers[incarnationHeader].trim() : "1";
|
||||
|
||||
this.incarnation = parseInt(incarnationValue);
|
||||
if (this.wtv) {
|
||||
this.wtv.set_incarnation(this.incarnation);
|
||||
this.secure = true;
|
||||
console.log("🔐 SECURE ON -- Starting decryption (incarnation =", this.incarnation + ")");
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
decrypt(data) {
|
||||
if (this.secure && this.wtv) {
|
||||
try {
|
||||
return this.wtv.Decrypt(0, data); // Use key1 by default
|
||||
} catch (e) {
|
||||
console.error("Decryption failed:", e);
|
||||
return data;
|
||||
}
|
||||
}
|
||||
return data;
|
||||
}
|
||||
}
|
||||
|
||||
// Main PCAP processing
|
||||
const parser = pcap.parse(fs.createReadStream('pcap.pcap'));
|
||||
|
||||
parser.on('packet', packet => {
|
||||
const data = packet.data;
|
||||
const ethType = data.readUInt16BE(12);
|
||||
if (ethType !== 0x0800) return; // Not IPv4
|
||||
|
||||
const ipHeader = data.slice(14, 34);
|
||||
const protocol = ipHeader[9];
|
||||
if (protocol !== 6) return; // Not TCP
|
||||
|
||||
const srcIP = ipHeader.slice(12, 16).join('.');
|
||||
const dstIP = ipHeader.slice(16, 20).join('.');
|
||||
const tcpHeaderStart = 34;
|
||||
const srcPort = data.readUInt16BE(tcpHeaderStart);
|
||||
const dstPort = data.readUInt16BE(tcpHeaderStart + 2);
|
||||
const tcpHeaderLen = (data[tcpHeaderStart + 12] >> 4) * 4;
|
||||
const tcpPayloadOffset = tcpHeaderStart + tcpHeaderLen;
|
||||
|
||||
const payload = data.slice(tcpPayloadOffset);
|
||||
if (payload.length === 0) return;
|
||||
|
||||
const isServer = srcIP === server_ip;
|
||||
const connKey = connectionKey(srcIP, srcPort, dstIP, dstPort);
|
||||
|
||||
if (!connections.has(connKey)) {
|
||||
connections.set(connKey, new ConnectionState());
|
||||
}
|
||||
|
||||
const state = connections.get(connKey);
|
||||
state.feed(payload);
|
||||
|
||||
const decrypted = state.decrypt(payload);
|
||||
|
||||
/* TODO
|
||||
if (decrypted.includes("wtv-lzpf")) {
|
||||
const headers = decrypted.toString('utf8').split("\n\n")[0];
|
||||
const lzpf_data = decrypted.slice(headers.length + 2);
|
||||
const lzpf = new WTVLZPF();
|
||||
process.stdout.write(headers);
|
||||
const decomp_data = lzpf.Decompress(lzpf_data);
|
||||
process.stdout.write(decomp_data);
|
||||
}
|
||||
*/
|
||||
process.stdout.write(decrypted);
|
||||
});
|
||||
|
||||
parser.on('end', () => {
|
||||
console.log('\n✅ Done parsing PCAP.');
|
||||
});
|
||||
Reference in New Issue
Block a user