Skip to content

Instantly share code, notes, and snippets.

@ChiefORZ
Last active October 7, 2021 14:48
Show Gist options
  • Star 1 You must be signed in to star a gist
  • Fork 0 You must be signed in to fork a gist
  • Save ChiefORZ/4b08266cf78c333f8403dceb5b52f0e3 to your computer and use it in GitHub Desktop.
Save ChiefORZ/4b08266cf78c333f8403dceb5b52f0e3 to your computer and use it in GitHub Desktop.
QLocker File Revery
PASSWORD=<<YOUR 7 ZIP PASSWORD>>
const path = require('path');
require('dotenv').config({ path: path.join(__dirname, '.env') })
const { exec, spawn } = require('child_process');
const fs = require('fs');
const fsPromises = require('fs').promises;
const util = require('util');
const chalk = require('chalk');
const { extractFull } = require('node-7z');
const glob = require('glob');
const cliProgress = require('cli-progress');
const dayjs = require('dayjs')
const logger = require('./logger');
const mv = util.promisify(fs.rename);
const password = process.env.PASSWORD;
const progressBar = new cliProgress.SingleBar({}, cliProgress.Presets.shades_classic);
const convertArgsToMessage = (...args) => {
if (args.length > 1) {
return util.format(args[0], ...args);
} else {
return args[0];
}
}
const consoleLog = (...args) => {
logger.info(convertArgsToMessage(args));
// console.log(...args)
};
const consoleError = (...args) => {
logger.error(convertArgsToMessage(args));
// console.error(chalk.red(...args))
};
const consoleTitle = (...args) => {
logger.info(convertArgsToMessage(args));
console.log(chalk.bgGreen.white(...args));
}
const consoleStep = (...args) => {
logger.info(convertArgsToMessage(args));
// console.log(chalk.bgBlue.white(...args));
}
const consoleSub = (...args) => {
logger.info(convertArgsToMessage(args));
// console.log(chalk.gray(...args));
}
function execPromise(command, args, opts = {}, internalOpts = { ignoreError: false }) {
return new Promise(function(resolve, reject) {
consoleStep(command, args.join(' '));
const child = spawn(command, args, opts);
const output = [];
child.stdout.on('data', (data) => {
data.toString().split('\n').forEach(line => output.push(line))
consoleLog(data.toString());
});
child.stderr.on('data', (data) => {
consoleError('stderr: ' + data.toString());
});
child.on('close', (code) => {
if (!internalOpts.ignoreError && code !== 0) {
return reject()
}
return resolve(output);
});
});
}
function extractAsync(archivePath, outputPath, options = {}) {
return new Promise(function(resolve, reject) {
const myStream = extractFull(archivePath, outputPath, {
...options,
})
const filesExtracted = [];
myStream.on('data', function (data) {
filesExtracted.push(data.file);
})
myStream.on('end', function () {
resolve(filesExtracted);
})
myStream.on('error', (err) => {
reject(err)
})
})
}
(async function exe() {
const zipFilesFound = await glob.sync('./**/*.7z');
consoleTitle(`${zipFilesFound.length} zips to extract...`);
progressBar.start(zipFilesFound.length, 0);
for (let zipPath of zipFilesFound) {
const outputDir = path.join(path.dirname(zipPath), 'tmp');
try {
const files = await extractAsync(zipPath, outputDir, { password });
for (let file of files) {
await mv(path.join(outputDir, file), zipPath.replace('.7z', ''));
}
await fsPromises.rmdir(outputDir, { recursive: true })
await fsPromises.unlink(zipPath)
progressBar.increment();
consoleSub(`Extracting ${zipPath} done!`, { path: zipPath });
} catch(err) {
consoleError(err, {path: zipPath});
}
}
})()
const path = require('path');
const winston = require('winston');
require('winston-daily-rotate-file');
const logger = winston.createLogger({
format: winston.format.combine(
winston.format.timestamp({
format: 'YYYY-MM-DD HH:mm:ss'
}),
winston.format.json(),
),
transports: [
new winston.transports.DailyRotateFile({
name: 'access-file',
level: 'info',
filename: path.resolve(`${__dirname}/logs/access-%DATE%.log`),
json: true,
datePattern: 'yyyy-MM-DD',
prepend: true,
}),
new winston.transports.DailyRotateFile({
name: 'error-file',
level: 'error',
filename: path.resolve(`${__dirname}/logs/error-%DATE%.log`),
json: true,
datePattern: 'yyyy-MM-DD',
prepend: true,
}),
],
});
module.exports = logger;
{
"name": "recover-qlocker",
"version": "1.0.0",
"main": "index.js",
"license": "MIT",
"dependencies": {
"chalk": "^4.1.1",
"cli-progress": "^3.9.0",
"dayjs": "^1.10.4",
"dotenv": "^9.0.2",
"glob": "^7.1.7",
"node-7z": "^2.1.2",
"winston": "^3.3.3",
"winston-daily-rotate-file": "^4.5.5"
}
}
@ChiefORZ
Copy link
Author

I got attacked by those buggers too - but luckily could find out the password they used to encrypt my files.
Then i tried a bash script to decrypt all my files, but my script was unstable - so i wrote a Node.js script that decrypts all 7zip archives from an specific directory recursively.

  1. Install Node.js on your Qnap NAS (i did it through Qnapclub's QPKG Store https://qnapclub.eu/en/howto/1)
  2. ssh into your NAS
  3. create a folder somewhere (e.g. mkdir /share/Public/recover-qlocker)
  4. copy the files from this gist into the newly created folder (https://gist.github.com/ChiefORZ/4b08266cf78c333f8403dceb5b52f0e3)
  5. go to the folder and install the npm dependencies (cd /share/Public/recover-qlocker; npm install;)
  6. edit the .env and paste your 7zip password
  7. go to the folder, where you want to start the recovery (cd /share/CACHEDEV3_DATA)
  8. run the script (node /share/Public/recover-qlocker)

@Red-1000
Copy link

Thanks for your review mate,
but luckily could find out the password they used to encrypt my files........how did you find the passs?

tia

@ChiefORZ
Copy link
Author

@Red-1000 i found it in my 7zip logs - you can follow this tutorial to try to find the password for your system https://www.youtube.com/watch?v=aq_cIdY_ksQ

@Red-1000
Copy link

hey mate
thanks for your response i did reboot many times the nas during the attack .so i did follow all the manual and get a 7z file but empty and with another script but no password ...got this::
x -y /share/CACHEDEV1_DATA/.qpkg/SurveillanceStation/.nvr_web_target/home/httpd.nvr/cgi-bin/QMon.cab
no succes anyway appreciate your response thanks

@vladkatz
Copy link

vladkatz commented Jun 12, 2021

Hey,

I followed your instructions (thanks!), but unfortunately, it fails to extract. I checked and the password is correct, with all dependencies installed.
As it starts running, the bar and the file count shows, but nothing happens. As I break, in the log files I see multiple of these lines:

{"message":[{"stderr":"\n\nERROR:\n7-Zip cannot find the code that works with archives.\n"},{"path":"<>"}],"level":"error","timestamp":"<>"}

I tried running from different folders, and changing permissions, with no help. Any idea what am I doing wrong?
I'm using the 7z 16.02 that came with the QNAP, should I install any other version for it to work?

7-Zip [64] 16.02 : Copyright (c) 1999-2016 Igor Pavlov : 2016-05-21
p7zip Version 16.02 (locale=en_US.UTF-8,Utf16=on,HugeFiles=on,64 bits,4 CPUs LE)

@hadewijch
Copy link

Hi. I'm not able to edit the .env file.
That file is not in the downloadable zip.

I also tried to put the password directly into the script, but that requires some knowledge, which I don't have.
Is it neccessary to add some symbol before and after that password?

@ChiefORZ
Copy link
Author

@hadewijch
hey, i just tried it - the .env file gets downloaded with the zip.
but its hidden by default - so you have to "unhide" it (for MacOs it is Command + Shift + . in the finder)
or you just edit it in your terminal with vi /share/Public/recover-qlocker/.env - please read a guide on how to use vim

@ChiefORZ
Copy link
Author

@vladkatz
it seems that 7-zip is not recognizing your password as a valid password...
have you entered your password into to .env file??

@hadewijch
Copy link

hadewijch commented Jun 18, 2021 via email

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment