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