# [write-up] Ekoparty CTF - Crypto 50, 100, 200

"Ekoparty takes place annually in Buenos Aires. In this event, attendees, guests, specialists and references on the subject from around the world, have the opportunity to engage with the latest technological innovations, vulnerabilities and tools, in a relaxed environment of knowledge sharing."
The 2015-edition of the "Ekoparty" conference took place from 21-23 October 2015 in Buenos Aires. Starting from the 22nd there was also a CTF that should run until the evening of the 23 October.
I decided to participate together with Sebastian from the Internetwache even though we were both pretty exhausted due to our participation in the hack.lu CTF in the days before.

## Crypto Challenges

The CTF was structured into six categories Trivia, Web, Crypto, Reversing, Pwning and Misc.
Each category had several tasks woth a different amount of points ranging from 50 to 500.
This post will deal with the challenges from the crypto category that were focused on cryptograpy related problems.

## Crypto 50 - SCYTCRYPTO

Being worth just 50 points this should be an easy task.

Description: Decrypt this strange word: ERTKSOOTCMCHYRAFYLIPL

We were given a string, all caps, that should have a meaning that we had to uncover.
My first thought was that this was some kind of rot-n cipher a classic substitution cipher that shifts each character n positions in the alphabet to receive the corresponding character in the ciphertext.

I tried all of them with the help of a little python script I wrote some time ago but no luck. Each shifted ciphertext was just nonsense.

## The solution

While eating lunch I remembered the first cipher I was taught back then in school. We had to roll long strips of paper around a stick and read the text from the top to the bottom.
Looking at our string "ERTKSOOTCMCHYRAFYLIPL" I noted that it has 21 characters. That can be split into 7*3.
Writing the whole thing down in three rows with seven characters each yielded the following:
ERTKSOO
TCMCHYR
AFYLIPL

Reading each row from the top to the bottom we get:
ETARCFTMYKCL..
Nah, that doesn't look right.

We try the other one and get a solution:
ERT
KSO
OTC
MCH
YRA
FYL
IPL

EKOMYFIRSTCRYPTOCHALL or EKO MY FIRST CRYPTO CHALL

Which corresponds to the flag that was accepted and got me 50 points:
EKO{MYFIRSTCRYPTOCHALL}

## Crypto 100 - Weird Vigenere

This challenge, despite being worth only 100 points and thus looking like an easy target, was only solved by 30 people during the contest.
To put this in perspective the tasks Crypto 50 and Crypto 200 were solved by 202 and 160 teams.
I too didn't solve it while the CTF was still running. But when after a couple of days of waiting still no write-ups for this challenge had appeared on the internet I took some time to revisit it.

Description: Crack it!

TEAMNWNNGNIRBYEHNKVMGXEDLYWFJPQGPAVYQLEMPAWMQWRBNNXFDJAJANLXHYQP
EXVBDYNTHNWDUNBBIKODKWNBCAKNBWBBOYKOTARRARCQQJWONOOTHQWYBYHBWJAB
IGOUMQAAZYLXRCQHLEQRAJLUCNKQMWCXEZQDNMTNNTOSRILXRKBRTJETGNTBDHER
PRDRGQDXMJBCBTAXVNWRPHXXMRMVQJBTQTALQFAKXDBCQJIUTYCFRWXTHYGDKEXB
AMAOSWMJMRZBRTWPXKOMIAGUTWKTGPBXQKGTMASBMRMVQJAOANXTWEQKIOCXHWWJ
TVAOJXLUTYNBOSRJXJVYCYXNFGGTCOEKANXMNARPFZVRPHXXFYHBDQWYBYHXCYNB
FAHXOTMZMDAUMFAQLZVBHWBIFYKFSTAKBNRQJAWOODXMNWZTOYKBHHXILFKYGELN
CNLLSWEWCRMMMMRTZELBDILBGOGMHMSJBFGMNSBTQEVNNMIPVVAOKSBTQJVXHOGU
PRLCUORRLOZOGZEAITMRFSAYLYVYQCNXTGDFJSUXLFOCQABTBPAKQJGIFOOHMVQH
GOODGOPHANXJUSLBBNMRHXLULZGNSMQQLYLRQIIUTYGJUNLTAYAXPSLPTHKNUOWJ
AREBMHMAPPOLCWWJRAKJQXWIZCBRBZAZTXWBMHBNPZBMJSUXHNAOPWAQBYHOQALX
GNLEWOANCYHXHQMTHRWTUJLBBZAORMAJGYDXKWLXTPHXHUEOQZHRAPBOAQKYQJAY
TFBVMXEQIKKCGWMTBCDFWUEPPZVYQCNXTGDFJSUXTELMNWRTAKOQFWRXQRLRGJQM
LNWXCIQHNKVFTMEKQJWAGGRYAKGNMIWIAKGNMIIUPAKXTWTNGLGVHMIXYNXHGNAU
PAKBZWRTOJTBHWJXCFKMBTAPGNTBDHEQJNLMGHXXHFOHHWJXCKKFDVNNHYHBIAYB
VDUOUIMFPAKFJPEQLHKJQFAAPNBNFMQOONLEUZGWFDLFBICUFDDJNWRFPKUSOWNX
QMAOCHABJYHBTSLJFMCBUHLUTYQRAXWYINVNJSPIMAALOTIXCNZOQYXXXNLFHXLB
BYKUQIMFPWKEQWRYFFGSUHAYSTWFRSMIBDXXONQKPONHBTABERVYQHWZAKKAQELU
TYHFRIQPPYHXHULNANODNVQHGOUNYSTQLEIQALWQBQUMBTQJPMKJUJAQLHKCDMPJ
FMSFBWNTGYHBRWMXCYVYMIWJFXXJGJTYGDSMNWINCGLRPHXXPGKDBJQOTELMNWMF
LYMYBTAAPRUMWMZIMNNFAXIXHREBAIANORWBDFWZPRDOQABDPUGNBSRVXJVYGGLM
TTGSOVQKXKOMSMKQQQKCMJLZMNOQMVWIXRWSBJKOSTZOGVWIPNXXHUYQZYVRHIEO
QTALSATQZZMOMOWOTGWJQWHMIDXBUNBDFXMFJPKJRAGTMNEQBVKNQWUBOYKOKNQF
INLZQARYVDUDUPTHBPXXISRBIZSBQDWJAVGMNMKIBHGSSMTNCVGMNMKIGRVXGNEQ
LYQJMHXNZYXBJSYTFXWEMAMBGOQRAYEQIXWDDSSTGRDNWMKAZJDCUHQPLPNRIZMD
FVBZGMBDPYSBDWLUPPXXISRBIZQBCSEPTPXXISRBIFQDDSSXLZVYUHQWRXXXGIWI
VFQDDSSXLZVYUHQWKXLZMNYMPDZUQZGFMRVMNWGJTTOSRHXTGHBRBEXBAYHBWPQN
JGGVQOGZCJCBMILUTYAAGGLJHRXMMNYDFXWRIWLULEIMNALDFXSXJPRXYNXAGJYT
YNCBPMNTTFOYUYUXCRBCBTWJLZCHIARTONWMGCQHHRQNBMPIMJWXHXWGLOUFJZKI
FAV

I had already run some basic scripts to crack vigenere ciphers that I wrote for a cryptography course during the contest to no success. So I tried some other stuff, looked at the letter frequencies, tried some more stuff, experimented with different keys (it was not "ekoparty") but the only thing that became clear was that the key length was 8 characters.
You could find that out by comparing the offsets of repeating strings in the ciphertext.

## The solution

The title of this challenge says something about "weird" vigenere ciphers. From this I might have concluded that this is indeed not the normal vigenere cipher but some other form. That is why I searched the web for other forms of the vigenere cipher and found one that was called Beaufort Cipher.
It is comparable to the vigenere cipher and uses a similar encryption and decryption algorithm but uses another order of the alphabet.

Not having a custom tool to solve a beaufort cipher I used an online tool which outputted the correct key together with the decrypted ciphertext:

ANOTHERONEGOTCAUGHTTODAYITSALLOVERTHEPAPERSTEENAGERARRESTEDINCOM
PUTERCRIMESCANDALHACKERARRESTEDAFTERBANKTAMPERINGDAMNKIDSTHEYREA
LLALIKEBUTDIDYOUINYOURTHREEPIECEPSYCHOLOGYANDSTECHNOBRAINEVERTAK
TFORCESSHAPEDHIMWHATMAYHAVEMOLDEDHIMIAMAHACKERENTERMYWORLDMINEIS
AWORLDTHATBEGINSWITHSCHOOLIMSMARTERTHANMOSTOFTHEOTHERKIDSTHISCRA
PTHEYTEACHUSBORESMEDAMNUNDERACHIEVERTHEYREALLALIKEIMINJUNIORHIGH
ORHIGHSCHOOLIVELISTENEDTOTEACHERSEXPLAINFORTHEFIFTEENTHTIMEHOWTO
REDUCEAFRACTIONIUNDERSTANDITNOMSSMITHIDIDNTSHOWMYWORKIDIDITINMYH
NDACOMPUTERWAITASECONDTHISISCOOLITDOESWHATIWANTITTOIFITMAKESAMIS
TAKEITSBECAUSEISCREWEDITUPNOTBECAUSEITDOESNTLIKEMEORFEELSTHREATE
NEDBYMEORTHINKSIMASMARTASSORDOESNTLIKETEACHINGANDSHOULDNTBEHERED
VEINSANELECTRONICPULSEISSENTOUTAREFUGEFROMTHEDAYTODAYINCOMPETENC
IESISSOUGHTABOARDISFOUNDTHISISITTHISISWHEREIBELONGIKNOWEVERYONEH
EREEVENIFIVENEVERMETTHEMNEVERTALKEDTOTHEMMAYNEVERHEARFROMTHEMAGA
INIKNOWYOUALLDAMNKIDTYINGUPTHEPHONELINEAGAINTHEYREALLALIKEYOUBET
YOURASSWEREALLALIKEWEVEBEENSPOONFEDBABYFOODATSCHOOLWHENWEHUNGERE
DFORSTEAKTHEBITSOFMEATTHATYOUDIDLETSLIPTHROUGHWEREPRECHEWEDANDTA
OFWATERINTHEDESERTTHISISOURWORLDNOWTHEWORLDOFTHEELECTRONANDTHESW
AYINGFORWHATCOULDBEDIRTCHEAPIFITWASNTRUNBYPROFITEERINGGLUTTONSAN
DYOUCALLUSCRIMINALSWEEXPLOREANDYOUCALLUSCRIMINALSWESEEKAFTERKNOW
LEDGEANDYOUCALLUSCRIMINALSWEEXISTWITHOUTSKINCOLORWITHOUTNATIONAL
ITYWITHOUTRELIGIOUSBIASANDYOUCALLUSCRIMINALSYOUBUILDATOMICBOMBSY
OUWAGEWARSYOUMURDERCHEATANDLIETOUSANDTRYTOMAKEUSBELIEVEITSFOROUR
OWNGOODYETWERETHECRIMINALSYESIAMACRIMINALMYCRIMEISTHATOFCURIOSIT
YMYCRIMEISTHATOFJUDGINGPEOPLEBYWHATTHEYSAYANDTHINKNOTWHATTHEYLOO
KLIKEMYCRIMEISTHATOFOUTSMARTINGYOUSOMETHINGTHATYOUWILLNEVERFORGI
VEMEFORIAMAHACKERANDTHISISMYMANIFESTOYOUMAYSTOPTHISINDIVIDUALBUT
YOUCANTSTOPUSALLAFTERALLWEREALLALIKETHISISYOURFLAGEKOCRYPTOBEAUFORT

That is the text from the Hacker's Manifesto together with the correct flag at the end:
EKO{CRYPTOBEAUFORT}

The key was: trofuaeb (Beaufort backwards)

## Crypto 200 - XOR Crypter

For this task we were provided with the following description:

Description: The state of art on encryption, can you defeat it?
CjBPewYGc2gdD3RpMRNfdDcQX3UGGmhpBxZhYhFlfQA=

Provided as well was the source code for the "encrypt-function".

import struct
import sys
import base64

if len(sys.argv) != 2:
print "Usage: %s data" % sys.argv[0]
exit(0)

data = sys.argv[1]
padding = 4 - len(data) % 4
data = data + "\x00" * padding

result = []
blocks = struct.unpack("I" * (len(data) / 4), data)
for block in blocks:
result += [block ^ block >> 16]

output = ''
for block in result:
output += struct.pack("I", block)

print base64.b64encode(output)

We can see that the function takes the input in 4 byte blocks
blocks = struct.unpack("I" * (len(data) / 4), data)
for block in blocks:
result += [block ^ block >> 16]

and for each of these blocks the block gets XOR-ed with a by 16 bit shifted version of said block.

There are two important facts that we can take from this:
1) We can view each block independently
2) For each block we have two characters of the ciphertext that are the same as in the plaintext

## The solution

With these two points the solution was pretty straight forward.
The second fact is due to way the XOR happens in the script. Here is a short example:
Assume the input "abcd" and thus
0x61626364
Shift that by 16 bit we get:
0x63640000
If we XOR both values the last two bytes remain the same:
0x02066364

We can also see that if we XOR the resulting ciphertext with the two bytes (that we already have) again we receive the original plaintext for the other two characters.
I wrote a short script that does exactly that and it provided me with the flag:
EKO{unshifting_the_unshiftable}

Here is the script:

ciphertext_b64 = "CjBPewYGc2gdD3RpMRNfdDcQX3UGGmhpBxZhYhFlfQA="
ciphertext_hex = base64.b64decode(ciphertext_b64)
#print ciphertext_hex.encode("hex")

plaintext = ""

for i in range(0,len(ciphertext_hex)/4):

s1 = ciphertext_hex[i*4] # first byte
s2 = ciphertext_hex[i*4+1] # second byte
s3 = ciphertext_hex[i*4+2] # third byte
s4 = ciphertext_hex[i*4+3] # fourth byte
plaintext += chr(ord(s1) ^ ord(s3)) #1st XOR 3rd byte
plaintext += chr(ord(s2) ^ ord(s4)) #2nd XOR 4th byte
plaintext += s3  # 3rd byte is already plaintext
plaintext += s4 # 4th byte is already plaintext

print plaintext

23.10.2015

zurück