GoogleCTF2023 Writeup

news2024/11/14 17:43:29

GoogleCTF2023 Writeup

Misc

  • NPC

Crypto

  • LEAST COMMON GENOMINATOR?

Web

  • UNDER-CONSTRUCTION

NPC

A friend handed me this map and told me that it will lead me to the flag.
It is confusing me and I don’t know how to read it, can you help me out?

Attachment


使用Graphviz工具将hint.dot转换为图片,得到下图

dot -Tjpg  hint.dot -o hint.jpg
hint.jpg

分析代码:

  • USACONST.TXT中随机选择N个单词生成密码,使用passphrase.encrypt(secret, password)对flag加密

  • 对于password中的每个字母,创建一个带有唯一ID的节点,并添加到图中

  • 按照password的顺序,对密码中相邻的两个字符创建一条边

  • 向图中随机添加int(len(password) ** 1.3)条边

  • 随机打乱图中节点和边的顺序

  • 随机交换一些节点的起点和终点,由于random() % 2只有在random函数返回值为0时才为False,我们假定图中每条边的起点和终点都被交换了一遍

  • 将节点和边的信息写入到hint.dot文件中

我是思路是:

  1. 遍历words_list中的单词,从每个节点出发,使用DFS判断是否可以在图中找到,这样过滤后得到30个单词,即密码一定由该30个单词中的某几个单词组成

  2. 枚举密码中所用的单词个数N

  3. 使用组合数从这30个单词选择N个单词

  4. 判断所选的N个单词组成的字符集和hint.dot中的字符集是否一致

  5. 对N个单词进行全排列,并尝试解密 (为了提高效率,这里还用到了多进程)

当N=5时,得到passwordstandardwatersigngivenchosenflag.

代码
import concurrent.futures
import itertools
import re
from collections import Counter

from pyrage import passphrase

from encrypt import get_word_list


class Node:
    def __init__(self, letter):
        self.letter = letter
        self.adjacent = []

    def __str__(self) -> str:
        return f"{self.letter} -> {[x.letter for x in self.adjacent]}"

    __repr__ = __str__

def build_nodes():
    pattern = r"\s+(\d+)\s+\[label=(\w+)\];"
    pattern2 = r"\s+(\d+)\s+--\s+(\d+);$"
    nodes = dict()
    with open("hint.dot", "r") as f:
        for line in f:
            if "label" in line
                match = re.match(pattern, line)
                node_id = match.group(1)
                letter = match.group(2)
                nodes[node_id] = Node(letter)
            elif "--" in line:
                match = re.match(pattern2, line)
                start = match.group(1)
                end = match.group(2)
                # nodes[start].adjacent.append(nodes[end])
                nodes[end].adjacent.append(nodes[start])
    return nodes

visited = set()

def dfs(node, index, word):
    if index == len(word):
        return True

    if node.letter != word[index]:
        return False

    visited.add(node)

    for adj in node.adjacent:
        if adj not in visited:
            if dfs(adj, index + 1, word):
                return True

    visited.remove(node)
    return False

def check(password):
    with open("secret.age", "rb") as f:
        enc = f.read()
    try:
        print("[FLAG] is ", passphrase.decrypt(enc, password))
        print("Password is ", password)
    except Exception as e:
        pass


def filter_words(nodes):
    ans = set()
    for word in get_word_list():
        visited.clear()
        for node in nodes.values():
            if dfs(node, 0, word):
                ans.add(word)
                break

    print("ans:", len(ans), ans)
    return ans


def main():
    nodes = build_nodes()

    letters = sorted([node.letter for node in nodes.values()])

    ans = filter_words(nodes)
    for num in range(1, len(ans) + 1):
        print(f"Num is {num}")
        for comb in itertools.combinations(ans, num):  # 组合
            if sorted("".join(comb)) == letters:
                passwords = [
                    "".join(perm) for perm in itertools.permutations(comb, len(comb))
                ]

                with concurrent.futures.ProcessPoolExecutor(max_workers=8) as executor:
                    executor.map(check, passwords)


if __name__ == "__main__":
    main()

flag: CTF{S3vEn_bR1dg35_0f_K0eN1g5BeRg}

参考:

  • concurrent.futures — 启动并行任务 – Python 3.11.4 文档

LEAST COMMON GENOMINATOR?

Someone used this program to send me an encrypted message but I can’t read it! It uses something called an LCG, do you know what it is? I dumped the first six consecutive values generated from it but what do I do with it?!

Attachment


generate.py
from secret import config
from Crypto.PublicKey import RSA
from Crypto.Util.number import bytes_to_long, isPrime

class LCG:
    lcg_m = config.m
    lcg_c = config.c
    lcg_n = config.n

    def __init__(self, lcg_s):
        self.state = lcg_s

    def next(self):
        self.state = (self.state * self.lcg_m + self.lcg_c) % self.lcg_n
        return self.state

if __name__ == '__main__':

    assert 4096 % config.it == 0
    assert config.it == 8
    assert 4096 % config.bits == 0
    assert config.bits == 512

    # Find prime value of specified bits a specified amount of times
    seed = 211286818345627549183608678726370412218029639873054513839005340650674982169404937862395980568550063504804783328450267566224937880641772833325018028629959635
    lcg = LCG(seed)
    primes_arr = []

    dump = True
    items = 0
    dump_file = open("dump.txt", "w")

    primes_n = 1
    while True:
        for i in range(config.it):
            while True:
                prime_candidate = lcg.next()
                if dump:
                    dump_file.write(str(prime_candidate) + '\n')
                    items += 1
                    if items == 6:
                        dump = False
                        dump_file.close()
                if not isPrime(prime_candidate):
                    continue
                elif prime_candidate.bit_length() != config.bits:
                    continue
                else:
                    primes_n *= prime_candidate
                    primes_arr.append(prime_candidate)
                    break

        # Check bit length
        if primes_n.bit_length() > 4096:
            print("bit length", primes_n.bit_length())
            primes_arr.clear()
            primes_n = 1
            continue
        else:
            break

    # Create public key 'n'
    n = 1
    for j in primes_arr:
        n *= j
    print("[+] Public Key: ", n)
    print("[+] size: ", n.bit_length(), "bits")

    # Calculate totient 'Phi(n)'
    phi = 1
    for k in primes_arr:
        phi *= (k - 1)

    # Calculate private key 'd'
    d = pow(config.e, -1, phi)

    # Generate Flag
    assert config.flag.startswith(b"CTF{")
    assert config.flag.endswith(b"}")
    enc_flag = bytes_to_long(config.flag)
    assert enc_flag < n

    # Encrypt Flag
    _enc = pow(enc_flag, config.e, n)

    with open ("flag.txt", "wb") as flag_file:
        flag_file.write(_enc.to_bytes(n.bit_length(), "little"))

    # Export RSA Key
    rsa = RSA.construct((n, config.e))
    with open ("public.pem", "w") as pub_file:
        pub_file.write(rsa.exportKey().decode())

分析可知:

  • flag是使用RSA加密的,已知公🔑 文件,即n,e

  • 使用LCG线性同余生成器生成素数

  • 已知LCG的种子和前6个连续生成的数字

  • config.it = 8

  • config.bits = 256

LCG是伪随机数生成器和流密码的一种,递推公式是 𝑋𝑛+1=(𝑎𝑋𝑛+𝑐) 𝑚𝑜𝑑 𝑚

已知初值和随后LCG连续生成的6个值,未知增量、乘数和模数.

我们可以通过攻击得到这三个值,然后模拟原算法通过LCG得到8个素数后,进一步计算n的欧拉函数并求逆元得到d,解密即可.

题解:
import math
from functools import reduce

import gmpy2

from Crypto.PublicKey import RSA
from Crypto.Util.number import bytes_to_long, isPrime, long_to_bytes

dump_file = open("dump.txt")
output_values = [int(x) for x in dump_file.readlines()]  # 已知的 LCG 输出值

def crack_unknown_increment(states, modulus, multiplier):
    """
    已知:a,m,s0,s1
    求c
    """
    increment = (states[1] - states[0] * multiplier) % modulus
    return modulus, multiplier, increment

def crack_unknown_multiplier(states, modulus):
    """
    已知:m,s0,s1,s2
    求a
    """
    multiplier = (
        (states[2] - states[1]) * gmpy2.invert(states[1] - states[0], modulus) % modulus
    )  # 注意这里求逆元
    return crack_unknown_increment(states, modulus, multiplier)

def crack_unknown_modulus(states):
    """
    已知:s0-s6
    求a,c,m
    """
    diffs = [s1 - s0 for s0, s1 in zip(states, states[1:])]
    zeroes = [t2 * t0 - t1 * t1 for t0, t1, t2 in zip(diffs, diffs[1:], diffs[2:])]
    modulus = abs(reduce(math.gcd, zeroes))
    return crack_unknown_multiplier(states, modulus)

class LCG:
    def __init__(self, lcg_m, lcg_c, lcg_n, lcg_s):
        self.state = lcg_s
        self.lcg_m = lcg_m
        self.lcg_c = lcg_c
        self.lcg_n = lcg_n

    def next(self):
        self.state = (self.state * self.lcg_m + self.lcg_c) % self.lcg_n
        return self.state


m, a, c = crack_unknown_modulus(output_values)
seed = 211286818345627549183608678726370412218029639873054513839005340650674982169404937862395980568550063504804783328450267566224937880641772833325018028629959635
lcg = LCG(a, c, m, seed)
print(a, c, m)
primes_n = 1
primes_arr = []
for i in range(8):
    while True:
        prime_candidate = lcg.next()
        if not isPrime(prime_candidate):
            continue
        elif prime_candidate.bit_length() != 512:
            continue
        else:
            primes_n *= prime_candidate
            primes_arr.append(prime_candidate)
            break

print(list(primes_arr))

phi = 1
for k in primes_arr:
    phi *= k - 1

key = RSA.importKey(open("public.pem", "r").read())
n = key.n
e = key.e
d = gmpy2.invert(e, phi)

enc = open("flag.txt", "rb").read()

flag = pow(int.from_bytes(enc, "little"), d, n)
print(long_to_bytes(flag))

flag: CTF{C0nGr@tz_RiV35t_5h4MiR_nD_Ad13MaN_W0ulD_b_h@pPy}

参考:

  • 攻击线性同余生成器(LCG) | 码农网
  • LCG(线性同余生成器)_lcg线性同余_WustHandy的博客-CSDN博客

UNDER-CONSTRUCTION

We were building a web app but the new CEO wants it remade in php.

Attachment
https://under-construction-web.2023.ctfcompetition.com
https://under-construction-php-web.2023.ctfcompetition.com


题目提供了Flask和PHP两个站点,用户可以在Flask站点进行注册,注册的账号可以同时用于登录Flask和PHP两个站点.

分析代码:

Flask会将HTTP请求原始查询参数转发到PHP应用程序中完成用户注册.

# File: /flask/authorized_routes.py
@authorized.route('/signup', methods=['POST'])
def signup_post():
    raw_request = request.get_data()
    ...
    requests.post(f"http://{PHP_HOST}:1337/account_migrator.php", 
        headers={"token": TOKEN, "content-type": request.headers.get("content-type")}, data=raw_request)
    return redirect(url_for('authorized.login'))

只有gold级别的用户,在PHP站点登录后才可以看到FLAG

# File: /php/index.php
function getResponse()
{
    ...
    $response = "Login successful. Welcome " . htmlspecialchars($username) . ".";

    if ($tier === "gold") {
        $response .= " " . getenv("FLAG");
    }

    return $response;
}

Flask会对查询参数进行校验,防止创建高权限的用户.

# File: /flask/authorized_routes.py
@authorized.route('/signup', methods=['POST'])
def signup_post():
    ...
    tier = models.Tier(request.form.get('tier'))
    if(tier == models.Tier.GOLD):
        flash('GOLD tier only allowed for the CEO')
        return redirect(url_for('authorized.signup'))
    ...

HTTP查询参数中存在重复的key时,在Flask和PHP有不同的行为,flask会取第一个值,而PHP会取最后一个值.

因此我们可以构造如下命令,以此绕过Flask对查询参数的校验,并在PHP中注册高权限用户.

curl

curl -X POST https://under-construction-web.2023.ctfcompetition.com/signup -d "username=admin&password=admin&tier=blue&tier=gold"

然后用上述的用户名和密码去PHP站点登录即可.

flag: CTF{ff79e2741f21abd77dc48f17bab64c3d}

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

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

相关文章

Unity 批处理详讲(含URP)

咱们在项目中&#xff0c;优化性能最重要的一个环节就是合批处理&#xff0c;&#xff0c;在早期Unity中&#xff0c;对于合批的处理手段主要有三种&#xff1a; Static Batching Dynamic Batching GPU Instancing 如今Unity 为了提升合批范围与效率&#xff0c;提供了…

昇思 25 天学习打卡营第 15 天 | mindspore 实现 VisionTransformer 图像分类

1. 背景&#xff1a; 使用 mindspore 学习神经网络&#xff0c;打卡第 15 天&#xff1b;主要内容也依据 mindspore 的学习记录。 2. Vision Transformer 介绍&#xff1a; mindspore 实现 VisionTransformer 图像分类&#xff1b;VisionTransformer 论文地址 VisionTransfo…

掌握Python:三本不可错过的经典书籍

强烈推荐Python初学者用这三本书入门! Python3剑客 一、《Python编程从入门到实践》 这本书适合零基础的Python读者&#xff0c;旨在帮助他们快速入门Python编程&#xff0c;并达到初级开发者的水平。书中深入浅出地介绍了Python的基础概念&#xff0c;如变量、循环、函数等…

华清数据结构day4 24-7-19

链表的相关操作 linklist.h #ifndef LINKLIST_H #define LINKLIST_H #include <myhead.h> typedef int datatype; typedef struct Node {union{int len;datatype data;};struct Node *next; } Node, *NodePtr;NodePtr list_create(); NodePtr apply_node(datatype e); …

开源XDR-SIEM一体化平台 Wazuh (1)基础架构

简介 Wazuh平台提供了XDR和SIEM功能&#xff0c;保护云、容器和服务器工作负载。这些功能包括日志数据分析、入侵和恶意软件检测、文件完整性监控、配置评估、漏洞检测以及对法规遵从性的支持。详细信息可以参考Wazuh - Open Source XDR. Open Source SIEM.官方网站 Wazuh解决…

秒懂C++之string类(上)

目录 一.string类的常用接口说明 二.不太常用接口&#xff08;了解接口&#xff09; 三.string类的遍历访问 3.1 迭代器iterator 3.2 反向迭代器 四.string的其他功能 4.1 reserve(扩容&#xff09; 4.2 resize 4.3 at 4.4 append 4.5 4.6 insert 一.string类的常用…

VS2015加断点(红色),修改过后,断点变为白色不能命中

实际这个问题是因为&#xff1a;源文件和原始版本不同。解决方法有二&#xff1a; 一&#xff0c;在断点上右键&#xff0c;选择“位置”》勾选”允许源代码与原始版本不同&#xff1b; 二&#xff0c;点击菜单栏“调试”》“选项和设置”》“常规”》去掉“要求源文件与原始…

外卖霸王餐运营规划,系统该怎么选择?

在当今的外卖市场中&#xff0c;竞争日益激烈&#xff0c;如何吸引并留住消费者成为了每个餐饮商家关注的焦点。霸王餐作为一种创新的营销策略&#xff0c;以其独特的魅力&#xff0c;吸引了大量消费者的目光。然而&#xff0c;如何有效地运营霸王餐活动&#xff0c;选择合适的…

浅谈断言之XML Schema断言

浅谈断言之XML Schema断言 “XML Schema断言”是一种专门用于验证基于XML的响应是否遵循特定XML Schema定义的标准和结构的断言类型。下面我们将详细探讨XML Schema断言的各个方面。 XML Schema断言简介 XML Schema断言&#xff08;XML Schema Assertion&#xff09;允许用户…

EXO项目解析:pynvml怎么实现监控的,包括什么参数

目录 pynvml怎么实现监控的,包括什么参数 pynvml实现监控的方式 pynvml包括的主要参数 GPU功耗的组成 举例说明 注意事项 EXO项目解析:https://github.com/exo-explore/exo?tab=readme-ov-file 这段代码是一个使用setuptools库编写的Python包安装脚本,主要用于定义和…

std的时间函数——chrono

参考&#xff1a; C 标准库 分数运算&#xff08;ratio库&#xff09; 再也不被时间束缚&#xff1a;C stdchrono时间库全面解析 C11时间类 c11 chrono全面解析(最高可达纳秒级别的精度) C std::chrono库使用指南 (实现C 获取日期,时间戳,计时等功能) 一、std的分数ratio…

Android 防止重复点击

1.第一种方式&#xff1a; private static final int MIN_DELAY_TIME 2000; // 两次点击间隔不能少于2000ms private static long lastClickTime System.currentTimeMillis(); public static boolean isFastClick() { boolean flag true; long currentClickTime …

JMeter接口测试-3.断言及参数化测试

1. 断言 JMeter官方断言&#xff08;Assertion&#xff09;的定义 用于检查测试中得到的响应数据是否符合预期&#xff0c;用于保证测试过程中的数据交互与预期一致 断言的目的&#xff1a; 一个取样器可以添加多个不同形式的断言&#xff0c;根据你的检查需求来添加相应的…

dou dian滑块captchaBody

声明(lianxi a15018601872) 本文章中所有内容仅供学习交流使用&#xff0c;不用于其他任何目的&#xff0c;抓包内容、敏感网址、数据接口等均已做脱敏处理&#xff0c;严禁用于商业用途和非法用途&#xff0c;否则由此产生的一切后果均与作者无关&#xff01; 前言(lianxi a…

基于生物地理算法的MLP多层感知机优化matlab仿真

目录 1.程序功能描述 2.测试软件版本以及运行结果展示 3.核心程序 4.本算法原理 4.1 生物地理算法&#xff08;BBO&#xff09;原理 4.2 多层感知机&#xff08;MLP&#xff09; 4.3 BBO优化MLP参数 5.完整程序 1.程序功能描述 基于生物地理算法的MLP多层感知机优化mat…

Git之repo sync -l与repo forall -c git checkout用法区别(四十九)

简介&#xff1a; CSDN博客专家&#xff0c;专注Android/Linux系统&#xff0c;分享多mic语音方案、音视频、编解码等技术&#xff0c;与大家一起成长&#xff01; 优质专栏&#xff1a;Audio工程师进阶系列【原创干货持续更新中……】&#x1f680; 优质专栏&#xff1a;多媒…

Java 集合框架:TreeMap 的介绍、使用、原理与源码解析

大家好&#xff0c;我是栗筝i&#xff0c;这篇文章是我的 “栗筝i 的 Java 技术栈” 专栏的第 021 篇文章&#xff0c;在 “栗筝i 的 Java 技术栈” 这个专栏中我会持续为大家更新 Java 技术相关全套技术栈内容。专栏的主要目标是已经有一定 Java 开发经验&#xff0c;并希望进…

SpringBoot原理解析(二)- Spring Bean的生命周期以及后处理器和回调接口

SpringBoot原理解析&#xff08;二&#xff09;- Spring Bean的生命周期以及后处理器和回调接口 文章目录 SpringBoot原理解析&#xff08;二&#xff09;- Spring Bean的生命周期以及后处理器和回调接口1.Bean的实例化阶段1.1.Bean 实例化的基本流程1.2.Bean 实例化图例1.3.实…

leetcode算法题之接雨水

这是一道很经典的题目&#xff0c;问题如下&#xff1a; 题目地址 给定 n 个非负整数表示每个宽度为 1 的柱子的高度图&#xff0c;计算按此排列的柱子&#xff0c;下雨之后能接多少雨水。 解法1&#xff1a;动态规划 动态规划的核心就是将问题拆分成若干个子问题求解&#…

相信开源的力量,MoonBit 构建系统正式开源

MoonBit 构建系统正式开源 作为由 AI 驱动的云服务和边缘计算开发者平台&#xff0c;MoonBit 自设计之初便注重工具链与语言的协同效果。MoonBit 为开发者提供了一套开箱即用的工具链&#xff0c;包括集成开发环境&#xff08;IDE&#xff09;、编译器、构建系统和包管理器&…