代码注入
利用系统没有对其输入进行强制执行或检查的假设向计算机系统中引入代码的技术。
标记语言
SGML(satandard Generalized Markup Language):标准的通用标记语言
HTML(HyperText Markup Language):超文本标记语言
DHTML(Dynamic HTML):动态超文本标记语言
XHTML(Extensible HTML):可扩展超文本标记语言
XML(eXtensible Markup Language):可扩展标记语言
注入的分类
OS命令注入
定义:
OS指令执行是为了通过网络接口在Web服务器执行OS命令的一种技术。如果应用程序使用外部输入的字符串或者受外部影响的字符串组装命令,并且,没有经过正确的过滤就可能导致OS命令的注入攻击。
类型:
1.应用程序执行一个自己控制的固定程序,通过用户输入的参数来执行命令。这时候可以通过参数的分隔符,在参数中注入命令,来执行攻击者想要运行的命令。
2.应用程序将输入的整个字符串作为一个命令,应用程序在只是做个中转,将命令传给操作系统执行。
一般的系统命令分割符:
;:如果每个命令都被一个分号(;)分割,那么命令会连续的执行下去。
&&:执行错误检查命令,如果其左侧的命令不返回预期的结果,其右侧的命令就不会执行
&:不执行错误检查和运行所有命令
||:若遇到 可以成功执行的命令,那么命令停止执行,即使后面还有正确的命令
| : 即使遇到可以成功执行的命令,命令也会继续执行下去。
eg:
在允许用户上传或者下载文件的网站上,如果输入的参数不当可能引起的注入问题:
http://www.exaple.com/ls.php?dir=3%3Bcat%20/etc/passwd #%3b(;)
http://www.example.com/download/filename=test.pdf + | +cd %20/;ls%20-l
shell代码的转义字符:
’(单引号):又称硬转义,其内部所有的shell元字符、通配符都会被关掉(硬转义中不允许出现‘(单引号),如果出现必须转义)
"(双引号):又称软转义,其内部只允许出现特定的shell字符:$用于参数代换、`(反引号)用于命令替换
\(反斜杠): 又称转义 ,去除其后紧跟的元字符或通配符的特殊意义
在windows中,可以将给定的字符串用双引号括起来,|,^,&等字符是特殊字符,出现在双引号内部无须转义,外部需要转义;转义规则:
^^代表^
^|代表|
^" 代表“
如果字符串中有双引号,需要将双引号转义:""代表";
如果出现两个双引号:"hello" " world!" --> " hello " " " " " "world!"(必须三个双引号连写)
反斜杠 的转义 规则:
对于n个反斜杠外加一个双引号,会被转义,如果n为奇数,那么会被转义成(n-1)/2个\字符外加一个“字符;如果n为偶数,那么会被转义成n/2个\字符,然后再进入或者结束引用范围;如果反斜杠后边并不跟随双引号,
反斜杠就不转义。
注意事项 :
不要仅在客户端过滤 ,也要在 服务器端过滤 。
使用最小权限运行程序,不要给与程序多余权限,最好只允许在特定路径下运行。
程序执行出错时,不要显示与内部实现相关的细节
如果 只允许运行有限的命令,使用 白名单方式
对于需要运行的命令的请求,尽可能减少需要从外部输入的数据
如果客户端下载文件,可以给文件分配一个id号,通过ID来访问,而不是通过文件名访问。
XPath注入
简介:
XPath即XML路径语言(XML Path Language),是一种用来确定XML文档中某部分位置的语言。XPath基于XML的树状结构,提供在数据结构树种寻找节点的能力。XPath提出的初衷是将其作为一个统用的 介于XPointer和XSL之间的语法模型。
XPath使用路径表达式在XML文档中进行导航。
XPath包含一个标准函数库
XPath是XSLT中的主要元素
XPath是一个W3C标准
最常见的XPath表达式是路径表达式,是从一个XML节点(当前的上下文节点)到另一个节点或一组节点的书面步骤顺序。这些步骤以“/"字符分开,每一步由3个成分构成:
轴描述:(用最直接的方式接近目标节点)
节点测试:(用于筛选节点位置和名称)
节点描述:(用于筛选节点的属性和子节点特征)
XPath1.0定义了4种数据类型:节点型(本身无序的节点组)字符串型、数字型与布尔型。有效运算符如下:
/、//以及..运算符,一般用于轴描述
合集运算符|把两个节点形成联集
布尔运算符and,or以及not()函数
数学运算符+,-,*,div(除)以及mod(取余数)
比较操作子 =,!=,<,>,<=,>=
字符 | 描述 | 例子 |
/ | 用“/"开始的路径代表元素的绝对路径 | /Employees/employee表示选择元素Employees中的所有employee元素 |
不用”/"开始得路径代表元素的相对路径 | Employee/Fistname表示当前路径下的所有的Employee的Firstname | |
// | 从匹配选择的当前节点选择文档中的节点,而不考虑他们的位置 | //Employee代表所有的Employee元素 |
. | 表示当前节点 | |
.. | 表示当前节点的父节点 | //Type[name(..)='Employee'] 表示选择Type元素,并且Type的父元素是Employee |
* | 可用于选择未知XML元素 | /Employees/Employee/*表示元素Employee中的所有Employee元素 的子元素 /Employees/*/Firstname表示元素order下所有 孙子辈节点的Frist name元素 |
[] | 指定特定元素 | /Employees/Employee[1]表示元素Employees中的第一个Employee的子元素 /Employees/Employee[last()]表示元素Employees中的最后一个Employee的子元素 |
| | 在XPath表达式中,使用|运算符可以选择几个路径 | //Fistname|//Lastnaem表示从文档中选择所有Fristname和lastname元素 /Employees/Employee/Fistname|/Employees/Employee/Lastname 表示寻找Employees/Employee路径下的Fistname和lastname |
@ | 选取属性 | //@id表示选取所有名为id的属性 //Employee[@*]表示选取所有具有任何属性的Employee元素 |
XPath注入问题:
XPath注入攻击指的是利用Xpath解析器的松散输入与容错性,在URL,表单或其他信息上附带恶意的XPath查询代码,以获得权限信息的访问权并更改这些信息。XPath注入攻击是针对Web服务应用的 心得攻击方法,它允许攻击者在事先不了解XPath查询相关知识的情况下,通过XPath查询得到一个XML文档的完整内容。
XPath注入攻击两种利用方式:
1)XPath查询布尔化
eg:
需要Alex登录时XPath语句://Employee[UserName/text()='alexw' and Password/text()='alexwang']
用户的输入若为:alexw' or 1=1 or 'a'='a
那么查询语句变为://Employee[UserName/text()='alexw' or 1=1 or 'a' ='a' and Password/text()='aaaa']
或者: ' or 1=1 ' ' = '
查询语句变为:://Employee[UserName/text()=' ' or 1=1 or ' ' = '' and Password/text()='']
2)XPath扫描
eg:
通过一些无效的用户名和密码,修改XPath查询结构探测更多的节点,也可以输入一个无效的密码‘NonExistPass’ 和用户名:
NonExistUser'] | P | // Employee[UserName/text()='NonExistUser
那么查询语句会变成:
String(//Employee[UserName/text()= 'Test' ] | P | //Employee[UserName/text()='NonExistUser' and password/text()='NonExistPass']/UserName/text())
因为第一个条件和第三个条件都是False,所以只要结合xpath提供的函数和属性修改P就可以逐步探测到xml整个文档的内容。修改前面的例子如下:
String(//Employee[UserName/text()='abc' or name(//Employee/UserName[1])='triedname' or 'a=b' and Password/text()=''])
通过修改triedname逐步尝试,根据返回的结果可以猜测到用户名,如果修改表达式可以猜测密码甚至整个xml文档结构。
XPath 注入的危害:
通过XPath注入攻击,可以控制用来进行XPath查询的XML数据库。这种攻击可以有效的对付使用XPath查询来执行身份验证,查找或者其他操作。XPath注入攻击与sql注入攻击类似,但相比有一 下优势:
1)广泛性。XPath注入利用的是XPath语法,由于XPath是一种标准语言,因此只要是利用XPath语法的web应用,如果未对输入的XPath查询做严格处理,都会存在XPath注入漏洞,所以可能在所有
的XPath实现中都有该弱点。
2)危害性大。XPath语言几乎可以引用XML文档的所有部分,而这样的引用一般没有访问控制限制
XPath注入预防:
1)在服务器段开始处理用户提交 的请求数据前,对输入的数据进行验证,验证每一个参数的类型,长度和格式
2)对于系统出现的错误信息,以IE错误编码信息替换,屏蔽系统本身出错信息,这样可以向攻击者提供更少的信息进行下一步注入攻击
3)检查是否有特殊字符,如果有特殊字符,就转义特殊字符或者替换
4)参数化XPath查询,编译构建的XPath查询表达式,将用户的输入以变量的形式传递,而不是作为表达式的一部分,变量不是可以执行的脚本
5)对于密码等敏感信息,需要通过 使用盐和带键值的SHA哈希 算法处理,尽量采用哈希值比较长的 哈希算法,可以避免碰撞的发生。
字符 | 转义 |
& | & |
< | < |
> | > |
" | " |
' | ' |
LDAP注入
LDAP是Lightweight Directory Access Protocol的缩写,即轻量型目录访问协议。LDAP是X.500协议的一个子集。所以LDAP客户机与X.500客户机相比规模更小,速度更快,实现更简单。LDAP协议
使 用Abstract Syntax Notation 1(ASN.1)描述,并且使用ASN.1的BER(Basic Encoding Rules)编码方式的一个子集进行传输。
LDAP注入问题:
运算符种类 | 运算符 |
逻辑运算符 | AND "&" |
OR "|" | |
NOT "!" | |
关系运算符 | <= , >=,=,~= |
通配符 | * |
组成括号 | () |
LDAP搜索过滤器是用波兰表示法建立的,也成为前缀表示法。即搜索过滤器中的伪代码条件:
find(”cn=John&userPassword=mypass")
会产生:
find("(&(cn=John)(userPassword=mypass")))")
LDAP注入问题,就产生在客户端发送查询请求时,输入的字符串中含有一些特殊字符,导致修改了LDAP本来的查询结构,从而 可以访问更多的未授权数据。
LDAP注入问题检测:
要检测LDAP注入问题,可以尝试一些特殊字符,如*,(,),\,NUL,通过注入这些特殊 字符,查看LDAP服务器应答消息。例如,注入*,返回的结果比预期的多,还有注入括号,会导致LDAP服务
器的过滤解析失败,可以通过查看返回的应答消息的错误信息得知。
LDAP注入问题预防:
字符 | 转义 |
* | \2a |
( | \28 |
) | \29 |
\ | \5C |
NUL | \00 |
LDAP URL:
LDAP还可以通过一种URL的形式访问,这样LDAP客户端可以通过URL直接访问。RFC1959描述了关于LDAP URL的格式。描述如下 :
:: = "ldap://"[] "/" [ "?" [ "?" "?" ] ]
:: = [ " :" ]
:: = a string as defined in RFc 1485
:: = NULL |
:: = | [ "," ]
:: = a string as defined ini RFC 1777
:: = "base" | "one" | "sub"
LDAP前缀表示在LDAP服务器端提供LDAP协议服务,默认的端口是389.后面紧跟的是LDAP服务器的DNS,最后是要查询的属性列表,如果没有属性,就应该返回所有的属性。
“base","one",”sub"是指查询的范围。base用于基础对象搜索,one用于搜索一个级别的对象,Sub用于搜索子树。如果没有范围的指定,默认是“base”。
SQL注入
SQL攻击(sql Injection),是发生在应用程序和数据层之间的安全漏洞。即在输入的字符串之中注入了SQL指令,在设计不良的程序中忽略了检查,那么这些注入的指令就会被数据库服务器误认为是正常的SQL指令而执行,因此造成数据库泄露或者数据的破坏
Sql注入产生的原因:
1)SQL命令可以进行查询,插入,更新,删除等操作,也可以将这些命令串接起来,采用风字符区分不同命令。
2 ) sql命令对于传入的 字符串参数是用单引号字符保卫起来的
3)在sql命令中 ,可以 注入注释,例如 :“--”后的文字为注解;“/*"和”*/"包围起来的文字为注释。
4)在组合SQL的命令字串时,如果没有对单引号“ ’ ”这个特殊字符做取代处理,将会导致该字符串在填入命令字符串时,恶意篡改原本的SQL语句结构的作用。
登录网站的sql代码可能为: Sting userName = request.getParameter("username"); String password = request.getParameter("password"); String query = ""select id From users Where username = " + username + " And password ="+ password; Statement stmt = conn.createStatement(query); ResultSet rs = conn.executeQuery(query); if (rs.next()) { //登录成功 .... } else { //登录失败 ... } 如果恶意输入用户名: username = “ ‘ or '1'='1"; 或者输入密码为: password = " ' or '1'='"; 将会导致原来的sql语句被篡改为: StrSQL = “Select id From users Where username = ' ' or '1'='1' And password= ' ' or '1' ='1';" 实际上等价于: Select id From users;
SQL注入的危害:
1)探知数据库的具体结构,为后续攻击做准备
2)泄露数据
3)取得更高系统权限之后可以更改数据库结构等。
4)如果是SQLSever数据库,取得操作系统的命令可能控制整个服务器。
5)可以在服务器上挂马
SQL注入的检测:
1.通过Get方法发送的请求的参数的后面添加单引号(’),看看是否有报错。如果报错,可能存在SQL注入风险。对于不同类型的参数,判断方法也不相同。
1)整型参数
sql语句:select * from table where 字段名=输入的值
测试步骤:
a.http://www.sample.com/test.asp?id=1',如果sQL语句变成select * from table where id = 1‘,那么test.asp页面会出现异常运行。
b.http://www.sample.com/test.asp?id=1 and 1=1 页面运行正常,而且结果与http://www.sample.com/test.asp?id=1 运行相同
c.http://www.sample.com/test.asp?id=1 and 1=2 页面异常或者和http://www.sample.com/test.asp?id=1结果不同
以上3步全满足则一定存在sql注入。
2)字符串参数
可能的Sql语句 :select * from table where 字段名=’输入的值'
测试步骤:
a.http://www.sample.com/test.asp?id=1',如果语句变成http://www.sample.com/test.asp?id=1' ',那么页面异常;
b.http://www.sample.com/test.asp?id=1' and '1'='1 页面正常,而且与http://www.sample.com/test.asp?id=1结果 一样;
c.http://www.sample.com/test.asp?id=1' and '1' ='2 运行页面异常,或者 和http://www.sample.com/test.asp?id=1结果不同
满足以上3步,则一定存在sql注入
2.对于一些通过post的请求,可以通过一些工具的辅助达到测试目标。
SQL注入的预防
1.输入检查,即对所有的输入都进行 检查,包括长度,类型或者根据业务需求定义的一些规则,不仅在客户端也在服务器端进行检查。
2.转义特殊字符
1)单引号转义
eg:Select id From users Where username='o''neal' And password='o''neal' (mysql中用\'代替单引号)
2)通配符转义
a)[]转义
通配符 | 含义 | 转义字符 |
% | 包含0个或更多字符的任意字符串 | [%] |
_ | 任意单个字符 | [_] |
[] | []指定范围(如[a-f])或 集合(如[abcdd])内的任意单个字符 | [[][]] |
[^] | 不在指定范围内的任意单个字符(如[^a-f]或者[^abcdef]) | [[][^][]] |
b)ESCAPE转义
在oracle数据库中,使用{}转义而不是[],需要注意的是如果查询字符串中已经有了一个},必须使用}}替换,否则查询字符串中的}将会提前结束转义,从而引入漏洞;除此还可以用ESCAPE
自定义的转义符。
例如,查询需要以_li和_la结尾的用户:
Select * From users Where name Like '%/_li' ESCAPE '/' Or name Like '%_la' ESCAPE '/'
c)\转义
有的数据库支持"\"作为 转义符,如MySQL中需要查询”%“或者”_"这样的通配符,就必须使用“\"进行转义,"%"-->”\%", "_"-->"\_","\"-->"\\"
3)预编译SQL语句
J2EE可以使用PreparedStatement,提供了3种sql语句的类Statement,PreparedStatement和Callable-Statement;Statement由createStatement创建,用来执行简单的sql语句;
preparedStatement对象仅包含了SQL语句,而且这些语句在大多数情况下已经预编译过,只要DBMS执行SQL即可,使用时也 只需要改变其中的变量值即可;循环执行的SQL使用
preparedStatement可以减少与数据库的通信量,加快执行速度。CallableStatement由prepareCall创建,用来执行存储过程。
实例代码: String user = request.getParameter("username"); String pass = request.getParameter("password"); String query = "Select id From users Where username=? And password=?"; PreparedStatement stmt = conn.prepareStatement(query); stmt .setString(1,user); stmt.setString(2.pass); ResultSet rs = stmt.executeQuery(); if (rs.next()) { //登录成功 ... } else { //登录失败 .... }
.Net可以使用含有SqlParameter的sqlCommand,
PHP可以使用PDO的PDOStatement::bindParam
也可以使用对SQL注入免疫的ORM库(object relational mapping)。对象关系映射是一种为了解决面向对象与关系数据库存在的互不匹配现象的技术。ORM通过使用描述对象和数据库之间
映射的元数据,将 java程序中的对象自动持久化到关系数据库中,本质就是将数据库从一种形式转换到另一种形式。
参数名称 绑定: String user = request.getParameter("username"); String pass = request.getParameter("password"); String query = "Select id From users Where username=:nm And password=:pw"; Qeury query = Session.createQuery(query) query.setString("nm",user) query.setString("pw",pass);
如果一个SQL中有多个同样的参数,参数名称绑定只要一次: String seachValue = request.getParameter("searchValue"); String query = "select * from users Where username like :searchValue And email like :searchValue"; List result = session.createQuery(query).setString("searchValue",searchValue);
位置参数绑定: String user = request.getParameter("username"); String pass = request.getParameter("password"); String seach = "Select id From users Where username=:nm And password=:pw"; Query query = session.createQuery(search) query.setString(0,user) query.setString(1,pass);
使用PreparedStatement注意以下3点:
1)使用PreparedStatement安全执行SQL语句是建立在驱动器实现了这个类的基础上的。
2)必须正确使用preparedStatement,这包括强类型绑定的参数
3)PreparedStatement解决有关特殊字符插入到数据库的问题,特殊字符如单引号“‘”,双引号“"”,反括号“)”,问好“?”,%,*没有进行特殊处理,如果查询语句中含有通配符,还需要自己在代码中
提前进行转义。
4)存储过程
一定程度上能减少SQL注入问题,但不能完全避免,因为如果存储过程中动态组成sql语句,那么仍然会存在注入