go 语言代码审计靶场 -funnygo
很久没发帖了,今天来水一贴,前几天朋友在比赛他发了个web代码审计的题出来,我看了看挺有意思的,但是当时没空做,这两天根据题目的源码加上我的一些修改把环境搭出来了,打包成了docker,正好记录一下此题的解法~
funny-go
点击按钮获取源码
解压后是这样的,vendor是go的静态依赖可以暂时忽略
所以实际上的目录结构是这样的
ok, 可以开始代码审计了
代码审计
模板文件
先看看模板文件
都只是一些简单的静态页面,就upload.html这个文件里可以得到一个信息,这里上传的是请求的/upload目录
main文件
查看依赖
共有三个三方依赖
godaemon 一个可以让go程序在后台执行而不占用终端前台的依赖
go-wkhtmltopdf 知名将html转换成pdf的框架
go-gin 知名web框架
gorm 知名数据库orm框架
ok, 知道都有哪些依赖后,可以从主函数入手了
313~324
行都是在做数据库的连接和处理,如果ctf这个数据库没有相应的数据表则会被orm自动创建
在319
行可以看到自动创建的表的结构
提取主要信息,就是表内的token、username、password
这三个字段是关键字段
331~358
行就是开始注册web的路由了
路由信息:
/
/
路由可以忽略,就是主页面
/login
GET /login
可以看到路由返回的信息是Try to use POST
,在355
行也有一个/login
不过是POST请求的,注册的处理函数是LoginHandler
大致意思就是从post的请求体里获取username
还有password
的参数,然后使用UserFirst
进行数据库查询
查询后就开始校验密码是否正确,正确的就会设置token
/upload
/upload
路由的处理函数cookieCheckMiddleware
这个函数整体的逻辑就是在校验有没有token
,没有token
就重定向到/login
, 还检查token
是否和数据库里的token
相同,不相同就重定向到/login
然后到POST的/upload
这里有一个很明显的漏洞就是任意文件上传,在126
行上, 直接把文件与路径拼接然后保存
这个CreatePdf
函数会创建一个把刚才上传的文件转换成pdf,在很多的html转换成pdf的软件都有一个通病,就是会不可避免的会执行html模板里的标签,所以这个wkhtmltopdf
的转换是有一个ssrf的漏洞
wkhtmlTOpdf 0.12.6 is vulnerable to SSRF
然后函数里的pdf
的模板里,有四处地方字符串拼接
一处是文件大小,但是文件大小这个不受我们控制
一处是文件名,但是文件名会被切割
一处是文件内容,但是文件内容会被转码成base64
还剩最后一个地方那就是Content-Type
这个位置了,这个位置是我们可控的并且也没有做任何加工处理就直接和html模板进行拼接了,所以这个地方是整整好好的
/move
这个路由有一个ip检查的函数IpSecurityCheck
这个函数返回了一个匿名函数检查远端发起请求的是不是127.0.0.1
或者是localhost
,属于有一个ip检查校验(此函数有一个伏笔,后续会说)
然后到SubmitMoveHandler
这个函数
这个函数接收一个json
然后直接把json的值直接与命令拼接,并没有做任何过滤,所以这里是有一个非常明显的命令执行漏洞,但是因为有前面IpSecurityCheck
函数做检查,所以这个函数只能内网去请求
/download
download
函数接收一个json
获取filename
, 然后用正则去忽略大小写的检查filename
是不是出现env
或者xxx/
还有flag的字符串
这是过滤了一部分,然后就直接拼接filename
去把文件下载,所以这里是有一个过滤了一些关键字的任意文件下载的漏洞
/user
然后到user
路由的函数,这个路由应该是一个api去查询token相关的用户信息,这里还做了校验,如果如果token
不符合就返回一个错误,但是呢,这里有一个gorm的一个特性,gorm对go结构体的零值的处理是直接忽略而不是生成相应的sql, 比如说102
行这里传入的是一个UserTab
的结构体,生成的sql应该是
select * from tablename where email="" and token="xxxx" and username="" and password=""
但是gorm的特性,会忽略掉结构体中是零值的片段,也就是说会生成以下sql
select * from tablename where token="xxxx"
这样就很好理解了,user
路由的处理函数这里接收一个get token
的请求参数,所以这个时候传入的值是零值,gorm就会生成这样的sql
select * from tablename
这样就可以查询到所有的用户信息了
入侵思路
一通代码审计下来知道这个站点有以下漏洞
- 任意上传 (无条件任意上传)
- ssrf (修改
Content-Type
的内容注入js的标签) - 任意下载 (可以下载规定目录下除了env还有flag的任意文件)
- 命令执行 (只有回环地址可以请求的命令执行)
- 敏感信息泄漏 (orm框架的特性造成的敏感信息泄漏)
可以通过以上的漏洞构造以下一个组合拳的思路
-
敏感信息泄漏获取到的用户名和密码登陆得到token
-
ssrf注入js标签请求
/move
资源进行命令执行, 把flag重命名到下载目录 -
下载重命名的flag文件
得到flag
回收伏笔
好了,上面把作者的正常思路都过完了,现在来看一下前面埋的一个伏笔吧
在/move
这个路由的处理函数中有一个校验ip的函数
仔细看58
行这里返回的匿名函数里面的if检查
这里的if是在检查请求是不是来自回环的ip发送的请求,如果不是则结束函数
是的这里只是在结束函数而不是结束请求,可以看60~61
这两行,这两行只是在把错误信息写入到页面里,然后就结束了函数
也就是说本来正常的逻辑是
发送请求->IpSecurityCheck -> 失败 -> 中断 -> 不执行 SubmitMoveHandler
但是这里只是结束了函数,并没有中断请求
所以逻辑就变成了
发送请求->IpSecurityCheck->失败-> 不中断 -> 继续执行 SubmitMoveHandler
所以这个地方不需要ssrf也能直接访问这个/move
路由
可以看到虽然出现了IpSecurityCheck
函数写入的错误信息,但是还是能继续执行SubmitMoveHandler
函数
使用download下载得到flag
结语
题目整体不是很难,但是也挺有意思的,锻炼信息收集,代码审计以及组合拳的能力的同时也保证了一定的趣味,就是在/move
的那个地方的伏笔,不好说是作者故意这样留的还是忘了写c.Abort()
函数去中断请求,如果是故意留的那作者还是很有水平的,让题目的有一些趣味性,也增加了一些实战的意味,有几种解题思路,并没有为了出题而出题
最后附上我打包好的docker环境,鱼排有想玩的小伙伴可以自己用我写好的docker-compose去拉一下镜像玩玩
funnygo.zip
可以 很强