某OA系统的审计(新手学习)

一次偶然机会,找人要了套源码,是Stuts2和spring框架混用的系统,比较简单适合入门学习

某OA系统的审计(新手学习)

一次偶然机会,找人要了套源码,是Stuts2和spring框架混用的系统,比较简单适合入门学习

0x01 SQL注入

该项目是基于mvc框架开发的,与数据库交互的代码在DAO目录下,在core/base/dao中存在IBaseDao类属于该项目的数据库操作的基类

image-20230907155510687

根据其定义的函数名,都能看出是传入SQL语句进行查询,我们找的他的实现类

image-20230907155538251

可以看到其方法实现也是传入SQL语句字符串直接它通过hibernateTemplate API直接进行查询。其实现SQL查询的方法也有很多。image-20230907155622721

同时我们发现该baseDao类有七百多个继承者

image-20230907151141401

跟进去发现基本都继承了BaseDao的方法,并没有进行重载,于是我们全局搜索相关危险方法,这里以sqlFind方法为例

image-20230907160033608

发现在构造SQL语句是用了?占位符,这样的语法是无法进行注入的,于是我们寻找直接拼接参数到SQL语句字符串中的方法

image-20230907161017336

根据sqlFind方法的重装情况,当参数param为空则不会进行占位符参数绑定,于是我们可以缩小搜索范围

向下翻找就找到在BaseDataService类中listBaseDataByFlowId方法,存在直接拼接参数构造SQL语句的情况,

image-20230907161616783

这里直接将flowId等参数拼接到语句中调用sqlFind方法进行数据库查询,我们向上找是哪个controller调用该危险方法

最终定位在BaseDataController中的listBaseDataByFlowId方法中

image-20230907161958425

这里直接将请求中type和flowId传入service层进行查询,我们构造该接口的请求

image-20230907162240488

提示我们没有登录,应该是spring进行了拦截处理

0x02 权限绕过

我们查看spring配置文件,找的拦截器的实现类

image-20230907162648937

可以看到该项目有个全局拦截器PermissionInterceptor,我们跟进到里面,根据spring拦截器实现原理直接来到prehandle方法,主要处理逻辑如下

image-20230907163033544

该拦截器定义了两个成员数组UN_LOGIN_EXPOSE_CONTROLLERSUN_LOGIN_EXPOSE_SERVLET_PATH,当我们请求的控制器或者接口url在这两个数组中是会放行我们的请求,也就是定义了白名单请求,很遗憾我们的listBaseDataByFlowId方法的请求不在白名单中,会进入if判断。

首先判断session中是否有登录凭证(显然没有),如果没有会获取请求中jwt参数的值进行JSON Web Token的认证,检查token的有效性。

看到jwt 认证,很快就想到会不会硬编码jwt 密钥,因为在初期翻配置文件中没看到有jwt 密钥的定义

于是我们跟进到jwtCheck方法,果然验证了我们的猜想

image-20230907164822180

于是我们根据密钥生成token

image-20230907164928048

带着jwt token访问目标接口,发现不在提示未登录

image-20230907165131183

根据配置文件该系统是属于了sql server数据库,于是构造语句

flowId = 1'+(case when (1=1) then '' else char(1/0) end)+'

当为正是,正常返回

image-20230907173029086

为假是进行char(1/0)构造报错会返回500

image-20230907173101190

证明存在SQL注入

0x03 总结

1、mvc框架中service层一般是负责sql语句的封装,dao层一般负责与数据库交互,所以我们可以在dao层中找直接执行sql语句的方法,在service层调用可疑查询方法处查看sql语句构造情况,找的可能存在sql注入的点,向上找的controller层的具体调用接口

2、主要还是看鉴权,通过配置文件找的鉴权类,如使用了request.getRequestUri等危险方法来获取请求url做判断时很容易就被bypass,或是看jwt等第三方鉴权组件硬编码的问题

0x04 另外发现

1、未授权文件上传

在鉴权文件中存在白名单controller类和接口数组UN_LOGIN_EXPOSE_CONTROLLERSUN_LOGIN_EXPOSE_SERVLET_PATH,我们关注一下

image-20230913181059687

翻找未授权类时在appCallController中,有文件操作的方法

@RequestMapping({"uploadFile"})
    @ResponseBody
    @Transactional(
        readOnly = false
    )
    public String uploadFile(@RequestParam(value = "file",required = false) MultipartFile file) {
        try {
            String id = UUIDUtils.getuid();
            String extension = StringUtils.getFilenameExtension(file.getOriginalFilename());
            if (StringUtils.hasText(extension)) {
                extension = "." + extension;
            }

            String fileUrl = "../xxx_Ueditor_File/app/files/" + id + extension;
            String savePath = getCurRequest().getSession().getServletContext().getRealPath(fileUrl);
            File saveFile = new File(savePath);
            if (!saveFile.exists()) {
                saveFile.mkdirs();
            }

            file.transferTo(saveFile);
            APPFiles af = new APPFiles();
            af.setFileLength(file.getSize());
            af.setId(id);
            af.setFilePath(saveFile.getAbsolutePath());
            af.setFileName(file.getOriginalFilename());
            af.setTime(new Date());
            af.setFileType(file.getContentType());
            af.setMsgId("");
            af.setType(2);
            this.dao.save(af);
            return id;
        } catch (Exception var8) {
            var8.printStackTrace();
            return "";
        }
    }

该方法是获取随机数id,然后取到表单上传中的文件名后缀名,然后将其保存在/xxx_Ueditor_File/app/files/目录中,最后讲随机数id返回给前端,因为未对文件后缀名做限制导致可以上传jsp文件

构造请求上传

image-20230914170411814

image-20230914170500004

成功上传文件

2、未授权SQL注入

该项目除了用到spring框架外还用到了st2框架,在struts.xml查看配置信息,这里也定义了拦截器

image-20230914171023854

具体实现逻辑在doIntercept()方法中

image-20230914171257111

可以看到这里也是定义白名单方法名和类名

private Set<String> UN_LOGIN_EXPOSE_METHODS = new HashSet<String>() {
        {
            this.add("saveStaffInfo");
            this.add("updateStaffInfo");
            this.add("listRegionTree");
            this.add("CostCloudProject");
            ...
            this.add("handle");
            this.add("listProjAuditQGC");
            this.add("getlistProjAuditDataQGC");
        }
    };
    private static List<Class<?>> TEMP_USER_EXPOSE_ACTIONS = new ArrayList<Class<?>>() {
        {
            this.add(IndexAction.class);
            ...
        }
    };
    private static List<String> TEMP_USER_EXPOSE_METHODS = new ArrayList<String>() {
        {
            this.add("showPhoto");
            this.add("download");
        }
    };

看到白名单方法中有些get操作的函数,如getlistProjAuditDataQGC看名称是获取list数据,跟进其中

image-20230914172238302

可以看到调用findByBusinessByQueryQGC方法进行查询,来到findByBusinessByQueryQGC方法实现,可以看到其是获取请求中的特定参数,并将其直接拼接在sql变量中

image-20230914172639900

最后调用sqlFindPage拼接sql变量执行

image-20230914172738295

image-20230914172926441

此处也是存在SQL注入

image-20230914173102686

  • 发表于 2023-09-26 09:00:02
  • 阅读 ( 21443 )
  • 分类:漏洞分析

2 条评论

qrling
佬,源码是开源的么,可以分享下吗
请先 登录 后评论
ybdt
想知道是啥OA :)
请先 登录 后评论
请先 登录 后评论
中铁13层打工人
中铁13层打工人

79 篇文章

站长统计