《攻击JavaWeb应用》学习笔记

前段时间记录的笔记,绝对不是懒才拖到现在的!

JavaEE基础

JSP:全名为 java server page,其根本是一个简化的 Servlet 设计

Servlet:Servlet 是一种服务器端的 Java 应用程序,可以生成动态的 Web 页面。

JavaEE: JavaEE J2EE 新的名称。改名目的是让大家清楚 J2EE 只是 Java 企业应用。


Web请求简要流程

配置信息一般放在WEB-INF文件夹

例如,数据库连接配置:

  • 大多数的 Spring 框架配置在 applicationContext.xml 中
  • Hibernate 框架在 hibernate.cfg.xml 中
  • 服务器的数据源配置文件

一般情况下 Java 的数据库配置都在 WEBROOT 下的 WEB-INF 目录下的多数情况在

*.xml、*.properties、*.conf

当然也有直接写在源代码中的情况

APACHE RESIN 做负载均衡,Resin 用来做 JAVAWEB 的支持,APACHE 用于处理

静态和 PHP 请求,RESIN 的速度飞快,RESIN 和 apache 的配合应该是比较完美的吧。

域名解析:

apache 的 httpd.conf:

需要修改:Include conf/extra/httpd-vhosts.conf(一定要把前面的#除掉,否则配置不起作

用)

普通的域名绑定:

直接添加到 httpd.conf

1
2
3
4
5
6
7
<VirtualHost *:80>
ServerAdmin admin@noel.cn
DocumentRoot $wwwpath
ServerName noel.com
ErrorLog $path/error_log
CustomLog $path/log common
</VirtualHost>

配置好 APACHE 对 Resin 的支持后即可

第一章多为配置信息,看的不尽详细,先挖个坑,有空自己搭建环境。

XSS

漏洞原理及危害不予考虑,这里仅探究代码

创建一个 Servlet 内容如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
FirstServlet
package XSS;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.util.Enumeration;

public class FirstServlet extends HttpServlet {
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
resp.setContentType("text/html");
PrintWriter out = resp.getWriter();
Enumeration e = req.getHeaderNames(); // 获取所有请求头
while(e.hasMoreElements()){
String name = (String) e.nextElement(); //获取key
String value = req.getHeader(name); //得到对应的值
out.println(name + "=" + value + "<br/>"); //输出值
}

out.flush();
out.close();
}
}

在 Web.xml 中添加该 Servlet

1
2
3
4
5
6
7
8
9
<servlet>
<servlet-name>FirstXSS</servlet-name>
<servlet-class>XSS.FirstServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>FirstXSS</servlet-name>
<url-pattern>/First</url-pattern>
</servlet-mapping>

访问http://localhost/webapp/First 即可看到打印出来的请求头信息,只需要抓包修改任意值构造 XSS 即可

session 操作

1
2
3
4
5
设置:
session.setAttribute("name",name);//将name放到 session 当中
session.setAttribute("ip",request.getRemoteAddr());//获取用户请求 Ip 地址
获取:
session.getAttribute("name"); //获取session中name的值
SQL注入

jdbc SQL 注入(使用 Mysql 数据库时要先将 mysql 的 Jar 包导入项目的 lib 中)

新建一个 Java文件

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
SQLTest

package SQL;

import java.sql.*;

public class SQLTest {
private static String url = "jdbc:mysql://localhost:3306/hackers";
private static String username = "root";
private static String password = "root";
private static Connection conn = null;
public void List(String name) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver"); //加载MYSQL驱动
conn = DriverManager.getConnection(url,username,password);// 获取数据库连接
String sql = "select * from users where user_name='" + name + "'";

PreparedStatement ps = conn.prepareStatement(sql); //预编译

ResultSet rs = ps.executeQuery(); //执行SQL语句
System.out.println("SQL: " + sql);
rs.first(); // 获取第一条数据
System.out.println("ID: " + rs.getString("id"));
System.out.println("Name: " + rs.getString("user_name"));
rs.close();
ps.close();
}

public static void main(String[] args) throws SQLException, ClassNotFoundException {
SQLTest sql = new SQLTest();
sql.List("Test");
}

}

虽然是预编译的语句,但是预编译前拼接了字符串而不是占位的方式,所以还是有SQL注入存在

修改最后一句为

1
sql.List("' union select user(),2,3,'4");

执行

修改一下代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
package SQL;

import java.sql.*;

public class SQLTest {
private static String url = "jdbc:mysql://localhost:3306/hackers";
private static String username = "root";
private static String password = "root";
private static Connection conn = null;
public void List(String name) throws ClassNotFoundException, SQLException {
Class.forName("com.mysql.jdbc.Driver");
conn = DriverManager.getConnection(url,username,password);
// String sql = "select * from users where user_name='" + name + "'";
String sql = "select * from users where user_name= ? ";
PreparedStatement ps = conn.prepareStatement(sql);
ps.setObject(1,name); //使用预编译SQL
ResultSet rs = ps.executeQuery();
System.out.println("SQL: " + sql);
rs.first();
System.out.println("ID: " + rs.getString("id"));
System.out.println("Name: " + rs.getString("user_name"));
rs.close();
ps.close();
}

public static void main(String[] args) throws SQLException, ClassNotFoundException {
SQLTest sql = new SQLTest();
sql.List("' union select user(),2,3,'4");
}

}

这个时候就会因为没有查询到结果报错,也就是注入不存在了

可以 debug 跟踪一下 setObject

发现在 PreparedStatement.class 的 setString 方法下对单引号进行了处理

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
for(int i = 0; i < stringLength; ++i) {
char c = x.charAt(i);
switch(c) {
case '\u0000':
buf.append('\\');
buf.append('0');
break;
case '\n':
buf.append('\\');
buf.append('n');
break;
case '\r':
buf.append('\\');
buf.append('r');
break;
case '\u001a':
buf.append('\\');
buf.append('Z');
break;
case '"':
if (this.usingAnsiMode) {
buf.append('\\');
}

buf.append('"');
break;
case '\'':
buf.append('\\');
buf.append('\'');
break;
case '\\':
buf.append('\\');
buf.append('\\');
break;

x 是我们传入的,而经过处理后就变为了 parameterAsString

最后执行的语句则是

如此一来自然不存在注入

Web 应用

新建一个数据库连接类

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
SQLDB
package SQL;

import java.sql.Connection;
import java.sql.DriverManager;

public class SQLDB {
private static String url = "jdbc:mysql://localhost:3306/hackers";
private static String username = "root";
private static String password = "root";

public static Connection getMysql(){
try{
Class.forName("com.mysql.jdbc.Driver");
return (Connection) DriverManager.getConnection(url,username,password);
}catch (Exception e){
e.printStackTrace();
return null;
}
}

新建一个 Servlet

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
SQLServlet
package SQL;

import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.IOException;
import java.io.PrintWriter;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;

public class SQLServlet extends HttpServlet {
private String sql;
private String name;
private Connection conn=null;
private PreparedStatement ste = null;
@Override
protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {

PrintWriter out = resp.getWriter();
try {
name = req.getParameter("name");
}catch (Exception e){
name = "Noel";
}
conn = SQLDB.getMysql();
sql = "select * from users where user_name='" + name + "'";
try {
ste = conn.prepareStatement(sql);
ResultSet rs = ste.executeQuery();
rs.first();
out.println("ID: " + rs.getObject("id").toString());
out.println("Name: " + rs.getObject("user_name").toString());
out.println("Acount: " + rs.getObject("acount").toString());
} catch (SQLException e) {
e.printStackTrace();
out.println("Something error~~");
}


}
}

在 Web.xml 中增加如下内容

1
2
3
4
5
6
7
8
9
<servlet>
<servlet-name>FirstSQL</servlet-name>
<servlet-class>SQL.SQLServlet</servlet-class>
</servlet>

<servlet-mapping>
<servlet-name>FirstSQL</servlet-name>
<url-pattern>/FirstSQL</url-pattern>
</servlet-mapping>

传递参数 ?name= 即可

Oracle 数据库是个坑,有空开挖。

MVC

MVC 是三个单词的缩写,分别为: 模型(Model),视图(View)和控制(Controller)。 MVC 模式的目的就是实现 Web 系统的职能分工。

Model 实现系统中的业务逻辑,通常可以用 JavaBean 或 EJB 来实现。

View 用于与用户的交互,通常用 JSP 来实现

Controller Model View 之间沟通的桥梁,它可以分派用户的请求并选择恰当的

视图用于显示,同时它也可以解释用户的输入并将它们映射为模型层可执行的操作。

Sturts2

官网下载最新文件,或者 idea 创建 Struts2 项目时下载 lib 文件,将主要的 jar 包放在 lib 文件夹下

PS:MySQL 连接文件为上一部分 SQL 中添加

创建 HelloWorldAction.java

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
HelloWorldAction.java

package Struts2;

import com.sun.net.httpserver.Authenticator;

public class HelloWorldAction {
private String name;

public String execute(){
return "success";
}


public String getName() {
return name;
}

public void setName(String name) {
this.name = name;
}
}

这是一个非常简单的具有“name”属性的类。对于“name”属性,我们用标准的getter和setter方法,以及一个返回“success”字符串的执行方法。Struts2 框架将创建一个“HelloWorldAction”类的对象,并调用execute方法来响应用户的动作。业务逻辑放进 execute 方法里,最后会返回字符串常量。name 可通过 get 或者 post 方法获取

web 目录下新建 index.jsp , HelloWorld.jsp

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
index.jsp
<%@ page language="java" contentType="text/html; charset=ISO-8859-1"
pageEncoding="ISO-8859-1"%>
<%@ taglib prefix="s" uri="/struts-tags"%>
<!DOCTYPE html PUBLIC "-//W3C//DTD HTML 4.01 Transitional//EN"
"http://www.w3.org/TR/html4/loose.dtd">
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h1>Hello World From Struts2</h1>
<form action="hello">
<label for="name">Please enter your name</label><br/>
<input type="text" name="name" id="name" />
<input type="submit" value="Say Hello"/>
</form>
</body>
</html>
1
2
3
4
5
6
7
8
9
10
11
HelloWorld.jsp
<%@ page contentType="text/html;charset=UTF-8" language="java" %>
<%@ taglib prefix="s" uri="/struts-tags"%>
<html>
<head>
<title>Hellow World</title>
</head>
<body>
Hello World,<s:property value="name" />
</body>
</html>

Taglib指令告知Servlet容器这个页面将使用Struts2 标签,并且这些标签会被s放在前面。s:property 标签显示Action类“name”属性的值,这个值是使用 HelloWorldAction 类的 getName() 方法返回的。

src 目录下新建 sturts.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
<?xml version="1.0" encoding="UTF-8"?>

<!DOCTYPE struts PUBLIC
"-//Apache Software Foundation//DTD Struts Configuration 2.0//EN"
"http://struts.apache.org/dtds/struts-2.0.dtd">

<struts>
<constant name="struts.devMode" value="true" /><!--开发环境-->
<package name="helloWorld" extends="struts-default" >
<action name="hello" class="Struts2.HelloWorldAction" method="execute">
<result name="success">/HelloWorld.jsp</result>
</action>
</package>
</struts>

package 定义数据包,当有多个 Action 时数据包非常有用,name 为数据包命名

action name 设置为 hello 对应路由 /hello.action, class 指定了确切的动作 HelloWorldAction ,如果 execute 方法返回的值是 success 的话,将进入 HelloWorld.jsp

为 Web.xml 配置filter

1
2
3
4
5
6
7
8
<filter>
<filter-name>struts2</filter-name>
<filter-class>org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter</filter-class>
</filter>
<filter-mapping>
<filter-name>struts2</filter-name>
<url-pattern>/*</url-pattern>
</filter-mapping>

运行程序

STRUTS2 框架内部流程

\1. 客户端发送请求的 tomcat 服务器。服务器接受,将 HttpServletRequest 传进来。

\2. 请求经过一系列过滤器(如:ActionContextCleanUp、SimeMesh 等)

\3. FilterDispatcher 被调用。FilterDispatcher 调用 ActionMapper 来决定这个请求是否要调用某

个 Action

\4. ActionMapper 决定调用某个 ActionFilterDispatcher 把请求交给 ActionProxy

\5. ActionProxy 通过 Configuration Manager 查看 struts.xml,从而找到相应的 Action 类

\6. ActionProxy 创建一个 ActionInvocation 对象

\7. ActionInvocation 对象回调 Action 的 execute 方法

\8. Action 执行完毕后,ActionInvocation 根据返回的字符串,找到对应的 result。然后将 Result

内容通过 HttpServletResponse 返回给服务器。

Struts2 请求处理流程分析:

  1. 服务器启动时会自动去加载当前项目的web.xml
  2. 在加载 web.xml 配置的时候会去自动初始化 Struts2 的 Filter,然后把所有的请求先交 Struts 的 StrutsPrepareAndExecuteFilter.java 类去做过滤处理.
  3. 而这个类只是一个普通的 Filter 方法通过调用 Struts 的各个配置去初始化。
  4. 初始化完成后一旦有 action 请求都会经过 StrutsPrepareAndExecuteFilter 的 doFilter 过滤.
  5. doFilter 中的 ActionMapping 去映射对应的 Action。
  6. ExecuteOperations

ST2-016:

暂时还是个坑……有空分析

Spring MVC

使用 idea 新建 Spring MVC 项目

web.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
<web-app id="WebApp_ID" version="2.4"
xmlns="http://java.sun.com/xml/ns/j2ee"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://java.sun.com/xml/ns/j2ee
http://java.sun.com/xml/ns/j2ee/web-app_2_4.xsd">

<servlet>
<servlet-name>HelloWeb</servlet-name>
<servlet-class>
org.springframework.web.servlet.DispatcherServlet
</servlet-class>
<load-on-startup>1</load-on-startup>
</servlet>

<servlet-mapping>
<servlet-name>HelloWeb</servlet-name>
<url-pattern>/</url-pattern>
</servlet-mapping>

</web-app>

HelloWorld-servelt.xml

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
<beans xmlns="http://www.springframework.org/schema/beans"
xmlns:context="http://www.springframework.org/schema/context"
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-3.0.xsd
http://www.springframework.org/schema/context
http://www.springframework.org/schema/context/spring-context-3.0.xsd">

<context:component-scan base-package="controller" />

<bean class="org.springframework.web.servlet.view.InternalResourceViewResolver">
<property name="prefix" value="/WEB-INF/jsp/" />
<property name="suffix" value=".jsp" />
</bean>

</beans>

HelloController

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
package controller;

import org.springframework.stereotype.Controller;
import org.springframework.ui.ModelMap;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestMethod;
import org.springframework.web.servlet.ModelAndView;

@Controller
@RequestMapping("/hello")
public class HelloController {
@RequestMapping(method = RequestMethod.GET)
public String printHello(ModelMap model){
model.addAttribute("message","Hello Spring MVC Framework!");
return "index";
}
}

方法上方有一个 @RequestMapping, 是用于匹配请求的路径, 返回的 index 结合 HelloWorld-servelt.xml 表示加载 /WEB-INF/jsp/ 下的 index.jsp 文件

index.jsp

1
2
3
4
5
6
7
8
9
<%@ page contentType="text/html; charset=UTF-8" %>
<html>
<head>
<title>Hello World</title>
</head>
<body>
<h2>${message}</h2>
</body>
</html>

message 为 Hello 中设置的 message

Server

各种服务器相关漏洞,后续慢慢填充