Day 11
https://www.ripstech.com/java-security-calendar-2019/
这段代码的作用是将tar文件里面的内容提取到tomcat临时目录中以test开头的文件夹中。"/tmp/uploaded.tar"是攻击者可控的。tar文件里的每个文件或文件夹都映射到一个TarArchiveEntry对象。由于第16行的TarArchiveEntry.getName()获取tar文件里的文件名,然后通过一个简单的对"../"的过滤到达接收器java.io.File。由于过滤不严,可以绕过../的检查,将文件写入到任意目录下,从而可导致RCE攻击。
由于此攻击需要Linux系统下才能成功,使用docker复现
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>day11</servlet-name>
<servlet-class>com.ananasker.day11.ExtractFiles</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>day11</servlet-name>
<url-pattern>/day11</url-pattern>
</servlet-mapping>
</web-app>
ExtractFiles.java
...
protected void doPost(HttpServletRequest req, HttpServletResponse res) {
try {
extract();
} catch (Exception e) {
e.printStackTrace();
}
}
...
首先,../的检查很容易通过..././来绕过,因此可以将文件写入任意位置。例如,此例中tomcat的web目录位于/usr/local/tomcat/webapps,因此可构造如下文件名的恶意文件
..././..././..././..././..././usr/local/tomcat/webapps/ROOT/index.jsp
直接无法生成此文件名的文件,可先构造等长文件名的文件,然后将其打包为uploaded.tar,然后使用二进制修改器,将里面的名称修改为"..././..././..././..././..././usr/local/tomcat/webapps/ROOT/index.jsp"。如图所示
将修改后uploaded.tar放入/tmp目录下
如图所示,发送POST请求后,成功在web目录下写入文件
Day 12
https://www.ripstech.com/java-security-calendar-2019/
在index.jsp中,首先定义一个customClass为"default",在包含的init.jsp中,customClass被覆盖为请求参数customClass的值,然后此值在未经过滤的情况下,被赋值给div标签的属性值中。然后customClass与username都被ESAPI给HTML编码了。此处的漏洞在于第一个div属性的customClass值完全由攻击者可控,且未经任何过滤,因此可以突破属性的限制,从而导致XSS。
在resources目录下添加,ESAPI.properties以及validation.properties。文件内容在此题中,我设置为均为空,为空表示取默认值。
通过双引号,突破属性值的限制。即最终的payload可为如下所示:
/index.jsp?username=123&customClass=1" </div><script>alert(1)</script>
结果如下所示:
Day 13
https://www.ripstech.com/java-security-calendar-2019/
这段代码支持一个文件上传,并将上传的文件存入临时目录。通过ServletFileUpload类的重要方法parseRequest,对请求消息体内容进行解析,将每个字段的数据包装成独立的Fileitem对象。为了阻止攻击者上传如.jsp的文件,对消息的Content-Type进行了简单的判断是否属于text/plain,但可被简单的绕过。另一个由攻击者控制的是文件名且未经过任何检查,这导致目录遍历漏洞,因为类似/../的字符串在java中是有效的文件名。
最终,攻击者可以上传任意文件,通过目录遍历漏洞可设置上传文件的地址,最终导致RCE远程命令执行。
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>day13</servlet-name>
<servlet-class>com.ananaskr.day13.UploadFileController</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>day13</servlet-name>
<url-pattern>/day13</url-pattern>
</servlet-mapping>
</web-app>
upload.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<html>
<head>
<title>Upload</title>
<meta http-equiv="Content-Type" content="text/html; charset=UTF-8">
</head>
<body>
<form method="post" action="/Day13_war_exploded/day13" enctype="multipart/form-data">
<input type="file" name="uploadFile"/>
<br/><br/>
<input type="submit" value="upload"/>
</form>
</body>
</html>
在打印出上传路径后,得知只需返回父级目录即可。或者不确定的,可使用绝对路径。payload如下
Day 14
https://www.ripstech.com/java-security-calendar-2019/
这段代码主要是想要利用CSV注入这个漏洞点。其中CSV文件的一个单元格内容是由攻击者完全可控的,因此可造成注入。
但是代码的逻辑却是将攻击者的输入以CSV格式响应给攻击者自身,即攻击者自己获取到了有恶意代码的CSV文件,打开在攻击者自身的环境下执行了恶意行为。因此,未进行复现。
Day 15
https://www.ripstech.com/java-security-calendar-2019/
这段代码使用"find"系统命令并向用户公开当前文件夹的目录。这会导致信息泄露,但攻击者可利用此代码造成更大的伤害。第9行代码建立了基本的命令(find . -type d)。第12-15行代码接收参数options,并将其附在"find . -type d"命令后。最后调用java.lang.ProcessBuilder来执行命令。
首先直接的命令执行不可能,因为所有的输入都附在find命令后,会被当成参数。然而,攻击者控制的此命令的某些选项/参数,会导致参数注入漏洞。通过注入find命令的参数-exec,攻击者可以执行任意系统命令。
web.xml
<!DOCTYPE web-app PUBLIC
"-//Sun Microsystems, Inc.//DTD Web Application 2.3//EN"
"http://java.sun.com/dtd/web-app_2_3.dtd" >
<web-app>
<display-name>Archetype Created Web Application</display-name>
<servlet>
<servlet-name>day15</servlet-name>
<servlet-class>com.ananaskr.day15.FindOnSystem</servlet-class>
</servlet>
<servlet-mapping>
<servlet-name>day15</servlet-name>
<url-pattern>/day15</url-pattern>
</servlet-mapping>
</web-app>
将doGet换成doPost
find命令的-exec参数后面跟command命令,它的终止是以;结束的。与之类似的是-ok参数,-ok参数是以一种更为安全的模式来执行该参数所给的shell命令,在执行每一个命令之前,都会给出提示,让用户来确定是否执行。因此,选择-exec参数,可构造如下payload
options=-exec cat /etc/passwd ;
Day 16
https://www.ripstech.com/java-security-calendar-2019/
此代码中通过@RequestParam注释接受用户输入的name参数值,然后经过escapeQuotes函数过滤后,拼接到HQL sql语句中,进而被执行。。代码中的escapeQuotes函数的作用是将输入中的'变成两个'。因此payload中包含\'即可绕过此限制,从而导致sql注入。
<?xml version="1.0" encoding="UTF-8"?>
<web-app xmlns="http://xmlns.jcp.org/xml/ns/javaee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://xmlns.jcp.org/xml/ns/javaee http://xmlns.jcp.org/xml/ns/javaee/web-app_4_0.xsd"
version="4.0">
<filter>
<filter-name>encodingFilter</filter-name>
<filter-class>org.springframework.web.filter.CharacterEncodingFilter</filter-class>
<init-param>
<param-name>encoding</param-name>
<param-value>UTF-8</param-value>
</init-param>
<init-param>
<param-name>forceEncoding</param-name>
<param-value>true</param-value>
</init-param>
</filter>
<filter-mapping>
<filter-name>encodingFilter</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>
<context-param>
<param-name>contextConfigLocation</param-name>
<param-value>/WEB-INF/applicationContext.xml</param-value>
</context-param>
<listener>
<listener-class>org.springframework.web.context.ContextLoaderListener</listener-class>
</listener>
<servlet>
<servlet-name>dispatcher</servlet-name>
<servlet-class>org.springframework.web.servlet.DispatcherServlet</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>
<servlet-mapping>
<servlet-name>dispatcher</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>
</web-app>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xmlns:context="http://www.springframework.org/schema/context"
xmlns:mvc="http://www.springframework.org/schema/mvc"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd http://www.springframework.org/schema/context https://www.springframework.org/schema/context/spring-context.xsd http://www.springframework.org/schema/mvc https://www.springframework.org/schema/mvc/spring-mvc.xsd">
<context:component-scan base-package="com.ananaskr.day16" />
<context:annotation-config/>
<mvc:default-servlet-handler/>
<mvc:annotation-driven/>
<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix">
<value>/WEB-INF/</value>
</property>
<property name="suffix">
<value>.jsp</value>
</property>
</bean>
</beans>
<?xml version="1.0" encoding="UTF-8"?>
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://www.springframework.org/schema/beans http://www.springframework.org/schema/beans/spring-beans.xsd">
<bean id="FindController" class="com.ananaskr.day16.FindController">
</bean>
</beans>
<?xml version='1.0' encoding='utf-8'?>
<!DOCTYPE hibernate-configuration PUBLIC
"-//Hibernate/Hibernate Configuration DTD 3.0//EN"
"http://www.hibernate.org/dtd/hibernate-configuration-3.0.dtd">
<hibernate-configuration>
<session-factory>
<property name="connection.driver_class">com.mysql.cj.jdbc.Driver</property>
<property name="connection.url">jdbc:mysql://localhost:3306/day</property>
<property name="connection.username">root</property>
<property name="connection.password"></property>
<property name="dialect">org.hibernate.dialect.MySQLDialect</property>
<property name="current_session_context_class">thread</property>
<property name="show_sql">true</property>
<property name="hibernate.hbm2ddl.auto">update</property>
<mapping class="com.ananaskr.day16.UserEntity" />
</session-factory>
</hibernate-configuration>
FindController.java
15 List <UserEntity> users = session.createQuery("from UserEntity where FIRST_NAME ='" + escapeQuotes(name) + "'", UserEntity.class).list();
改为
15 List <UserEntity> users = session.createQuery("from UserEntity where firstName ='" + escapeQuotes(name) + "'", UserEntity.class).list();
UserEntity.java
所有持久化类必须要求有不带参的构造方法(也是JavaBean的规范)。Hibernate需要使用Java反射为你创建对象。当类中声明了其他带参构造方法时,需在类中显示声明不带参数构造方法。
public UserEntity(){
}
代码中的escapeQuotes函数的作用是将输入中的'变成两个'。因此payload中包含\'即可绕过此限制。然后在最后加上注释#,绕过最后的单引号。最终的payload如下:
test\' or 1=1 #
由于代码中只返回查询结果的数量,因此可实施盲注。
数据库仅包含如下数据
当查询正确时
当使用payload时