sql注入与数据库利用
sql注⼊的基础代码样例
php:
1 | $db = init_db(); |
.net/aspx:
1 | string connectionstring = "xxx"; |
java:
1 | conn = DBHerpel.getConnection(); |
python(flask):
1 |
|
以上全都是不同语⾔在不调⽤orm框架,直接调⽤原⽣数据库操作函数时的⽤例。(其实orm
框架的底层也是调⽤了原⽣数据库操作函数,只是orm帮开发者做了封装和对象映射的步骤)
就可以理解这个漏洞:
- 仅仅抓住输⼊
- 当数据流⼊侵到控制流时,漏洞就产⽣了
- “数据流⼊侵控制流”产⽣的⻛险点,在于不同层⾯组件的交汇处(如:代码层与数据库层)
java/MyBatis:
MyBatis需要有⼀个xml配置⽂件来来绑定映射关系
1 | <select id="findUserByName" parameterType="java.lang.String" |
1 |
|
简单讲就是MyBatis有两种变量绑定⽅式,分别是:#{}和${}
- **#**是绑定变量的形式,底层会⽤#{}会被替换为?号,有参数映射,会在
DefaultParameterHandler中进⾏设置占位符的操作 –>预编译 - **$**也是绑定变量的形式,{value}是直接被替换为了对应的值,没有参数映射,不会进⾏设置占位
符的操作 –>拼接
很多代码审计初学者或者很多课程,上来就让去找$符号的原因
python/flask/sqlalchemy:
sqlalchemy是flask最经常配套的orm框架,在许多django项⽬中也常常看到身影。也是⽬前
python上⽤得最⽕的orm框架。
https://blog.csdn.net/weixin_47906106/article/details/123774620
https://blog.csdn.net/weixin_46549605/article/details/123458430
https://blog.csdn.net/qq_41341757/article/details/109462158
这⾥是⼀个有注⼊漏洞的例⼦
1 |
|
原因跟上⾯MyBatis类似,依然是⽤户输⼊其实是拼接后才导⼊sqlalchemy层的(不同层⾯组
件的交汇处)
正确的写法:
1 |
|
sql注⼊到底在注⼊什么
sql联合查询注⼊
sql堆叠注⼊
sql报错注⼊-N个payload
sql时间盲注
sql布尔盲注
sql带外数据
sql注⼊执⾏命令
核⼼思维
sql注⼊,就是在执⾏⼀段sql语句,关键是数据库类型
想法设法去执⾏⼀条完全的sql语句,把数据带出来或把命令传进去。
产⽣注⼊的输⼊点:
输⼊点决定了我们能⽤什么样的Vector(攻击向量),以及是否需要绕过。
1 | select $username$,password from $table$ where $username2$ = '$fuck$' order by $username3$ desc limit $0$,1 |
每一个可输入点都又可能存在注入
宽字节注⼊
在utf-8还不那么通⽤的时候,各种⽜⻤蛇神都群魔乱舞。各种字符集都存在。(包括现在,如果你去搞⼀个jp、或者kr的站,很可能碰到其他的宽字符集)就造成了⼀种新的注⼊⽅式,叫宽字节注⼊。
概念
我们知道字节是计算机存储世界中最⼩的衡量单位,1Byte = 8bits。所以⼀个字节最⼤能够表示2^8=256个字符。
所以:
对ascii编码⽽⾔,⼀个字符⽤⼀个字节就可以表示,所以ascii编码最多可以表示256个字符。
对GBK编码⽽⾔,⼀个汉字字符需要⽤两个字节表示。所以gbk编码理论上最多可以表示
256*256个字符。
利⽤
因为宽字符的存在,导致⼀些防注⼊的⽅式会被绕过。我们以代码的形式来讲解。
1 | $db = init_db(); |
我们来考虑下sql语句会是怎么样的呈现,输⼊fuck’ and 1=1#
1 | select * from table where username = 'fuck\' and 1=1#' |
在这种场景下,我们没办法进⾏sql注⼊,因为我们的单引号被转义了,我们没办法侵⼊到控制流去。
数据在存储的时候⼀定是以字节存储的,但是数据解读的时候,都是以字符的标准去解的。
所以,在连接数据库进⾏sql执⾏(执⾏就是⼀种对存储的解读)时,会按照字符集编码规范去解读,所以遇到了\xDF\x5C的时候,会解读成⼀个字符。
1 | url输⼊-->php字符串变量-->addslashes-->sql语句-->数据库 |
所以,代码审计中审阅宽字节注⼊的⽅式,就是查看数据库连接⽂件(⼀般名字类似conn.php),检查其字符集类型是什么,如果不是utf-8(因为脚本的字符集默认是utf-8)就可能有注⼊产⽣。
预编译模式下的注⼊
还是是宽字节注⼊
我们先看例⼦
1 |
|
在这种情况下,是⽆法解决宽字符注⼊的问题的。
因为,在
1 | $stmt = $conn->prepare("select * from table where username =:username"); |
⽆法预编译的输⼊点
like关键字
我们知道sql语句的模糊查找⾥⾯⽤的关键字like,⽽like关键字默认是不会预编译的(如果使⽤Mybatis则是预编译报错)。数据库⽅给出的原因好像是like预编译会造成慢查询和DOS。
只能⼿动去添加预编译。
1 | $username=$_GET['username']; |
可能很多开发会遗漏这个点,导致存在注⼊。
或者⼀些java的开发,Mybatis编译报错,然后他们⾃⼰去添加过滤(过滤没写好)或者不过滤(使⽤原⽣语句),导致GG。
与之类似的还有IN关键字,该位置也默认不能预编译,需要在预编译语法中去for循环,有些程序员为了⽅便,可能也会在这边偷⼯减料。
不能加引号的关键字
我们刚才分析了预编译模式下的宽字节注⼊,我们可以发现,预编译+绑定变量的效果,有点类似于做了两个步骤
1 | $newInput = addslashes($input) //内容转义 |
那么,结合我们刚才讲的,产⽣注⼊的输⼊点
1 | select $username$,password from $table$ where $username2$ = '$fuck$'order by $username3$ $desc$ limit $0$,1 |
是否有输⼊点是必然不能加单引号的呢,如果不能加单引号,那么就不能预编译于是我们找到了
1 | $username$,$username2$,$table$,$username3$,$desc$,$0$ |
这些地⽅都是不能加单引号的,总结就是
1 | 表名、列名、limit⼦句、order by[desc/asc`] |
跟刚才⼀样,可能很多开发会遗漏这个点,导致存在注⼊。