# GlacierCTF 2023 writeup
- Tuần vừa rồi, mình đã tham gia đánh giải GlacierCTF 2023 cùng với `TheRoundTable`. Bọn mình đã end giải với thứ hạng 25 và mình đã solve được 4/6 câu crypto. (Do trình còn non nên chưa solve được 2 câu khó còn lại 😢)
![image](https://hackmd.io/_uploads/rJU70yfrp.png)
- Dưới đây là writeup cho các challenge mà mình đã giải được.
## writeup available for:
### Crypto:
1. [ARISAI](#ARISAI)
2. [MissingBits](#MissingBits)
3. [SLCG](#SLCG)
4. [GlacierSpirit](#GlacierSpirit)
4. [WalkingToTheSeaSide](#WalkingToTheSeaSide) (update someday ...)
4. [ShuffledAES](#ShuffledAES) (update someday ...)
## ARISAI
>I heard that RSA with multiple primes is more secure. My N is very large, so there should not be a problem.
Attachments:
* chal.py
```py=0
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import getPrime
PRIME_LENGTH = 24
NUM_PRIMES = 256
FLAG = b"gctf{redacted}"
N = 1
e = 65537
for i in range(NUM_PRIMES):
prime = getPrime(PRIME_LENGTH)
N *= prime
ct = pow(bytes_to_long(FLAG), e, N)
print(f"{N=}")
print(f"{e=}")
print(f"{ct=}")
```
* output.txt
```
N=1184908748889071774788034737775985521200704101703442353533571651469039119038363889871690290631780514392998940707556520304994251661487952739548636064794593979743960985105714178256254882281217858250862223543439960706396290227277478129176832127123978750828494876903409727762030036738239667368905104438928911566884429794089785359693581516505306703816625771477479791983463382338322851370493663626725244651132237909443116453288042969721313548822734328099261670264015661317332067465328436010383015204012585652642998962413149192518150858822735406696105372552184840669950255731733251466001814530877075818908809387881715924209232067963931299295012877100632316050826276879774867425832387424978221636157426227764972761357957047150626791204295493153062565652892972581618176577163744310556692610510074992218502075083140232623713873241177386817247671528165164472947992350655138814891455499972562301161585763970067635688236798480514440398603568227283629452476242623289661524243073929894099518473939222881149459574426407208658860251686137960952889074096311126991477096465624470265619377139983649503903820480974951491378311837933293607705488991162022547957926530402988912221198282579794590930661493745233069145707902854299501706154802038942258911515981663207152069613126155243024789689987554767962281273345273757236723762684230158310314189489269922058062081424352003908442430243686562569467793068370441732743572240164014190275463904986105758545036928880621165599686076511511089276388190078187849622221351011692443859919384379432387437072419707649486293684966456033518855679391672980173280496419686363359529398834403906418139786395934302273747490127295066208248715874656180233559644161531014137838623558729789331274400542717269108353265885948166102045041669627782992845494987948783304254174326130201166965174477449798721151991240203641
e=65537
ct=268829805459609475588440899873097740407996768854076329496002425282199615879909227647380967635165606878898541606457683227761652305836586321855100255485305118037701500609605019785162541750877335573032359895573772603246111506991979320486028250721513277767642375361127152574528694298160906073442383962020636918610527024050576972769852306021296823499884948279413653216802756618690182635446020844210831886652986287932378470425746444631963933610367607515800649608436183004088441881238148504635598468243968695248287570279766119573944421327504565309861792437849662128566261080923059583840204287527201636471106753069738472306223410300379312983945939043519755909420737707495224846116170095923898104488099329762265149868062693687303917610957104520999978944379566136253252697346935036425206126213766976582551430726756840294537354912787885103742021813054656962241068550049435394355553796824094853195888610994254949530524531633088750916669188277025883371307926545593346345011181011886157628805587723572874545440223921942144548540109099572715194182349314576321627183804149379561322969725485272107142991680959335537127382716195040449341448266408777436145121388591741613272241408064729715121476227737259932422493622000014673154665474739974557976672498027364986075870354093242809763072555932073688776712239151696700128393589329790478951588551070833013708885416360627613835550721939073618725634813608997025047929327270234611128029339388251117036658410438813874667672407000490721438737857471847655487642835059784967516451098631494261100960513521722400650533821661854325599281416744189966724295645707952292786069145361070873245192529272080607536319284389065418040578100669665069777133031446812281199863684982910055858515634879595144557407925298026899908970790756383369461817536923660051327566555421265363733995050644914554395836353253513
```
Nhận xét:
- Đây là dạng bài warm up của RSA, khi đề cho ta số $N = \prod\limits_{i=1}^{256}{p_i}$ với các $p_i$ ~ 24 bits.
- Và vì các $p_i$ là khá nhỏ, vì thế ta chỉ cần ném số **N** lên bất kì 1 tool factor nào và boom -> RSA broken.
- Giải thích chi tiết lí do vì sao khi factor được **N** thì RSA lại broken là vì:
$$
\begin{aligned}
\text{Giả sử} : \ \
N &= p_{1}^{e_1} * p_{2}^{e_2} * ... * p_{n}^{e_n} \\
\Rightarrow \phi({N}) &= (p_1 - 1)*p_{1}^{e_1-1} * ... (p_n - 1)*p_{n}^{e_n-1} \\
\Rightarrow d &\equiv e^{-1} \ (mod \ \phi({N})) \\
\Rightarrow m &\equiv c^{d} \ (mod \ N)
\end{aligned}
$$
Script:
```python = 0
from sage.all import *
from Crypto.Util.number import *
N=...
e=...
ct=...
primes = list(factor(N))
phi = 1
for i, j in primes:
phi*=(i-1)*pow(i, j-1, N)%N
d = inverse(e, int(phi))
m = pow(ct, d, N)
print(long_to_bytes(int(m)))
#b'gctf{maybe_I_should_have_used_bigger_primes}'
```
-> Flag : `gctf{maybe_I_should_have_used_bigger_primes}`
## MissingBits
>Somehow your private key lost some bits. Are you able to reconstruct them and decrypt the ciphertext?
Attachments:
* encode_file.py
```python=0
from Crypto.PublicKey import RSA
from Crypto.Util.number import bytes_to_long
from Crypto.Util.number import long_to_bytes
content = open("2048_key_original.priv", "rb").read()
key = RSA.import_key(content)
filecontent = open("plaintext_message", "rb").read()
ct = pow(bytes_to_long(filecontent), key.e, key.n)
open("ciphertext_message", "wb").write(long_to_bytes(ct))
```
* priv.key
```=0
xwiBgUBTtaSyUvdrqfgAEduRdnzRbKcwEheMxwIDAQABAoIBAAqaJbojNCwYqykz
n0Fn2sxMsho4PhThPQcX79AGqSxVNxviWK2GXETP7SsnvWGmRXHIRnR6JGOhyHVe
dTDYZxOAOhl85FksVQYVUaygf9Epekja/vSj5OE8NIcAdEBr3aZ6gdLxi+q1a5Kh
1nEmsF6FiYGpsPkN63ovbow/CKt4N7UQJqZEQw380rNA0sOQenmzXRFOpXA8PRFb
G6itGRiP1gB9tpdQnWggQ5n+x8/2k+k3CRW6/xIP9dMAVZh2jVombenLxgnhQCJB
bYaR4I8B0zzYqXqFfeHCMNl+pJmmmFcvs2ZE71fqyjRid6ZDqS4GXtSuRQM0UL7L
UQVBaYECgYEA5BiLN7FjwgOuT4FKxFdzizdq/t5mvRksbmBP/JWk3vxQYeCmMiPQ
xrQUqdHGGxG8iMIwH7dnhNaPa81lrP9fCKyij/caEbe4lmEm+VdM/xZQF+PiCc1f
ziYXphz9wuAc8++kvKxM2EaiDe8F25nsXW+FaxNoXKbJg0zTQLyzKiECgYEA8SLi
hbAwo2l0zal8GMIemzr+APxLw+fmd4aryVAMov8ANkG8KDMwdmvvkn3rL7WaKym5
fakqvXR45/QGPe8niVzx6oaWGSSfijeVan27pG/bzVqyymFHZP9cRhEHW4HN57hO
pXy0kUFqVaxJWCs+thH0LTZoToAepg+sr82FaecCgYEAnJnpQzRsHDEwxO8sqP6t
moBS2mdRPDUDV0iSwgTvrBSpD3oQQM5sMXBD24/lpoIX4gEIz025Ke+xij77trmh
wq/b8GGjqVRsy/opqvjwKRZlqPFRKI+zXjKy+95dryT1W9lFTjAxli9wZYaci/fy
2veNL0Wk2i+8nIPraj/j9mECgYEA0ou6LC7OGTDwKs7sqxV78eBNfoDMis7GPeEZ
x9ocXom3HqjA6HzhuNS/xzIpE2xGo5939c+qoOe81hMNDDDwXZEJLdS75FJE90NX
NDd6iracvi6OZAUSeI47fHZL7UtmhQg5q2c6poXumcWn+NMxm3oLsRqLcteNa0PO
bWZPMksCgYBidblknACvEinDUQB8dvElzROql0ZUMX9hQOsSrg0ju3smun8qujcT
PJQrWctoNw0ZXnQjDkVzaJxog1F3QkKUg6B1Rn2Q0RYuCAeNDn+KqBkTT18Du7yw
+GU/4U6EMw+uL7dNjasD1jjx90ro6RmDDBmGDQuludO0G7h9XQzl+Q==
-----END RSA PRIVATE KEY-----
```
* ciphertext_message
```
��*�\�%Ѧ;h�����
Zǿӏ�> U����9Y��C�0�v�2K���^��#J����^ �.1C�ޮ*���e?�����
```
Nhận xét:
- Đọc qua file encode_file.py, mình thấy đây là 1 bài RSA nhưng key được mã hóa theo PEM format và dựa vào tên file gốc là **`2048_key_original.priv`**, mình đoán rằng **p, q** được gen ban đầu có giá trị cỡ 1024 bits. Ngoài ra, đề còn cho ta file private key nhưng đã bị mất phần đầu.
- Để chắc chắn với lập luận trên, mình thử viết 1 đoạn code và gen dummy key để so sánh
```python=0
from Crypto.PublicKey import RSA
from Crypto.Util.number import *
p = getPrime(1024)
q = getPrime(1024)
n = p*q
e = 0x10001
d = inverse(e, (p-1)*(q-1))
key = RSA.construct([n, e, d])
open("dummy_key.key", "wb").write(key.export_key("PEM"))
```
![image](https://hackmd.io/_uploads/r1tn0RZBp.png)
- Well với format gần y chang nhau và số dòng bằng nhau đã củng cố suy đoán trên của mình là đúng.
- Tiếp theo đó, các bạn có thể tham khảo 1 blog rất hay nói về vấn đề **Reconstruct PEM file RSA private key** [ở đây](https://blog.cryptohack.org/twitter-secrets).
- Tóm tắt, từ blog trên, ta biết rằng khi trình bày private ở dạng PEM, sẽ có những quy tắc đã được định nghĩa từ trước, và đồng thời cũng có header để phân tách các giá trị private với nhau.
![image](https://hackmd.io/_uploads/Bk1geJfH6.png)
![image](https://hackmd.io/_uploads/HJY0yJMB6.png)
- Vì thế, áp dụng vô bài này, mình sẽ giải mã đoạn data còn lại được mã hóa bằng base64, sau đó, căng mắt lên tìm kiếm các header để phân tách các private key đã được cho trong file.
script:
```python=0
from Crypto.Util.number import *
from base64 import *
test1 = """xwiBgUBTtaSyUvdrqfgAEduRdnzRbKcwEheMxwIDAQABAoIBAAqaJbojNCwYqykz
n0Fn2sxMsho4PhThPQcX79AGqSxVNxviWK2GXETP7SsnvWGmRXHIRnR6JGOhyHVe
dTDYZxOAOhl85FksVQYVUaygf9Epekja/vSj5OE8NIcAdEBr3aZ6gdLxi+q1a5Kh
1nEmsF6FiYGpsPkN63ovbow/CKt4N7UQJqZEQw380rNA0sOQenmzXRFOpXA8PRFb
G6itGRiP1gB9tpdQnWggQ5n+x8/2k+k3CRW6/xIP9dMAVZh2jVombenLxgnhQCJB
bYaR4I8B0zzYqXqFfeHCMNl+pJmmmFcvs2ZE71fqyjRid6ZDqS4GXtSuRQM0UL7L
UQVBaYECgYEA5BiLN7FjwgOuT4FKxFdzizdq/t5mvRksbmBP/JWk3vxQYeCmMiPQ
xrQUqdHGGxG8iMIwH7dnhNaPa81lrP9fCKyij/caEbe4lmEm+VdM/xZQF+PiCc1f
ziYXphz9wuAc8++kvKxM2EaiDe8F25nsXW+FaxNoXKbJg0zTQLyzKiECgYEA8SLi
hbAwo2l0zal8GMIemzr+APxLw+fmd4aryVAMov8ANkG8KDMwdmvvkn3rL7WaKym5
fakqvXR45/QGPe8niVzx6oaWGSSfijeVan27pG/bzVqyymFHZP9cRhEHW4HN57hO
pXy0kUFqVaxJWCs+thH0LTZoToAepg+sr82FaecCgYEAnJnpQzRsHDEwxO8sqP6t
moBS2mdRPDUDV0iSwgTvrBSpD3oQQM5sMXBD24/lpoIX4gEIz025Ke+xij77trmh
wq/b8GGjqVRsy/opqvjwKRZlqPFRKI+zXjKy+95dryT1W9lFTjAxli9wZYaci/fy
2veNL0Wk2i+8nIPraj/j9mECgYEA0ou6LC7OGTDwKs7sqxV78eBNfoDMis7GPeEZ
x9ocXom3HqjA6HzhuNS/xzIpE2xGo5939c+qoOe81hMNDDDwXZEJLdS75FJE90NX
NDd6iracvi6OZAUSeI47fHZL7UtmhQg5q2c6poXumcWn+NMxm3oLsRqLcteNa0PO
bWZPMksCgYBidblknACvEinDUQB8dvElzROql0ZUMX9hQOsSrg0ju3smun8qujcT
PJQrWctoNw0ZXnQjDkVzaJxog1F3QkKUg6B1Rn2Q0RYuCAeNDn+KqBkTT18Du7yw
+GU/4U6EMw+uL7dNjasD1jjx90ro6RmDDBmGDQuludO0G7h9XQzl+Q=="""
extract = b64decode(test1).hex()
print(extract)
e = 0x10001
d = 0xa9a25ba23342c18ab29339f4167dacc4cb21a383e14e13d0717efd006a92c55371be258ad865c44cfed2b27bd61a64571c846747a2463a1c8755e7530d86713803a197ce4592c55061551aca07fd1297a48dafef4a3e4e13c34870074406bdda67a81d2f18beab56b92a1d67126b05e858981a9b0f90deb7a2f6e8c3f08ab7837b51026a644430dfcd2b340d2c3907a79b35d114ea5703c3d115b1ba8ad19188fd6007db697509d68204399fec7cff693e9370915baff120ff5d3005598768d5a266de9cbc609e14022416d8691e08f01d33cd8a97a857de1c230d97ea499a698572fb36644ef57eaca346277a643a92e065ed4ae45033450becb5105416981
print(d.bit_length())
p = 0xe4188b37b163c203ae4f814ac457738b376afede66bd192c6e604ffc95a4defc5061e0a63223d0c6b414a9d1c61b11bc88c2301fb76784d68f6bcd65acff5f08aca28ff71a11b7b8966126f9574cff165017e3e209cd5fce2617a61cfdc2e01cf3efa4bcac4cd846a20def05db99ec5d6f856b13685ca6c9834cd340bcb32a21
q = 0xf122e285b030a36974cda97c18c21e9b3afe00fc4bc3e7e67786abc9500ca2ff003641bc283330766bef927deb2fb59a2b29b97da92abd7478e7f4063def27895cf1ea869619249f8a37956a7dbba46fdbcd5ab2ca614764ff5c4611075b81cde7b84ea57cb491416a55ac49582b3eb611f42d36684e801ea60facafcd8569e7
print(p.bit_length())
print(q.bit_length())
n = p*q
c = bytes_to_long(open("ciphertext_message", 'rb').read())
m = pow(c, d, n)
print(long_to_bytes(m))
#b'Hey Bob this is Alice.\nI want to let you know that the Flag is gctf{7hi5_k3y_can_b3_r3c0ns7ruc7ed}'
```
-> Flag : `gctf{7hi5_k3y_can_b3_r3c0ns7ruc7ed}`
## SLCG
>In cryptography class we learned about random numbers and algorithms to create pseudo random number generators. I think I build a solid cipher that nobody can break. This is why I call it SecureLongCiphertextGenerator, SLCG for short
Attachment:
* chal.py
```python=0
from __future__ import annotations
import os
FLAG = b"gctf{???????}"
class LCG:
def __init__(self, mod: int, mult: int, add: int, seed: int):
self.mod = mod
self.mult = mult
self.add = add
self.value = seed
def __next__(self) -> int:
self.value = (self.value * self.mult + self.add) % self.mod
return self.value
def __iter__(self) -> LCG:
return self
@classmethod
def random_values(cls):
return LCG(
int.from_bytes(os.urandom(16)),
int.from_bytes(os.urandom(16)),
int.from_bytes(os.urandom(16)),
int.from_bytes(os.urandom(16))
)
class Encryptor:
def __init__(self):
self.lcgs: tuple[LCG] = (LCG.random_values(), LCG.random_values())
def encrypt(self, message: str) -> list[int]:
result = []
for ascii_char in message:
bin_char = list(map(int, list(f"{ascii_char:07b}")))
for bit in bin_char:
result.append(next(self.lcgs[bit]))
self.lcgs = (
LCG(
next(self.lcgs[0]), next(self.lcgs[0]),
next(self.lcgs[0]), next(self.lcgs[0])
),
LCG(
next(self.lcgs[1]), next(self.lcgs[1]),
next(self.lcgs[1]), next(self.lcgs[1])
)
)
return result
def main() -> int:
encryption = Encryptor()
print(f"ct = {encryption.encrypt(FLAG)}")
return 0
if __name__ == "__main__":
raise SystemExit(main())
```
* ciphertext.txt
```=0
ct = [114293481651692805418121538415147589604, 54633022358060826155954146262572096344, 39246964755280114087344695441503859529, 703463377822278186043454332542885631, 125313023815946519926697407430683658442, 162960109688532363602735593403961340669, 169104787290291550198916185039818769417, 13372371689034608934042109259651932913, 51422260128019952349405691786844709461, 1051777155079167180002756329036502707, 15923116890164513335569368667703873, 971358536846996655674257654714172515, 25204581825024414471206078194323632214, 23500231184211815510804184187742001447, 2379381023229713239273408997788051784, 2879885915245668425181114446703811854, 3112702453712052233092578707053087554, 927669813382289621755794625268082452, 834433535584809394710922489203298074, 313563733833157852430102794396702787, 944323926949619412414979250796256793, 91717176861821570143294495808173674, 52491683375438185937328405118585970, 562902158889616768851916729259322760, 239097371607957075191287752287148847, 76238022626131454651591457135092138, 289425170380755779854486501977374546, 313519669607689910328667862844315309, 577829319421425064009918698244993630, 505266435119561487868754134859795702, 759859325586953800596818729729968722, 512690647196804023738947989498910204, 346213412899815521402585882290927207, 155499553397124669610409568175991478, 178390908143993770691473011050263374, 730336353049705201033505573335004552, 401038838861799443381259276896411192, 62522802402325448639454211180743836, 70128026993853587348178136285880714, 270468171896930680168562773476062156, 68944207631401982741949386410868354, 28794858681066468291931415836050596, 286663035287217594499906865137515456, 80718253243276206845249853209869454, 3713837217262237612874018016540336, 16488192370707544847317466186135748, 18062417034061094139089752173392122, 11726156020588650726051853953493216, 29952876407456039971662622592963770, 3951468417125234262710756483314205, 6108256686951010608427678500763840, 409614211632056397738470299915802922, 10118836381453913641380092261871566, 8507726865358444591886502030371909, 9619191946874532625727888255615201, 15529371542565664478385146600546754, 1008012615212795231596672240458771, 73002819916955035882118301875217962, 30322792298255259533193879184197360, 3237272104826256109796281336359491, 58806057979702216159853067957716050, 3327978693457064603095094817246651, 1615805105431371848642733130622244, 818457227439045512790497438622154, 5972870434531768792652747902651134, 1242737596703142269806832363864512, 347239827113107915669856462892022, 916248999816087150057401289833332, 165324305795703078802760600379826, 761731111183586921956651131746044, 420351026458229547525207185078820, 304635165087495732525613546708632, 578977321829565725347207857967728, 588127506536690546582158996485984, 130723651010817052114717758815500, 275734025095575498716310084308847, 841649218923857866841085634620309, 134794772662902356787697693365952, 113739428144824653226812792446444, 167103543150235445151294738487632, 13678906989659363088885602833314, 219886167313326359012780093710434, 179670727571406710686294764564320, 87553053686788602106947680417328, 121346915154929063084499394911984, 73610558587305253963595572076688, 264843071386553232214831288822116, 263375982050650870882508171894940, 32143293135200986736837640555552, 4863003162724954666690441679832, 86217380664331186252079713772664, 88904528544401948757481819201188, 241083969053441690393801131202832, 94183119121761838397597054123844, 101674656104514000121636172713332, 73432121862125830297974492411036, 64932946798081207082668868567988, 11961507214749622294953115845984, 99544098028512176660075181295044, 21286714514384781858804074790948, 2802032573287323810961943394264, 130087061021929078261104064220244, 284415927669169792485482454773964, 36486481569720335705902443060132, 36915977762968309748107432531596, 26229488956795828947796518864096, 11538628889884650064079151465168, 48241435563761216190059317613172, 7882288795792666082008690694064, 11185134920196863412131291887776, 27348181613012074399762539616848, 55558482807789571556957861434188, 15665985561344401718910820211328, 20952631219593681266001816455136, 1823094348846890558373425826528, 11169268147922225928017490780072, 15232100405145960599038998229344, 5841431385219110239861840854624, 9694236872806463066167619735040, 3191776518269613652238653442496, 11220188175493529131161798959808, 13629306480523758013430579713344, 11293092669376026975512114123664, 813938876156049395174969332752, 6413568281600142434084122449024, 14218873152545145295490962182960, 2646923460169868062713745139232, 4982881421826289014576775626240, 1942587487831279209136663620096, 4757406824331719321080938290208, 5155982764900971654036997905696, 8996351637294912636513532680768, 3776246323021949501883635259552, 2940742362032162606816569758240, 3439097086893626651084849382048, 5443491360207960320552054428896, 152934822599001464362826076192, 146266593339492149764754557440, 100896110532442028928061708272, 204430400033486968386903175632, 19076544675405075275422328304, 1476591481902400835117860574784, 136708629859754503576693801056, 422139194402161576934942579376, 2660870374303248688937023150560, 3499521751230910070902254849600, 3419138564705114474763144860640, 128906026540747262619327214560, 347809648425114846051515361312, 373398730133200815475220586336, 45791014111269931111954971456, 98158330018070604450054839088, 32203165552656756592666135248, 444860266890086213265586095840, 21335492788882169791885761024, 191233906645404038483377604448, 111179721759564963452368440096, 21228401214758039410384347312, 4618774005119592546622967280, 7445481763570808502883560768, 13331302146719807014972421136, 15957110172481929754491523104, 31902687175058950950671035680, 6071576637917064812078775312, 28994458433876328634956947232, 29602108412678606387074585632, 31136297181167291968259143200, 4377061609401537595150092720, 18739088262758069747508512160, 1697215374897177611503935216, 9991995150615182264253554784, 27275896277939620125032049216, 35323266323148568870945194432, 35705457138464438746004892000, 21418454208738578379900440544, 15610538334664622361209750784, 11983129630865266305673245312, 4751133203942822675615823648, 1504344887532686254350315168, 6871456202899194825550654176, 12482775371851112838372797088, 16148909733935812384094647488, 301820234829397006027359744, 179515152912681501914785248, 570327849912535705153370592, 369221247757843820408161440, 132466460753886294319653600, 22155315333130772552601504, 147739587342961290572487456, 18434258669507552995118688, 14902050119637183064265088, 14663565206222549091928992, 83566333140801926129829216, 182902810872058016388181344, 12320743139935718308083648, 17407711863986253248378016, 18054228258971038767989664, 10145795613011605088476704, 70122761346018924538011072, 507206557111633694685216, 13434332134738626922966848, 16478829090109411285851360, 43289158667652071857724832, 11287909012552826990886144, 6803125004639592520170048, 4148700224926893076270368, 697270791918553121360448, 6175696471191324604916928, 7210370598960718221566688, 4059762162428611940228928, 3727724334198460109258976, 10839231460482685972731936, 1294528907758485509843040, 570763929228578395722720, 1526284916065825791299424, 4078018803752278681953888, 1387747355450504633167968, 121667390912716438204800, 279402896646991436612832, 70491292887529038948192, 364022339518764603333216, 1517848441376541151929408, 275799208478701380869088, 342037868066698381278624, 5239939805548865636560992, 88820710527726344037024, 38276815978085386141440, 3732532266048490230721920, 6364130886328972839145056, 2185726294522308965410368, 5691527035619341958731872, 12723981342565559840928, 127793683146336082192800, 36352497860287615724256, 100928923891168362564960, 43519383964501288750368, 44740302764523558036768, 68469510949331922482976, 432221721769068279289920, 69537201634054651631904, 23098983498213417194400, 488932246134880550283456, 62591932353807071484192, 3065399212098546993684672, 50717127409858033873056, 23445113825124779999904, 2473864736185555640928, 21562990615047293618976, 3743854691337964567679616, 3933965387355303853296576, 22915805740103037107808, 22809182381128085763744, 1176615007757043884761536, 3312150909413221969632, 7921216705871171191104, 2975969574211957404911424, 2376524313512238367195200, 5925340470077421912288, 7091662260735571071168, 7137860161330251039456, 506450172496669698528, 548504735750623236356352, 357925904073528987591360, 402798372707125404438912, 7586463558208026159360, 7859808314391962196576, 3770059969469476178496, 36886896474868927872, 1045576281168912366720, 433848929689860471360, 1979181152593775178048, 47158321693749302998080, 3245890009539555721728, 1392232548129352376832, 1434446052649400343168, 1763457293898986406336, 7023363386178753730368, 41897416281473804042880, 861563238833929040640, 2005070816980905250752, 3389988855841313136768, 308177334270302156352, 600392179437953740992, 1693096926216397297344, 486042234462699699456, 309404024536209615936, 606855056954616949248, 17419361311898973504, 474919461789271785216, 28000588215675886272, 2723947356985259328, 4461639227509597056, 265922676257769558720, 214372950706606595904, 33383871019763111232, 17705249320245423552, 549865274091688884672, 36844128609151167360, 370137264524363097216, 451598930852828980032, 27272157269714368704, 54781155788543864064, 24770781581091416640, 525051359906128368768, 309016571846670821952, 3494824844200506432, 21775785516948576960, 2648712030327546624, 15301475659865767680, 36977119316788565376, 21627728497884358080, 87435619420004061696, 47063390462185027392, 108640324483640449920, 5380411831211180736, 25933983480398097216, 52526171078188581312, 8218301158282235328, 82664328890028223296, 77395400937614765376, 31914313836549084864, 5674669006127381568, 6443792779765904640, 16022911883239622592, 4275041973814225152, 75025908130106566080, 71541202982849308800, 600699981666109248, 52943936593263053184, 14077712390600339712, 16105722409370135808, 12940327033590648384, 9998251257061178496, 2744484385998979008, 5692393054107809280, 1430438408657197632, 22363824254423512896, 800361609633831168, 5245390673512577280, 3460854991000986432, 8207885334172753152, 10131021590581584000, 17688417915784875840, 395539410746075904, 889655849165825280, 456846656890853376, 2267940394295186688, 729379744944060672, 291788040218266368, 14960706674216268672, 375064819498220544, 882850425329359872, 4625063492831324928, 232341644167928064, 384530928881476608, 1116985437963360000, 3751180509040683264, 58637696311256832, 240902928711954432, 230671930915464192, 42232583219726592, 457038486803872512, 1160326914127210368, 217832909060777472]
```
Nhận xét:
- Đây là 1 bài LCG có cái source khá quằn nhưng cũng ~~làm mình trầy trật từ sáng đến chiều~~ rất hay 😁
- Mình sẽ tóm tắt sơ qua flow bài:
- Ban đầu, như bao bài LCG khác, bài toán định nghĩa hàm LCG là $f(x) \equiv a*x + b \ (mod \ p)$, với **a** gọi là multiplier; **b** gọi là add; **p** gọi là modulus và **x** gọi là value.
- Tuy nhiên, trong bài này, ta thấy code đã định nghĩa 2 hàm $LCG_0$ và $LCG_1$ mục đích để làm gì thì 1 xíu ta sẽ rõ.
- Theo sau đó, với mỗi hàm LCG đều có bước **Init** bộ 4 giá trị (p, a, b, x) là random.
- Tiếp theo, ta thấy flag sẽ được lấy từng chữ cái một, chuyển sang list 7 số nhị phân và với từng bit 0/1 trong các list đó sẽ gọi hàm $LCG_0$ và $LCG_1$ tương ứng.
- Điều đặc biệt tiếp theo ta cần để ý đó là khi đã thực hiện xong việc encrypt 1 chữ cái trong flag, tức là đã encrypt xong 7 bit nhị phân của chữ cái đó, 2 hàm LCG đều **Re-Init** lại bộ 4 giá trị (p, a, b, x).
- Okay, sau khi nắm được flow bài, thì ta cùng đi đến flaw của bài. Ta có thể thấy ngay rằng prefix của flag là 1 cụm ta đã biết và cũng được biết toàn bộ kết quả encrypt của nó.
- Như vậy, nếu như bằng 1 cách nào đó, ta có thể từ những thứ đã biết để recover lại bộ giá trị (p, a, b, x) của hàm LCG ở một state nào đó. Thì các state phía sau, ta hoàn toàn có thể tự mô phỏng lại được do đã nắm được cách hoạt động của bài.
- Và công đoạn recover lại các tham số của LCG thì đã có kha khá các writeup ở trên mạng, nên các bạn có thể tham khảo điển hình như [ở đây](https://flocto.github.io/writeups/2023/deadsecctf/lcg-writeup/)
- Còn đây là tool mình xài :))) [link](https://github.com/jvdsn/crypto-attacks/blob/master/attacks/lcg/parameter_recovery.py)
- Ở bài của mình thì mình sẽ chỉ xài mỗi hàm $LCG_1$ là đủ để dựng lại toàn bộ các kết quả sau đó so sánh với output để recover lại các bit 1 được xài, những bit so sánh fail sẽ là bit 0.
Script:
```python=0
known = b"gctf{"
ct = eval(open("ciphertext.txt", 'r').read()[4:])
print(len(ct))
pos = 0
temp = ct[7*pos:7*(pos+1)]
print(temp)
res = []
cnt = 0
for i in f"{known[pos]:07b}": # g 1100111 c 1100011 { 1111011
#print(i)
if i == '1':
res.append(temp[cnt])
cnt+=1
print(res, len(res))
recover_mod = 169974530670679507160215045440114217513
recover_mult = 91118855455091955591252219966671075725
recover_add = 12840644087969167590602449189275535577
def f(p, a, b, x):
return (a*x + b) % p
flag = [str(i) for i in str(bin(ord('g')))[2:]]
print(flag)
print(res, len(res))
for i in range(7, len(ct)):
if i % 7 == 0: # reinit LCG
mod = f(recover_mod, recover_mult, recover_add, res[-1])
mult = f(recover_mod, recover_mult, recover_add, mod)
add = f(recover_mod, recover_mult, recover_add, mult)
value = f(recover_mod, recover_mult, recover_add, add)
recover_mod = mod
recover_mult = mult
recover_add = add
calc = f(mod, mult, add, value)
if calc == ct[i]:
flag.append('1')
res.append(ct[i])
calc = f(mod, mult, add, calc)
else:
flag.append('0')
blocks = [flag[i:i+7] for i in range(0, len(flag), 7)]
flag = [int("".join(i), 2) for i in blocks]
print(bytes(flag))
#b'gctf{th15_lcg_3ncryp710n_w4sn7_s0_5s3cur3_aft3r_4ll}'
```
-> Flag : `gctf{th15_lcg_3ncryp710n_w4sn7_s0_5s3cur3_aft3r_4ll}`
## GlacierSpirit
>You have climbed the mountain and reached the top. You are now in the presences of the glacier spirit which seems to have an affinity for ASCON. But it seems confused about how to use it.
>
>author: mcsch
>
>nc chall.glacierctf.com 13379
Attachments:
* challenge.py
```python=0
#!/usr/bin/env python3
import ascon
import secrets
from secret import FLAG
BLOCK_SIZE = 16
def xor(a, b):
return bytes([x ^ y for x, y in zip(a, b)])
def split_blocks(message):
return [message[i:i + BLOCK_SIZE] for i in range(0, len(message), BLOCK_SIZE)]
def mac_creation(key, message):
assert len(message) % BLOCK_SIZE == 0
message_blocks = split_blocks(message)
enc_out = b"\x00" * BLOCK_SIZE
for message in message_blocks:
chaining_values = xor(message, enc_out)
enc_out = ascon.encrypt(key, chaining_values, b'', b'')
assert len(enc_out) == BLOCK_SIZE
return enc_out
def pad_message(message):
first_block_pad = len(message) % BLOCK_SIZE
first_block_pad = 16 if first_block_pad == 0 else first_block_pad
return (first_block_pad.to_bytes() * (BLOCK_SIZE - first_block_pad)) + message
def encrypt(key, message):
assert len(message) % BLOCK_SIZE == 0
message_blocks = split_blocks(message)
assert len(message_blocks) < BLOCK_SIZE
nonce = secrets.token_bytes(BLOCK_SIZE-1)
cts = []
for ctr, message in enumerate(message_blocks):
cipher_input = nonce + (ctr+1).to_bytes(1, 'little')
enc = ascon.encrypt(key, cipher_input, b'', b'')
ct = xor(message, enc)
cts.append(ct)
return nonce, b''.join(cts)
def create_message_and_mac(key, message):
padded_message = pad_message(message)
nonce, ct = encrypt(key, padded_message)
tag = mac_creation(key, padded_message)
return nonce, ct, tag
if __name__ == "__main__":
print(" Glacier Spirit\n\n")
print(" , /\.__ _.-\ ")
print(" /~\, __ /~ \ ./ \ ")
print(" ,/ /_\ _/ \ ,/~,_.~''\ /_\_ /'\ ")
print(" / \ /## \ / V#\/\ /~8# # ## V8 #\/8 8\ ")
print(" /~#'#'#''##V&#&# ##\/88#'#8# #' #\#&'##' ##\ ")
print(" j# ##### #'#\&&'####/###& #'#&## #&' #'#&#'#'\ ")
print(" /#'#'#####'###'\&##'/&#'####'### # #&#&##'#'### \ ")
print(" J#'###'#'#'#'####'\# #'##'#'##'#'#####&'## '#'&'##|\ ")
key = secrets.token_bytes(BLOCK_SIZE)
print("The spirit of the glacier gifts you a flag!\n")
nonce, ct, tag = create_message_and_mac(key, FLAG)
print(f"{nonce.hex()}, {ct.hex()}, {tag.hex()}")
print("\nNow you can bring forth your own messages to be blessed by the spirit of the glacier!\n")
for i in range(8):
print(f"Offer your message:")
user_msg = input()
try:
msg = bytes.fromhex(user_msg)
except:
print("The spirit of the glacier is displeased with the format of your message.")
exit(0)
nonce, ct, tag = create_message_and_mac(key, msg)
print("The spirit of the glacier has blessed your message!\n")
print(f"{nonce.hex()}, {ct.hex()}, {tag.hex()}")
print("The spirit of the glacier has left you. You are alone once more.")
```
Nhận xét:
- Đây là một bài tương đối EZ nếu như các bạn chịu khó đọc source và phân tích 😅.
- Nói sơ về flow bài, thì ta biết được rằng flag sẽ qua 1 hàm encrypt gì đó và server trả về bộ 3 giá trị (nonce, ciphertext, tag). Sau đó, ta được phép gửi 8 plaintext dạng hex vào server để thu bộ 3 các giá trị (nonce, ciphertext, tag) của chúng.
- Và toàn bộ quá trình tạo bộ 3 tham số (nonce, ciphertext, tag) thì mình sẽ tóm tắt qua ảnh này:
![image](https://hackmd.io/_uploads/rJhKhkMHa.png)
- Exploit của bài này cũng khá đơn giản khi toàn bộ các giá trị bạn đều được biết. Khi đó, nếu ta gửi vào server các giá trị theo format `nonce||number` thì **tag** được trả ra của bọn chúng sẽ giúp chúng ta recover flag.
- Cụ thể, thì những chỗ khoanh đỏ của mình sẽ như nhau :
![image](https://hackmd.io/_uploads/Bym_TkzHT.png)
Script:
```python=0
from pwn import *
host, port = "chall.glacierctf.com", 13379
io = remote(host, port)
io.recvuntil(b"The spirit of the glacier gifts you a flag!\n")
nonce = bytes.fromhex(io.recvuntil(b", ")[:-2].decode())
print(len(nonce))
ct = bytes.fromhex(io.recvuntil(b",")[:-1].decode())
print(ct)
blocks = [ct[i:i+16] for i in range(0, len(ct), 16)]
res = []
for i in range(8):
io.recvuntil(b"Offer your message:")
io.sendline((nonce + (i+1).to_bytes(1, 'little')).hex())
io.recvuntil(', ')
io.recvuntil(', ')
res.append(bytes.fromhex(io.recvline().rstrip().decode()))
flag = [xor(i, j) for i, j in zip(blocks, res)]
print(b''.join(flag))
io.interactive()
#b'\x0fgctf{CTr_M0d3_cbc_M4C_ASCON_DeF3AT$_TH3_$p1rIT}'
```
-> Flag : `gctf{CTr_M0d3_cbc_M4C_ASCON_DeF3AT$_TH3_$p1rIT}`
## Kết luận
- Ở trên là danh sách 4 bài mảng crypto mình solve được trong giải GlacierCTF 2023.
- Mọi thắc mắc của các bạn xin vui lòng liên hệ với mình qua discord: ``tranminhprvt01``
- Cuối cùng, cảm ơn các bạn đã đọc. Have a good day! 🥰