进入题目可以看到一个是要用自己的JinKela去购买flag
这种类型的题目我想到之前都是有一个jwt认证,通过伪造验证信息修改金额以达到购买的效果
点击work抓包可以发现确实有jwt(通过一段字符一个点来看出),便解码试试
之前做过将alg这个算法改为none即可实现无算法认证,但是这里改后发现构造不出来jwt认证结果,不知什么原因,看来只能寻找密匙了。JWT介绍_没逛够的博客-CSDN博客
通过dirsearch扫描可以扫到robots.txt文件
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("").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
不知道什么语言的代码,反正我是没见过,因为我们要获取密匙,因此寻在哪个方块可以获取密匙
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("").result
end
end
可以看到work板块有三个我们可以自主控制的参数do和name和SECRET
当参数do的值等于name参数的前七位+is working则会执行jkl随机数的增加,也就是我们点击work为什么每次增加的数不一致的原因
可以看到它将name参数并入了输出语句alert,我寻思肯定要利用这个地方,但是我没接触过这个,因此只能看看别人如何做的了
【技术分享】手把手教你如何完成Ruby ERB模板注入 - 安全客,安全资讯平台
Ruby语言的erb模板注入其实跟之前pythonSSTI挺像的,只不过python是用{{}}或{%%}来执行判断,而Ruby则是用<%=%>,我们可以试试最简单的<%=1%>是否成功
这里注意要将<%=1%>进行url编码 ,避免%这种敏感字符
现在我们就需要获得密匙了,可以看见源码开头给出了环境变量密匙是一个随机数,并且上面密匙还要经过正则过滤,这里过滤有点跟我们不一样Ruby Regexp match()用法及代码示例 - 纯净天空
可以看到 搜索对象值.match("搜索的字符串") ,如果我们这里SECRET参数传入数据的话,他会因为后面的字符串是任意数字和字母,返回的就是SECRET参数的值,如果再进行一次匹配的话,密匙肯定不完整
还有字符数的限制,因此这里要用到ruby语言的预定义变量了globals - Documentation for Ruby 2.4.0
因此当我们传入$'的时候,它会返回最后一次成功匹配的右边字符串Ruby 预定义变量和常量_Looooking的博客-CSDN博客
去修改jwt,并在购买界面伪造,将返回的jwt解密
关于ruby的预定义变量看了好久,终于理解一点为什么要用$'了


![[SCTF2019]Flag Shop erb模板注入 [SCTF2019]Flag Shop erb模板注入](http://www.mshxw.com/aiimages/31/820207.png)
