![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=1739136396-uAhnUEzx1nLxCBnzxWVSERLVyuHxUoJB-0-f9b9d16395f6c8f2e45401ca44795b03)
SQL语句:select*from news where id=1 and 1=1。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/16_02.jpg?sign=1739136396-vSVuyHNqGzYcWf78HWjNObdSJ8igsaa1-0-0cd27df83a3732cae94d86c1ca33bad5)
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=1739136396-N1V8WjkgsPh9lQKDXf2VX3JZnxes7PK3-0-91df9194dcac097d8aa0c1de75f62c7a)
SQL语句:select*from news where id=1 order by 1。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/17_02.jpg?sign=1739136396-SvDpZCE0nXDyVLh8IVxeid37BLVA9KAL-0-affba85ca5c710d107d53e6d28d5ddb7)
SQL语句:select*from news where id=1 order by 2。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/17_03.jpg?sign=1739136396-mWiVdu9qnZV7FnG2H4mRSaSncjEc7SYw-0-44304cfc9c0240d5ae840384f44ac4b0)
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=1739136396-AiZcoKqt9Upmb52dbo3fMqOK91BHBKsC-0-e441073b015f0ad3b5628859014a9571)
SQL语句:select*from news where id=1 union select 1。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/17_05.jpg?sign=1739136396-FMvIGCCLwMwEpfv8fO3RwY9m93pwZuK7-0-0886f097782b651d6f568e29632d1d3f)
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=1739136396-TLp3hhwKR1FJ9meRRAeE0d1Gjp9osLdI-0-5b64143d0b642e6c9b065a5caa8cf2ea)
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=1739136396-qQefvQz1bgFU3J9DoppEx6o71YiYAzds-0-bbace0f201613188b7ec0d39b18e7b8f)
图1-1 SQL注入查询出数据库名
MySQL5.0以上的版本中,有一个名为information_schema的默认数据库,里面存放着所有数据库的信息,比如表名、列名、对应权限等,我们可以通过它查询数据库表名,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/18_01.jpg?sign=1739136396-3gJnhVCSjogiBW9FFgwXLa2zUgi2058A-0-926e1190b412831ad04a8b6938129099)
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=1739136396-RoBYPRKZZFk7iYXqOHvgOKLTgIRNYUUr-0-765c6d751578c11509e1db943c59d446)
图1-2 SQL注入查询出表名
虽然得到了数据库sqli中的一个表名为news,但是数据库中一般会有多个表,当出现需要查询的数据不只一条,而回显只能显示一条数据的情况时,可以通过group_concat()函数将多条数据组合成字符串并输出,或者通过limit函数选择输出第几条数据。
这里我们使用group_concat()函数一次查询出所有表名,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/18_03.jpg?sign=1739136396-YIFeDn5RI9JDH0SxR96NDiScxRbSoI5n-0-b518b62e9171492e044867e58ce07d6b)
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=1739136396-9PX2P6KelDiWs6hZ9A4GDofUueQN1dV0-0-36c22d0758265e78b023687fafd70664)
图1-3 使用函数查询出多个表名
得到news表与flag表,我们需要的数据就在flag表中。
查询表中的列名同样是通过information_schema数据库进行查询,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/19_01.jpg?sign=1739136396-jsuXtRS8EJdY6DneRnbaPVX2g9MHualc-0-79686769017ae69cfc6a610577e287a8)
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=1739136396-buWJK3iKaEf3vdd3u8Sg8bFnpH0i3Rsx-0-81d2962fd26543f91395306107d85c3c)
图1-4 查询列名
查询到flag表中只有一个列flag。
数据库名、表名、列名已经被我们通过注入查询出来了,下面直接查询列中的数据即可,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/19_03.jpg?sign=1739136396-giC0cL4Lg0OdElj4NL6tyHD1HcrBYNV1-0-df4908b99faffc09445553594a56efc4)
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=1739136396-A98HDBpXnCDnPTWeNvsg4dC0wS8mjy3s-0-07345239bac5cacaeadbd8432e89cb60)
图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=1739136396-O1sx9CJEC6yysf219v2OkENLI1GtlTIb-0-6e1b15ae9789b72bca145d0b611f2093)
SQL语句:select*from news where id='1' and '1'='1'。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/20_02.jpg?sign=1739136396-caiFX2HAZyOZ257coejPAKfGZ9nyjrqE-0-634bf5e50d7f340767b069e4276efa6b)
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=1739136396-mRSXKsXFiWWc1N2IkgXV0AxKDFsIaJxg-0-03b41d4b3debcc827ca0bc250661a737)
执行结果如图1-6所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/20_04.jpg?sign=1739136396-MN8JsIOQ1BkxPq4Skg2uuKC5vB5uAzAK-0-646a6ac5d7a978bcee1aa774ee5f5a26)
图1-6 利用floor()函数报错注入
下面介绍利用extractvalue()函数报错注入的方法。
extractvalue()函数语法如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/20_05.jpg?sign=1739136396-TvjO6HXPaRRRfwQ32rvTlUAekFROrmus-0-638d9337767219ce95d249a2a79271aa)
第一个参数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=1739136396-Dr4OpZzlFILNxBzfD0I0B4wiIUusVYVJ-0-05822f76b7196a844c6396a2e27ea7da)
图1-7 利用extractvalue()函数报错注入
extractvalue()函数最长报错32位,在注入时经常需要利用切片函数,如substr()函数获取完整数据。
下面介绍利用updatexml()函数报错注入的方法。
updatexml()函数使用不同的XML标记匹配和替换XML块,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/21_02.jpg?sign=1739136396-UCXt0EAO4tJPcL1275IuPdfwphdHpsmH-0-cf28f6521ae13e0dbc5ae62a00d88186)
第一个参数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=1739136396-dtOM0KDVnm3oC7DXPsLlr0wldn2OGHGN-0-55fd161118c0b12eeb7382c79821532d)
~表示按位取反操作,可以达到溢出的效果。
整型溢出是利用子查询引起BITINT溢出,从而设法提取数据。我们知道,如果一个查询任务成功返回,其返回值为0,那么对其进行逻辑非运算的结果就会变成1,例如对(select * from(select user())x)进行逻辑非运算,返回值就是1。我们通过组合取反运算和逻辑非运算可以构造报错并回显信息,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/21_04.jpg?sign=1739136396-FafiIMTwrepwCrhrb4rUk2Y2gh8ZvdlX-0-b2f96bef4c71c0c2603909809fab765e)
下面介绍一种在Oracle 8g、9g、10g版本中不需要任何权限就能构造报错的方法。需要注意的是,在Oracle 11g及之后的版本中,官方加强了访问控制权限,必须有网络访问权限,才能使用此方法,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_01.jpg?sign=1739136396-E2b1D8eS9MuoY0JJq7fq6LueeNMU6W2i-0-1e0dace0b046d0c8bcb354ba3fae7374)
ctxsys.drithsx.sn()函数在Oracle中用于处理文本,当传入参数类型错误时,会返回异常,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_02.jpg?sign=1739136396-DBnPyAVG8uex2qAaOK3trcDkkLdWDNAY-0-3b78f6e76879d1e42a1ecba4ab746423)
CTXSYS.CTX_REPORT.TOKEN_TYPE()函数的作用与ctxsys.drithsx.sn()函数类似,用于处理文本。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_03.jpg?sign=1739136396-7VEMlhvowkoxw1k8T6LJ4lZjA4B1Lg0K-0-0095bf5eeaf7e737e90f908d64272502)
XMLType在调用的时候必须以<:开头,以>结尾。需要注意的是,如果返回的数据中有空格,返回结果会被截断,导致数据不完整。这种情况下应先转为十六进制编码,再导出。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_04.jpg?sign=1739136396-bnqqFQqmbQr1gwsm3FvElwqb8Zg5952c-0-f0ba93cf71e58f9b0c2397c1c310e7d4)
SQL Server的报错注入主要利用的是在类型转化错误时,显示类型转换失败的值,类型转换函数如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_05.jpg?sign=1739136396-TJKKYSKNxCgd6KxNUkOnKmT1hiqeMp4k-0-4d0457f20ce9f3b03866ed24fc47acc1)
下面以CTFHub技能树中的报错注入靶场为例进行介绍。打开靶场输入参数1',结果如图1-8所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_06.jpg?sign=1739136396-wUwgAPl0kgJT7BsK2rI1scV6ryoUj0yg-0-38ea7014dced09e95e72b9571225f82e)
图1-8 探测报错注入
可以看到回显了报错信息,我们尝试报错注入。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/22_07.jpg?sign=1739136396-FgTBTdvBrk6D5XNsglhkZ9ab8DcBMxn7-0-401c191c83ce077d6776e269444ec243)
结果如图1-9所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/23_01.jpg?sign=1739136396-8ELEAWyQB1XjfLadGkAY6EDpZvFzKRTN-0-e91661078e79064a750e25d055f0032a)
图1-9 报错注入得到数据库名
运行结果表明,成功在报错信息中报出数据库名为sqli。
接着爆破出sqli库下的表,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/23_02.jpg?sign=1739136396-Nj6zqvwHWjqNlZfBHMTJtPI2hGDrnai1-0-fad72f53be1dda6a2e2e14be975e188f)
执行结果如图1-10所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/23_03.jpg?sign=1739136396-7Yd8uD7FIxrxmwo981ne9P0ub3fArjZh-0-b4065a24cbb97beb7bae86e03355e6e1)
图1-10 报错注入得到表名
得到news表和flag表之后可以发现,我们要找的数据在flag表中。继续爆破出flag表下的列,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/23_04.jpg?sign=1739136396-ORYKefGB7BZRDqVD0TiJcW6q5lMQUJ3f-0-74792d041a643e5c2d20ee95f14c8908)
执行结果如图1-11所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/23_05.jpg?sign=1739136396-Euzk8GY0fkSIUmc5FAPhlYRWBheEEBWx-0-8ea0f2becc1c81d58cf686106bf16ea5)
图1-11 报错注入得到列名
flag表下只有一个flag列,直接读出数据,查询flag表下的flag列,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/24_01.jpg?sign=1739136396-tsYBSLJB9Y7PrcmM4zdohYwk6nY8GKM5-0-0505d1b5478281f821e36fcb88d4ac0c)
执行结果如图1-12所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/24_02.jpg?sign=1739136396-b7jGqXzT9HZAkbf3NerpR1KqoZOkGtXa-0-ada7025dad3251c29acfa5b1d3788776)
图1-12 报错注入得到部分flag
成功得到flag,注意观察回显的数据不是完整的,这是因为extractvalue()函数和updatexml()函数一次最多只能爆出32位字符,所以需要通过字符串截取函数获取剩余的字符。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/24_03.jpg?sign=1739136396-L0OfIE0O0lSw5B9hY6w37QS9rzC0wVfp-0-fc522477d33b064094050e63d798e362)
执行结果如图1-13所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/24_04.jpg?sign=1739136396-ebfGd4zm2On0Tv06CAac9AqXlxuRMaMB-0-2ab0f5214a4178b93133102ecc713c8c)
图1-13 截取后的结果
4.布尔盲注
当注入点没有直接的回显,只有True(真)和False(假)两种回显时,我们可以通过回显的结果,推断注入的语句执行结果是True还是False。即使没有直接回显数据,我们也能通过不断调整判断条件中的数值,逐个字符地枚举数据库,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/24_05.jpg?sign=1739136396-2DLRuxjt8DuviTwkSoYwUCex4e4SLu6N-0-b8d85f8913d39aac1d841a4adb446579)
布尔盲注最重要的步骤是构造布尔条件,下面列出一些常见的绕过方法。
● 正常情况:'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=1739136396-GMVaj8KeiUByN5zcfXHZagQDPDlUPvm3-0-ce0e7e814b6936ffb90861d06ed7610b)
下面以CTFHub技能树中的布尔注入靶场为例进行介绍。
输入一些测试数据,发现只有两种回显,query_success、query_error,并不会回显具体的数据,数据结果如图1-14、图1-15所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/25_02.jpg?sign=1739136396-fJyuRiOBVDzP5mEoQRsjRnqOLIqendRq-0-cbd67a65b63f71a7b96cb44bc0e15624)
图1-14 正常查询
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/25_03.jpg?sign=1739136396-mQpUIFuMuWIETgeMHJBNTIDKzcpOmPci-0-0f021be4854d63309f2031f8fd466642)
图1-15 错误查询
我们构造一个布尔条件来判断注入语句的执行结果。输入1 and(1=1),执行结果如图1-16所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/26_01.jpg?sign=1739136396-xzPiaoEmWexq0gUs0eCWtRLLufqUEvWO-0-94b9d8788928cdcd16d9703d446d8816)
图1-16 布尔条件为真
输入1 and(1=2),执行结果如图1-17所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/26_02.jpg?sign=1739136396-ruH643tH1IZHXWhIBxGVywmBwjnU8O7J-0-4d5bee1fdb9d2a8476f52189f8e7d8a8)
图1-17 布尔条件为假
可以看到,当拼接后的语句正确时,回显结果为query_success,否则回显结果为query_error。我们通过不同的回显结果,逐个字符地枚举数据,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/26_03.jpg?sign=1739136396-nTObUK2jMMLHnEW48wVIeQb43TYbIGC4-0-eab4392f0c2740daded3dbd8806c936f)
代码中substr((select database()), 1, 1)='a'的意思是判断select database()语句查询结果的第一个字符是否为a。这样我们只需要遍历字符就能判断出数据库名的第一位字符。执行结果如图1-18所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/26_04.jpg?sign=1739136396-8vvzLwV1qAUG955IsrpoS9tmGgv5GUN4-0-0206af3fccc72e540e2c8388dcd44c8c)
图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=1739136396-gjLPkyEuIfOarQVQHThELrXCNqSIXyrd-0-d7eeb15a6bc6209496bd4c39f7b42c3b)
图1-19 判断出数据库第一个字符
接着继续枚举第二个字符,直到枚举出所有数据,这个过程可以通过脚本实现。
5.时间盲注
时间盲注与布尔盲注类似,区别在于时间盲注是通过页面的响应时间判断语句的真假,一般格式如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/27_02.jpg?sign=1739136396-lsZPonO5xs8J4IPsIMsADxhgqJIrN4M2-0-e73787c0697476b8c666904d6cd03404)
两个常用的延时函数如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/27_03.jpg?sign=1739136396-y0mOiMaQ88Ila3TyCU9fEAUGu69lKbvW-0-b59f5bf538122a6106c50bc1b623c6da)
其他导致延时效果的方法如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/27_04.jpg?sign=1739136396-FeLuy1G0o8nLciwPm8Hc85qx9WCWQ9ax-0-d1145ea3b2ce4778582745e18b97447b)
利用笛卡儿积延时注入的代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/27_05.jpg?sign=1739136396-RzicCZmv7yPYAjRySRr8XUhcVsWcaQ5L-0-5bda7ad5a0ce29a837736bf11e1901e3)
下面以CTFHub技能树时间盲注靶场为例进行介绍。
无论输入什么,回显结果都是空的,我们无法通过回显结果来判断SQL语句是否执行成功。正常数据回显如图1-20所示。
这时候可以利用时间盲注来注出数据。输入1 and sleep(0),执行结果如图1-21所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/28_01.jpg?sign=1739136396-DdQEf7P9ZHbegOsxRrkbg8dV7nZUmSrB-0-b6cff67383a2c3745dd4c396cd3c704b)
图1-20 正常数据回显
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/28_02.jpg?sign=1739136396-k9Ud657wIJraOEKitZw3faLKJCYYccaW-0-597ac71ccdd38ea43bf9ca7b8087dd0e)
图1-21 睡眠0秒响应时间
输入1 or sleep(5),执行结果如图1-22所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/28_03.jpg?sign=1739136396-1ucetf4VEUQy7y4xpnqfFu9If3UdrMI2-0-b6e1e9489a81f47e6da990244713d032)
图1-22 睡眠5秒响应时间
根据响应包的时间可知,输入的延时语句确实被执行了。输入我们构造好的语句,当语句执行结果正确时执行延时函数,错误时不执行延时函数,这样就可以通过响应包的时间逐个字符枚举出数据。
例如,构造一个SQL语句,当if语句中的判断结果正确时延时3秒,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/29_01.jpg?sign=1739136396-IaWl6OOGGB9ivxQ2IDUr0b7MNEBfsyBd-0-a9bcfcb1e6efffd5f6f1eca457aefea1)
执行结果如图1-23所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/29_02.jpg?sign=1739136396-0pcekhB1JBkbsD06BwsM4mggX22fa9u6-0-b94c70c30b5ac06974d170a049d81d89)
图1-23 语句正确时延时3秒
if语句中的判断结果错误时无延时,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/29_03.jpg?sign=1739136396-lX9pWtlhIiFl6pce1NgrrMZEGStKAOGp-0-e52cbfce3fb1d7ed393c8e030a702762)
执行结果如图1-24所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/29_04.jpg?sign=1739136396-zx1FJZA1ZwXH08STf8OJYm8l3hKnk7y6-0-7da495ac96532dd6e57a59e49572fac4)
图1-24 语句错误时无延时
利用延时来判断结果,我们通过此方法枚举数据库名的第一个字符,代码如下。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/30_01.jpg?sign=1739136396-zeXiLmd72akhl7AnFjpaFWzPrs3uD4F7-0-226152dc6d3309862f821811e0b35117)
执行结果如图1-25所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/30_02.jpg?sign=1739136396-xSkEptyFHsFKMeoBnv7lZW0l9pqqLwQF-0-b65e0c32bf31398a3e3e2fcd6582a400)
图1-25 语句错误时无延时
当枚举到s字符时延时了3秒,说明数据库名的第一个字符为s,如图1-26所示。
![](https://epubservercos.yuewen.com/2B7BD9/27167019607668606/epubprivate/OEBPS/Images/30_03.jpg?sign=1739136396-ZPdzUVxMRMfzEGjY51TdeovKXUkNWqsG-0-003beea3ada873daa63999c9d6f43d1c)
图1-26 判断出数据库名的第一个字符
接着继续枚举第二个字符,直到枚举出所有数据,这个过程可以通过脚本实现。
值得一提的是,因为延时的原因,时间盲注的枚举速度慢,在有其他方法能够注出数据时一般不建议使用时间盲注。