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 :-(
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:
Flag: 29C3_ProxyTrust

