oasys是一个OA办公自动化系统,使用Maven进行项目管理,基于springboot框架开发的项目,mysql底层数据库,前端采用freemarker模板引擎,Bootstrap作为前端UI框架,集成了jpa、mybatis等框架。
源码可以访问链接下载:https://gitee.com/aaluoxiang/oa_system?_from=gitee_search
环境搭建没有什么特别的,数据库创建oasys的数据库后导入oasys.sql,然后在application.properties文件中配置数据库地址就可以了。
首先拿到代码后看一下依赖,发现项目中使用了mybatis,然后在映射文件看一下有没有用${}的:可以看到,下面的接口使用了${}字符串拼接。
接下来在控制器中搜索一下看哪个控制器使用了该接口,最终在通知列表处找到一次使用:
通过构造paylaod,成功实现sql注入。
这个注入和上面一样,同样因为使用${}进行字符串拼接。
映射文件如下:
控制器代码如下:
构造请求如下:
漏洞位于部门管理->添加部门处:
可以看到没有进行任何过滤直接调用deptdao.save将数据存入数据库。
用户管理->编辑用户处存在存储XSS。
可以看到同样没有任何过滤就直接存入数据库,说明XSS漏洞在全站存在多处。
漏洞位于流程管理->我的申请->查看流程处:
可以看到URL中有个id参数,猜测该参数为流程id标识,看一下后端代码:
这里只放上部分关键代码,可以看到首先获取了当前用户对象,然后根据URL中的ID参数获取了流程对象,并且对比userID判断是申请人还是审批人。可以看出并没有对其他用户越权访问进行处理。
使用另外一个低权限账号登录系统,首先查看流程列表:
可以看到没有任何流程,接下来使用该用户cookie来调用查看流程的接口来枚举流程:
使用另外一个低权限账号登录系统,首先查看流程列表:
可以看到没有任何流程,接下来使用该用户cookie来调用查看流程的接口来枚举流程:
可以看到枚举出很多流程,证明越权确实存在。
漏洞文件位于:src/main/java/cn/gson/oasys/controller/user/UserpanelController.java
src/main/java/cn/gson/oasys/controller/process/ProcedureController.java
两个文件都存在该漏洞,原理一致,这里拿第一个来说明。
在该控制器的image方法,存在任意文件读取漏洞,代码如下:
可以看到代码中首先通过getRequestURI方法当前访问的相对路径,然后将该路径中的iamge替换为空。接下来与rootpath拼接然后通过File打开文件后返回前端。
通过替换”/image”的操作,我们可以构造…/来造成目录穿越从而进行任意文件读取,BP请求如下:通过替换”/image”的操作,我们可以构造…/来造成目录穿越从而进行任意文件读取,BP请求如下:
src/main/java/cn/gson/oasys/controller/inform/InformController.java 68行
有一个informlistpaging方法
在该方法中定义了一个字符串请求参数basekey,用list集合接收了sortMyNotice方法处理basekey等参数的结果,跟进查看哪里定义了nm
跟进NoticeMapper
转到mapper层
在此数据持久层搜索调用到的selectid
在like后面直接用${%%}进行模糊查询,导致了漏洞的产生
根据controller的路由构造url:
sqlmap.py -r D:\test.txt
sqlmap.py -r D:\test.txt --random-agent --dbs --current-db
创建一个普通职工账号 test test
大致浏览pom.xml代码时发现了mybatis,mybatis中存在${}SQL注入,全局搜索${},发现在/src/main/resources/mappers/address-mapper.xml和/src/main/resources/mappers/notice-mapper.xml中存在${}
接下来就去看那个地方对该参数进行了调用,往上跟踪cn.gson.oasys.mappers.NoticeMapper接口——>sortMyNotice追踪到/src/main/java/cn/gson/oasys/controller/inform/InformController.java的如下代码中
可以看到baseKey是可控的,直接从前端传入,下面给的有url,然后直接访问就行 http://localhost:8089/informlistpaging?baseKey=1
报错注入
通过构造payload可以使用报错注入的方式在控制台处得到返回的报错信息
Payload
http://localhost:8089/informlistpaging?baseKey=1' and (select extractvalue(“0x7e”,concat(‘~’,(select user())))a) and ‘1’=’1
同样可以构造时间盲注的payload
http://localhost:8089/informlistpaging?baseKey=1' and (select * from(select(sleep(2)))a) and ‘1’=’1%23
在测试功能点时发现在写笔记时将标题名可直接写为XSS恶意代码
代码中并未对该处输入坐任何限制,在存储时也是直接将得到的Title存储.
在resources\mappers中,发现了mapper文件,在第16行用了$拼接,那么会造成SQL注入,但前提是看一下pinyin这个参数是不是可控。
那就一步步往上跟:
cn.gson.oasys.mappers.AddressMapper接口------>allDirector方法直接全局搜索,定位到Controller中
进入到AddrController中outAddress方法,可以确定在Mapper中pinyin这个参数是可以被利用的。既然是RequestMapping,不用再去找页面中摸索功能点了。那么直接进行get方式访问。
直接一手sqlmap跑出来。
访问上述功能,点击新建文件夹,输入文件夹名为XSS验证语句<img src=1 onerror=alert(1) />,如下图所示:
成功实现弹框。
对于CSRF漏洞基础概念不熟悉的小伙伴,可回顾学习WEB安全原创入门学习课程下的第 2.2 课 --- CSRF漏洞基础入门。
学习链接:https://t.zsxq.com/EieMBuj
本系统也存在多处CSRF漏洞,举例说明。
①、访问用户面板功能,点击写便签,输入任意内容。
②、打开BurpSuite,并打开浏览器代理,指向BurpSuite。BurpSuite打开Intercept is on进行拦截。
③、然后点击保存便签,BurpSuite拦截输入包如下图所示:、
获取到输入的内容之后,没有做任何限制。
这是 Java Web 应用程序中用于保存“便签纸”对象的方法。它似乎正在使用 Spring MVC,因为它具有基于注释的请求映射和会话属性。
该方法接受三个参数:
● Notepaper npaper:这是一个代表便条纸的对象。它具有各种字段,例如标题、内容和用户 ID。
● @SessionAttribute("userId") Long userId:这是一个保存用户 ID 的会话属性。它带有 注释@SessionAttribute,这意味着它将由 Spring MVC 框架从用户的会话中自动检索。
● @RequestParam(value="concent",required=false)String concent:这是一个名为“concent”的请求参数。它被注释为@RequestParam,这意味着它将由 Spring MVC 框架从请求中自动检索。该required=false属性表明该参数是可选的。
该方法执行以下操作:
它从会话属性中检索与用户 ID 关联的用户对象。
它将便条对象的创建时间设置为当前日期。
它将 notepaper 对象的用户 ID 设置为在步骤 1 中检索到的用户对象。
如果它为 null 或为空,它将便条对象的标题设置为“无标题”(中文意思是“无标题”)。
如果“concent”请求参数为 null 或为空,它将 notepaper 对象的内容设置为该值。
它使用对象保存便条纸对象ndao,该对象可能是某种数据访问对象。
它向客户端返回“重定向”响应,将客户端定向到“/userpanel”URL。这将导致客户端浏览器向“/userpanel”URL 发出新请求。
从渗透测试黑盒角度测试,我们发现了日程管理处存在越权删除漏洞。现在我们从代码角度看看问题出在了哪里。
通过日程删URL中的dayremove,可以定位到日程删除位于src\main\java\cn\gson\oasys\controller\daymanage\DaymanageController.java,第204-211行,如下图所示:
单从上面几行代码来看,仅是查询rcid参数后,进行了删除。我们进一步追下流程。
①、从上面可以看到,使用了daydao.findOne(rcid),来操作rcid,查看daydao发现来自DaymanageDao,如下图所示:
②、进入DaymanageDao,发现并没有findOne,发现DayManageDao继承了JpaRepository,而进入JpaRepository之后也未发现findOne,但发现它继承了QueryByExampleExecutor,而findOne就在这里面,如下图所示:此时大家应该发现JpaRepository和QueryByExampleExecutor代码属于引入依赖的代码了,这两个代码来源自Spring Data,其中findOne是其中一个方法,具体意思如下
就是说:
● findOne返回一个Optional对象,可以实现动态查询;而Optional代表一个可能存在也可能不存在的值。
此时总结上面流程,简单说,日程删除功能先使用findOne去查询rcid是否存在该值。
然后再使用daydao.delete(rc)进行删除,此时我们追踪一下这个方法,发现这个delete方法也就是Spring Data CrudRepository中的delete(删除)方法,如下图所示:
最后,我们回顾整个流程,也就是说查询了rcid的值,然后进行删除。整个流程没有任何权限的验证,因此导致了越权的操作。
REF:
http://myblog.ac.cn/archives/2022-08-10-10-53-26