go 语言代码审计靶场 -funnygo

很久没发帖了,今天来水一贴,前几天朋友在比赛他发了个web代码审计的题出来,我看了看挺有意思的,但是当时没空做,这两天根据题目的源码加上我的一些修改把环境搭出来了,打包成了docker,正好记录一下此题的解法~

funny-go

image.png

点击按钮获取源码

image.png

image.png

解压后是这样的,vendor是go的静态依赖可以暂时忽略
所以实际上的目录结构是这样的

image.png

ok, 可以开始代码审计了

代码审计

模板文件

先看看模板文件

image.png

image.png

都只是一些简单的静态页面,就upload.html这个文件里可以得到一个信息,这里上传的是请求的/upload目录

main文件

查看依赖

image.png

共有三个三方依赖

godaemon 一个可以让go程序在后台执行而不占用终端前台的依赖

go-wkhtmltopdf 知名将html转换成pdf的框架

go-gin 知名web框架

gorm 知名数据库orm框架

ok, 知道都有哪些依赖后,可以从主函数入手了

image.png

image.png

313~324行都是在做数据库的连接和处理,如果ctf这个数据库没有相应的数据表则会被orm自动创建
319行可以看到自动创建的表的结构

image.png

image.png

image.png

提取主要信息,就是表内的token、username、password这三个字段是关键字段

331~358行就是开始注册web的路由了

路由信息:

/

/路由可以忽略,就是主页面

/login

GET /login 可以看到路由返回的信息是Try to use POST,在355行也有一个/login不过是POST请求的,注册的处理函数是LoginHandler

image.png

大致意思就是从post的请求体里获取username还有password的参数,然后使用UserFirst进行数据库查询

image.png

查询后就开始校验密码是否正确,正确的就会设置token

/upload

/upload路由的处理函数cookieCheckMiddleware

image.png

这个函数整体的逻辑就是在校验有没有token,没有token就重定向到/login, 还检查token是否和数据库里的token相同,不相同就重定向到/login

然后到POST的/upload

image.png

这里有一个很明显的漏洞就是任意文件上传,在126行上, 直接把文件与路径拼接然后保存

image.png

image.png

这个CreatePdf函数会创建一个把刚才上传的文件转换成pdf,在很多的html转换成pdf的软件都有一个通病,就是会不可避免的会执行html模板里的标签,所以这个wkhtmltopdf的转换是有一个ssrf的漏洞

wkhtmlTOpdf 0.12.6 is vulnerable to SSRF

然后函数里的pdf的模板里,有四处地方字符串拼接

一处是文件大小,但是文件大小这个不受我们控制
一处是文件名,但是文件名会被切割
一处是文件内容,但是文件内容会被转码成base64

image.png

还剩最后一个地方那就是Content-Type这个位置了,这个位置是我们可控的并且也没有做任何加工处理就直接和html模板进行拼接了,所以这个地方是整整好好的

/move

image.png
这个路由有一个ip检查的函数IpSecurityCheck

image.png

这个函数返回了一个匿名函数检查远端发起请求的是不是127.0.0.1或者是localhost,属于有一个ip检查校验(此函数有一个伏笔,后续会说)

然后到SubmitMoveHandler这个函数

image.png

这个函数接收一个json

image.png

然后直接把json的值直接与命令拼接,并没有做任何过滤,所以这里是有一个非常明显的命令执行漏洞,但是因为有前面IpSecurityCheck函数做检查,所以这个函数只能内网去请求

/download

image.png

download函数接收一个json获取filename, 然后用正则去忽略大小写的检查filename是不是出现env或者xxx/还有flag的字符串

这是过滤了一部分,然后就直接拼接filename去把文件下载,所以这里是有一个过滤了一些关键字的任意文件下载的漏洞

/user

image.png

然后到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框架的特性造成的敏感信息泄漏)

可以通过以上的漏洞构造以下一个组合拳的思路

  1. 敏感信息泄漏获取到的用户名和密码登陆得到token

    image.png

    image.png

  2. ssrf注入js标签请求/move资源进行命令执行, 把flag重命名到下载目录

    image.png

    image.png

    image.png

  3. 下载重命名的flag文件

    image.png

    image.png

得到flag

回收伏笔

好了,上面把作者的正常思路都过完了,现在来看一下前面埋的一个伏笔吧

/move这个路由的处理函数中有一个校验ip的函数

image.png

仔细看58行这里返回的匿名函数里面的if检查

这里的if是在检查请求是不是来自回环的ip发送的请求,如果不是则结束函数

是的这里只是在结束函数而不是结束请求,可以看60~61这两行,这两行只是在把错误信息写入到页面里,然后就结束了函数

image.png

也就是说本来正常的逻辑是

发送请求->IpSecurityCheck -> 失败 -> 中断 -> 不执行 SubmitMoveHandler
但是这里只是结束了函数,并没有中断请求

所以逻辑就变成了

发送请求->IpSecurityCheck->失败-> 不中断 -> 继续执行 SubmitMoveHandler

所以这个地方不需要ssrf也能直接访问这个/move路由

image.png

可以看到虽然出现了IpSecurityCheck函数写入的错误信息,但是还是能继续执行SubmitMoveHandler函数

使用download下载得到flag image.png

结语

题目整体不是很难,但是也挺有意思的,锻炼信息收集,代码审计以及组合拳的能力的同时也保证了一定的趣味,就是在/move的那个地方的伏笔,不好说是作者故意这样留的还是忘了写c.Abort()函数去中断请求,如果是故意留的那作者还是很有水平的,让题目的有一些趣味性,也增加了一些实战的意味,有几种解题思路,并没有为了出题而出题

最后附上我打包好的docker环境,鱼排有想玩的小伙伴可以自己用我写好的docker-compose去拉一下镜像玩玩
funnygo.zip