代码审计_基础篇
代码审计__基础篇
前言
开这个坑主要还是想帮助一些初步学习了Java基础缺不知道如何审计的同学。加上我也在学习Java审计,想做个学习历程的记录。
本系列审计内容主要分为以下内容:
- 审计需要具备的基础
- 审计基础漏洞的案例介绍(其中包括SQL注入、XSS、RCE等等)
- 以及后面会有一些经典漏洞的分析等(这个不一定有,因为我一直也在分享其他的CVE分析文章。主要还是看个人精力吧)
工具和环境准备:
安装的话网上都有,最好看看用IDEA搭建WEB环境之类的。可以用框架,也可以不用框架,初学者建议不要使用框架,代码靠自己手写会记忆深刻一些。因为我们需要自己动手写代码,一方面可以知道漏洞如何产生?然后如何去修复漏洞?这样自己动手写代码学习下来,学习后会比较深刻。一定一定一定自己动手做!!!!(如果你想学审计的话)
- IDEA(版本无所谓,最好不要用
Community
版本因为我下的那个Community
版本里面没有tomcat相关的启动组件。这个是重点,不会的话可以先看看IDEA的基础用法) - Tomcat(版本的话Tomcat7 和 Tomcat8都可)
- MySQL(这个可以用小皮代替,确实比较方便。)
- Java(JAVA8!!! 你问我什么是Java8? **1.8.***的都是,最好把版本限制在1.8.3之内 )
- JD-GUI(这一一款Javaclass反编译工具,可以根据关键字类名等等信息进行搜索定位。其实IDEA也能办到但是根据关键字定位的话还是JD比较好)
- FortifySCA (跟PHP审计的那个差不多,自动审计结果很水。仅供参考)
知识准备
其实就是想把Tomcat的结构图给大家清晰的展示一下,其他知识点给大家附了学习链接,这里就不过多介绍了。
Tomcat架构
Tomcat的内容,在之前的文章里面有提及过,这个结构一定要清楚其中包括三大件的详细知识。这也是Javaweb开发必备的知识。本篇文章就不过多介绍细节,简单介绍一下每个模块的基础概念。
Connector
:连接器,用于处理客户端与服务器之间的连接。它负责监听端口、接受传入的请求,并将请求传递给 Tomcat 的处理引擎。ProtocolHandler
:协议处理器,用于解析特定协议的请求。它负责接收原始的网络数据流,并将其转换为可识别的请求对象。Endpoint
:端点,表示 Tomcat 与客户端之间的连接点。它管理客户端连接的生命周期,包括连接的建立、维护和关闭。Processor
:处理器,用于处理客户端请求。它接收来自 Connector 的请求,并调用适当的 Adapter 来处理请求。Adapter
:适配器,用于将 Tomcat 接收到的请求适配给不同类型的容器。它将请求转发到适当的容器(如 Servlet 容器)进行处理。Container
:容器,在 Tomcat 中指的是一个通用的组件,用于托管 Servlet、JSP 等。它负责管理和执行特定类型的 Web 组件。Engine
:引擎,代表 Tomcat 的核心处理引擎,用于处理接收到的请求并将其路由到相应的虚拟主机(Host)。Host
:主机,指的是 Tomcat 中的虚拟主机,用于托管一个或多个 Web 应用程序。Context
:上下文,用于表示一个 Web 应用程序在 Tomcat 中的上下文路径和配置信息。Wrapper
:包装器,用于包装和管理 Servlet,每个 Servlet 对应一个 Wrapper 对象。Servlet
:Java Web 应用程序中的核心组件,用于处理客户端请求并生成响应。
JSP基础
基础、基础、基础!!!
JDBC基础
学习JDBC主要为了产出Sql注入和一些其他的非常规漏洞。
Spring框架学习
学习主流框架可以让我们在今后审计过程中快速定位关键点。
- Y4tacker | JavaSec
- Native8418 | Spring Security 一
- Native8418 | Spring Security 二
- Native8418 | Spring Security 三
层级学习
整个层级流程如下图,这里也是简单介绍一下对应的层级概念。
Controller层
Controller层负责具体的业务模块流程的控制,controller层负责前后端交互,接受前端请求,调用service层,接收service层返回的数据,最后返回具体的页面和数据到客户端。
DAO层
DAO层=mapper层,现在用Mybatis逆向工程生成的mapper层,其实就是dao层。DAO层会调用entity层,DAO中会定义实际使用到的方法,比如增删改查。DAO 层的数据源和数据库连接的参数都是在配置文件中进行配置的,配置文件一般在同层的XML文件夹中。数据持久化操作就是指,把数据放到持久化的介质中,同时提供增删改查操作。
Service层
Service层主要负责业务模块的逻辑应用设计。先设计放接口的类,再创建实现的类,然后在配置文件中进行配置其实现的关联。service层调用dao层接口,接收dao层返回的数据,完成项目的基本功能设计。
额外的:
Model实体层(或者叫Entity、Bean)
Entity层是实体层,也就是所谓的model,是数据库在项目中的类,该文件包含实体类的属性和对应属性的set、get方法;
基础漏洞产生原因
这里借一下su18师傅的基础漏洞审计。大家可以去师傅的文章链接学习。我放在这里是为了自己方便查看。
SQL注入
漏洞成因
- 直接使用SQL语句拼接,将用户传入的参数通过字符串拼接的方式传入查询语句。
示例:String sql = "select * from test where id = " + id;
- 预编译使用有误,没有调用 set 方法将变量与占位符进行对应。
示例:
1 |
|
- 对 % 和 _ 没有进行显示处理, 导致用户可以自行拼接进行模糊查询。
- 不能参数化的位置,还是有可能存在拼接的情况,如order by后面。
示例:String sql = "select * from test where id = ? order by '" + time + "' asc";
- Mybatis中,#{}是预编译处理,${}是字符串替换,使用${}就可能导致SQL注入。
示例:Select * from news where title like '%#{title}%'
审计策略
- 重点关注创建查询的函数如
createQuery()
、createSQLQuery()
、createNativeQuery()
。 - 定位SQL语句上下文,查看是否有参数直接拼接,是否有对模糊查询关键字的过滤。
- 是否使用预编译技术,预编译是否完整,关键函数定位
setObject()
、setInt()
、setString()
、setSQLXML()
关联上下文搜索set*
开头的函数。 - Mybatis中搜索${},因为对于like模糊查询、order by排序、范围查询in、动态表名/列名,没法使用预编译,只能拼接,所以还是需要手工防注入,此时可查看相关逻辑是否正确。
- JPA搜索
JpaSort.unsafe()
,查看是否用实体之外的字段对查询结果排序,进行了SQL的拼接。以及查看EntityManager
的使用,也可能存在拼接SQL的情况。
如何修复
正确使用预编译;无论是 SQL/HQL/JPQL,都不进行SQL语句字符串拼接;正确理解占位符、预编译、替换、参数注入等形式的使用。
XSS/链接注入/框架注入
漏洞成因
对于用户传递参数,没有进行过滤,导致恶意攻击者可以插入一些恶意的js语句、标签、frame等来获取应用或用户的敏感信息。
审计策略
审计过程要点还是定位用户的输入输出,也就是梳理数据交互以及前端展示的过程。找到一条完整的利用链之后,就是结合现有的安全措施(输出编码、过滤器等)进行判断,例如是否存在绕过的可能,或者是没有任何安全防护可直接造成攻击。
下面是一些可以快速定位的关键字:
1 |
|
一般会使用全局的Filter进行xss的过滤,但通常可能存在漏网之鱼,所以也需要审计全局过滤规则是否完善。可以通过关键字 XssFilter
进行搜索。
如何修复
自定义全局过滤器,对用户提交输入进行检查和过滤;对用户输入并用来展示的数据进行HTML转义,可用的工具类包括org.springframework.web.util.HtmlUtils
、org.apache.commons.lang3.StringEscapeUtils
、ESAPI.encoder().encodeForHTML
等。
XXE
漏洞成因
- XML文档结构包括XML声明、DTD文档类型定义(可选)、文档元素。文档类型定义(DTD)的作用是定义 XML 文档的合法构建模块。DTD 可以在 XML 文档内声明,也可以外部引用。当允许引用外部实体时,恶意攻击者即可构造恶意内容访问服务器资源。
示例:
1 |
|
- 使用不可信数据来构造XML会导致XML注入漏洞,如果用户被允许输入结构化的XML片段,则他可以在XML的数据域中注入XML标签来改写目标XML文档的结构与
内容。
审计策略
XML解析涉及的业务功能点: WebServices接口、RESTful接口、Excel文件解析、Soap协议等。
漏洞触发点就在XML解析时,因此重点审计XML解析器是否设置了相关的安全属性,禁用DTDs或者禁止使用外部实体。还有是否使用了不安全的漏洞组件。部分解析器如下:
1 |
|
除此之外,关注StreamSource
、XMLConstants
、StringReader
等方法的调用,在项目中搜索. xsd文件。
如何修复
使用XML解析器时禁止使用外部实体;使用白名单的方式对用户输入进行处理,避免用户输入改变XML结构或内容。
各种Features:
1 |
|
反序列化
漏洞成因
- Java程序使用
ObjectInputStream
对象的readObject()
方法将反序列化数据转换为java对象。如果被反序列化的对象重写了readObject()
方法,则会执行该对象的此方法。因此,当输入的反序列化的数据可被用户控制,那么攻击者即可通过构造恶意输入,让反序列化产生非预期的对象,在此过程中执行构造的任意代码。 - 反序列化一个类时,通常会伴随调用 get/set 方法注入参数,并为这个类创建一个新的实例化对象,因此会执行构造方法及静态代码块,在这种情况下,攻击者也可以挖掘gadget来执行危险的动作。
- 根据权限最小化原则,一般情况下反序列化的类中的
readObject()
、writeObject()
、readResolve()
、writeReplace()
方法必须被声明为 private void。否则如果 Serializable 的类开放 writeObject 函数为 public 的话,给非受信调用者过高权限,潜在有风险。 - 在 Java 环境中,允许处于不同受信域的组件进行数据通信,从而出现跨受信边界的数据传输。如果反序列化类中存在未加密的敏感数据,将面临泄露或被篡改的风险。
- 对非静态内部类的序列化依赖编译器,且随着平台的不同而不同,容易产生错误。对内部类的序列化会导致外部类的实例也被序列化。这样有可能泄露敏感数据。
审计策略
反序列化操作的功能位置:导入模版文件、网络通信、数据传输、日志格式化存储、对象数据落磁盘或DB存储等业务场景。
可以通过对网络抓包寻找序列化数据:java序列化的数据一般会以标记(ac ed 00 05)开头,base64编码后的特征为rO0AB。
一些服务的传输可能存在反序列化:多平台HTTP通信、RMI、JMX。
一些反序列化触发点:
1 |
|
主要查看这些触发点的参数是否由用户可控。
全局搜索是否具有public权限的一些方法:public * writeObject/readObject/readResolve/writeReplace
。
查看反序列化类中是否包含敏感数据。
全局查找implements Serializable 的所有内部类。
如何修复
- 对要反序列化的对象设置黑白名单,像 fastJSON、Jackson这种官方会维护一个黑名单,持续更新,但还是建议使用白名单,可通过Hook函数
resolveClass()
来校验反序列化的类从而实现白名单校验;也可以使用Apache Commons IO Serialization包中的ValidatingObjectInputStream
类的accept()
方法来实现反序列化类白/黑名单控制。 - 对于一些敏感的属性,将其声明为 transient,或进行加密处理。
- 避免内部类的序列化,或把内部类声明为静态,但还是要注意敏感信息的问题。
命令执行
漏洞成因
由于业务需求,应用程序可能含有执行系统命令的功能,如果执行的命令任意用户可控,则将产生极大的危害。
审计策略
重点关注能执行命令的一些功能及函数:
1 |
|
如何修复
避免使用这样的功能,如必须,待执行命令尽量不由用户传入。
SSRF
漏洞成因
SSRF形成的原因大都是由于代码中提供了从其他服务器应用获取数据的功能但没有对目标地址做过滤与限制。比如从指定URL链接获取图片、下载等。
审计策略
出现SSRF漏洞的主要业务有:
- 通过URL地址分享网页内容
- 在线服务
- 通过URL地址加载或下载图片
- 加载远端配置
重点关注一些HTTP请求操作函数:
1 |
|
除了建立HTTP协议连接,还可能直接通过 Socket建立连接,因此应该同样关注Socket相关类:
1 |
|
如何修复
- 使用白名单校验HTTP请求url地址,例如通过InetAddress对象的isSiteLocalAddress()方法进行判断,禁止内网地址的网络请求。
- 避免将请求响应及错误信息返回给用户
- 禁用不需要的协议及限制请求端口
文件上传
漏洞成因
文件上传时,由于校验不全、限制不当,可能导致被上传webshell、拒绝服务、任意文件写入等安全问题。
审计策略
- 首先关注文件后缀验证,使用白名单或黑名单,建议使用白名单。使用
lastIndexOf()
方法获取文件后缀,使用IndexOf()
可能被绕过。如果是白名单验证时,使用toLowerCase()
处理再进行对比,或使用equalsIgnoreCase()
,避免被大小写绕过。 - 是否校验了文件的大小。
- 是否校验了文件类型
getContentType()
,这种方式虽然能够被绕过,但还是会增加攻击成本。 - 对于使用Hutool的FileTypeUtil的
getType()
或ImageIO.read()
通过读取文件流中前N个byte值来判断文件类型的,也可以使用类似图片马的方式进行绕过。 - “%00”截断能否绕过。
- QP编码特性能否绕过。
javax.mail.internet.MimeUtility.encodeWord()
方法。 - 有一些安全校验的顺序有问题,先将文件保存,再进行安全检测,如果不通过检测则进行删除,此时可以在文件保存后触发报错终止流程,导致不删除文件。
重点是文件上传相关类或函数:
1 |
|
如何修复
对上传文件后缀进行白名单验证,验证文件大小,强制重命名后缀,上传文件单独服务器保存。
任意文件读/写/删除/复制/移动/遍历
漏洞成因
应用程序由于业务需求,提供了文件操作的一系列功能,但文件名、文件路径等由用户控制,在校验不当的情况下,用户可以绕过限制对服务器上任意文件进行操作。
审计策略
首先关注包含这些功能的类和函数:
1 |
|
在使用这些函数时,对于用户传递来的文件对象/文件名/文件路径,是否进行了正确的处理:
- 是否限制了可操作文件的路径、文件类型、文件所有者。
- 是否将敏感文件进行排除。
- 查找
getPath()
,getAbsolutePath()
,查看是否有错误的路径判断。
再排查程序的安全策略配置文件,全局搜索permission
、Java.io.FilePermission
、grant
字样。查看是否只为程序的某部分路径赋予读写权限。
如何修复
- 对要操作的文件名进行黑白名单限制。
- 对用户输入的数据进行过滤,过滤掉”./“、”…/“、”%”、”/“
- 针对不同的功能,将可操作文件路径限制在某个目录内,禁止攻击者通过 ‘../‘的方式穿越路径,建议使用
getCanonicalPath()
标准化路径,之后再进行限制和判断。 - 使用 FilePermission 限制权限。
URL重定向
漏洞成因
由于Web站点有时需要根据不同的逻辑将用户引向到不同的页面,如典型的登录接口就经常需要在认证成功之后将用户引导到登录之前的页面,整个过程中如果实现不好就可能导致URL重定向问题,攻击者构造恶意跳转的链接,可以向用户发起钓鱼攻击。
审计策略
出现URL重定向主要的功能有:
- 用户登录、统一身份认证处,认证完了会通过url=的形式跳转到类似操作的页面。
- 用户分享、收藏内容后跳转。
- 跨域认证授权后进行跳转。
一些相关函数及关键字:sendRedirect
、setHeader
、forward
、redirect:
、<c:redirect
、self.location.href
、location.href
、windows.location.href
等。
一些常见的参数名称:redirect
、redirect_do
、redirect_url
、url
、jump
、jump_to
、target
、to
、link
、domain
等。
审计的思路为定位可能存在redirect业务的代码段,审计跳转的URL是否来自于前端参数,是否具有校验和限制。
如何修复
对传入的URL做认证处理,保证该URL来自于信任域。例如如下方式:
- 通过限制Referer保证将要跳转URL的有效性,避免攻击者生成自己的恶意跳转链接。
- 加入有效性验证Token,保证所有生成的链接都来自于可信域,通过在生成的链接里加入用户不可控的Token对生成的链接进行校验。
关键参数前端不可控。
对跳转的URL进行白名单检查
业务逻辑漏洞
越权漏洞
漏洞成因
- 在应用程序处理当前用户请求时,没有对用户权限进行校验,或校验不足、失效,导致低权限用户使用高权限功能,或同权限用户操作对方数据。
- 对用户的身份标识信息从可伪造的参数或headers中获取,而不是从session中获取的话,可能存在越权。
- 一些跨域的服务导向架构,尤其是一些数据处理的接口,如果缺少类似token的认证机制的话,也会存在类似的越权访问问题。
审计策略
针对系统任意功能都可能存在越权,审计关注的是:
- 操作是否需要身份标识或其他标识。
- 此标识是否由用户可控。
- 是否能够可猜解,不同主体的标识是否具有规律性。
- 查询信息场景下,信息是否与身份标识进行绑定。
- 对应处理的函数方法中是否使用注解或其他方式对当前用户进行权限校验。
- 校验能否被绕过或失效。
如何修复
- 处理请求之前先进行权限校验,可通过Filter、AOP或拦截器进行实现。
- 用户标识从缓存中取出,尽量不从参数中取。
图形验证码漏洞
漏洞成因
- 当验证码图片的长宽由前端参数控制,并且后端没有进行校验时,攻击者可以并发生成超大的图片来进行DDOS攻击。
- 当验证码认证了一次后,无论成功还是失败,没有及时清空,就可以被重复使用,导致爆破攻击。
- 简单的图片验证码可被OCR识别,导致爆破攻击。
- 在后端逻辑校验过程中并未对前端传递验证码参数为null进行相关的逻辑判断,直接删除验证码参数或置空值即可绕过。
审计策略
在登陆,重要数据的增删改功能处一般会添加图形验证码功能,可以通关关键字搜索:captcha
、checkCode
等等。
重点审计验证码生成逻辑,以及验证码的判断校验逻辑、顺序。
如何修复
图形验证码由后端控制大小;验证一次后无论是否成功都立即清空缓存;生成复杂的图形验证码,降低遭到机器识别的风险;对验证码参数是否为空进行判断。
短信服务漏洞
漏洞成因
- 如果应用程序对短信接口没有发送频率限制、或校验逻辑有误,则会导致短信炸弹漏洞。
- 如果对手机号进行校验或格式化处理逻辑有误,可导致攻击者通过使用空格、分隔符、手机区号、字母等方式绕过对手机号的限制,在进行缓存计数时进行绕过。
- 如果短信内容的提示有部分通过参数内容获取,则可能导致攻击者篡改消息内容,对部分用户进行定点钓鱼攻击、或垃圾短信等。
审计策略
重点审计发送短信验证码功能的业务逻辑,一般通过关键字sms
进行搜索。审计业务逻辑、缓存次数逻辑、非空判断逻辑、短信内容逻辑等等。
如何修复
针对手机号的格式化以及号码缓存的业务逻辑设计一定要正确,对短信发送接口及验证接口的业务顺序要正确。
Zip文件提取
漏洞成因
在业务中可能存在上传压缩文件并提取的功能,利用此类功能可能存在如下安全问题:
- 如果提取出的文件标准路径落在解压的目标目录之外,攻击者可以利用此功能进行任意文件写入。
- 提取出的文件消耗过多的系统资源。zip算法的本性就可能会导致zip炸弹(zip bomb)的出现,
由于极高的压缩率,即使在解压小文件时,比如ZIP、GIF,以及gzip编码的HTTP内容,也
可能会导致过度的资源消耗。
审计策略
审计重点主要关注应用是否存在ZIP解压缩功能:
1 |
|
如果出现getSize基本上就需要特别注意了。
如何修复
针对ZIP解压缩功能,应该在解压每个条目之前对其文件名进行校验。如果校验不通过,就终止整个解压过程,或对其进行忽略。
除了校验文件名,还应该对每个文件大小进行限制,过大的单个文件应该不予处理,或抛出异常。
最后对文件总个数也应该有所限制,避免过多的文件数目导致占用系统资源。
自动绑定漏洞
漏洞成因
攻击者可能将非预期的HTTP请求参数绑定到一个对象上,使用这种方法来创建、修改、更新开发人员或者业务本身从未打算变更的参数,而这些新参数反过来又会影响程序代码中不需要的新变量或对象,进而触发一些业务逻辑漏洞。
审计策略
重点审计应用程序接参时是否使用自动绑定注入完整对象,而对象是否又对应了数据层的实体类,在更新数据时是否能够通过添加参数的方式更新非预期的属性值;或这些参数能否影响后续业务逻辑。
如何修复
取原始数据由DAO层取;存放于session中的对象,如用户等,在更新信息时注意敏感字段;使用自动绑定以实体类来接参时,确保类属性与实际参数能够一一对应。
其他业务逻辑漏洞
漏洞成因
在代码中可能存在各种业务逻辑类的漏洞,要具体看开发人员实现的代码逻辑。在实现有误的情况下,如校验不完全、校验顺序错误、校验逻辑设计错误等,将会产生各种业务逻辑漏洞。
审计策略
最常见的就是任意密码重置、验证码绕过等等,下面简单列举了一些。
1 |
|
如何修复
在业务逻辑实现时详细设计,代码实现时注意一些常见的坑,避免出现以上提到的漏洞。
其他问题
硬编码
漏洞成因
如果将敏感信息(包括口令和加密密钥)硬编码在程序中,可能会将敏感信息暴露给攻击者。任何能够访问到class文件的人都可以反编译class文件并发现这些敏感信息。因此,不能将信息硬编码在程序中。
审计策略
审计源代码中是否有硬编码敏感信息。
1 |
|
如何修复
动态获取敏感信息,通过配置文件、读取数据库或其他手段。
临时文件删除
漏洞成因
开发人员会在全局可写的目录中创建临时文件。这类目录中的文件可能会被定期清理,然而,如果文件未被安全地创建或者用完后还是可访问的,攻击者便可以利用共享目录中的文件操作获取本地文件系统访问权限。
审计策略
重点文件应用程序创建新文件时是否为临时文件,在使用后是否对临时进行及时的删除。
可以搜索关键字File
、FileOutputStream
、tempFile
、FileUtils
进行查找。
如何修复
可以使用NIO中的createTempFile()
方法创建临时文件,这种方法无论是否有异常,都会自动关闭文件,使用DELETE_ON_CLOSE
选项,在文件关闭时自动删除。
或在业务逻辑处理完成后显式地进行删除,但要注意条件竞争问题。
不安全的反射
漏洞成因
开发人员如果提供了接口,可以由访问人员为应用程序提供确定实例化哪个类或调用哪个方法的参数值,那么就有可能创建一个贯穿于整个应用程序的控制流路径,而该路径并非是应用程序开发者最初设计的。
而攻击者可以利用这样的接口调用非预期的任意方法执行文件写入、命令执行或者其他的恶意操作。
审计策略
查看开发人员是否对反射调用方法、反射创建类实例进行了封装,并是否在对外的接口中进行了相关的调用。
1 |
|
如何修复
避免对外开放具有相关功能的接口,或在调用前进行严格的审查。
使用了不安全的组件
漏洞成因
开发人员在开发应用程序时可能会引用第三方的组件,这些组件可能被报出过安全漏洞,但是使用者由于种种原因没有及时升级,因此可能导致存在历史遗留的问题。
组件例如 fastjson、jackson、xstream,框架例如 struts2、spring 等等,都要注意使用的版本问题。
审计策略
关注 maven 和 gradle 等包管理软件处理的应用程序依赖,关注开发人员所使用的应用程序版本,查看是否具有存在漏洞的版本,并查看其具体调用位置,看是否能够组合成相关漏洞。
必要时可借助 SCA 审查工具来实现。
如何修复
确保应用程序使用较新的、或安装了安全补丁的组件。
思路准备
以下仅为个人学习观点,如有不对或者需要补充还请师傅们斧正。
入门
其实学完这些东西之后,实际你大概率还是不能自己审计的。但是此时的你已经对Java审计有了进一步的认识。这个时候你可以自己搭建一些之前的热门环境漏洞比如 Struts2、Shiro、Fastjson、或者网上有一些新的热门漏洞可以跟着文章进行复现分析。一定多做,即可以帮助自己深刻理解之前学习到的概念,还能多了解新的漏洞点。
进阶
多接触热门框架漏洞。熟悉主流框架的结构,到时候会有自己审计的侧重点和关注点。然后自己找一些小众的项目自己进行代码审计。
高阶
高阶我不了解,因为自己还是太菜了。但是安全的尽头是开发。我估计也离不开开发。。。。。