PYRE 0-1

Python source

def save_cookie_expiration(cookiejar):
    cookie_path = "./cookies.db" 
    try:
        for cookie in cookiejar:
            print cookie.path
            cookie_file = open(cookie.path, "a+")
            cookie_file.write("%s:%s\n%s\n%s\n"  % (cookie.domain, cookie.expires, cookie.name, cookie.value))
            cookie_file.close()
    except Exception, e:
        logger.debug("cError: %s" % str(e))

    return

The vulnerability is that an arbitrary path can
be set by the cookie to overwrite an arbitrary file.

A first target would be some of the modules pyrefox imports.
Unfortunately this turned out to be a little difficult because
it is hard to get valid python code for the cookie domain.

Other options include ~/.bash_profile, ~/.bash_history, ~/.bashrc,
and .ssh/authorized_keys.

During the game we tried authorized_keys to no avail. In
hindsight cron would have been a good target.

The home directory was leaked via
user_agent = "pyrefox-0.1 [%s]" % os.environ['HOME']

But .ssh/authorized_keys also seeemd to work.

Exploit follows
header("Set-cookie: asdf=123; path=./asdf");

Note: it is a good idea to use header to avoid html encoding
done by php's set-cookie ()

original client code
#!/usr/bin/env python2.5

import sys
import os
import getopt
import logging
import random
import string
import urllib2
import urllib
import urlparse
import BeautifulSoup
import socket
import cookielib

logger = None

def save_cookie_expiration(cookiejar):
    cookie_path = "./cookies.db" 
    try:
        for cookie in cookiejar:
            print cookie.path
            cookie_file = open(cookie.path, "a+")
            cookie_file.write("%s:%s\n%s\n%s\n"  % (cookie.domain, cookie.expires, cookie.name, cookie.value))
            cookie_file.close()
    except Exception, e:
        logger.debug("cError: %s" % str(e))

    return

def usage(fname):
    print "%s [-d (debug)] [-x <proxy>] [-p prompt]" % fname

class Usage(Exception):
    def __init__(self, msg):
        self.msg = msg

def main(argv=None):
    global logger

    proxy = None
    prompt = "COMMAND> " 
    user_agent = "pyrefox-0.1 [%s]" % os.environ['HOME']
    url = None
    page = None
    soup = None

    debug = False
    loglevel = logging.INFO

    if argv is None:
        argv = sys.argv

    try:
        try:
            opts, args = getopt.getopt(argv[1:], "dhx:p:")
        except getopt.error, msg:
            raise Usage(msg)
        for o, a in opts:
            if o == "-d":
                debug = True
            if o == "-h":
                usage(os.path.basename(argv[0]))
                return 1
            if o == "-x":
                proxy = a
            if o == "-p":
                prompt = a
    except Usage, err:
        usage(os.path.basename(argv[0]))
        return 2

    if debug:
        loglevel = logging.DEBUG

    logging.basicConfig(level=loglevel)

    logger = logging.getLogger('pyrefox')
    cookie_processor = urllib2.HTTPCookieProcessor()

    if proxy != None:
        opener = urllib2.build_opener(urllib2.ProxyHandler({'http': "http://%s/" % proxy}), cookie_processor)                                                  
    else:
        opener = urllib2.build_opener(cookie_processor) 

    # Infinite loop
    url = None
    page = None
    soup = None
    links = None

    while True:
        sys.stdout.write(prompt)
        line = sys.stdin.readline().strip().split(' ')
        if len(line) == 0:
            continue
        if len(line[0]) == 0:
            continue
        if line[0] == '#':
            continue
        elif line[0] == 'q':
            break
        elif line[0] == 'u':
            try:
                if len(line) == 1:
                    logger.error("Missing URL")
                    continue
                url = line[1]
                logger.debug("Getting URL: %s"  % url)
                req = urllib2.Request(url)
                req.add_header("User-Agent", user_agent)
                resp = opener.open(req)
                page = resp.read()
                soup = BeautifulSoup.BeautifulSoup(page)
                links = soup.findAll('a')
                save_cookie_expiration(cookie_processor.cookiejar)
                print page
            except Exception, err:
                logger.error("aError: %s" % str(err))
                url = None
                page = None
                soup = None
                links = None
                continue
        elif line[0] == 'p':
            try:
                if len(line) != 3:
                    logger.error("Malformed POST request (p <url> <data>)")
                    continue
                url = line[1]
                data = line[2]
                logger.debug("Posting to URL: %s with data: %s"  % (url, data))
                req = urllib2.Request(url)
                req.add_header("User-Agent", user_agent)
                resp = opener.open(req, data)
                page = resp.read()
                save_cookie_expiration(cookie_processor.cookiejar)
                soup = BeautifulSoup.BeautifulSoup(page)
                links = soup.findAll('a')
                print page
            except Exception, err:
                logger.error("bError: %s" % str(err))
                url = None
                page = None
                soup = None
                links = None
                continue
        elif line[0] == 'l':
            if page == None:
                continue
            for link in links:
                href = link['href']
                absurl = urlparse.urljoin(url, href)
                print absurl
        else:
            logger.error("Unknown command: %s" % str(line[0]))

    logger.debug("Terminated!")
    return 0

if __name__ == "__main__":
    res = 0
    try:
        res = main()
    except KeyboardInterrupt:
        logger.info('Browser is shutting down...')
        res = 1
    sys.exit(res)

Also available in: HTML TXT