[SCTF2019]Flag Shop ruby REB

[GXYCTF2019]StrongestMind
在这里插入图片描述
计算算式1000次给flag,写一个脚本就行。重点考察正则的使用:

from time import sleep

import requests
import re

url = "http://944c02ef-5777-4072-84fa-3fabe8c45ae9.node4.buuoj.cn:81/index.php"

s = requests.session()
r = re.compile(r"[0-9]+ [+|-] [0-9]+")
res = s.get(url)
for i in range(0,1001):
    sleep(0.1)
    play = r.findall(res.text)[0]
    result = eval(play)
    print(i,end=",")
    print(result)
    data = {
    
    "answer":result}
    res = s.post(url, data=data)
    res.encoding = "utf-8"
    print(res.text)

[SCTF2019]Flag Shop
在这里插入图片描述
有三个选项:buy flag需要你的JKL大于10e27,work可以增加你的JKL,大概是0-5的随机数,reset没用。
bp抓包看一下,这是work的,可以看到GET方法传了/work?name=bot&do=bot%20is%20working,然后你的JKL就增加了。
在这里插入图片描述
注意/shop返回的html页面上是没有你的JKL数量的,它是又调用了/api/info去查看的
在这里插入图片描述
在这里插入图片描述
这里的cookie是一个jwt,记录你的uid和jkl。一般想法肯定是去修改cookie里的jkl,我直接修改了然后重放会报错,看来这里是需要密钥的。尝试了jwt直接破解密钥,不行。
在这里插入图片描述
我又尝试了用脚本不断点击work,如果不是buu上的waf限制了访问间隔说不定还真可以,我把脚本放在这里哪位勇士想的话可以尝试一下。

from time import sleep
import requests

i=0
cok="auth=eyJhbGciOiJIUzI1NiJ9.eyJ1aWQiOiI1OWYxOWU0Mi03NGFiLTQyYmMtYjhjZS0wNmZlMDY0ODE1NTkiLCJqa2wiOjE1NH0.VvXFVvRaWQVH7e0PDAOJaUkdnllTXRRaqX1Yoe5puKM"
while(i<=1000000000000000000000000001):
    sleep(0.2)
    url = "http://8faad94e-0038-49e8-80d8-993ff1b34e56.node4.buuoj.cn:81/work?name=bot&do=bot%20is%20working"
    s = requests.session()
    s.headers['Cookie']=cok
    res = s.get(url)
    cok=res.headers['Set-Cookie']
    print(res.text)
    s.headers['Cookie'] = cok
    url1="http://8faad94e-0038-49e8-80d8-993ff1b34e56.node4.buuoj.cn:81/api/info"
    re=s.get(url1)
    a1=re.text.split(':')[2][:-1]
    i=int(a1)
    print(a1)
    print(cok)

url2 = "http://8faad94e-0038-49e8-80d8-993ff1b34e56.node4.buuoj.cn:81/shop"
result = s.post(url2)
result.encoding="utf-8"
print(result.text)

看了writeup知道是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 = 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

重点是这里,用了REB模板渲染,name参数是我们可控的

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

那么怎么样让name输出密钥呢?这里对服务器中的SECRET与我们的参数SECRET做了一次match比较(说实话我现在才知道GET还可以传SECRET)。RUBY有预定义变量$`输出上次match左边的字符串。https://docs.ruby-lang.org/en/2.4.0/globals_rdoc.html

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

因此我们构造name=<%=$'%> ,要记得转成十六进制
在这里插入图片描述
拿到密钥构造jwt重放,获得flag。flag也是在jwt里的。
在这里插入图片描述
在这里插入图片描述

猜你喜欢

转载自blog.csdn.net/scrawman/article/details/121700617