Mysql操作中的条件竞争漏洞

今天发现一个有趣的web条件竞争漏洞,本地复现了一下。

这一个常见情景:A和B互相转账。数据库操作也非常简单,取出A的余额与转账金额比较,如果转账金额小于余额就成功,大于就失败。
简单的代码如下:

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
<?php
$db_host = '';
$db_name = '';
$db_user = '';
$db_pass = '';
$mysqli = new mysqli($db_host,$db_user,$db_pass,$db_name);
if(mysqli_connect_error()){echo mysqli_connect_error();exit();}
if(isset($_POST['submit'])){
$transfer = addslashes($_POST['transfer']);
$from = addslashes($_POST['from']);
$to = addslashes($_POST['to']);
$check = "select money from money where user = '".$from."'";
$check_res = $mysqli->query($check);
while($row = $check_res->fetch_assoc()){
$currmoney = addslashes($row['money']);
}
if($currmoney<$transfer){
echo '余额不足。';
$mysqli->close();
}else{
$mysqli->query("UPDATE money SET money = money + 100 WHERE user ='".$to."'");
$mysqli->query("UPDATE money SET money = money - 100 WHERE user ='".$from."'");
}
}
@$mysqli->close();
?>

数据库内容:

1
2
3
4
5
6
7
8
mysql> select * from money;
+----------+-------+----+
| user | money | id |
+----------+-------+----+
| homaebic | 1000 | 1 |
| godot | 1000 | 2 |
+----------+-------+----+
2 rows in set (0.00 sec)

发送数据包:

1
2
3
4
5
6
7
8
9
10
11
12
POST /secstu/tiaojianjingzheng/index.php HTTP/1.1
Host: localhost
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/47.0.2526.73 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: zh-CN,zh;q=0.8,en-US;q=0.5,en;q=0.3
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 43
Connection: close
Upgrade-Insecure-Requests: 1
submit=&from=homaebic&to=godot&transfer=100

再看数据库:

1
2
3
4
5
6
7
8
mysql> select * from money;
+----------+-------+----+
| user | money | id |
+----------+-------+----+
| homaebic | 900 | 1 |
| godot | 1100 | 2 |
+----------+-------+----+
2 rows in set (0.00 sec)

多发包几次,homaebic的money到0之后就不再动了。

TIM截图20170725172724.png

(MYSQL监控也显示只进行select操作,没有update操作。)

这时候要使用Burpsuite的多线程功能了,重复此操作N次

TIM截图20170725172922.png

TIM截图20170725172928.png

发送50个包,25线程。

再来看数据库

1
2
3
4
5
6
7
8
mysql> select * from money;
+----------+-------+----+
| user | money | id |
+----------+-------+----+
| homaebic | -5000 | 1 |
| godot | 6900 | 2 |
+----------+-------+----+
2 rows in set (0.00 sec)

TIM截图20170725173446.png

数据库的顺序也非常混乱。

将这个操作扩展到其他方面,只要是从数据库取出的数据比较后再次操作数据库,就有可能产生这样的问题。而这样的操作是非常多的,大到用户注册,小到添加栏目都会有。但并不是每一个操作都会加锁,所以这时如果服务器处理速度缓慢,就有可能产生上面的情况。
我不是很了解系统上并发,多线程的原理之类,所以想了蛮久也不清楚为何会出现这样的情况。

解决方法也很简单,给MYSQL上个锁:mysql第一句话之前加上一句BEGIN,结束后加上一句操作COMMIT,就不会发生上面的情况了。

支持一下
扫一扫,支持forsigner