Gisec Ctf 2022 Write Ups
This march 21 to march 23 was Yogosha’s GISEC CTF 2022 in which I participated. This was a team competition but I decided to participate solo. My team was PsyDucks in which I (letigredununavu) was the only participant. I manage to end the CTF at the 14th place. Here are some challenges and solutions I found interesting.\
WEB
EatC00KiEs
Looking at the challenge’s title, we already know we’ll have to deal with some cookies. Upon arriving on the website, the first thing I do is looking at the cookies with the developper’s tool. Here, I find a cookie named ‘role’ with the value ‘Guest’ and the site hint us that we need to make this cookie ‘Admin’.
To do this, I fire up burp suite and setup the proxy to this site. I intercept the request and add this line to it ‘Cookie: role=Admin’, when we get the response, we can see the flag in the html code.
We get the flag : GISEC{Cookies_C4n_B3_ModIfIeD_TOO?}\
RecurPHP
In this challenge, we arrived at some php code. The code take an argument named ‘naruto’ and some lines later it compares it with the string secret, so first thing I do is insert the secret string into the argument, but we get ‘Are u understanding the cod ?’.
I look a bit more at the code given and see that it use the preg_replace function to remove instances of the secret string in our input. So I google a little bit about this function and what I learn is that this function is not recursive. So if it found one or many instance of ‘I_want_to_become_a_hockage’, it will replace it with the null string, and then give the result away, but do not check if the result does contain the secret string, so we just need to make so that once the function replace our secret string, another secret string is created that the preg_replace won’t check.
We get the flag : GISEC{I_1Ov3_Php_4nd_NaruT0_Oukii?}\
TypeJugg
For this challenge I had to search a bit more, by the title I knew I had to exploit the type and the ‘==’ operation of php which look at the value of the operand and not the type. We were given code that takes 2 arguments from the url, look that they are not equal and then verify that their hashes are equal, if yes, it gives us the flag. After a bit, I found some old CTF writeups that explained the concept of magic hashes. Thoses magic hashes are hashes that starts with ‘0e’ so when used as operands with the ‘==’ operator, they evaluates to 0 if all the digits after ‘0e’ are in the range 0-9. So I look at the list of strings that gives us magic hashes on github and chose 2 that were differents, input it in the arguments and got this result !
We got the flag : GISEC{php_typing_is_weak_just_like_you:)}\
Reverse
elf1
For this reverse engineering challenge we were giving a binary file, when executing it we get this ouput asking us to give an input and responding by ‘wubba lubba dub dub’.
So we know we need to find a kind of password. I first try the ‘strings’ command, but I did not see anything interesting. I then try to disassemble it via IDA Free and go see the pseudocode for the main function. We see there that our input get store in a variable called ‘s’, then it does some kind of operations on it. They xor it with the index of the caracter then add to the result value 22 (20 + 2).
Finally, it compares the value with the string called ‘s2’. We follow the reference to this string and get the the following bytes values :
s2 = ]^g]\x94inY]PriVeqnFI\x84
After theses discoveries it’s easy to build a small python script to restitute the original password :
def deXoring(x):
x = [i for i in x]
for i in range(len(x)):
x[i] = ord(x[i]) - 22
x[i] ^= i
x[i] = chr(x[i])
return ''.join(x)
if __name__ == '__main__':
y = ']^g\]\x94inY]PriVeqnFI\x84'
print(deXoring(y)) # GISEC{U_KN0W_MATH!!}
We get the flag : GISEC{U_KN0W_MATH!!}
Crypto
In this category I will do the write up of the four RSA challenges
RSA01
We have this python file and have to decode the given output :
from Crypto.Util.number import getPrime, long_to_bytes as ltb ,bytes_to_long as btl
def encrypt(data,modulus,exponent):
m = btl(data)
assert m<modulus
return pow(m,exponent,modulus)
# Initilizing parameters
p = getPrime(110)
q = getPrime(110)
e = 65537
N = p*q
# Message
flag = b"Redacted"
enc = encrypt(flag,N,e)
print(f"Public Modulus: {N} and Public exponent: {e}")
print(f"The encrypted message is: {enc}")
######## output
# N = 1004995496566346075873930707800493321236968239524622949507762127653
# enrypted = 391954716711415946350985916860618751332906967581871181532410342734
Its easy bruteforcing p and q because they are only on 110 bits, so we use http://factordb.com to decompose N and find p,q and decode it using this code :
N = 1004995496566346075873930707800493321236968239524622949507762127653
enrypted = 391954716711415946350985916860618751332906967581871181532410342734
e = 65537
# using this site, we find p and q :
p = 814932504518555994849435568524401
q = 1233225440136389051050014854974453
#calculating phi
phi = (p-1)*(q-1)
#calculating d
d = inverse(e,phi)
#decoding ciphertext; i.e: M = pow(C , d) % n
m=pow(enrypted,d,N)
print(bytes.fromhex(hex(m)[2:]).decode("ascii")) # GisecCTF{Easy_peasy_right?}
RSA02
This was the challenge script :
from Crypto.Util.number import long_to_bytes as ltb, bytes_to_long as btl, getPrime
def encrypt(data,modulus,exponent):
m = btl(data)
assert m < modulus
return pow(m,exponent,modulus)
p = getPrime(1024)
e = 65537
flag = b'REDACTED'
enc = encrypt(flag,p,e)
print(f"The public modulus is {p} and the public exponent is {e}")
print(f"the encrypted message is {enc}")
### output
# Modulus : 112982701372174135714541042310119603841026379112918124295881022614737112371220762441907782664659731337882611618073932602250654493138913463938845301253189700405866110321629159954074959013313377755909094741063956063693566385004932946379171399803086360345144176502740660343813066761541114575303369296657820791163
# exponent : 65537
# enc : 88141540951737078954711359053703593325078389546811543323485621484335479181438565486859296481048233669690960889452061331519730183047651396823229616235927347293019123019960473648346632385013116149218399356862585923576702106272464521515411861334123999555184256317150974435399215722800702933617092969151016036160
For this challenge, the encryption use a prime number for N, and following the math reasoning behind RSA we know that phi(N) when N is prime is N - 1.
And we decoded it with this code :
N = 112982701372174135714541042310119603841026379112918124295881022614737112371220762441907782664659731337882611618073932602250654493138913463938845301253189700405866110321629159954074959013313377755909094741063956063693566385004932946379171399803086360345144176502740660343813066761541114575303369296657820791163
enc = 88141540951737078954711359053703593325078389546811543323485621484335479181438565486859296481048233669690960889452061331519730183047651396823229616235927347293019123019960473648346632385013116149218399356862585923576702106272464521515411861334123999555184256317150974435399215722800702933617092969151016036160
p = N
q = 1
#calculating phi
phi = (p-1)
#calculating d
d = inverse(e,phi)
#decoding ciphertext; i.e: M = pow(C , d) % n
m=pow(enc,d,N)
print(bytes.fromhex(hex(m)[2:]).decode("ascii")) # GisecCTF{Easy_peasy_right?}
RSA03
Challenge script :
from Crypto.Util.number import long_to_bytes as ltb, bytes_to_long as btl, getPrime
def encrypt(data,modulus,exponent):
m = btl(data)
assert m < modulus
return pow(m,exponent,modulus)
p = getPrime(1024)
q = getPrime(1024)
r = getPrime(1024)
N1 = p*q
N2 = p*r
e = 65537
flag1 = b"REDACTED"
flag2 = b"REDACTED"
enc1 = encrypt(flag1,N1,e)
enc2 = encrypt(flag2,N2,e)
print(f"The First Modulus is {N1}")
print(f"The Second Modulus is {N2}")
print(f"The Public exponent is {e}")
print(f"The First encrypted message is {enc1}")
print(f"The Second encrypted message is {enc2}")
## output
# N1 : 14210100835811893798591078574673509799599435785629793053564426327501908443183140520609964712650358642983958098057476878561030530736365215542967098664119375313082664944585486342081477101282777511562123969111180556724024895406974743062387838042255090695147973536720609137941107044960129028680194236971517888073002151100105846731521577011388381786481148592693877947633594752266751375339343845793557960247402876231491392310849835544042849854976618763858282692179517051698041046443665430087664775796279151997682716153471202272855992352652294689251775067915427628426475352635713405663233084590482443696929909113022275932963
# N2 : 9909416432857077839842635526397016570706233946465755602951432796949043674880406344119650144972805415270789891434962888017099110866828044976063665090118377534234873371879049768690146720022551696102687134324629943276999478646284416067186629436851833656583809441164967966550090424942940548947231116810115548706732879023945410706654471380653149453240459855653515761752212366682744164796337152295199350766681091460733764953863450049565749196047542053106675478773561074112623631487376796831953278385382743014813370224165409285606161953552927575831815431930860698082290914569442342899424158943471050819735954770403576952597
# e : 65537
# enc1 : 11285068260062957431988077411736024737206589628789265002651924086474736320840849806849636018251251828077239003620308962675643100015540623853365642746583641951453604279131710660086093301811269653391260552592819184650086888954000144374933327696033327786284567656140368742793009894248924494218462754463148693302847012797824700163591585045003846574959107642615757249440973617079019352414537053550355304175853187762339891042353537700564042584001146787565804386975441940440343063218321437735266377940408404084148571212095663044215726786503139610972710995694977370136413410338805394618181460386598021764830520920207038119737
# enc2 : 230503441339098213197943518548655312076875819226223970855219569665753573103664712609350437983808420986583641073068105854125060471913930836416667369009514170582831848651921939378451110006113562704005699620419197602549448752802650444589462646332170281268255813665905615387269348720692230068701721670742423050385974680358920701195299973845205128906960697891429932020649292849065497077440117767693616929767088848790368510300776199101092705714635613968674042749221267282018181705865629738843289759917919084751195485925805417428843563894632311906290912939257073244799385899512676381701455731445225057370347845858046332075
This challenge said : ‘Im lazy, I will common prime’, and they used 3 primes numbers to encrypt 2 messages, so it is easy to decoded them using gcd method from the math package of python with N1 and N2 to get ‘p’ and now we know N1 and N2 are divisible by ‘p’. Here is the script used :
from math import gcd
N1 = 14210100835811893798591078574673509799599435785629793053564426327501908443183140520609964712650358642983958098057476878561030530736365215542967098664119375313082664944585486342081477101282777511562123969111180556724024895406974743062387838042255090695147973536720609137941107044960129028680194236971517888073002151100105846731521577011388381786481148592693877947633594752266751375339343845793557960247402876231491392310849835544042849854976618763858282692179517051698041046443665430087664775796279151997682716153471202272855992352652294689251775067915427628426475352635713405663233084590482443696929909113022275932963
N2 = 9909416432857077839842635526397016570706233946465755602951432796949043674880406344119650144972805415270789891434962888017099110866828044976063665090118377534234873371879049768690146720022551696102687134324629943276999478646284416067186629436851833656583809441164967966550090424942940548947231116810115548706732879023945410706654471380653149453240459855653515761752212366682744164796337152295199350766681091460733764953863450049565749196047542053106675478773561074112623631487376796831953278385382743014813370224165409285606161953552927575831815431930860698082290914569442342899424158943471050819735954770403576952597
enc1 = 11285068260062957431988077411736024737206589628789265002651924086474736320840849806849636018251251828077239003620308962675643100015540623853365642746583641951453604279131710660086093301811269653391260552592819184650086888954000144374933327696033327786284567656140368742793009894248924494218462754463148693302847012797824700163591585045003846574959107642615757249440973617079019352414537053550355304175853187762339891042353537700564042584001146787565804386975441940440343063218321437735266377940408404084148571212095663044215726786503139610972710995694977370136413410338805394618181460386598021764830520920207038119737
enc2 = 230503441339098213197943518548655312076875819226223970855219569665753573103664712609350437983808420986583641073068105854125060471913930836416667369009514170582831848651921939378451110006113562704005699620419197602549448752802650444589462646332170281268255813665905615387269348720692230068701721670742423050385974680358920701195299973845205128906960697891429932020649292849065497077440117767693616929767088848790368510300776199101092705714635613968674042749221267282018181705865629738843289759917919084751195485925805417428843563894632311906290912939257073244799385899512676381701455731445225057370347845858046332075
p = gcd(N1,N2)
q1 = N1 // p
q2 = N2 // p
d1 = pow(e, -1, (p-1)* (q1 - 1))
d2 = pow(e, -1, (p-1) * (q2-1))
m1=pow(enc1,d1,N1)
m2=pow(enc2,d2,N2)
s1 = bytes.fromhex(hex(m1)[2:]).decode("ascii")
s2 = bytes.fromhex(hex(m2)[2:]).decode("ascii")
print(s1 + s2) # GisecCTF{what_is_the_problem_in_using_a_common_prime?Easy_factorization!}
RSA04
Here’s the final challenge :
from Crypto.Util.number import *
from sympy import *
def getPrimes():
while True:
p=getPrime(1024)
q=nextprime(p)
return p,q
def encrypt(data,modulus,exponent):
m = bytes_to_long(data)
return pow(m,exponent,modulus)
p,q=getPrimes()
n=p*q
e=65537
flag=b'REDACTED'
encrypted = encrypt(flag,n,e)
print(f"modulus N : {n}")
print(f"encrypted message : {encrypted}")
## output
# N : 32219919510426152586745238462649846338856481501819261146488316825965855721457811962585612767322123075029014657633852192407854077747563039617613911265405809733465026568360333424003409099686474913664136521459660017839882803821030404889448337865632805211648367923951305187383572695647101337464402291662464996445236498940229779310623780344162769846221850742109201215053773533136088807007609854117395555230462172074537197893160531285779716438101477700106712937070212574440180234268471977381509645029287186503047434243990221804033394490658118899601265880531489306947634567090005521121362945120861257418372602804954782336679
# C : 17552096424859510977092930360732323539522176558134679498735487491739875408448150133984590260023653976535459076334255165071668209148920842455301832130700694570152799695358683522181484886942897675200804761021836530194116282951027361683594371713428899936834229050124797356404498440751104779827345304245900904255976048865039997051799407805943793352553654942723277966262545196330073401305053126409633919451477054182835431218032209414333276441659480900964864691220770851928431232233794315904644700838299181746804466682417732732091259452631598499303529324015892457416611728775391883219742918980974137422081765639502244594915
For this one we had to see that the 2 primes numbers used are close to eachother. Knowing this, we can used the fermat’s little theorem to effectively bruteforce the right numbers, I used this little script that use a fermat’s little theorem implementation I found on github :
N = 32219919510426152586745238462649846338856481501819261146488316825965855721457811962585612767322123075029014657633852192407854077747563039617613911265405809733465026568360333424003409099686474913664136521459660017839882803821030404889448337865632805211648367923951305187383572695647101337464402291662464996445236498940229779310623780344162769846221850742109201215053773533136088807007609854117395555230462172074537197893160531285779716438101477700106712937070212574440180234268471977381509645029287186503047434243990221804033394490658118899601265880531489306947634567090005521121362945120861257418372602804954782336679
enc = 17552096424859510977092930360732323539522176558134679498735487491739875408448150133984590260023653976535459076334255165071668209148920842455301832130700694570152799695358683522181484886942897675200804761021836530194116282951027361683594371713428899936834229050124797356404498440751104779827345304245900904255976048865039997051799407805943793352553654942723277966262545196330073401305053126409633919451477054182835431218032209414333276441659480900964864691220770851928431232233794315904644700838299181746804466682417732732091259452631598499303529324015892457416611728775391883219742918980974137422081765639502244594915
# https://github.com/Headorteil/RsaCtfTool/blob/master/fermat.py
def isqrt(n):
x = n
y = (x + n // x) // 2
while y < x:
x = y
y = (x + n // x) // 2
return x
def fermat(n):
a = isqrt(n)
b2 = a*a - n
b = isqrt(n)
count = 0
while b*b != b2:
a = a + 1
b2 = a*a - n
b = isqrt(b2)
count += 1
p = a+b
q = a-b
assert n == p * q
return p, q
p,q = fermat(N)
phi = (p-1)*(q-1)
#calculating d
d = inverse(e,phi)
#decoding ciphertext; i.e: M = pow(C , d) % n
m=pow(enc,d,N)
print(bytes.fromhex(hex(m)[2:]).decode("ascii")) # GisecCTF{Easy_peasy_right?}
thanks again to the entire organizing team, really fun Ctf !!