系列文章专栏地址:
一、序言
本期,我们继续对网页进行搭建,同时靶场模块引入了更多的漏洞页面。
其中网页更新:论坛模块、课程管理、靶场模块
靶场页面设计:java反序列化
团队第一次开始合并分支,将各自的内容进行整合,并且完整可运行。1.0版本已上传至gitee的master分支
二、网页设计
前三期,我们完成了网页基础搭建:包括导航栏、登录注册、管理模块、靶场模块、论坛模块。
在这一期,我们对网页部分逻辑进行优化和更新,重点更新了:靶场模块
同时在最后一部分,我将介绍我们团队如何将写好的几个靶场页面整合进去。
1.登录注册引入token
用户发送token与服务器交互,可以进行身份认证和权限控制。如果用户没有登录就无法进行进一步的操作如get和post请求,只可以参观网站不可以做题和点击一些按钮。
@Component
public class TokenUtils {
private static IUserService staticUserService;
@Resource
private IUserService userService;
@PostConstruct
public void setUserService() {
staticUserService = userService;
}
/**
* 生成token
*/
public static String genToken(String userId, String sign) {
return JWT.create().withAudience(userId) // 将 user id 保存到 token 里面,作为载荷
.withExpiresAt(DateUtil.offsetHour(new Date(), 2)) // 2小时后token过期
.sign(Algorithm.HMAC256(sign)); // 以 password 作为 token 的密钥
}
/**
* 获取当前登录的用户信息
*
* @return user对象
*/
public static User getCurrentUser() {
try {
HttpServletRequest request = ((ServletRequestAttributes) RequestContextHolder.getRequestAttributes()).getRequest();
String token = request.getHeader("token");
if (StrUtil.isNotBlank(token)) {
String userId = JWT.decode(token).getAudience().get(0);
return staticUserService.getById(Integer.valueOf(userId));
}
} catch (Exception e) {
return null;
}
return null;
}
}
拦截器:
@Configuration
public class InterceptorConfig implements WebMvcConfigurer {
@Override
public void addInterceptors(InterceptorRegistry registry) {
registry.addInterceptor(jwtInterceptor())
.addPathPatterns("/**") // 拦截所有请求,通过判断token是否合法来决定是否需要登录
.excludePathPatterns("/user/login", "/user/register")
.excludePathPatterns( "/**/*.html", "/**/*.js", "/**/*.css", "/**/*.woff", "/**/*.ttf"); // 放行静态文件
}
@Bean
public JwtInterceptor jwtInterceptor() {
return new JwtInterceptor();
}
}
2.靶场模块更新
点击可以展开
用户点击题目之后,跳转到新页面
想要实现点开每个折叠栏后显示对应的题目信息,我们先给数据库存点东西:我们需要每道题对应的id信息和题目介绍等。
表problem:
最终渲染后的结果:
3.论坛模块
分页:
<el-pagination
v-model:currentPage="currentPage"
:page-size="pageSize"
:disabled="disabled"
:background="background"
layout="total, prev, pager, next"
:total="totalpage"
@size-change="handleSizeChange"
@current-change="load"
style="padding-top: 20px"
/>
@GetMapping("/page")
public Result findpage(@RequestParam(defaultValue = "1") Integer pageNum,
@RequestParam(defaultValue = "5") Integer pageSize,
@RequestParam(defaultValue = "") String search){
Page<Forum> forumPage=forumMapper.selectPage(new Page<>(pageNum,pageSize), Wrappers.<Forum>lambdaQuery().like(Forum::getForumid,search));
return Result.success(forumPage);
}
三、漏洞页面设计
1.java反序列化
Java 序列化是指把 Java 对象转换为字节序列的过程;Java 反序列化是指把字节序列恢复为 Java 对象的过程;我们知道,当两个进程进行远程通信时,彼此可以发送各种类型的数据,包括文本、图片、音频、视频等, 而这些数据都会以二进制序列的形式在网络上传送。同样的序列化与反序列化则实现了 进程通信间的对象传送,发送方需要把这个Java对象转换为字节序列,才能在网络上传送;接收方则需要把字节序列再恢复为Java对象。
Java 反序列化漏洞是与 java 相关的漏洞中最常见的一种,也是网络安全工作者关注的重点。如果Java应用对用户输入,即不可信数据做了反序列化处理,那么攻击者可以通过构造恶意输入,让反序列化产生非预期的对象,非预期的对象在产生过程中就有可能带来任意代码执行。
防范手段:通过Hook resolveClass来校验反序列化的类、使用contrast-rO0防御反序列化攻击等等
我们设计了一个网页含有java反序列化高危漏洞,用户(学生)去构造攻击获取flag
部分源码:
前端
<!DOCTYPE html>
<html lang="en">
<head>
<meta charset="UTF-8">
<title>java反序列化</title>
</head>
<body>
<p>
java反序列化漏洞(源码分析),部分序列化代码如下:
</p>
<input type="button" value="点击执行java序列化代码"
onclick="javascrtpt:window.location.href='http://localhost:8080/java_deser'" />
<p>
序列化后转化为字符数组形式,16进制表示为:
(你需要自己编写代码,把该序列化后的字符反序列化,即可得到flag)
</p>
<form action="http://localhost:8080/java_deser_test" method="get">
输入flag: <input type="text" name="id" />
<input type="submit" value="提交" />
</form>
<form action="http://localhost:8080/answer_code" method="get">
(没做出来的话)输入序列化后的16进制形式,查看答案: <input type="text" name="id" />
<input type="submit" value="查看" />
</form>
</body>
</html>
后端:
```javascript
package com.example.java_deserialization.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import java.util.Optional;
@Controller
public class java_deser {
@RequestMapping("/java_deser")
public void java_derser_start(String flag){
flag Flag = new flag("1234abc");//flag
System.out.println("被序列化对象:"+Flag.toString());
//将对象转换为二进制字节数组(序列化)
Optional<byte[]> bytes = ByteArrayUtils.objectToBytes(Flag);
System.out.println(bytes);
byte[] ret = bytes.get();
//序列化后的反序列化是新的对象
String r1 = ByteArrayUtils.toHexString(ret);
System.out.println("序列化后转换字节数组-16进制表示:"+r1);
}
}
2.文件上传漏洞
文件上传漏洞是 web 安全中经常用到的一种漏洞形式。是对数据与代码分离原则的一种攻击。上传漏洞顾名思义,就是攻击者上传了一个可执行文件如木马,病毒,恶意脚本,WebShell 等到服务器执行,并最终获得网站控制权限的高危漏洞。大部分的网站和应用系统都有上传功能,而程序员在开发任意文件上传功能时,并未考虑文件格式后缀的合法性校验或者是否只在前端通过 js 进行后缀检验(很容易绕过)。这时攻击者可以上传一个与网站脚本语言相对应的恶意代码动态脚本,例如(jsp、asp、php、aspx 文件后缀)到服务器上,从而访问这些恶意脚本中包含的恶意代码,进行动态解析最终达到执行恶意代码的效果,进一步影响服务器安全。
我们设计了一个靶场,它含有基础的上传按钮和操作,用户(学生)需要通过构造和编写恶意脚本去攻击服务器获取隐藏的flag。
3.文件下载漏洞
由于业务需求,很多网站往往需要提供文件(附件)下载的功能块,某些文件下载功能实现过程是,根据参数filename的值,获得该文件在网站上的绝对路径,读取文件内容,发送给客户端进行下载。但是如果对下载的文件没有做限制,直接通过绝对路径对其文件进行下载,那么,恶意用户就可以利用这种方式下载服务器的敏感文件,对服务器进行进一步的威胁和攻击。
package com.example.filedownload.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@Controller
public class FileDownloadController {
@RequestMapping("/Download")
public void MVCDownload(@RequestParam("filename") String filename,
HttpServletRequest request, HttpServletResponse response) throws IOException {
byte[] bytes = new byte[10];
int len = -1;
try {
InputStream inputStream = new FileInputStream("D:\\Code\\SDUWeb\\FileDownload\\flag\\" + filename);
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");
response.addHeader("content-Type", "application/octet-stream");//二进制文件
response.addHeader("content-Disposition", "attachment;filename=" + filename);
while ((len = inputStream.read(bytes)) > 0) {
response.getOutputStream().write(bytes, 0, len);
}
response.getOutputStream().close();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
后端:
package com.example.filedownload.controller;
import org.springframework.stereotype.Controller;
import org.springframework.web.bind.annotation.RequestMapping;
import org.springframework.web.bind.annotation.RequestParam;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.InputStream;
@Controller
public class FileDownloadController {
@RequestMapping("/Download")
public void MVCDownload(@RequestParam("filename") String filename,
HttpServletRequest request, HttpServletResponse response) throws IOException {
byte[] bytes = new byte[10];
int len = -1;
try {
InputStream inputStream = new FileInputStream("D:\\Code\\SDUWeb\\FileDownload\\flag\\" + filename);
response.setCharacterEncoding("utf-8");
response.setContentType("text/html;charset=UTF-8");
response.addHeader("content-Type", "application/octet-stream");//二进制文件
response.addHeader("content-Disposition", "attachment;filename=" + filename);
while ((len = inputStream.read(bytes)) > 0) {
response.getOutputStream().write(bytes, 0, len);
}
response.getOutputStream().close();
inputStream.close();
} catch (Exception e) {
e.printStackTrace();
}
}
}
测试:
点击下载后,burpsuite 拦截到该请求的数据包
在这里插入图片描述
把请求下载的文件修改为上级目录中的 flag 文件
在这里插入图片描述
forWord,发现已经下载好 flag 文件
测试:
Burpsuit拦截数据包
进行修改
…更多测试见https://blog.youkuaiyun.com/m0_47470899/article/details/124541968?spm=1001.2014.3001.5502
四、整合和发布1.0版本
整合的难点在于:
- 大家是独立开发的,但又有部分交集,难以明确界限
- 写漏洞页面时重后端,轻前端,只用Html写,和Vue存在兼容问题
- 一些配置问题
首先我们对整个网页进行整合,将冗余功能去除。
然后将存放漏洞靶场页面的独立处理:在前端页面处新建一个文件夹,以及在后端controller里新建一个文件夹,这样与主体网页互不影响,做到去耦合。
接着,我们开始合并漏洞靶场
1.html转Vue
去<!DOCTYPE html><html lang="en"><head>
这些html标签,改成Vue格式三大件<template><script><css>
对逻辑进行重改:用Vue的双向数据绑定以及element组件替换
2.后端文件修改
主要是统一接口的地址,因为在我的电脑上Vue使用的端口是8080,后端是9090.
3.添加配置
修改pom.xml
最后,我们得到了一个逻辑完整可以运行的版本1.0,传入gitee的主分支
gitee地址
https://gitee.com/cgchacker/SduCSRP/tree/master/CSRP