定制软件开发[SCTF2019]Flag Shop——瞧,这是个新来的

[SCTF2019]Flag Shop

一.前言

定制软件开发这几天一直有事,定制软件开发导致之前的比赛的WP也没看,定制软件开发然后靶场这方面的学习定制软件开发也没推进下去,定制软件开发终于找了个时间的空挡定制软件开发做了道关于的模板注入。定制软件开发为了这道题我甚至抽出了我本就不多的时间去学了学Ruby的语法,话说学完Ruby之后,我的感触是他与Python等脚本语言很像,但是它的语法中字里行间都透露着Java等语言的关系。关于这次的SSTI题目,与其他的SSTI有着很大的不同。

二.正文

老样子,我们先看题目

题目的意思很简单就是我们要通过足够的jinkela来购买flag。但是我们发现我们的初始金额数是不够的,那怎么办呢?我们可以通过点击work选项来增加jinkela的数量,但是通过work所获得的jinkela数量有限。可是那又怎么样呢?我们只要点的多,我们总会达到那个数字的(bushi。
咳咳,咱们言归正传,我们扫描一下目录发现有robots.txt文件,我们访问一下看看。

我们直接访问一下/filebak目录,里面是这个网站的源码。

require 'sinatra'require 'sinatra/cookies'require 'sinatra/json'require 'jwt'require 'securerandom'require 'erb'set :public_folder, File.dirname(__FILE__) + '/static'FLAGPRICE = 1000000000000000000000000000ENV["SECRET"] = SecureRandom.hex(64)configure do  enable :logging  file = File.new(File.dirname(__FILE__) + '/../log/http.log',"a+")  file.sync = true  use Rack::CommonLogger, fileendget "/" do  redirect '/shop', 302endget "/filebak" do  content_type :text  erb IO.binread __FILE__endget "/api/auth" do  payload = { uid: SecureRandom.uuid , jkl: 20}  auth = JWT.encode payload,ENV["SECRET"] , 'HS256'  cookies[:auth] = authendget "/api/info" do  islogin  auth = JWT.decode cookies[:auth],ENV["SECRET"] , true, { algorithm: 'HS256' }  json({uid: auth[0]["uid"],jkl: auth[0]["jkl"]})endget "/shop" do  erb :shopendget "/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  endendpost "/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"})  endenddef islogin  if cookies[:auth].nil? then    redirect to('/shop')  endend
  • 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

我们发现这个源码中有JWT,我们抓一个包试试看。

我们拿着这个JWT去解密。

我们发现这个网站的jinkela数量是通过JWT进行传递的,所以我们只要能够得到这道题的加密密钥,从而伪造JWT,就行了。那么,我们该如何拿到密钥呢?从上面的整个代码中着重看如下代码。

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  endend
  • 1
  • 2
  • 3
  • 4
  • 5
  • 6
  • 7
  • 8
  • 9
  • 10
  • 11
  • 12
  • 13
  • 14
  • 15
  • 16
  • 17
  • 18
  • 19

这里涉及到了一些关于Ruby ERB模板注入的知识,具体的就不细说了,原理都差不多,只是语法的区别。

我们要通过<%=%>进行模板注入,我们想要得到SECRET但是在<%=%>中我们不能直接写SECRET,因为params[:name][0,7]的存在导致我们只能在模板内写入2个字符。但是幸运的是,Ruby为我们提供了预定义字符。

$’ 最后一次模式匹配中匹配部分之后的字符串

让我们看看运行到这句话之前的最后一个模式匹配在哪里?

unless params[:SECRET].nil?    if ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}")      puts ENV["FLAG"]    end
  • 1
  • 2
  • 3
  • 4

就是在匹配SECRET,这个预定义字符的作用是将匹配之后的字符进行返回。什么意思呢?我来举个例子

hello world //我设置匹配字符为ello world//这就是返回值
  • 1
  • 2

我们要想得到完整的SECRET,那就必须传进去一个空的SECRET,让最后的返回值是完整的。
所以我们如此构造payload

?name=<%=$'%>&do=<%=$' is working%>&SECRET=
  • 1


结果如下

我们拿着这个密钥去伪造JWT

我们把这个伪造的JWT发过去。

我们看到服务端又给我返回了一个新的JWT,我们这次把他解密看看。

返回了flag.

三.后记

虽然这段忙的时间还没有过去,但是总有时间是可以腾出来的。之后就应该开始SSRF的学习了,SSTI也要告一段落了,应该和SQL一样把他们放在比赛中继续磨练了。总之当下的问题是先熬过这段艰苦的时光。

网站建设定制开发 软件系统开发定制 定制软件开发 软件开发定制 定制app开发 app开发定制 app开发定制公司 电商商城定制开发 定制小程序开发 定制开发小程序 客户管理系统开发定制 定制网站 定制开发 crm开发定制 开发公司 小程序开发定制 定制软件 收款定制开发 企业网站定制开发 定制化开发 android系统定制开发 定制小程序开发费用 定制设计 专注app软件定制开发 软件开发定制定制 知名网站建设定制 软件定制开发供应商 应用系统定制开发 软件系统定制开发 企业管理系统定制开发 系统定制开发