• SQL手工注入基础篇


    0.前言

    本篇博文是对SQL手工注入进行基础知识的讲解,更多进阶知识请参考进阶篇(咕咕),文中有误之处,还请各位师傅指出来。学习本篇之前,请先确保以及掌握了以下知识:

    • 基本的SQL语句
    • HTTP的GET、POST请求,URL编码

    文中所有例题选自sqlilab,可以先配置好一起边看边操作。因为虚拟机炸了,所以我自己搭建了一个简陋的平台,sqlilab可自行进行搭建练习,在后面的博客也会写一些关于sqlilab的wp。

    1.准备工作

    在开始SQL注入之前,我们首要需要了解SQL注入的原理,对于一个新安装好的MySql数据库,你至少会包含三个已经建立好的数据库分别是user、infomation_schema、performance_schema如下图所示

    而SQL注入要干的,这些系统数据库中存储了MySql各个数据库的属性以及用户信息,我们要做的就是绕过过滤再来利用这些系统表进行查询。

    了解了这个之后我们还要了解一点就是PHP的GET方法和POST方法传参的区别,如果使用GET方法,则会自动进行一次url解码,例如传入%23实际得到‘#’,而POST则会将数据原封不动的传输。下面我们开始进入正式的SQL注入阶段。

    2.判断注入类型

    一般的对于SQL的查询语句,有字符型和数值型查询,而这两者的区别就是是否有单引号,这决定了我们接下来应该如何构造SQL注入语句。

      判断方法有如下几种,

    1. +-数值,如果是数值型的,你可以尝试使用1+1,1+2,这样的语句,例如  看网页回显是否正确。
    2. and 1=1,and 1=2,直接在后面添加“1 and 1=1”和“1 and 1=2”(前面有个空格)来进行查询,若1=1回显正确而1=2回显错误则为数值型。
    3. 加‘#,在后面添加'#进行查询,若回显正确则表明为字符型。

    除了上述几种方法还可以用其他方法进行判断,但原理都是构造SQL语句进行判断。

    3.查列数

      通过上述方法知道了注入类型之后,我们就可以进行下一步的操作了,在这里我搭建了一个简易的存在字符型查询漏洞的页面,大家可以在本地搭建一下一边学习一边练习。

    SQL代码如下:

     1 /*
     2 Navicat MySQL Data Transfer
     3 
     4 Source Server         : Mysql
     5 Source Server Version : 50553
     6 Source Host           : localhost:3306
     7 Source Database       : test
     8 
     9 Target Server Type    : MYSQL
    10 Target Server Version : 50553
    11 File Encoding         : 65001
    12 
    13 Date: 2019-09-18 23:17:53
    14 */
    15 
    16 SET FOREIGN_KEY_CHECKS=0;
    17 
    18 -- ----------------------------
    19 -- Table structure for secret_table
    20 -- ----------------------------
    21 DROP TABLE IF EXISTS `secret_table`;
    22 CREATE TABLE `secret_table` (
    23   `fl4g` varchar(32) DEFAULT NULL
    24 ) ENGINE=MyISAM DEFAULT CHARSET=utf8;
    25 
    26 -- ----------------------------
    27 -- Records of secret_table
    28 -- ----------------------------
    29 INSERT INTO `secret_table` VALUES ('flag_is_here');
    30 
    31 -- ----------------------------
    32 -- Table structure for student
    33 -- ----------------------------
    34 DROP TABLE IF EXISTS `student`;
    35 CREATE TABLE `student` (
    36   `id` int(11) NOT NULL AUTO_INCREMENT,
    37   `name` varchar(255) DEFAULT NULL,
    38   `class` varchar(255) DEFAULT NULL,
    39   `age` int(11) DEFAULT NULL,
    40   `note` varchar(16) DEFAULT '',
    41   PRIMARY KEY (`id`)
    42 ) ENGINE=MyISAM AUTO_INCREMENT=9 DEFAULT CHARSET=utf8;
    43 
    44 -- ----------------------------
    45 -- Records of student
    46 -- ----------------------------
    47 INSERT INTO `student` VALUES ('1', 'zhangsan', 'nss', '18', '');
    48 INSERT INTO `student` VALUES ('2', 'lisi', 'nss', '19', '');
    49 INSERT INTO `student` VALUES ('3', 'wangwu', 'nss2', '20', '');
    50 INSERT INTO `student` VALUES ('4', 'zhaoliu', 'nss2', '21', '');
    51 INSERT INTO `student` VALUES ('5', 'sunqi', 'nss2', '22', '');
    52 INSERT INTO `student` VALUES ('6', 'qianba', 'nss', '23', '');
    53 INSERT INTO `student` VALUES ('7', 'liujiu', 'nss', '24', '');
    View Code

    PHP代码如下:

     1 <?php
     2     if (!empty($_POST['st'])) {
     3         $conn = mysqli_connect("localhost","root","root","test");
     4         $name = $_POST['name'];
     5         $sql = "select * from student where name='".$name."';";
     6         
     7         $result = mysqli_query($conn,$sql);
     8 
     9         echo "<table border='1'>
    10         <tr>
    11         <th>Id</th>
    12         <th>Name</th>
    13         <th>Class</th>
    14         <th>Age</th>
    15         <th>Note</th>
    16         </tr>";
    17         
    18         while($row = mysqli_fetch_array($result)) {
    19             echo "<tr>";
    20             echo "<td>" . $row['id'] . "</td>";
    21             echo "<td>" . $row['name'] . "</td>";
    22             echo "<td>" . $row['class'] . "</td>";
    23             echo "<td>" . $row['age'] . "</td>";
    24             echo "<td>" . $row['note'] . "</td>";
    25             echo "</tr>";
    26         }
    27         echo "</table>";
    28 
    29         mysqli_close($conn);
    30     }
    31 ?>
    32 
    33 <!doctype html>
    34 <!-- flag in SQL -->
    35 <form action="" method="POST">
    36     <input type="text" placeholder="Input A Name" name="name" value="">
    37     <button type="submit" name="st" value='1'>提交</button>
    38 </form>
    39 <div style="position: absolute;bottom:0;100%;display:flex;flex-direction:column;">
    40     <hr >
    41     <a style="align-self:center;" href="./source.txt">Source</a>
    42 </div>
    View Code

     通过上面的判断我们知道是字符型注入,现在我们需要查出这个数据表的列数来为后面的联合查询做铺垫。

    当查询语句最后为where xx 的时候我们使用order by num;

    当查询语句最后为limit xx的时候我们使用into @,@;

    对于第一种order by num; num代表数值,语义就是以第几列进行排序,当列不存在是就会报错,我们就可以用二分的方法找出正确的列数。如下

     

     对于lisi' order by 6#,lisi是数据库中的正常数据,单引号是为了闭合前面的select语句,#是mysql的单行注释语句,提示报错,换成5则正确,说明该表有5列。

    对于limit xx的情况,我会在另一篇额外讲解。

    4.确定字段位置

      当我们获得表的列数之后,就可以通过联合查询获得数据库的信息,但在此之前,我们还需要确定每个字段显示在网页上的位置,方便查看后面的数据。

      对于例题,我们知道列数为5之后,构造参数lisi' and 1=2 union select 1,2,3,4,5#即可知道每个字段的位置

      完整的SQL语句就是select * from student where name = ‘lisi’ and 1=2 union select 1,2,3,4,5#';

       and 1=2是为了避免一些只显示一行的页面过滤掉后面的联合查询的数据,所以让前面的查询条件永远为false,这里你不加这个,但为了方便我们都加上这个。

      union就是合并操作,完整语义就是使用union合并两个select的结果集,前者为空,后者结果为1,2,3,4,5,所以就会把这些结果合并然后显示到对应的字段。

    5.获取数据库信息

      当列数和字段位置都知道后,我们就可以通过进一步的查询来获取数据库信息了,下面提供一些常用的数据库函数。

      database():查看当前数据库名称
      version():查看数据库版本信息
      user():返回当前数据库连接的用户
      char():将ASCII码转化成字符,用于分隔每个字段的内容

       使用方法就是将函数放在列的位置,例如提交lisi' and 1=2 union select 1,user(),database(),4,5#,结果如下:

      

      同样在最开始的时候我们提到了MySql的几个系统表,我们也可以从这些系统表中获取所有表名,列数,字段名等数据。

      例如查询所有表名,提交lisi' and 1=2 union select 1,2,3,4,TABLE_NAME from INFORMATION_SCHEMA.TABLES where TABLE_SCHEMA='test,结果如下

      

      第五个位置也就是查询INFORMATION_SCHEMA.TABLES表中数据库为test的所有表名,MySql的表属性会存储在这个表中,因为后面还有一个单引号,所以这里右边就不要单引号了。

      这样我们就查询到了所有的表名,现在我们发现有个表叫做secret_table,猜测flag隐藏在其中,那么我们再来获取这个表所有的字段名。

      提交lisi' and 1=2 union select 1,2,3,4,COLUMN_NAME from INFORMATION_SCHEMA.COLUMNS where TABLE_NAME='secret_table。结果如下

      

      语义同上,这里就不在做解释了,现在我们可以看到这个表有一个叫fl4g的字段,那flag就藏在这里没错了。

      接下来要做的就简单了,我们只需要提交lisi' and 1=2 union select 1,2,3,4,fl4g from secret_table#

      

       到此我们就找到了最终的flag。

    6.总结

      SQL手工注入的基础知识道驰就结束了,推荐看完了这篇再去学习SQLmap的知识,可以很快上手也可以了解其本质的东西,不推荐直接学习SQLmap成为脚本小子。

      再梳理一遍上述知识,首先找到注入点,注入点一般是网页的某个提供查询的地方,然后确定是字符型还是数值型,当确定了注入类型之后,就是进行确定一些数据表的信息,方便后面的盲注,最后在从这些注入点得到我们想要的信息。

      对于CTF题目来说,一般不是让你盲注,会将代码给你,这时候注入点肯定是会有诸多的过滤,这时候我们就不能直接执行上述语句了,我们就需要构造一些语句去绕过执行,但最终要达到的效果和上述内容是一致的。

      对于更多的SQL注入知识,以及一些绕过技巧我将会在后面的进阶篇详解阐述。

  • 相关阅读:
    Samba服务器搭建
    Nginx优化
    Nginx配置
    LNMP环境搭建
    mysql主从同步
    Linux系统文件权限体系详解
    强大的grep,sed和awk--用案例来讲解
    Linux中关机,重启,注销命令
    如何解决一个问题(一)
    Linux基础命令讲解(二)
  • 原文地址:https://www.cnblogs.com/xenny/p/11552645.html
Copyright © 2020-2023  润新知