[394]Scalers技术笔记:SQL注入原理与关键步骤(1) — ScalersTalk成长会 – 持续行动,刻意学习 – ScalersTalk Wonderland

[394]Scalers技术笔记:SQL注入原理与关键步骤(1)

成长分享 scalerstalk 浏览 0条评论

1.29.jpg

最近小玩了一下SQL注入,感觉到在测试的过程中,很有乐趣。于是把一些感受以及一些技术笔记写下来。我觉得还是应该不时的写一些技术的文章,不然大家真的以为我就是专门搞英语或者口译的了……

0x00 关于数据库、SQL语言和SQL注入

先重新温故一下这些概念的定义,对它们的理解,会随着我们实践的深入,而不断加深。

数据库:A database is an organized collection of data. The data is typically organized to model aspects of reality in a way that supports processes requiring information. 数据库是有结构有组织的数据的方式,以便读取信息。数据库中有一种是关系型数据库,目前是主要的数据库类型。目前在Web2.0时代兴起的还有非关系型数据库。

SQL:结构化查询语言。Structured Query Language is a special-purpose programming language designed for managingdata held in a relational database management system (RDBMS).The scope of SQL includes data insert, query, update and delete, schema creation and modification, and data access control. SQL就是用在关系型数据库中查询数据更新数据的语言。

SQL注入(SQLI):SQL injection is a codeinjection technique, used to attack data-driven applications, in which malicious SQL statements are inserted into an entry field for execution (e.g.to dump the database contents to the attacker).SQLI通过一些恶意构造的代码,插入到原来正常的查询域中,达到攻击目的,比如泄露一些未授权信息,甚至拖库。

0x01 SQL注入原理

其实SQL注入很根本的一个原因就是,由于默认对于输入的信任,导致本来应该用来当成数据的输入,被当成了代码来执行。

看一个基本的例子。假使某网站有一个http://www.sql*site.com/cgi-bin/uname.cgi?userName=Tom样式的查询链接,那么一般情况下,在后台可能需要根据userName传入的参数,拼接一条SQL查询语句,即下面的statement进行查询。

statement = “SELECT * FROM users WHERE name =’” + userName+ “‘;”

拼接完成后,查询语句就变成

SELECT * FROM users WHERE name =’Tom’;

然后整句作为一个字符串,双引号引出,即有

statement = “SELECT * FROMusers WHERE name =’Tom’;”

注意看代码中,首先是对输入的变量,即在URL中传入的值前后分别增加了单引号,然后再附在SQL语句后面,再加上分号,最后整体形成如上的查询语句。

于是在默认情况下,我们是把Tom当成一个变量的值,直接拼接,拼接后的字符串作为一个整体,送到数据库执行查询操作。如果世界都按预期的方式运行,那就完美无暇,但是你把信任开放给世界的时候,世界仍然可能伤害你。

如何伤害?一个基本的思路是,既然Tom是作为一个字符串输入并且被拼接执行,能不能在输入Tom的时代夹带一些私货,改变原有的查询语义?我们注意到在拼接的时候,会额外引入单引号,放在传入值的两侧,这主要是由于传入的变量是字符串。在SQL中,字符串的表示需要用一对单引号‘’标示出。

那如果要夹带私货,首先尝试的是,先跳出字符串的范围,因为字符串里的内容是不会被执行的,所以,在传入参数的时候,与其让后台加上单引号,不如先主动引入,形成闭合。于是我们构造的输入的前半段,可以是Tom’。这样就先闭合引号了。

当形成闭合后,于是又有了腾挪的空间。我们用一些最基本的二值逻辑尝试一下。我们知道或(or)命题,如果有一个分支为真,整体命题恒为真。其实在SQL语言中,WHERE后面就是逻辑真值判断的子句了。所以我们构造一个or 1=1的命题,它可以附加在其他命题后面,使得命题一直成立。但是你要注意到,这里的参数值是字符串形式,所以在前文闭合第一个单引号的情况下,留下了后台自动加上的后半个单引号。

所以,你需要附上的是字符串版本的或命题,即or ‘1’=’1。注意最右侧少了一个单引号,因为拼接后,系统会补上。

这样一来,通过URL传入的参数就变成了Tom’ or ‘1’=’1。相当于你以前在网页的表单中是要查询Tom的,而这次夹带私货的时候,需要输入Tom’ or ‘1’=’1。于是网站后台在收到你传入的参数后,拼接出来的语句是

statement = “SELECT * FROM users WHERE name =’Tom’ or ‘1’=‘1’;”

这样一来,这个WHERE子句就一直为真值了,所以查询语句从语义上等价如下含义

statement = “SELECT * FROM users;”

这相当于查了整张表。意思是,如果你本来只是对用户开放单条语句的查询,而现在,可以通过构造一个夹带私货的版本,查到了所有表里的信息。就像你从单位的工资系统里面查出了所有人的工资一样,这是安全问题。

再回到上面,其实我们可以再简单粗暴一些。Tom这个参数值其实也没有作用了,因为反正恒为真值。其实字符串版本的1也可以变成数字,or 1=1,那最后的单引号怎么办?简单粗暴的用注释符号#或者。注意后面要有空格,#后面可以不用。

于是上述的语句经过拼接后变成如下形式:

statement = “SELECT * FROM users WHERE name =‘’ or 1=1 #;”

注意区分,两个单引号在一起,和一个双引号的区别。那分号怎么办?SQL中的分号用于每条语句的区分,如果单条语句没有分号,不会造成影响。其实注释掉是个不错的思路,因为有时候你不知道后面会不会有其他的语句。

以上就是SQL注入的最基本的原理了。由于网站后台在拼接请求的时候,没有把数据和代码分离,天真地以为传进来的就是数值了,但是没想到却被当枪使,利用规则构造一个恒真的命题,便可以长驱直入了。

但是在后续的学习中,其实我们要始终牢记的点就是,把你的注入语句,放在网站后台代码处理的角度来看,这样你就知道什么时候用什么符号是否要闭合了。比如,参数如果是整数型的话,比如?id=3,那么其实就不用太考虑单引号的问题了。

本文作者Scalers,游走在口译世界的IT从业者。微信公众号ScalersTalk,网站ScalersTalk.com

转载请联系授权,否则将被公开谴责并追究法律责任。ScalersTalk已建立专业法律团队,侵权必究。

回复“VIP”查看2015年成长会会员招募。口译100小时训练计划群B 433686569

S君个人微信号,ScalersTalker 。打赏支付宝

scalerstalk [at] gmail [dot] com

回复任意小于标题括号中的数字查看历史文章。或者访问站点 ScalersTalk.com 查看。

与本文相关的文章