Java 代码审计常用漏洞总结
1.1 审计方法总结
主要代码审计方法是跟踪用户输入数据和敏感函数参数回溯:跟踪用户的输入数据,判断数据进入的每一个代码逻辑是否有可利用的点,此处的代码逻辑可以是一个函数,或者是条小小的条件判断语句。
敏感函数参数回溯,根据敏感函数,逆向追踪参数传递的过程。这个方法是最高效,最常用的方法。大多数漏洞的产生是因为函数的使用不当导致的,只要找到这些函数,就能够快速挖掘想要的漏洞。
以下是基于关键词审计技巧总结:
在搜索时要注意是否为整个单词,以及小写敏感这些设置
漏洞名称 | 关键词 |
密码硬编码、密码明文存储 | password 、pass、jdbc |
XSS | getParamter、<%=、param. |
SQL 注入 | Select、Dao 、from 、delete 、update、insert |
任意文件下载 | download 、fileName 、filePath、write、getFile、getWriter |
任意文件删除 | Delete、deleteFile、fileName 、filePath |
文件上传 | Upload、write、fileName 、filePath |
命令注入 | getRuntime、exec、cmd、shell |
缓冲区溢出 | strcpy,strcat,scanf,memcpy,memmove,memeccpy Getc(),fgetc(),getchar;read,printf |
XML 注入 | DocumentBuilder、XMLStreamReader、SAXBuilder、SAXParser SAXReader 、XMLReader SAXSource 、TransformerFactory 、SAXTransformerFactory 、 SchemaFactory |
反序列化漏洞 | ObjectInputStream.readObject 、ObjectInputStream.readUnshared、XMLDecoder.readObject Yaml.load 、 XStream.fromXML 、 ObjectMapper.readValue 、 JSON.parseObject |
url 跳转 | sendRedirect、setHeader、forward |
不安全组件暴露 | activity、Broadcast Receiver、Content Provider、Service、 inter-filter |
日志记录敏感信息 | log log.info logger.info |
代码执行 | eval、system、exec |
2.1 可基于关键词审计的漏洞
2.1.1 密码硬编码
审计方法密码硬编码最容易找,直接用 Sublime Text 打开项目目录,然后按 Ctrl + Shift + F 进行全局搜索 password 关键词:
2.1.2 反射型 XSS
审计方法:反射型 XSS 一般 fortify 一般都能扫描出来如果是手工找,可全局搜索以下关键词 getParamter <%= param.
漏洞代码示例:
-
EL 表达式输出请求参数:
!worddave1e966d6af1633429bc86ef2d7e58397.png|height=176,width=567!代码在 170 行和 305 行处获取请求参数中的 groupId 值,在未经检查参数合法性的情况下输出在 JavaScript 代码中,存在反射型 XSS 漏洞。
-
输出 getParamter 获取的参数
然后在 224 行打印到如下的 js 代码中:
2.1.3 存储型 XSS
审计方法:方法有主要有两种:
-
全局搜索数据库的插入语句(关键词:insert,save,update),然后找到该插入语句所属的方法名如(insertUser()),然后全局搜索该方法在哪里被调用,一层层的跟踪。直到 getParamter()方法获取请求参数的地方停止,如果没有全局 XSS 过滤器,跟踪的整个流程都没有对获取的参数过滤,则存在存储型 XSS。
-
从 getParamter 关键词开始 ,跟踪请求参数,直到插入数据库的语句,如果中间没有过滤参数,则存在存储型 XSS。
代码中 45 行和 46 行获取 usertype 和 name 的值,然后在 56 行存进数据库由于没有过滤传进来的参数,所以会在显示时出来触发 XSS
2.1.4SQL 注入
审计方法:
SQL 注入一般 fortify 一般都能扫描出来
手动找的话,一般直接搜索 select、update、delete、insert 关键词就会有收获如果 sql 语句中有出现+ append、 $() # 等字眼,如果没有配置 SQL 过滤文件,则判断存在 SQL 注入漏洞
当找到某个变量关键词有 SQL 注入漏洞时,还可以直接全局搜索那个关键词找出类似漏洞的文件,上面中可以直接全局搜索 tableName 关键词:
要查找那个页面调用到含有漏洞的代码,就要跟踪方法的调用栈。以上面的注入点 tableName 为例:
双击打开该文件,然后查看该变量所在函数:
发现该函数对应的 URL 为/lookOverCubeFile ,对应的功能为查看模型任务生成的文件。
2.1.5 任意文件下载
审计方法:全局搜索以下关键词 fileName filePath
getFile
getWriter
漏洞示例:
代码在 downloadFile()函数中获取请求参数中的 affixalName 的值,然后赋值给 FileName 变量,接着在 196 行处通过拼接字符串赋值给 downPath 变量,然后在 198 行处调用 download 函数并把 downPath 的值传进函数,download 函数的代码如下:
download 函数把 filePath 处的文件写到 http 响应中,在整个流程中并没有对文件名的合法性进行检查,存在任意文件下载漏洞,如通过把 affixalName 的值设置
为../../../WEB-INF/web.xml 可以下载网站的 web.xml 文件。
2.1.6 任意文件删除
审计方法:任意文件删除漏洞搜索以下关键词可以找到:
delete, deleteFile,fileName ,filePath
漏洞案例:
代码在 41 行获取 fileName 的值,在 44 行处调用 ds.deleteFile()函数删除文件,该函数的代码如下:
在整个流程中并没有对文件名的合法性进行检查,存在任意文件删除漏洞,如通过把 fileName 的值设置为../WEB-INF/web.xml 可以删除网站的 web.xml 文件。
2.1.7 文件上传
审计方法:
文件上传可以搜索以下关键词:(需注意有没有配置文件上传白名单) upload,write,fileName ,filePath
在查看时,主要判断是否有检查后缀名,同时要查看配置文件是否有设置白名单或者黑名单,像下面这种是检查了的:
下面的这种没检查:
List<FileItem> fileItems = servletFileUpload.parseRequest(request); for (int i = 0; i < fileItems.size(); ++i) {
FileItem fi = (FileItem) fileItems.get; String strFileName = fi.getName();
if (strFileName != null && !"".endsWith(strFileName)) {
String fileName = opId + "_" + getTimeSequence() + "."
+ getFileNameExtension(strFileName);
String diskFileName = path + fileName; File file = new File(diskFileName); if (file.exists()) { file.delete();
}
fi.write(new File(diskFileName)); resultArrayNode.add(fileName);
......
private String getFileNameExtension(String fullFileName) { if (fullFileName == null) { return null;
}
int pos = fullFileName.lastIndexOf(".");
if (pos != -1) {
return fullFileName.substring(pos + 1, fullFileName.length());
} else { return null;
}
}
2.1.8 命令注入
审计方法:可以搜索以下关键词:
getRuntime,exec,cmd,shell
在第 205 行中,通过拼接传过来的 ip 值来执行命令。如果 ip 值通过外部传入,则可以构造以下的 ip 值来执行 net user 命令:
127.0.0.1&&net user
2.1.9 缓冲区溢出
审计方法:主要通过搜索关键词定位,再分析上下文可搜索以下关键字:
strcpy,strcat,scanf,memcpy,memmove,memeccpy Getc(),fgetc(),getchar;read,printf
漏洞示例:
文件ktframepublictoolsocket_repeatermysocket.h 中第 177 行,这里的的参数 hostname 拷贝到 m_hostname,具体如下图所示:
m_hostname 的大小为 MAXNAME
:
继续看,可以看到大小为 255
如果传入的长度比 255 要大,就会造成缓冲区溢出。
2.1.10XML 注入
审计方法:
XML 解析一般在导入配置、数据传输接口等场景可能会用到,可通过搜索以下关键字定位:
DocumentBuilder、XMLStreamReader、SAXBuilder、SAXParser、SAXReader 、XMLReader、 SAXSource 、TransformerFactory 、SAXTransformerFactory 、SchemaFactory
涉及到 XML 文件处理的场景可留意下 XML 解析器是否禁用外部实体,从而判断是否存在 XXE 漏洞示例:在代码 6 行处、获取 DOM 解析器,解析 XML 文档的输入流,得到一个 Document
如果没有禁用 DTD 则存在 XXE 漏洞,以下代码为 XXE 防御代码
2.1.11 日志记录敏感信息
审计方法:
通过搜索关键词 log.info logger.info 来进行定位
在SFtpOperate.java文件中,代码134行处,直接将用户名密码记录在日志中
2.1.12URL 跳转
审计方法:通过搜索以下关键词定位:
sendRedirect、setHeader、forward 需注意有没有配置 url 跳转白名单漏洞示例:以下代码中 40 行处只判断 site 只是否为空,没有对 url 进行判断是否为本站 url,导致了 url 跳转漏洞
2.1.13 敏感信息泄露及错误处理
审计方法:查看配置文件是否配置统一错误页面,如果有则不存在此漏洞,如果没有再通过搜索以下关键词搜索定位,
Getmessage、exception 漏洞代码示例:在以下文件中代码 89 行处打印出程序发生异常时的具体信息
2.1.14 反序列化漏洞
审计方法:
Java 程序使用 ObjectInputStream 对象的 readObject 方法将反序列化数据转换为 java 对象。但当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。
反序列化操作一般在导入模版文件、网络通信、数据传输、日志格式化存储、对象数据落磁盘或 DB 存储等业务场景,在代码审计时可重点关注一些反序列化操作函数并判断输入是否可控,如下:
ObjectInputStream.readObject
ObjectInputStream.readUnshared
XMLDecoder.readObject
Yaml.load
XStream.fromXML
ObjectMapper.readValue JSON.parseObject
漏洞示例:
以下代码中,程序读取输入流并将其反序列化为对象。此时可查看项目工程中是否引入可利用的 commons-collections 3.1、commons-fileupload 1.3.1 等第三方库,即可构造特定反序列化对象实现任意代码执行。
2.1.15 不安全组件暴露
审计方法:
通过查看配置文件 AndroidManifest.xml,查看<inter-filter>属性有没有配置 false AndriodManifest.xml 文件中,代码 24 行处 activity 组件添加<intent-filter>属性,没有配置 false,
默认组件可被导出
3.1 其他漏洞审计方法
3.1.1CSRF
审计方法:通过查看配置文件有没有配置 csrf 全局过滤器,如果没有则重点看每个操作前有没有添加 token 的防护机制
在Smpkpiappealcontroller.java中200处,直接用用ids控制删除操作,而没有添加防 csrf的随机token验证检查,存在csrf漏洞。
Java/main/com/venustech/tsoc/cupid/smp/kpi/dao/smpkpideclardao.java 517行,对传
入的ids进行删除操作。
3.1.2Struts2 远程代码执行漏洞
审计方法:查看 struts 插件的版本信息是否为漏洞版本漏洞版本查询网址:https://www.exploit-db.com/
3.1.3 越权操作
审计方法:重点关注用户操作请求时查看是否有对当前登陆用户权限做校验从而确定是否存在漏洞,有些厂商会使用一些主流的权限框架,例如 shiro ,spring security 等框架,那么需要重点关注框架的配置文件以及实现方法
漏洞示例:
在以下文件中采用了 shiro 框架进行权限控制,在代码 58-72 行处为控制访问路径的权限设置,51-55 行处为对 admin 路径下访问限制,只有 SysyUserFilter 设置了 isAccessAllowed 方法,其他过滤均没有
SysUserFilter 中 isAccessAllowed 具体实现方法如下,90-93 行处没有对是否为当前用户进行判断,导致了越权
其他过滤文件均只设置了 onAccessDaniad()方法
如果没有使用框架的话,就要注意每个操作是否有权限
代码 7 行处获取 session 里的 username,只判断了 username 是不是为空,如果在截取数据包的时候将 username 再重新赋一个值就有可能造成越权漏洞。
以这个年度服务费用编制功能为例,测试一下,代码如图所示:
3.1.4 会话超时设置
审计方法:
Javaweb 应用会话超时设置一般有俩种方法:一是在配置文件 web.xml 设置
二是通过 java 代码设置
3.1.5 敏感数据弱加密
审计方法:
敏感数据弱加密主要看数据传输中的加密方法,一般写在工具类 util 中以下文件中为 base64 编码方法
4.1 工具使用
-
Fortify
-
新建扫描
-
命令行自定义扫描目录:如果想自定义 Fortify 扫描的目录的话,下面命令比较方便:
-
-
sourceanalyzer -scan -cp "lib/.jar" "src//.java" "web/*/.jsp" -f result.fpr
-cp 指定类库的路径,如果没有就不用这个选项
"src/*/.java" "web/*/.jsp" 这两个参数指定扫描 src 目录下的所有 java 文件和 web 目录中的所有 jsp 文件
-f 指定扫描结果的输出文件为 result.fpr,扫描完后双击就可以通过 Fortity 查看了。
-
-
-
图形化界面
-
-
-
-
查看结果
-
漏洞列表:
漏洞介绍:
Details
漏洞修复建议:
Recommendations
漏洞跟踪图:
Diagram
-
Sublime Text
-
打开项目相应目录:
-
-
-
全局搜索
-
Ctrl + Shift + F 全局搜索
上面红框的几个图标可以设置是否大小写敏感,是否搜索整个单词。
-
JD-GUI
没源码时,要分析 jar 包或者 class 文件,就要用到 JD-GUI。
直接拖动某个 jar 或者 class 文件进 jd-gui 就可以打开了,然后搜索关键词审计:
!worddavfc997c390599727e570894b49e58994a.png|height=328,width=554!把那些勾都勾上搜索。
-
文件浏览器
Windows 自带的文件浏览器可以方便地搜索某个文件或者 java,jsp 文件:
实际中,都是 Fortify、Sublime Text 和文件浏览器结合一起用最高效。
以上信息来源网络或书籍,只供学习,如存在侵权问题,请联系作者进行删除