29C3 CTF: Node writeup

This 29C3 from Chaos Computer Club hackers. We participate as dcua team, awesome people trying the best effort for the challenges.  Nice job!

Node

Points: 200
Solves: 18

Description

Node.js is smart, fast, easy and secure… Don’t you think so too?

Hint: google and other sites always look at one file before they access a website by themself, you might want to have a look that file.

So, a Node app that presents a login screen. We follow the hint that refers view the robots.txt. In this file we see a directive disallow:pages.js. Let’s see it:

var fs = exports.fs = module.parent.exports.fs,
    path = require("path"),
    auth = require("./auth.js"),
    less = require("less"),
    uglify = require("uglify-js"),
    logRequest = function (e) {};
exports.all = function (e, t, n) {
    if (e.path.match(/\.\./)) {
        n(e.who + " relative path " + e.path);
        return
    }
    var r = {
        ua: e.get("user-agent"),
        ip: e.ip
    };
    e.body.user && (r = [e.body.user, e.body.password, r]), auth.check("auth", "app/stats", r, function (t) {
        e.data = t, t && (e.who = e.who.replace(/^guest/, t.type)), n()
    })
}, exports.start = function (e, t, n) {
    logRequest(e);
    if (!e.data) {
        t.type("html");
        if (e.xhr) {
            t.setHeader("Connection", "close"), t.setHeader("Content-Length", 0), t.send(404, "");
            return
        }
        t.render("login", {
            xhr: e.xhr
        }, function (e, r) {
            if (e) {
                n(e);
                return
            }
            t.send(200, r)
        })
    } else if (e.data.type === "admin") {
        if (e.xhr) {
            t.send(200, "/admin");
            return
        }
        t.redirect("/admin")
    } else t.type("html"), t.render("user", function (t) {
        return t.xhr = e.xhr, t
    }(e.data), function (e, r) {
        if (e) {
            n(e);
            return
        }
        t.send(200, r)
    })
}, exports.static = function (e, t, n) {
    t.type(e.path.replace(/\/+/g, "")), fs.readFile(e.app.get("views") + e.path, function (r, i) {
        if (r) {
            n(r);
            return
        }
        i = i.toString();
        if (i.substr(0, 4) === "/**/") {
            t.send(i.substr(4));
            return
        }
        e.params[0] === "css" ? (logRequest(e), less.render(i, function (e, r) {
            if (e) {
                n(e);
                return
            }
            r = r.replace(/\r\n|\r|\n/g, "").replace(/\t/g, " ").replace(/\s+/g, " ").replace(/([,:;{]) /g, function (e, t) {
                return t
            }).replace(/ ([,:;{])/g, function (e, t) {
                return t
            }).replace(/;}/g, "}").replace(/(\D)0(\.\d)/g, function (e, t, n) {
                return t + n
            }), t.send(r)
        })) : e.path.match(/^\/auth\.js/) ? n(e.who + " server-side file " + e.path) : (logRequest(e), t.send(uglify(i)))
    })
}, exports.admin = function (e, t, n) {
    e.data && e.data.type === "admin" ? fs.readFile("app/stats.txt", function (r, i) {
        if (r) {
            n(r);
            return
        }
        var s = i.toString().replace(/^\n+|\n+$|\n(?=\n)/g, "").split("\n");
        s.some(function (e, t) {
            s[t] = JSON.parse(e)
        }), t.type("html"), t.render("admin", {
            flag: module.parent.exports.flag,
            users: s,
            xhr: e.xhr
        }, function (e, r) {
            if (e) {
                n(e);
                return
            }
            t.send(200, r)
        })
    }) : n(e.who + " admin only")
}

Some logRequest staff at top of the file that give us important hints about the final solution of the challenge. We see auth.js that cannot be accesed from http://94.45.252.237:1024/auth.js. It can be from http://94.45.252.237:1024//auth.js , notice the double slash. This js has crypto libs for authentication. There was two functions: check and add, that could be reference for adding users and i pay attention on this part of the add function:

 exports.add = function (e, t, n) {
    crypto.pbkdf2(n.pass, n.mail.substr(0, 10), 1e3, 36, function (r, i) {
        if (r) throw r;
        fs.appendFile("loginData.txt", n.user + ": " + n.pass + "\n", function (e) {
            if (e) throw e
        }), fs.appendFile(t, JSON.stringify({
            user: n.user,
            ua: n.ua,
            ip: n.ip
        }) + "\n", function (e) {
            if (e) throw e
        }), n.pass = i, fs.appendFile(e, JSON.stringify(n) + "\n", function (e) {
            if (e) throw e
        })
    })

So we have a user, a user-agent and an ip that could be important for the login process. We see in pages.js that there was a stats.txt. Let’s see it. Interesting, log files that give us a final approach.

Run LiveHttpHeaders to modify our request and see the response :-). We sent User-Agent, X-Forwarded-For and variable user with the data extracted from stats.txt . If we do this with users different than admin, we acces the profile page of them, but no flag :-(

Firefox 2

 

Let’s see what happened with user admin. The user admin has 8.8.8.8 as ip and «Mozilla/5.0 (compatible; MSIE 6.0; Windows NT 5.1)» as user-agent. If we try to launch the modified request to http://94.45.252.237:1024 it redirects to /admin but has 302 Moved Temporarily http state and a blank page appears, so we try against http://94.45.252.237:1024/admin . We see this page:

 

Firefox

Flag: 29C3_ProxyTrust

No hay contenido relacionado



Comentarios

Aún no hay comentarios. ¿Por qué no comienzas el debate?

Deja una respuesta

Este sitio usa Akismet para reducir el spam. Aprende cómo se procesan los datos de tus comentarios.