Prying Eyes

Description

Welcome to the Prying Eyes, a "safe space" for those curious about the large organisations that dominate our life. How safe is the site really?

Source

index.js

const express = require("express");
const nunjucks = require("nunjucks");

const cookieSession = require("cookie-session");
const { randomBytes } = require("node:crypto");

const Database = require("./database");
const { render } = require("./utils");
const FlashMiddleware = require("./middleware/FlashMiddleware");
const AuthRoutes = require("./routes/auth");
const ForumRoutes = require("./routes/forum");

const app = express();
const db = new Database("./database.db");

// Set up the templating engine
const env = nunjucks.configure("views", { autoescape: true, express: app });
env.addFilter("date", (timestamp) => { const date = new Date(timestamp); return `${date.toLocaleDateString()} at ${date.toLocaleTimeString()}`; });

app.use(cookieSession({ name: "session", secret: randomBytes(69), maxAge: 24 * 60 * 60 * 1000 }));
app.use("/static", express.static("public"));
app.use(
  "/uploads",
  express.static("uploads", {
    setHeaders: (res) => { res.setHeader("Content-Type", "image/avif"); },
  })
);
app.use(express.urlencoded({ extended: false }));
app.use(FlashMiddleware);

app.get("/", function (req, res) { res.redirect("/forum"); });
app.use("/auth", AuthRoutes(db));
app.use("/forum", ForumRoutes(db));
app.use("*", (req, res) => { res.status(404); render(req, res, "error.html", { errorMessage: "We can't seem to find that page!", errorCode: "404" }); });
app.use((err, req, res, next) => { console.error(err); res.status(500); render(req, res, "error.html", { errorMessage: "Something went wrong!", errorCode: "500" }); });

(async () => {
  await db.connect();
  await db.migrate();
  app.listen(1337, "0.0.0.0", () => console.log("Listening on port 1337"));
})();

routes/auth.js

routes/forum.js

Dockerfile

package.json

Solution

Prying_Eyes.png

No action can be done without being logged in, so let's register.

Creds: test02:test02

Forum rules mention about purchases going through moderators and in comments we can upload images.

Prying_Eyes-1.png

In Dockerfile we see that ImageMagick-7.1.0-33 is being installed on system, a very specific version..

Searching for this version CVE-2022-44268 Arbitrary File Read PoC - PNG generatorarrow-up-right turns up, but it says Tested on ImageMagick v. 7.1.0-48 and 6.9.11-60, so this might be vulnerable.

https://github.com/kljunowsky/CVE-2022-44268arrow-up-right

convertParams is taken from req.body and then later used in convert function, there doesn't seem to be any sanitization so we could sneak in some malicious param?

The uploaded image is converted to "AVIF" format, has random filename of length 32 and placed in /uploads.

The npm package is latest version, so unlikely that it's vulnerable: https://www.npmjs.com/package/imagemagick-convert?activeTab=versionsarrow-up-right

Looking into the source code of the function https://github.com/izonder/imagemagick-convert/blob/master/lib/convert.jsarrow-up-right we can observe that it uses const {spawn} = require('child_process'); in background for conversion...

Prying_Eyes-2.png

composeCommand function creates the command which is executed. The code performs raw concatenation of parameters and no sanitization, so it's vulnerable to injection.

String is manipulation is sometimes too awkward to observe, we can trim down process execution and observe it in node interactive shell.

By default we get these arguments.

Looking around the source I was not able to get idea about how to inject anything into spawn, because it takes a list of arguments. If you pass -l -a -h to ls as arguments it's not going to work, because that's not 3 flags, but 1 string for command. Unless split, no injection.

Funnily enough the source shows that composeCommand returns cmd, but if we download the 1.0.3 tag version we see that change doesn't exist here, hence we can inject code.

Prying_Eyes-3.png

Supported flags: https://imagemagick.org/script/convert.phparrow-up-right

We can use -write to save the file twice, one by application and two by the convert command. Now previously mentioned CVE is valid, because we control PNG image and can get LFI.

If you get error while using -write double check that the file was created and it's not application just sending you errors.

circle-check

Last updated