华夏erp代码审计

华夏erp代码审计

因为之前没咋接触过java,但是java又十分的重要,因此最近一段时间从se开始学到spring框架,记录一下最近自己进行的一次spring-boot框架开发的代码审计,本次使用的是华夏erp3-3版本。

前言-环境搭建

华夏erp是springboot+vue的前后端分离,所以我们需要配置nginx的代理转发流量
后端配置
先启动springboot,这里的jdk选择1.8版本。记得开mysql,redis和nginx,我这里使用的是小皮面板。
Pasted image 20250427003657
mysql数据库配置
导入数据库文件
Pasted image 20250427004942
Pasted image 20250427005025

启动后端的jshERP-boot文件

Pasted image 20250427004648
当后端出现最后一行时代表运行成功,接下来配置前端。
前端配置
先使用小皮面板搭建一个网站,我这里的域名是huaxia.com。
配置流量转发:

在文件的后面加上如下代码。
含义:
使得所有对/jshERP-boot/路径发起的请求全部转发至localhost:9999端口

location /jshERP-boot/ {
                proxy_set_header Host $host;
                proxy_set_header X-Real-IP $remote_addr;
                proxy_set_header REMOTE-HOST $remote_addr;
                proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
                proxy_pass http://localhost:9999/jshERP-boot/;
        }

整体的数据包如下所示
Pasted image 20250427005415
当出现验证码的时候代表搭建好了
Pasted image 20250427005649
但是刷新的时候会出现404的报错,在nginx的配置文件中添加如下这行,如果刷新的话就会重新再次访问url
try_files $uri $uri/ /index.html;
Pasted image 20250430163901

未授权访问漏洞

在代码审计时我首先关注了用户的登录,有如下代码
Pasted image 20250425184431
这个代码乍看之下好像没有大问题:
1、获取请求的路径,判断是否进行了登录
2、如果登录的url包含/doc.html,/user/login,/user/register等登录界面则不做拦截
3、如果请求的资源是前台资源则不进行拦截
但是这里使用了getRequestURI()函数,这个函数会接收访问请求的真实路径(不进行处理),这样的话我们就可以进行bypass,只需要让请求的路径中包含/doc.html然后再使用../返回上级目录,这样就可以通过包含的if判断。具体poc如下:

GET /jshERP-boot/doc.html/../user/getAllList HTTP/1.1
Host: huaxia.com
Accept: application/json, text/plain, */*
X-Access-Token: 8d17f85efb174a5889206842b99bd27f_132
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/125.0.6422.112 Safari/537.36
Referer: http://huaxia.com/user/login
Accept-Encoding: gzip, deflate, br
Accept-Language: zh-CN,zh;q=0.9
Cookie: Hm_lvt_1cd9bcbaae133f03a6eb19da6579aaba=1745132305,1745384703
Connection: keep-alive

Pasted image 20250425185604
审计过程如下:
Pasted image 20250425190222
可以看到if判断为ture,能够chain.doFilter,未授权漏洞get!

注意:
在未授权访问的时候需要去直接访问后端的接口,不能通过前端的web直接访问。因为nginx服务器会将../数据自动删除,如果直接访问传到后端的路径如下

Pasted image 20250424175902

任意密码重置漏洞

Pasted image 20250425192635
先看眼重置密码处的代码,嗯?只判断了id?那不是随便重置!再跟进一下resetPwd函数。
Pasted image 20250425192720
创建一个对象,获取对象的id,获取name,判断如果是要更改admin用户的密码不允许,如果不是admin则直接进行更改。没难度啊没难度,感觉已经直接拿下了,但是当我测试的时候却出现了问题。

随便注册了两个用户就是开干,当我改变id参数的时候给我报错了,显示未知状态异常。

Pasted image 20250425193101
这是为啥?看了一下控制台的报错信息,显示因为数据库中没有id=133,而且tenant=132的值,所以报错:
Pasted image 20250425193858
这个tenant的值是哪来的?跟进一下
Pasted image 20250425193645
发现再获得userid之前有个insertlog函数跟进一下
Pasted image 20250425221113
发现有一个getUserId的函数,看看是否和tenant值有关,再次跟进一下
Pasted image 20250425221435
发现从request中获取了userid的参数,且userid的参数是133,但是我们传的数据包中写的id为132,说明这个useridobj不是从id中获取。
Pasted image 20250425221702
在华夏erp中的cookie参数是X-Acess-Token,而这里面很明显有个133,猜测这个133可能就是tenant的值。但是还没有明显的显示出来,所以我们继续跟进。
Pasted image 20250425222312
Pasted image 20250425222124
这次可以很显然的发现了tenantid是怎么出来的了,就是通过_的分割,所以后面的tenantid就显然易见,就是133了。那我们怎么绕过呢?我首先想到的是,修改X-Acess-Token后面的_变成132,这样sql查询就正常了。但是sad,显示loginout
Pasted image 20250425223316
跟进了一下发现会在数据库中查询是否有这个X-Access-Token,难道要没了嘛!感觉没啥洞,但是看了一些大佬,能复现,为啥啊?emm还是根据大佬的数据包,尝试了一下。
Pasted image 20250425223528
哎,为什么大佬的可以?是没有X-Access-Token的问题嘛?还得继续跟进一下才行
Pasted image 20250425223726
Pasted image 20250425223827
发现X-Access-Token给的是null
Pasted image 20250425224020
哎这不是正好了,这个if判断如果X-Access-Token为null刚好绕过了提取tenant的判断,这样就没有tenantid了。
Pasted image 20250425224054
下面的判断中因为X-Access-Token为null无法提取tenantid,反而绕过了。嗯~真不错
Pasted image 20250425224139
这个漏洞个人感觉还是藏的挺深的,也算是跟进分析了一下。

暴力破解验证

Pasted image 20250427010518
发现传输的数据包没有判断验证码,同时进行了多次发送数据包尝试没有封ip的措施,可以进行暴力破解

代码执行漏洞

在springboot框架中没有jsp,所以普通的文件上传无法利用,但是在插件部分有安装插件,从而导致可以上传一个插件,然后代码执行。具体代码如下图,会先判断X-Access-Token和loginame,只能使用admin才能上传插件
Pasted image 20250429204932
因为不知道传什么文件,所以我先上传了一个zip文件,开始跟进。
Pasted image 20250429211000
会先判断是否是jar包或者zip文件,如果不是则返回插件不合法。如果是zip或者jar包则获取filename,并且创建临时的temp路径。
Pasted image 20250429211319
发现有个isApplicable函数,判断显示错误,然后给我们返回了plugin illegal,进入这个函数查看一下。
Pasted image 20250429211551
发现会判断是否为jar文件,所以我们应该传入一个jar文件。但是我们改传入什么jar文件才能够解析呢?在跟进的时候会发现进入了DefaultPluginOperator和PluginLegalVerify两个class文件中,我们可以去找一下这个路径,发现是在springboot-framework-2.2.1路径下,再通过刚才判断是否为jar包的函数中发现是pf4j-3.1.0版本的插件,因此可以判断这个插件应该需要用pf4j的格式,因此我们需要去找一个pf4j的插件进行上传。
Pasted image 20250429212109
Pasted image 20250429212528
去pf4j的官网下载一个demo进行尝试[[https://github.com/pf4j/pf4j/releases/tag/release-3.1.0]]
Pasted image 20250429213029
此时可以通过刚才的if判断,并且进行了插件合法化的判断,给了一个temp的插件路径。
Pasted image 20250429213315
继续跟进,发现存在一个copy函数,会将存放在temp路径下的插件复制一份传给plugins文件夹下,然后删除temp路径。
Pasted image 20250430003651
Pasted image 20250430003952
继续跟进发现在install函数中还有判断path的函数,但是此时的path是plugins,因此我们需要自己创建一个plugins文件夹。
Pasted image 20250430004702
继续跟进,发现使用了startPlugin去执行plugin插件,再次跟进这个函数。
Pasted image 20250430005108
这个函数先是去判断了一下plugin当前的状态,是否已经在运行,如果没有的话就去循环遍历依赖项,然后记录日志,启动当前插件!当看到start的时候代表开始执行当前的插件了!
Pasted image 20250430005507
弹出计算器!ok拿下代码执行漏洞!插入的恶意代码如下:

static {
        try {
            Runtime.getRuntime().exec("cmd.exe1 /c calc");
        }catch (IOException e){
            throw new RuntimeException(e);
        }
    }

在代码注入漏洞时候遇到的一些问题

问题一:

ERROR] Failed to execute goal on project pf4j-demo-plugin1: Could not resolve dependencies for project org.pf4j.demo:pf4j-demo-plugin1:jar:3.1.0: Failed to collect dependencies at org.pf4j.demo:pf4j-demo-api:jar:3.1.0: Failed to read artifact descriptor for org.pf4j.demo:pf4j-demo-api:jar:3.1.0: The following artifacts could not be resolved: org.pf4j.demo:pf4j-demo-api:pom:3.1.0 (absent): Could not transfer artifact org.pf4j.demo:pf4j-demo-api:pom:3.1.0 from/to central (https://repo.maven.apache.org/maven2): 不知道这样的主机。 (repo.maven.apache.org): Unknown host 不知道这样的主机。 (repo.maven.apache.org) -> [Help 1]
[ERROR] 
[ERROR] To see the full stack trace of the errors, re-run Maven with the -e switch.
[ERROR] Re-run Maven using the -X switch to enable full debug logging.
[ERROR]

Pasted image 20250430010234
在构建pf4j插件的demo时候,如果直接生成demo的package会报如上错误,显示无法在maven的仓库中找到org.pf4j.demo:pf4j-demo-api文件。
解决方式:在根路径直接生成package,它会自动按照顺序生成jar包

问题二:

There are test failures.
Please refer to C:\java\project\pf4j-release-3.1.0\pf4j-release-3.1.0\pf4j\target\surefire-reports for the individual test results.
Please refer to dump files (if any exist) [date].dump, [date]-jvmRun[N].dump and [date].dumpstream.

Pasted image 20250430010653
在test测试的时候出错

解决方法:禁用test即可
Pasted image 20250430010727
Pasted image 20250430010805

问题三:
jdk的版本是什么?
解决方式:在构建pf4j的插件的时候需要使用jdk11,如果版本在jdk9一下会创建失败

问题四:
当在测试的时候上传jar包上传不上去,且抓不到数据包
解决方式:测试的jar包太大了,上传小一点的jar包即可

posted @ 2025-04-30 19:18  清水的秋  阅读(115)  评论(0)    收藏  举报