每个人都有迷茫和快速成长的时期。做技术需要静下心来,两耳不闻窗外事,一心只读圣贤书。一段时间以后就发现窗外那些吵杂的声音早已经没法触动你的心思,这时候就谁也挡不住你成长了。 |
公众号现在只对常读和星标的公众号才展示大图推送,
建议大家把 听风安全 设为 星标 ,否则可能就看不到啦!
----------------------------------------------------------------------
原创作者:Bi8bo(多多关注公众号)
SQL注入
是否使用预编译技术,预编译是否完整。定位 SQL 语句上下文,查看是否有参数直接拼接,是否有对模糊查询关键字的过滤。Mybatis 框架则搜索 ${},四种情况无法预编译:like 模糊查询、order by 排序、范围查询 in、动态表名/列名,只能拼接,所以还是需要手工防注入,此时可查看相关逻辑是否正确。
关键函数或字符串查找
SQL语句动态拼接导致的SQL注入漏洞是先前最为常见的场景,createStatement() :创建一个 Statement 对象,之后可使用 executeQuery() 方法执行SQL语句。executeQuery(String sql) 方法:执行指定的 SQL 语句,返回单个 ResultSet 对象。
根据案例可以看到用户可以传入参数id,id未经过任何处理直接拼接到SQL语句里,然后使用executeQuery()执行了SQL语句。运行代码进行调试测试。
测试存在SQL注入漏洞。
预编译是能够有效防止注入的,但是存在预编译使用错误的情况,最后导致SQL注入的产生。
在上述案例中就是虽然使用了preparestatement预编译,但是未对参数进行标记而导致了SQL注入产生。启动代码调试
预编译正确写法
可以看到这次无法注入了。
在SQL语句中, order by 语句用于对结果集进行排序。而order by 语句后面需要是字段名或者字段位置,无论是preparestatement还是mybatis中,都无法直接预编译,因为会将传入的参数用单引号包裹,从而认为传入的是字符串而不是字段名,因此在使用order by时,无法使用预编译进行防止注入。
启动代码,使用sqlmap直接跑,可以看到存在注入。
源码地址: https://gitee.com/getrebuild/rebuild/tree/3.5.4/
搭建好系统后可以再pom.xml里看使用了什么框架,不存在mybatis框架就直接在代码里查看是否存在未预编译得代码。
第一步首先获取用户传入的feeds参数,这里的 "feedsId = '%s'" 是一个模板字符串,其中的%s是一个占位符,表示字符串类型的数据。feeds 是一个变量,其值将被插入到模板字符串中的%s位置。第四步再将sqlwhere拼接进SQL语句,第五步执行SQL语句。所以该参数不存在预编译。只用了getIdParameterNotNull方法进行处理。
跟进该方法, 可以看到使用getParameter方法获取传入的参数,然后再用isid校验了参数v,跟进参数v
第一个if需要实例一模一样,无法传入我们的语句,所以直接看第二个,可以看到需要满足上述三个条件才会进入下一步校验,分别是校验了是否是null,如果转换成字符串后是否是空,第三个是校验了传入的参数的长度。跟进长度要求.
可以看到上述为静态初始化块,静态初始化块(static 块)在 Java 程序中会在类被加载时自动执行。具体来说,静态初始化块有以下几个特点:自动执行:当类首次被加载时,静态初始化块会自动执行。执行时机:在类的任何成员(包括静态成员)被访问之前执行。所以当前的idLength取决于idGenerator类的getlenth,而idGenerator是通过反射获取的WeakIDGenerator,所以这里getlenth来源于WeakIDGenerator类,追进去查看。
可以看到长度为20。返回刚才的代码
验证完成后表达式 id.toString().charAt(3) == '-' 用于检查变量 id 转换为字符串后,第四个字符(索引为 3)是否为-,所以得出结果,我们需要传入的参数首先是20位,而且第四位参数必须为-,所以构造参数为000-0000000000000000
可以看到调用成功。添加payload 001-00000000%27or+1=1%23发现报错。由于长度限制未进行后续测试,主要是学习审计思路。
在mybatis中和#的区别就是一个有预编译一个没有,其中是未进行预编译直接进入数据库查询的,所以需要查看当前参数是否为用户所控制,如果是用户可控的情况下,那么说明存在SQL注入。
源码地址: https://gitee.com/mingSoft/MCMS/tree/5.2.8/
参数为用户所控制的情况下,未对传入的参数进行过滤就会造成SQL注入,接下来用代码举例说明。
可以看到直接使用了$,我们需要从下往上追,首先我们需要找到其对应的映射器,其位置一般位于最上方。
我们需要进入这个dao文件搜索出现存在$的id,就比如上述出现queryChildren。
其中也可能会存在在当前dao文件里未找到该id的情况,这个时候我们需要查看他的父类,也就是上述图片中的IBasedao文件。那么我们找到这个id后就需要追踪谁调用了这个方法 ,直接Ctrl+鼠标左键追踪即可查看到。
继续往上追,可以看到存在四个调用,这个时候就需要判断哪个调用用户可控且不存在过滤。
这里我直接进入存在注入的点进行分析,可以看到传入的参数为categoryEntity,继续追categoryEntity来源。
根据图上可以看到,categoryEntity来源于categoryid,而categoryid来源于目录{categoryId },再分析代码,只判断了categoryId是否为0,那么整个流程已经理清。
所以构造URL进行注入。
注入成功。
与JDBC预编译中order by注入一样,在 order by 语句后面需要是字段名或者字段位置。因此也不能使用Mybatis中预编译的方式。如果我们要防止注入,只能限制用户传入的数据使我们想要的数据,使用白名单方式即可,下面两种修复方式均可。
in在查询某个范围数据是会用到多个参数,在Mybtis中如果直接使用占位符 #{} 进行查询会 将这些参数看一个整体,进而引发报错,所以开发人员可能会直接使用$,从而导致了SQL注入的产生,而我们正确的做法应该是foreach配合占位符 #{} 实现IN查询。这样就不会导致注入的产生。
Like语句在查询时直接使用#{}会报错,如果开发人员经验不太充足的情况下可能会直接使用${}进行查询,从而导致SQL注入的产生。正确做法应该是like CONCAT(CONCAT('%',#{item.value}),'%')。
命令执行漏洞是指应用有时需要调用一些执行系统命令的函数,如果系统命令代码未对用户可控参数做过滤,则当用户能控制这些函数中的参数时,就可以将恶意系统命令拼接到正常命令中,从而造成命令执行攻击。首先我们了解一下命令连接符的使用。
Windows与Linux均支持:
cmd1 | cmd2 只输出cmd2的结果,但两个命令均会执行
cmd1 || cmd2 只有当cmd1执行失败后,cmd2才被执行
cmd1 & cmd2 先执行cmd1,不管是否成功,都会执行cmd2
cmd1 && cmd2 先执行cmd1,cmd1执行成功后才执行cmd2,否则不执行cmd2
Linux单独支持:
cmd1;cmd2 依次执行命令
需关注的函数:
java.lang.Runtime 公共类中的 exec()方法可以执行系统命令,其中需要关注是否为cmd /c,在不使用cmd /c的时候,只能执行单个命令,无法使用|和&进行连接执行多个命令,当使用|和&连接时会将其当成一个整体命令执行。而使用cmd /c就可以执行复杂命令。
上述代码中,userinput模拟用户输入,command为执行的命令,尝试执行ipconfig && dir。
执行成功。 但是在审计的时候除了正射调用,也可能存在反射调用exec进行命令执行。
上述代码就展示了反射如何调用java.lang.Runtime 公共类中的 exec()方法,尝试进行命令执行。
探索一下如何调用的,首先进入exec方法。
再次进入下面return的方法。
可以看到最终调用的是ProcessBuilder方法,那么下一个就是processbuilder的命令执行。
ProcessBuilder.start() 方法的主要作用就是启动一个新的操作系统进程。这个进程独立于当前的 Java 应用程序,有自己的输入、输出和错误流。通过这个方法,你可以执行外部命令或脚本,就像在命令行中手动执行一样。需要注意的是,在processbuilder中传入的cmd只能以字符串数组的形式来传递命令和参数,否则会出现报错。
测试命令执行。
执行成功。我们再跟进一下processbuilder.start()查看其调用,跟进start,可以看到最终执行是ProcessImpl.start()这里ProcessImpl更为底层。Runtime和ProcessBuilder执行命令实际上也是调用了ProcessImpl这个类。
ProcessImpl 类实际上是 Java 运行时环境内部用于实现 Process 接口的具体实现类。它通常是私有的,并且不是公共 API 的一部分,因此开发者通常不能直接实例化或调用 ProcessImpl 中的方法,所以一般调用此方法需要通过反射的形式进行调用。
代码示例如图所示,如果未经过过滤则会直接导致命令执行。
源码地址: https://github.com/xuxueli/xxl-job
搭建好后,分别启动调度中心和执行器。搭建好系统后进行测试,审计。了解到存在命令执行后进行测试,存在的点是后台任务管理执行脚本处,点击保存。
保存完后,修改GLUE IDE内容。
添加whoami,保存执行。
查看执行日志。
命令执行成功,跟踪代码查看。首先可以知道得是入口为/trigger,查看调用。
跟进addTrigger方法。
这里得代码主要是使用选定的 triggerPool_ 线程池来执行一个 Runnable 实例中的任务。尝试调用 XxlJobTrigger.trigger() 方法来触发任务,该方法包含了实际的任务处理逻辑,并允许传入任务ID (jobId)、触发类型 (triggerType)、失败重试次数 (failRetryCount)、执行分片参数 (executorShardingParam) 和执行参数 (executorParam)。跟进XxlJobTrigger.trigger()。
跟进后发现我们传入的参数executorParam添加进了jobinfo。
最后经过一系列判断后jobinfo被传入进了processTrigger方法里进行处理。
跟进processTrigger方法查看如何处理jobinfo,可以看到jobinfo传入进来之后,将其中得数据去除添加进triggerparam中,可以看到一个getgluesource,该参数就是我们上述传入得GLUE IDE内容。Debug查看一下。
继续跟进triggerParam的处理,可以看到有两个处理了triggerParam,其中第一个是根据指定的路由策略从一组注册的执行器中选择一个合适的执行器地址,以便将任务分发给该执行器进行执行。那么我们跟进第二个方法。
跟进runExecutor方法查看如何处理triggerParam,可以看到首先根据address获取执行器,这里获取执行器(9999端口启动的),并委托ExecutorBiz执行run方法。
跟进run方法,可以看到有一个实现,继续跟进这个实现
在该方法里看到,先是判断glueTypeEnum是否为BEAN,或者为GLUE_GROOVY,可以得知此处校验的是我们最初传入进来的powershell。
所以代码未走上述的两个条件,可以看到jobhandler为null,if判断是否为null,是的情况下创建了一个新的实例ScriptJobHandler。
再继续往下走由上述代码中可以看到jobThread = null;那么就应该走下面为null的代码,可以看到调用了registJobThread处理上述实例化的ScriptJobHandler。
继续跟进registJobThread方法,可以看到创建了一个JobThread 实例,通过调用 start() 方法启动了新的 JobThread 实例。这实际上会创建一个新的线程,并在该线程中执行 JobThread 的 run() 方法。
那么跟进JobThread 的run方法,实例中传入了handler,追踪handler参数,可以看到代码中使用if判断,发现无论走上面的if还是下面的else都会调用handler.execute去处理triggerParam.getExecutorParams()。
跟进execute方法,可以看到需要找具体的handler实现,而当初我们传进去的handler为ScriptJobHandler,那么直接追进ScriptJobHandler的execute方法中。
跟进ScriptJobHandler类当中,可以看到,获取了几个参数。
其中cmd为gluetype中获取的,根据我们最开始选择的powershell可得知,此处cmd为powershell。
这段代码的作用是构建一个脚本文件的路径,并确保该文件存在。如果文件不存在,则创建该文件并写入脚本内容,写入的内容也是我们传进来的gluesource。
继续往下看scriptParams来源于上述我们传入的params,最后再传入最下面的execToFile进行处理。
跟进execToFile发现我们传入的sciptfile被添加进了cmdarry中。
除了上述两个参数被添加近cmdarray当中,还将传入的params数组中的每个元素添加到一个集合 cmdarray 中。将List
常见的一些java文件操作类的漏洞:任意文件的读取、下载、删除、修改,这类漏洞的成因基本相同,都是因为程序没有对文件和目录的权限进行严格控制,或者说程序没有验证请求的资源文件是否合法导致的。
需关注的函数
对传入的路径未做严格的校验,导致攻击者可以自定义路径,注意操作文件是否存在过滤../和..\等相关操作。使用代码进行测试。
可以看到上述代码未经过过滤直接创建并返回了相应的文件资源,启动环境尝试一下。
可以看到读取成功。
审计方式也是同理主要是查看是否存在过滤相关的。
启动环境测试。
源码地址: https://gitee.com/mingSoft/MCMS/tree/5.2.6/
文件上传注意的点是需要查看代码对于上传是否有限制,如果有限制需要查看存在什么限制,是校验的什么,然后再针对进行绕过测试。那么我们开始审计。
首先看代码层面只校验了路径中是否包含../和..\主要是为了防止路径穿越。
再去看配置文件可以发现,全局存在黑名单校验,不允许上传如图所示的后缀。
从依赖中看到使用了模板引擎freemarker。
在Freemarker如果使用了自定义标签,并且这些标签在模板中被调用时没有正确过滤输入,也可能导致执行系统命令。可以覆盖存在的ftl文件来执行我们想执行的命令。
可以看到图上存在一个五个参数,其中null可以不传入,其他四个分别为上传路径,上传文件的名称,是否使用上传文件夹路径,是否重命名。先确定我们要上传的路径。
尝试覆盖上述的index.ftl,那么构造我们的payload,(<#assign ex="freemarker.template.utility.Execute"?new()>${ex("whoami")}),上传测试。
再去找我们对应的index.ftl文件的页面。
访问http://127.0.0.1:8080/ms/cms/category/index,命令执行成功。
源码地址:https://gitee.com/mjtop/JTopCMSV3/releases/tag/JTopCMSV3.0.2-OP
搭建完系统直接开始审计代码,全局搜索/download相关的信息。
存在两个下载接口,这两个接口均存在任意文件下载读取,我们追踪第二个进行审计。可以看到主要文件参数来源于我们传入的target。
而我们的target来源于上面的params, params的来源于处理完的request请求
查看如何处理的当前请求,跟进ServletUtil.getRequestInfo查看如何处理的。
继续跟进可以看到有一段代码处理了request,Enumeration enumeration = request.getParameterNames();Enumeration是一个枚举类,在这里用来列举出所有的参数名称。你可以通过这个Enumeration来遍历所有的参数名,并且对于每一个参数名,你可以使用request.getParameter(String paramname)方法来获取对应的值。
依次走下来,发现会在红框处处理我们的参数,判断decodeMode是true还是false,而decodeMode来源于上述直接传入的false,因此是走下面SystemSafeCharUtil.decodeDangerChar
跟进查看decodeDangerChar方法,就是将我们传入的**!1**这类型的转成正常的字符。
所以在这里的代码对于用户传入的target只有这上面一层处理,未发现处理../相关的方法,尝试构造../1.txt获取我们编辑的文本内容,根据最开始的路径可以得出,功能点在备份处,下载备份的功能。
下载抓包测试。
发送到repeater进行测试,发现未成功读取,既然当前代码层面不存在防御,那么查看是否存在过滤器。
在web.xml中找到了过滤器,一共存在两个与之对应的过滤器,其中securitySessionDispose 的过滤器是对应的是鉴权过滤器。
直接查看另一个过滤器,这个过滤器内容实在是太多,而且开发的命名也是极其抽象,无法阅读下去,只能搜索我们的../相关的信息,可以看到存在静态代码块包含了危险字符,猜测为过滤的内容。
分析$_6的调用,可以看到被当作参数传入了$_1
跟进_$1,但是由于代码写的很复杂,大概就是匹配了危险字符,并打印出来,与我们的打印台的错误对应上了
打印台报错内容为..
所以在此是被过滤器拦截了,但是,在我们之前审计过程中遇见了一个字符转换的处理功能,那么我们是否可以使用他这种处理方式来读取我们想要读取的内容呢?
构造测试,在上一级目录放置了一个1.txt文本,可以看到顺利读取成功。
查看代码获取到的我们的target参数,第一步初始参数为我们正常的传入的值。
第二部最后获取到的target参数。
审计完毕,读取到我们想要的内容。
SSRF 漏洞出现的场景有很多,如在线翻译、转码服务、图片收藏/下载、信息采集、邮件系统或者从远程服务器请求资源等。通常我们可 以通过浏览器查看源代码查找是否在本地进行了请求,也可以使用 DNSLog 等工具进行测试网页是否被访问。
重点关注 HTTP 请求操作函数。想要支持所有的协议,只能使用 URLConnection、URL
。
需关注的函数
除了建立 HTTP 协议连接,还可能直接通过 Socket 建立连接
在Java中,execute()方法常常在HttpClient ,HttpAsyncClient ,okhttp3
,Hutool库中使用,它负责发送HTTP请求并接收响应。审计execute()调用时,需要检查用于构造请求的参数(如URL)是否经过了充分的验证和过滤。如果execute()的参数可以直接被用户控制,而没有适当的输入验证,这可能表明存在SSRF风险。
在这个案例中,使用了execute执行了http请求,其中传入的URL为用户可控,而代码层面未对用户传入的URL参数进行过滤,导致了漏洞的产生。
在网页上尝试复现该漏洞
可以看到这个代码同样存在相同问题,URL未经过过滤直接传入,直接execute执行。
openConnection()是建立网络请求的第一步,它创建了一个到目标URL的网络连接。在SSRF审计中,审计人员会关注这里是否使用了用户提供的或不受信任的输入来构造URL,因为这可能允许攻击者控制服务器发起的请求。
在这个例子中URL未经过过滤,openConnection() 方法直接执行了HTTP请求,导致了ssrf漏洞的产生。 启动程序查看结果
openStream()是java.net.URL类中的一个方法,用于打开到由URL表示的资源的连接,并返回一个InputStream对象,该对象可以用来读取资源的内容。当调用URL对象的openStream()方法时,实际上会创建一个与指定URL对应的网络连接,并返回一个InputStream,这样你就可以通过这个流读取URL所引用的资源。这个过程相当于调用了URL对象的openConnection()方法,然后从返回的URLConnection对象中获取输入流。
在这个案例中就是使用了openstream方法直接访问了未经过滤的URL导致了ssrf漏洞的产生。
Socket类提供了基本的TCP/IP网络通信功能,允许程序创建客户端Socket连接到远程服务器,或者作为服务器接收来自客户端的连接。在审计代码中,Socket相关的方法调用通常会揭示应用程序如何与远程服务进行交互,包括发起HTTP请求、DNS查询等。
这个案例就是通过socket请求的相关资源,在Java中,使用Socket类来直接请求URL并不常见,一般会使用上述案例中的HttpURLConnection,HttpClient。我们启动一下代码查看结果
在Java中,ImageIO.read()方法经常被用于从远程URL加载图像资源,如果目标使用了 ImageIO.read 读取图片,且读取的图片地址可控的话,可能会存在SSRF漏洞。
上述案例中通过ImageIO.read()直接加载了用户传入的url,未经过过滤的情况下会造成ssrf漏洞。运行代码尝试构造ssrf攻击。
可看到直接读取到了图片。
Jsoup.connect()方法是一个重要的审查点。Jsoup是一个用于处理实际世界HTML的强大Java库,它提供了丰富的功能来解析、提取和操作HTML文档。Jsoup.connect()方法主要用于发起HTTP请求,获取网页内容,这使得它成为潜在SSRF攻击的入口。
这个案例中,代码里使用了jsoup.connect()方法发起http请求,而url未过滤直接传入进jsoup.connect()方法,导致了ssrf漏洞的产生。启动代码尝试复现
getForObject是RestTemplate类中的一个方法,用于发送一个GET请求到指定的URL,并将响应体转换为responseType指定的类型。它简化了HTTP请求的处理,避免了手动解析响应体,如果服务器端的代码使用getForObject()来处理用户提供的URL,而没有适当的验证和过滤,就可能成为SSRF攻击的入口。
启动程序访问http://127.0.0.1:8080/ssrf/getForObject?url=https://www.baidu.com
源代码地址https://gitee.com/getrebuild/rebuild,我用的版本是3.5.4
搭建好源代码审计,全局搜搜execute(),可以看到存在两个文件使用了这个函数
先看后面openApiSDK文件
其中是httpGet方法中使用了okHttpClient.execute()加载了url,往上追查看谁调用了httpGet方法
访问此方法查看传入进来的apiUrl是否受用户控制
可以看到baseurl为直接写在代码里,不受用户控制,所以不存在ssrf漏洞。
我们回到上面execute搜索到的文件,我们去OkHttpUtils查看代码情况
我们查看代码可看见,存在传入参数url未处理直接执行
我们往上追查看有哪些调用了这个get方法
访问当前类,查看传入的参数是否受用户控制。
可以看到路径为read-raw,用户传入的参数名字为url,未经过滤直接进入OkHttpUtils中的get方法直接进行请求,造成了ssrf漏洞。
启动环境,根据代码构造参数,可以看到需要用get方法传入三个参数,分别是url,charset,cut,传入我们的dnslog平台地址,查看结果,成功复现该漏洞。
其中SSRF漏洞不止这一处,可以自己尝试审计一下。
URL跳转分为301永久重定向和302临时重定向。
301 永久重定向:客户端(如浏览器)会记住这个重定向,并在后续请求中直接使用新的 URL。
302 临时重定向:客户端通常不会记住这个重定向,每次请求都会重新解析重定向
其中302重定向一般使用的是sendRedirect,而301重定向使用的是setHeader。在审计时主要关注这两个函数。
测试代码如下,传入的URL未经过任何过滤直接使用
启动环境尝试跳转。
修改代码为。
启动环境进行测试
源码地址: https://gitee.com/getrebuild/rebuild/tree/3.5.4/
在审计时全局搜索相关危险函数,可以看到,传入了filePath,根据代码的知,filePath来源于分割/filex/img/后所留下来的地址。
那么根据代码构造URL,http://localhost:18080/filex/img/https://ww.baidu.com即可跳转至baidu
当应用程序使用XML处理器解析外部XML实体时,可能会发生XXE漏洞。外部XML实体是指定义在XML文档外部的实体,它可以引用外部文件或资源。如果XML处理器没有正确配置,它可能会解析这些外部实体,并将外部文件或资源的内容包含到XML文档中。XML解析一般在导入配置、数据传输接口等场景可能会用到,涉及到XML文件处理的场景可查看XML解析器是否禁用外部实体,从而判断是否存在XXE。想要学习 XXE 漏洞代码审计,一定要先熟悉 XML 解析API。常见的XML解析有以下几种方式:1、DOM解析;2、SAX解析;3、JDOM解析;4、DOM4J解析;5、Digester解析。其中DOM和SAX解析为原生自带的,另外三个需要引入第三方库依赖。
需关注的函数
DOM的全称是Document Object Model,也即文档对象模型。DOM 解析是将一个 XML 文档转换成一个 DOM 树,并将 DOM 树放在内存中。使步骤大致为用DOM解析器加载XML文档,并构建DOM树。例如,可以使用 DocumentBuilderFactory 和 DocumentBuilder 来创建 Document 对象。解析器逐行读取XML文档,并构建一个内存中的树状结构,其中每个节点都代表文档中的一个元素、属性或文本片段。一旦DOM树构建完成,就可以通过DOM API来访问和操作树中的节点。
我们以代码来示例,这里展示的就是用户所输入的数据未经过过滤就直接通过DocumentBuilder解析输入源,最后导致xxe漏洞的产生。
启动环境,构造payload尝试一下,我在D盘下放了一个1.txt文档,尝试通过xxe文件读取文件里的内容。
读取成功。
当使用SAX解析器解析XML文档时,解析器会顺序读取文档,并在遇到特定的事件(如开始元素、结束元素、字符数据等)时调用用户实现的事件处理程序。这些事件处理程序通常定义在一个回调接口中,解析器会根据文档中的内容触发相应的回调方法。
与dom解析一样,只要直接通过调用parse()解析xml就可以触发xxe漏洞,但是这里有一个不同的就是一般来说,sax解析基本上都是无回显xxe。
因为是无回显xxe,我们尝试直接解析dnslog进行探测是否存在xxe漏洞。
虽然回显错误,但是已经执行了相关信息
在 JDOM2 中,SAXBuilder 类继承自 org.jdom2.input.Builder 接口,用于通过 SAX 方式构建 XML 文档。JDOM2 提供了一个更为现代的 API,并且支持更多的特性。所以造成漏洞得代码部分一般都为,直接使用
SAXBuilder 默认情况下可能允许外部实体的加载。如果攻击者提供的XML数据包含指向恶意或内部网络位置的外部实体声明,那么这可能会导致敏感数据泄露或其他问题。复现过程与上述得sax解析一样。 防御手段均为禁用外部实体处理。
DOM4J 是一个用于处理 XML 的 Java 库,它支持多种 XML 解析模式,如 DOM 和 SAX。DOM4J 在默认情况下可能易受 XXE(XML External Entity)攻击,这是因为默认配置下的解析器可能会解析 XML 中的外部实体,从而导致潜在的安全问题。DOM4J得解析流程为创建SAXReader的对象reader,通过reader对象的read()方法加载xml文件。
没有对SAXReader进行任何额外的配置来禁用外部实体的加载,那么这段代码可能会导致XXE(XML External Entity)攻击的风险。
Apache Commons Digester 是一个用于解析 XML 文档的 Java 库,它简化了基于模式的 XML 解析过程。Digester 采用事件驱动的方式处理 XML 输入,类似于 SAX 解析器,但提供了更为简洁的 API 来映射事件到 Java 方法调用。
parse方法是Digester的一个关键方法,它负责实际的解析工作。当你调用parse方法时,你需要传递一个Reader对象作为输入,这个对象可以是任何实现了java.io.Reader接口的对象,用于读取XML数据。
StAX提供了一种流式的XML解析方式,适合处理大文件和高并发环境。javax.xml.stream.XMLInputFactory 和 javax.xml.stream.XMLStreamReader是其常用api,
XMLInputFactory 是Java StAX(Streaming API for XML)的核心组件之一,用于创建解析XML文档所需的解析器对象。在StAX中,XMLInputFactory 扮演了工厂的角色,负责创建具体的解析器实例。XMLStreamReader:用于读取XML文档。这段代码存在的主要问题是,默认创建的 XMLInputFactory 实例没有明确禁用对外部实体(External Entities)的支持。如果传入得数据包含恶意构造的外部实体引用,那么解析器可能会尝试解析这些实体,从而导致XXE攻击。
源码地址: https://github.com/javamelody/javamelody/releases/tag/javamelody-core-1.73.1
搭建系统的话只需要创建 SpringBoot 项目,在 pom.xml 引入 JavaMelody 依赖后,启动项目即可。
查看漏洞修复修改了什么,可以看到添加了两处代码,分别是禁用了解析器处理DTD和外部实体。
确认代码位置为javamelody-core/src/main/java/net/bull/javamelody/PayloadNameRequestWrapper.java追踪代码查看,可以看到存在上述StAx解析,没有禁用解析器处理DTD和外部实体,查看调用路径。
查看谁调用了parseSoapMethodName,可以看到代码中,校验了Content-Type 头部是否以 "application/soap+xml" 开头或者"text/xml"开头,并且请求头中包含 "SOAPAction" 字段才走下面得调用代码。
继续跟进谁调用了initialize(),跟进monitoringFilter,查看了其他得调用,均是用于验证 PayloadNameRequestWrapper 类对请求得解析能力,传入的数据为固定的,用户无法操控,所以直接跟进monitoringFilter。
实例化PayloadNameRequestWrapper进行处理实例化的wrappedrequest,而wrappedrequest来源于上面代码创建的一个包装后的 HttpServletRequest 对象,继续跟进谁调用了createRequestWrapper并传入了request和response。
可以看到最终的调用来源于dofilter,而dofilter代表了这是一个过滤器的核心部分。
继续查看调用,可以看到先走了另一个过滤器再进入了这个dofilter。
至此,利用链差不多梳理完整了,就需要看过滤器注册的时候对url做了过滤,直接回到过滤器最上层,查看调用,存在三个web.xml,跟进每一个查看。
跟进后发现一个是test代码里的,一个是demo里的,只有web-fragment.xml是属于javamelody-core中的,查看注册的url路径有哪些,可以看到是/*,说明所有路径均存在xxe。
梳理完毕后搭建环境直接测试。
抓包构造payload,需要注意的点是上述审计的时候有校验请求中的header和Content-Type: text/xml,所以发包的时候记得加上,首先我们先启动一个http服务,用python启动,python -m http.server 8001,然后构建payload测试
%remote;
]>
root>
查看python服务是否收到相应的请求
至此漏洞复现结束。
首先xss着重查看用户输入输出得地方,因为在输入输出的时候未经过过滤就可能会产生xss问题。找到一条完整的利用链之后,就是结合现有的安全措施(输出编码、过滤器等)进行判断,例如是否存在绕过的可能,或者是没有任何安全防护可直接造成攻击。(个人感觉xss从代码层面不是特别容易审计,主要还是看是否有全局过滤器)
需关注的函数
request.getParamter
<%=
param
${
<c:out
<c:if
<c:forEach
ModelAndView
ModeMap
Model
request.setAttribute
response.getWrite().print(
response.getWrite().writer(
XssFilter
org.springframework.web.util.HtmlUtils
org.apache.commons.lang3.StringEscapeUtils
ESAPI.encoder().encodeForHTML
用户传入的数据在未经过滤的情况下直接返回给前端,就会造成反射型xss(数据未进入数据库)
启动环境测试。
环境问题,自己写demo无法使用,所以只展示代码了,将传入的message和我们自己写的危险message都使用model .addAttribute传递给view层。
而下面一共两种写法,当使用th:text得时候,thymeleaf会把所有字符都转义,而使用th:utext则会显示实际的html内容,如果存在恶意代码就会被浏览器执行。
request.setAttribute 这个共享到request域的方法,如果用户输入的数据直接通过request.setAttribute()存储然后在JSP页面中未经检查地输出,那么就可能存在xss漏洞,主要检查输入输出的时候是否进行了过滤。
下面就用漏洞代码示例展示,其中这段代码展示了从前端传入了一个comment参数,再用request.setAttribute共享到request域。
查看前端代码,<%= %> 是用来嵌入Java表达式的标签。当JSP页面被请求时,位于 <%= %> 标签内的Java代码会被执行,并且其结果将被转换为字符串并发送到客户端的浏览器。然后我们使用request.getAttribute将我们存储到request域的数据取出,如果输入的是恶意代码,那么将会被直接执行。
当视图解释器解析是ModelAndVIew,其中model本生就是一个Map的实现类的子类。视图解析器将model中的每个元素都通过request.setAttribute(name, value);添加request请求域中。这样就可以在JSP页面中通过EL表达式来获取对应的值。其实就是进行了一个简单的封装,方便于我们使用。
在这个示例中,用户输入的评论未经过过滤直接被添加到了ModelAndView的对象中,并将在名为examplePage的视图中展示。
跟进相应的输出页面分别用了request.getAttribute和
源码地址: https://gitee.com/mingSoft/MCMS/tree/5.2.6/
在html代码中看到存在{field.descrip}是一个变量表达式用于获取指定对象的属性值并在模板中显示该值,如果未经过处理则有可能存在xss漏洞,html为前端search.html,针对去找数据来源。
找对应的search接口,看到我们的请求字段field,跟进字段处理情况
跟进filed,可以看到将field传入给searchMap,跟进searchMap
将seachmap添加进params,跟进params
可以看到直接传入进了ParserUtil.rendering,继续跟进
可以看到传入的params接收参数未map,跟进map可以看到计入数据库查询了相关信息
然后最后走了模板渲染。
查看存储过程是否存在过滤,发现直接存储进数据库,未进行处理,那么存在xss问题。
查找全局过滤器,可以看到是存在过滤器的,但是只对search.do生效,可以看见设置了一个xssfilter,添加了生效的路径是/*
过滤器要不走xss过滤则需要(!this.handleIncludeURL(req, resp))为true,那么说明this.handleIncludeURL(req, resp)返回的信息需要为false才能进入下一个过滤器。而上面代码中includes添加了.*/search.do
可以看到就是确认请求的路径是否是includes里面的,如果是返回true,否则返回false,所以这个过滤器存在问题,只对search.do进行了过滤。
我们请求来源于update.do所以绕过了该过滤器,尝试复现
保存后返回search.do页面查看是什么情况,直接弹窗,复现成功。
★
欢 迎 加 入 星 球 !
代码审计+免杀+渗透学习资源+各种资料文档+各种工具+付费会员
进成员内部群
星球的最近主题和星球内部工具一些展示
加入安全交流群
信 安 考 证
CISP、PTE、PTS、DSG、IRE、IRS、 NISP、 PMP、CCSK、CISSP、ISO27001... |
推 荐 阅 读
|
腾讯时尚 · 【入流】你看不到武则天的真容 7 年前 |
|
亿邦动力 · 找电商工作,看他就行 7 年前 |
|
成方三十二 · 今日入伏丨不历尘埃三伏热,孰知风露九秋凉? 7 年前 |
|
数据宝 · 这两大板块联袂大涨,牛市的前奏? 7 年前 |
|
药学进展 · 巨噬细胞自噬与动脉粥样硬化的关系及相关药物的研究进展(独家原创) 7 年前 |