SQL注入的基础代码
以下知识仅为个人学习过程中的记录
<?php
$db = init_db();
$username = $_GET['username']; //input: 111' and 1=1#
$db->query("select * from table where username = '$username'");
?>string connectionstring = "xxx";
SqlConnection con = new SqlConnection(connectionstring);
con.Open();
string idCard = Request.QueryString["idCard"];
string sql = "select * from table where idCard = '" + idCard + "'";
SqlDataAdapter adapter = new SqlDataAdapter(sql, con);
DataSet dataSet = new System.Data.DataSet();
adapter.Fill(dataSet);
con.Close()conn = DBHerpel.getConnection();
if (conn == null)
return;
String username = request.getParameter("username");
String Sql = "select * from table where username = '" + username + "'";
stt = conn.createStatement();
set = stt.executeQuery(Sql);@app.route("/", methods=["GET"])
def test():
username = request.args.get('username')
sql = "select * from table where username = '" + username + "'"
conn = connect(host='localhost',port=3306,user='root',password='',database='test',charset='utf8')
cs1 = conn.cursor()
count = cs1.execute(sql)哪怕不是全部了解上面的语言,也可以看出来,这些代码都是没有使用ORM框架,直接调用原生数据库操作函数的实例。(ORM框架其实本身也是调用原生数据库操作函数,但是它做了一些封装和映射,只要按照规范写法,现代ORM基本都加了一些过滤在里面)
通过上面的代码,可以很明显看出来,这些sql语句都是拼接构成的,所以说他们都存在SQL注入,在数据库层面中的数据流(SQL语句)侵入到了控制流。通常这种漏洞点大概率会在不同层面组件的交汇点,像上面就是代码层和数据库层的交汇点出现了SQL注入。
调用ORM的错误写法
PHP
对于PHP来说没有什么特别出名的ORM框架,基本都是各个Web开发框架自带的ORM。对于这种情况,可以直接审计对应Web开发框架,因为这些开发框架/CMS的ORM可能写的并不规范,没有专门针对这方面进行维护,所以说相对而言存在漏洞点的可能更高。
Java/Mybatis
Mybatis需要有一个xml配置文件来绑定映射关系
<select id="findUserByName" parameterType="java.lang.String" resultType="cn.itcast.mybatis.po.User">
<!-- 拼接 MySQL,引起 SQL 注⼊ -->
SELECT * FROM table WHERE username = '${value}'
</select>@Test
public void testFindUserByName() throws Exception{
SqlSession sqlSession=sqlSessionFactory.openSession();
//创建UserMapper代理对象
UserMapper userMapper=sqlSession.getMapper(UserMapper.class);
//调⽤userMapper的⽅法
List<User> list=userMapper.findUserByName("fuck' and 1=1#");
sqlSession.close();
System.out.println(list);
}Mybatis存在两种变量绑定方式,分别是#{}和${}
#是绑定变量的形式,底层会将#{}替换为?,有参数映射会在DefaultParameterHandler中进⾏设置占位符的操作,这就是预编译$也是绑定变量的形式,{value}是直接被替换成对应的值,没有参数映射,不会进行设置占位符的操作,本质上就是拼接
在代码审计中可以直接搜$符号,针对小型的CMS来说简单而有效
Python/Sqlalchemy
Sqlalchemy是Flask最经常配套使用的ORM框架,在许多的Django项目中也经常使用,是目前Python语言中最火的ORM框架
@app.route("/", methods=["GET"])
def test():
username = request.args.get('username')
res = db.session.query(table).filter("username={}".format(username))这个存在漏洞的原因是跟上面的Mybatis差不多,就是先"username={}".format(username)拼接字符串,然后再使用ORM框架进行查询,这个地方就是不同层面的交汇处,因为不规范的写法导致SQL注入
正规写法如下:
@app.route("/", methods=["GET"])
def test():
username = request.args.get('username')
res = db.session.query(table).filter(table.username == username)SQL注入到底是在干什么
重要核心
看完了上面的原生数据库操作函数的调用导致的SQL注入以及ORM框架的错误写法导致的SQL注入,我们知道SQL注入到底是在干什么吗?
不需要特别关心SQL注入的类型有哪些,像什么联合查询注入、二次注入、布尔盲注等
其实,仔细总结上面的内容,可以得出一个结论:
SQL注入就是去执行一段SQL语句,而这个SQL语句长什么样子,关键跟数据库类型有关
不同的编程语言他们最终的目的都是将SQL语句送入数据库层进行执行,我们只要能发现注入点即可,至于用什么开发语言都是一样的
对于注入类型
注入类型相对于开发语言来说比较重要一些,不需要特别关注,但是我们得知道一些关键点
能否堆叠注入
像上面提到的,一条SQL语句最终会被解析成一段控制序列
action(动作): select
object(对象): table
subject(⽬标客体): *
condition(条件):
key: username
value: $username // ⽤户输⼊如果能够使用堆叠注入的话,那么我们就会跳出action的动作,进而执行其他动作,这样造成的危害程度肯定会更高
但是堆叠注入的出现越来越少,一般情况下:
Mssql数据库的堆叠注入最多;Oracle数据库的数量少一些;Mysql不多
爆数据的速度
报错~=联合查询>带外查询>布尔盲注>时间盲注
对于输入点
当我们发现一个注入点,我们也要关注这个注入点在SQL语句中的输入位置,根据不同的输入点,我们需要采用不同的payload,同时也要考虑是否需要进行绕过,后面的内容会联动这点
select $username$,password from $table$ where $username2$ = '$xxx$' order by $username3$ desc limit $0$,1有撸点的SQL注入类型
宽字节注入
概念
我们知道字节是计算机存储世界中最⼩的衡量单位,1Byte = 8bits。所以⼀个字节最⼤能够表示2^8=256个字符。
所以:
对ascii编码⽽⾔,⼀个字符⽤⼀个字节就可以表示,所以ascii编码最多可以表示256个字符。
对GBK编码⽽⾔,⼀个汉字字符需要⽤两个字节表示。所以gbk编码理论上最多可以表示256*256个字符。
利用方式
<?php
$db = init_db();
$db->query("SET NAMES 'gbk'); //设置gbk字符集
// addslashes会自动对单引号转义
$username = addslashes($_GET['username']); //input: fuck' and 1=1#
$db->query("select * from table where username = '$username'");
?>select * from table where username = 'fuck\' and 1=1#'SQL语句如上所示,在代码层会被直接插入一个\符号,从而对单引号进行转义,这样我们就没法入侵到控制流了
但是当我们输入是这样的时候,就会出现不一样的效果了
fuck%df\' and 1=1#数据在存储时采用字节方式存储,但是解读时采用字符标准去解读。
而字符标准跟采用什么字符集有关系。所以在连接数据库执行SQL语句时(这时候就是对数据的解读),会按照字符集编码的规则去解读。而当遇到\xDF\x5C时,就会解读成運

转码网站:https://www.23bei.com/tool/54.html#
\x5c就是\的十六进制表示

这里补充一下,并非只有\xdf才可以和\x5c组成字符,其他字符也OK,不必纠结这个,具体哪些字符组合可以自行查阅GBK编码
情景利用
所以在代码审计中审阅宽字节注入的方式就是查看数据库连接文件(一般是conn.php这种)。
检查其字符集是否为utf-8,如果不是utf-8,那么就有可能产生注入
参考文章
预编译模式下的注入
宽字节注入
<?php
$username = $_GET['username'];
$db = "mysql:host=127.0.0.1;dbname=test;charset=gbk";
$dbname = "root";
$passwd = "root";
$conn = new PDO($dbs, $dbname, $passwd);
$conn->query('SET NAMES GBK');
$stmt = $conn->prepare("select * from table where username = :username");
$stmt->bindParam(":username",$username);
$stmt->execute();
?>看似进行预编译好像安全了,但是在这种情况下是无法解决宽字节注入的问题的
因为,在数据的流转中是这样的
$stmt = $conn->prepare("select * from table where username = :username");
==>sql = "select * from table where username = '${addslashes($input)}'"
在此处我们有发现了addslashes这个函数,那不是跟传统的宽字节注入绕过方法一样吗?
所以在这种情况下,预编译是不起作用的
一些无法预编译的点
既然讲到预编译不起作用,那么这部分聊聊那些没法通过ORM/预编译去保护的输入点,这种情况是通用的,无视任何语言
like&in关键字
我们知道
sql语句的模糊查找⾥⾯⽤的关键字like。⽽
like关键字默认是不会预编译的(如果使⽤Mybatis则是预编译报错)。数据库⽅给出的原因好像是like预编译会造成慢查询和DOS。
所以对于like关键字,只能通过手动添加预编译来解决。
一些开发只会照着以往的样子进行编写,不会知道like关键字的预编译会失效,所以这时候就给了我们可趁之机
<?php
$username = $_GET['username'];
$db = "mysql:host=127.0.0.1;dbname=test;";
$dbname = "root";
$passwd = "root";
$conn = new PDO($dbs, $dbname, $passwd);
$conn->query('SET NAMES GBK');
$stmt = $conn->prepare("select * from table where username like '%:username%'"); //不⽣效
$stmt = $conn->prepare("select * from table where username like concat('%',:username,'%'"); //⽣效
$stmt->bindParam(":username",$username);
$stmt->execute();
?>一些Java开发发现Mybatis编译报错,他就会改用$符号,或者直接就不写预编译。
如果开发手写预编译的话,也可能没有那么完善,存在绕过的机会。
同样的,in关键字也是默认不能进行预编译的,需要在预编译语法中去for循环,有些开发为了方便,写的代码少一些,就可能在这边偷工减料
不能加引号的关键字
结合我们上面看到宽字节绕过预编译,我们可以看到预编译+绑定变量的效果大概有两个步骤
$str = addslashes($input) //内容转义
$sql = "select * from table where column = '$str'" //强制使用单引号包裹结合我们上面提到的产生注入的输入点
select $username$,password from $table$ where $username2$ = '$xxx$' order by $username3$ $desc$ limit $0$,1观察上面SQL语句中的输入点,有哪些地方是必然不能加单引号的呢?如果这个地方不能加单引号,那么就无法进行预编译
于是整合如下:
$username$,$table$,$username2$,$username3$,$desc$,$0$也就是说列名、表名、limit子句、order by[desc/asc]都不能添加单引号,所以他们都不能进行预编译。
在这种情况下,开发可能会遗漏这些点的检测,导致存在注入。
参考文章
https://forum.butian.net/share/1559
http://cn-sec.com/archives/286656.html
https://codeantenna.com/a/WzaLCig9qw
权限认知-数据平面
一些权限的例子
- 在
cms/web层⾯:web后台管理员权限>web前台会员权限>游客权限 - 在数据库层⾯:root⽤户/sa⽤户/postgres⽤户>普通杂七杂⼋⽤户
- 在操作系统层⾯:root > 所有其他⽤户
对于权限的重要认知
认清数据平面
未授权RCE
我们以游客的权限,拿到⼀个rce漏洞(也就是未授权rce),那么我们的权限是什么?
是web后台管理员权限吗?并不是,而是操作系统权限,此时我们的数据平面在操作系统层面
在操作系统层⾯,我们的**bash进程是httpd进程派⽣的,所以与httpd进程是同层级的,所以此
时我可以读写web⽬录的任意⽂件(前提是web⽬录本身对httpd可读写)**
SQL注入
如果我们以游客权限,拿到⼀个sql注⼊点,那么我们的权限是什么?
是web后台管理员权限吗?并不是,而是数据库权限,此时我们的数据平面在数据库层面
在数据库层⾯,我们⽬前就是mysql进程维护的⼀个通信线程,我们就是数据库的root或者普通库权限(取决于web数据库配置⽂件中定义的⽤户)。
SQL注入到命令执行
如果我们以游客权限,拿到⼀个sql注⼊点,并且这个注⼊点可以执⾏命令,那么我们的权限是什么?
是数据库的root吗?并不是,而是操作系统权限,此时我们的数据平面在操作系统层面
在操作系统层⾯,我们⽬前就是mysql进程派⽣的⼀个bash进程,所以与mysql进程是同层级的,所以此时我可以读写mysql数据库⽂件⽬录的⽂件。也⼤概率有权限读web⽬录的⽂件
评论 (0)