PGS Software CTF Contest + Sample Task

26 listopada, 2014 Joanna Rychel

PGS Software zaprasza na swój pierwszy konkurs Capture the Flag. Tym, którzy nigdy nie słyszeli o zadaniach CTF śpieszymy z wyjaśnieniami – zabawa polega na jak najszybszym rozwiązaniu zadań technicznych związanych z programowaniem, steganografią, kryptografią, itd. Start konkursu zaplanowany jest na 5 grudnia 2014 o godzinie 20.00

Konkurs jest inicjatywą dwóch developerów PGS Software, pasjonatów zagadek programistycznych, którzy postanowili rzucić informatyczne wyzwanie innym programistom. Opracowali 10 zagadek o różnym stopniu trudności i różnej punktacji. Zwycięzcą zostanie ten, kto zdobędzie jak najwięcej punktów w jak najkrótszym czasie. Konkurs wystartuje 5 grudnia o 20.00, wtedy opublikujemy zadania. Flagi będzie można wysyłać mailem do 15 grudnia do godziny 12.00.

10 zagadek, 10 dni, 3 nagrody – powodzenia!

Sample task

If you are a beginner who wants to try his hand at a CTF competition, we’ve prepared a sample task solution. We’ll walk you through it, so that you can understand how to approach these kinds of tasks successfully.

A few weeks ago, Polish part of the CERT institution organized a small, entry-point CTF contest, in which the prize was an entry-ticket to the SECURE 2014 conference along with a gadget box. Since the competition is long over, we may look at one of these tasks and try to solve it together.

The first task is called “Code See More”. When you open the task, all you get is a series of hex digits:
2e5R176:: 58681f:: 19eaa19486f9:: 0795d5055147:: 1fbda66f4812

But hey! There’s one character, which is uppercase and actually not hex. We’ve got an “R”, but that’s all we get.

Now let’s check if the hex string is constant. Refresh the page.
0ec57c:: ab1950:: 574b0f974200:: b4082b0700a5:: d24974c9eb87

Woah, the non-hex character is gone. And what if we refresh the page again?
4b3baEf:: 044869:: f18f84642e38:: 5739ec32c999:: 8bcd27f9d7da

Ok, now the character is a hex digit, but it’s uppercase, in contrast to all other ones. It looks like the hex string changes completely each time we refresh the page. But let’s try to first solve the riddle of the uppercase character before we get to the hex values. Since the sequence seems to be different each time we refresh the page, we may collect more data.

Of course we may now refresh page 20 times and write down the results, but hey, we have a few gigahertz at our disposal. Let’s write a short program, which will do that for us. Since time is of the essence in many CTF competitions, we’ll use high-level Python instead of any compilable language, because it’s extremely flexible and fast enough to process data. And it’s naturaly free:

import urllib.request

for i in range(20):

response = urllib.request.urlopen(„http://ctf.secure.edu.pl/pre/tasks/1/?pos=1”)

data = response.read().decode(„ASCII”).split(„::n”)

cleaned = [x.replace(„n”, „”) for x in data]

print(cleaned)

Running the script above gives us the following results:

[’1bO84f5′, '1e3951′, 'f64f726515da’, '7311a420f2ca’, 'f08dcd1db862′]
[’294Rc26′, ’57bbf0′, '3551af7c843f’, '60050c09a51c’, '802332435469′]
[’88d2a2′, '3ca498′, ’01db0f126e35′, '052b5bfb7552′, 'd4f2ef97c10d’]
[’3e56S73′, 'b74c92′, '9926362017fa’, '5e656e29cf39′, '5ea2dede7124′]
[’39a9S7a’, 'bae574′, ’84f41cffd844′, '216717dcac2b’, 'f2c288918917′]
[’40475E9′, 'cdbde9′, ’93e59f9c9302′, '1322dfc2ee8e’, '3ca7d7510bba’]
[’10O3dd6′, '4cf406′, 'df16595972b2′, ’18f824687724′, 'be493a1110cf’]
[’297R650′, 'e3937a’, '3e97ada38853′, '2b4bcf0c1f3f’, '836e8222d25d’]
[’10O3d4c’, 'b527ec’, ’10e8a3ac9a8c’, '4600c799fff5′, '1d3c79a5ea40′]
[’16O5f0d’, '161ab2′, '208495cb6032′, 'd16546f7d29e’, ’60ddbcfec47a’]
[’3930S47′, 'a0e693′, '1cf5ae83177c’, '248f4cf51392′, '399a01f81377′]
[’323aSc1′, '3c7e93′, 'f7a21974c8c3′, '184ede09d484′, '4f05aa1aa1b8′]
[’27aR3ec’, 'a8bcb1′, '4a3e0599f9f8′, '831f67741373′, '195313e84b6f’]
[’3ad8S97′, ’17a08f’, 'd945e60dc8da’, '3ddb23d1529d’, 'a39195887937′]
[’3d4aSdf’, 'c45a11′, 'd8859b14ef98′, 'a0fe438f0a39′, '7aedba38003e’]
[’3633S24′, ’34e84b’, '75576fc1960e’, '5845df1c2156′, ’05b08c1f189c’]
[’260Rb71′, ’99e439′, '2bd12fbe697e’, 'a7d19f6abe9b’, 'b9ac7ed4e16b’]
[’0M335cb’, 'cf5f84′, '181676868776′, ’06b369df4815′, '4acc44675953′]
[’2bfRcf3′, 'f6cc67′, '8bff90ce7771′, 'd6a54caa5d3c’, ’68e87796ab8e’]
[’0Mf79fe’, '5499b1′, 'ccb79567eb7c’, '7c5ffabcbb4a’, '3837d8b58aec’]

Hmm. The extra uppercase letters seem to always be in the same places. We may clean the data a little by temporarily removing all hex characters from the first segment.

import urllib.request

import re

for i in range(20):

response = urllib.request.urlopen(„http://ctf.secure.edu.pl/pre/tasks/1/?pos=1”)

data = response.read().decode(„ASCII”).split(„::n”)

cleaned = [x.replace(„n”, „”) for x in data]

clean_str = re.sub(r”[a-f0-9]”, ” „, cleaned[0])

print(clean_str)

Now, let’s run the script…

R

M

O

S

O

E

O

S

S

O

S

E

M

S

S

O

R

E

M

When we collect all letters in one string, we get the first clue: MORSE. So it seems we’ll be looking for a series of dots and dashes encoded in some way in the given string. A quick look at the network response from the server reveals there is no additional information hidden there:

Response headers also look normal:

1

HTTP/1.1 200 OK

Date: Mon, 24 Nov 2014 18:38:05 GMT

Server: Apache/2.2.22 (Debian)

X-Powered-By: PHP/5.4.4-14+deb7u14

Vary: Accept-Encoding

Content-Encoding: gzip

Content-Length: 73

Keep-Alive: timeout=5, max=100

Connection: Keep-Alive

Content-Type: text/html

It looks like we’re in a dead end. We have to get back to the data. It looks completely random, but maybe there is some kind of pattern in bits? Surely worth a try.

import urllib.request

import sys

import re

for i in range(20):

response = urllib.request.urlopen(„http://ctf.secure.edu.pl/pre/tasks/1/?pos=1”)

data = response.read().decode(„ASCII”).split(„::n”)

cleaned = [re.sub(„[^a-f0-9]”, „”, x) for x in data]

for item in cleaned:

x = bin(int(item, 16))[2:].zfill(4*len(item))

sys.stdout.write(x)

sys.stdout.write(„n”)

Woah. We’re actually looking at series 0’s and 1’s, like real hackers! Unfortunately, it looks like there’s nothing of interest.

001100110100100011010001110100111110000010110000000111000001011011101101011001101001001010000101101110101101111010101111100111000110110101100101101011110100000011000011000101100110011100111111
000011000001111010100101111010000001100010100101101100011010010101100100011010010010100111011101110101111110111010000111001010100101100010001000101101100101011010000001001001011101101100111100
000101001100111101001100101100100001000100110000100010101110011010111100100011011001100000111101001111011000100100010111110100000110101101101110101000000000000100000110000001100001101000100111
010001001110100111111101001100011011011000101001101100101110100111111101010011110010110011111110100001000001101101010010110100011111010111110101110001010011111100011101101110000111001111010011
001000010110101111100010001001111010011111001011111111111011000111100111001100001000111111101111011111000011111100101110110000100010011101110001011100011011011001101000000001111011000111100100
000011110010010111010110101100101101010001001000010010010000100001001001101110011110000100110100010100110110000000100101000101101110001110000110010100111110000000101100101011001000011010111110
001101100001011011110000010011000010010000010100011000010101001001101110011100011000001011001110001010101011000011000000010010001100000011110111010010001010100011100011110001011011010101110110
000101111100010011010110111011111111001000000100001111100111100011101011110100000010000110100101100001110001100000010011000111010111000100010010010101010000100111000100101001110011100100010101
001001110100111100001101111101000000010110111111011111110000011111001101011110101011110110000111100110010110011000110110100100101000110101111011000111010000110011110111001100010010101001100101
001000101110110101001000001000011001100001111010111000101110000010100011000010111000111001001100010100111001110100000111011110101001111011111100011100011000010110110000010111101101111001010111
100011101101110110001011010001011111010011110000111011001001110000101110010000101011100001010011111100100100010111011000100001110010100110111011000110001010100111100001101011010010001000101010
001110001111101100001010111001000000110100110110011101001011111101100100000010001001111000101000101010001100111110110111011110100110101010101011110110101011010011100001101100110001010101001100
001111011100101000000111111010111000101010110100110001010111001110001111110101001100110001000110111111001100110011101111010110010110100010100100101111110111011110111100010000101110000101101011
010010101110000011000001011010101101001011111001111110110100100011100010110000100100110110010100000110010101000000001101010011110010110001110111000110111001000001011110001111100010011010010101
000000011010111100111011111100111011111101010010010100011110011100000111111001001010000010111010110111100010111000011001011001101110010100001111001001101110001111101010010100101100100000010001
000110101111011110101001111001100111001000101110111011111001001010010111011110011110111011000001111100111000110110010001011110000111101000000110010100010111111001100000110011011110110110101010
001001100000010101010000001111110000001111011010100001010110011110101110110001000100111011100010011000100011000000101101001101001110101111100010101000011111110101011010001000010011110111101110
000010101110111000111101100111111100010010101101011010100100011000110011001011100000011111110100111010010011110101000000111111011101011100100111000000101100001011111100101100001111101011010101
000000110000101001100000100100000100111111101101001101100100100100010101011111110011001100010101110001011011010100000000100011010001000001111010110111101000111001010110010000001011010010000000
110010101110110101011010110100111100000010000100000001110101000100010010010110101100011010111110010000000111111110101110011111011000100100100111111001010011000011101101110101101100000101100011

Hmm. Do these hex digits actually contain any useful information? Let’s collect like 100 of them and sort to see how they distribute.

import urllib.request

import sys

import re

lines = []

for i in range(100):

response = urllib.request.urlopen(„http://ctf.secure.edu.pl/pre/tasks/1/?pos=1”)

data = response.read().decode(„ASCII”).split(„::n”)

cleaned = [re.sub(„[^a-f0-9]”, „”, x) for x in data]

s = „”

for item in cleaned:

s += item

lines.append(s)

lines.sort()

for line in lines:

print(line)

The result follows:

00281ed3596736d30bcc2cb4c873faadff803a08255cb7d2
008a8caf4e915737b9b72c4bfb5822104fc4983ecc9bf32d
00bf90a68195c857446338e7bd98e04c8273690ab0d9d38f
0161a1f864deb9160f4bcc36b0b2717d6bdfa28ae330812f
0201baf9f57da75885d05119df9d7670cb2689253d54382b
0722edc7c8b8c4b7a47cda849e4cb8c9e7f9655b3dbec971
0728dc79a9a9f57db33a8419c47ed02fad2bd775e232866b
07709cb066ccf0e96e2f51df39efa9f50f3ace469f88186f
097f8cb277a1ba9a5808ee775aa3d3993be2aa2850d2be60
0a731d90efc592695ef95ec7a286ecd77dea6b267b53dd99
0b16f1f4e965e1f10531d34fbf050bda5d3b91984b36d00b
0b4f314401fdb638fa29b800bda5fbb2d8a3f15ee50a4331
0c158cd4ed54ed901d8339ffc0bc9857abf5f24acab98577
0ca7de0f39a33218b7061407017400db60d84bceb7a54fb9
0d4383b73767fd9a842d78ff1f4c95606180cffff7d89879
0e6799aa7f2b737fc0eee17eb542f8c079d631a3e724972c
0ef05254d93d3b4dcfb4e4f74cdef80928d8b447e1939698
0fb1dc82fb34c5585e0d5ebc116dc52c97b4970fe89f679b
0ffc36bb3225dad2113f56c78f7c7043a89e1ad6ffe9631d
101f508acabecc071e019bc435bf7c2a09f8408caca9ff4d
10d67bf99ab4c005cb8f3a9e4c0de89cbf78dc23151654fb
10fd6896ea4d909d65e728bf9fa0ee8224e57e93e6c6d173
1143aa2eedb2f16b5bc09e20bb34ee2e537c9434008130d1
1412bfb4f3b8baceca24402bcb232322f55b234a5e141775
145bb3c24d279037ac80fb11ac6d9ae80b870adfa57217e4
14ca9c38c157138fa3c0b368c7fc7dc9ece7bc6fe71d12ed
151655964850e912628e9cdc6d61f4f7b442e236e790b362
165eb16d62fc7df4423f493fb74999b03528a8074645f879
18598c82aa861a7f819bf04f5de2429994286d7e2b2a796d
187af1d8858dc697da2f28dd80708cdf2d2225374fe96323
19b8a217dc4d1da57b92517a3f414ba098a569daa1a4939a
19f2434981623308b8e05a0fe218c354b72a2027c6b3f7e3
1ba08ffcb29dd5f1803928973c7d0518d25547edab949c83
1f59f0e49d61fbb0f3d3ce6793ddc6f0b7294ccd90f944ea
1fab283477d8fab1e4d971f5c658278c6d3a6093e709f34a
2013b6c6c4eacb5ceb5966025def6a1dec461bd3b9317f7c
20d841a7ece3eb561f9b8e73971cdd17858d323042c6d96f
21670f74a5b48319e39777f2bb77b8a0fa241b263d3c32ed
21d91ce1162f2f4fce7aa24ad3cdb3b3c67df76bf2960c0d
21dde6f91d881269d95aa59e8ed3b17cf90f667dbf615fbc
22ea8dbe46eba80e557332f5e4f2e286d6a0bacf66ae477a
230bc099a65066114b6bcd50408574bdfa0bdb472c06cd07
23439f8d9f7b6c1cc7ec386306f9ef9063408d3dc4cb41c6
24090fede9ca58bb65300ffc714af7c8e8df6ec73a6a5b09
242975c86c70a2eafe7e57d0191d53f9491143d03860a2f3
24dc396c1cb563200278c341aa55bbfd184205e530683128
255be39c0e4928621593ad05bf547fbc36319b396a1d371e
2598c3e37eb016b5dbc58f19c60add6913bd5a4ff5fbe150
25d41028c2b087e9c164455e7214d39faec2c112b7b95092
25f79c633fe3281a9824b39f772b04415deec51cc0441abb
26b05580d9fa84d5beba7985f996b3c6f1fb615af91696fa
2742acd8a887c188a5f021b74f293e31e5617fa8d20b564e
2775262543f2c9a645ff920d10b6fabd5492cc2f661ac24f
277fb8680edebd50f8479cf173a51e99a034f729550491cd
291a940570954e1aea361e2f10e87f0cd365fc3916d50c7c
291e53b12bcf779b52e07b61c741cd04d5686fed705072ba
29490b1a4344fbabdadb5e0763cd93ee4f318e7d94f1e8b3
29d455cd8c40787f718254c4c60de394ea4edd34d60eb5c8
2bf8a5ad4f4bc8c18c26b5e4bd861e6bfbb5ccbb90498ecf
2c60f6db53cf67a74b473b3705ea8d47c888b0be6c0cbe3b
2ca793ce8cbb0cdc08a8e71f61fdf6c46309e823feebf800
2d4c5c1ca2fe131cc02cba0a7c26c37fe24e384e610cd4d4
2ec38fb5fd94b221429c5b23d934a1f56b9bdf724dc32e4d
2f6f5a924be4e58c83f9717dd97ba71411975d16a37c87c3
2f8872f3d51ca6b7c595e725cf0ac9074a3e939f707bb561
2fe6af4fd647b705528c13c1167325363bfe217abe1363df
300bb3ac37454be49a9cd94e1f257fef4e400b5abe74f2b9
310f766081c729064e5033d469e82f282fadcb556a93d9ca
31d4b0d0889e9f6dda755823c9e44e9bc82a4c1110772227
32728480c63ed6f8c0f48e236ff3cb92e8ba8ba1a59bb7f7
329fe2c3211a9c767d6d4a566d2bc187b1573eb51993dda5
32dce071ff8e8610888e6dbb7c592ccb9762a423dfe3cc4d
32ff545ce56d969d6622ad4c8761aa612113e73b1051de94
330d7fe7c91d78a54477bfe9d58ccabdb2161ea6b56cf1c9
3611c7249f96396ce4e37e3e9efba10ec4e234851f3f413f
377a22311767e294a4961746c6dd36c400c09689a2b2009b
38e16a0f9e08fff0da9c93bd641ef5f55d9bbcb9b52cdef5
38e2656b514dc4582be378b010ab42a9707767f545cc5545
39f71ad377a788167b673787be91a9179d8ae7ed14b4fb8b
3abb0e4874899bbda60efaaf6e4bf68f2df80efd9011030a
3bf6ff99df763e65684dddb1a7fca878d1724d4200e05c58
3c32ba4e28a555e70c24556e11467dbb10195bc704a5005c
3cca3f9c4f2ccc4180e079ec5358fae19d1835ba23b209c4
3d3441e7efddb849823d5e9706d2d3ab5337571deb8e745c
3db905f22efd3e98d34b2f1022f5a4fb27cd4e0377662e58
3ee342a4b2275e89b246b34e4287fa192091e48fb58258d4
4118435fa6ac6da1169316c9751f8281cc8227a181caabf8
412f3d7f18d8e4f6d162495687b806ffd247ecc1b5bf4476
4259c75275351232b61b652f6ccbbe102fd1b8c86a5ad84c
4338c72af555dd5e97dcecdafab23ac8e9aa75b86dc085ab
4566f3a6e91844d5e0b2762844b3a4a161f150260b46f4ec
46593d69a0f5f58964b0641537c992f2537b5b911a9a546d
4a45c7250a30da1c13c5520afbfd8cad5b1684daed0a1654
4afd2d31f99ca97ccb7bc27fbdd4c60fde1f2f2a6610a9a5
4d4312434eedb2f4d59ebabd6da3ed952934335c3756847b
4dc6fe2d3686ca4a4ad860176b79e45fbfaa079797c15026
4df9e6815f562b71b15fd0dd605911569a1c7f8eabdcdb9b
5a71c13892b1a24f1fd27e8c1c84792e90fbde97f87dea82
97bd309c9c7e261633e600ee69287518f2118dcf5922ed9e
98c37987e2332971a4060a77aaf70d28f87eef6382322463

That’s a whole lot of hex values – but despite the fact that first digit is usually small (relatively to all others), the series looks to be quite evenly distributed. So these hex values are most probably just some random data. So where’s the flag?

Ok, so if the data doesn’t matter, maybe the form in which it is stored does? We’re searching for morse code characters – dots and dashes – and we have short and long series of hex digits here, so that would be ..—, what translates to „2”. Hmm. According to rules, the flag should match the „s2k14_[a-zA-Z0-9]” regular expression. So it seems, that we have (mostly probably) a second character of the flag here. But where’s the rest?

A-ha! URL of this task differs a little from other task URL’s:

http://ctf.secure.edu.pl/pre/tasks/1/?pos=1

Now, what happens if we exchange „1” with other value?

http://ctf.secure.edu.pl/pre/tasks/1/?pos=2

4062eE0cc42b9:: b22d79:: dedb18758641

This is -.-, what translates to „k”. 2k… got it! Now we have to check how many characters are there. But that’s easy, http://ctf.secure.edu.pl/pre/tasks/1/?pos=23 gives us:

Too far

Now we can extract the whole flag:

import urllib.request

import re

import sys

morsetab = {

’a’: ’.-’,
'b’: ’-…’,
'c’: ’-.-.’,
'd’: ’-..’,
'e’: ’.’,
'f’: ’..-.’,
'g’: ’–.’,
'h’: '….’,
'i’: ’..’,
'j’: ’.—’,
'k’: ’-.-’,
'l’: ’.-..’,
'm’: ’–’,
'n’: ’-.’,
'o’: ’—’,
'p’: ’.–.’,
'q’: ’–.-’,
'r’: ’.-.’,
's’: '…’,
't’: ’-’,
'u’: ’..-’,
'v’: '…-’,
'w’: ’.–’,
'x’: ’-..-’,
'y’: ’-.–’,
'z’: ’–..’,
'0′: ’—–’,           ’,’: ’–..–’,
'1′: ’.—-’,           ’.’: ’.-.-.-’,
'2′: ’..—’,           ’?’: ’..–..’,
'3′: '…–’,           ’;’: ’-.-.-.’,
'4′: '….-’,           ’:’: ’—…’,
'5′: '…..’,           „'”: ’.—-.’,
'6′: ’-….’,           ’-’: ’-….-’,
'7′: ’–…’,           '/’: ’-..-.’,
'8′: ’—..’,           '(’: ’-.–.-’,
'9′: ’—-.’,           ’)’: ’-.–.-’,
’ ’: ’ ’,               '_’: ’..–.-’,

}

for i in range(0, 22):

response = urllib.request.urlopen(„http://ctf.secure.edu.pl/pre/tasks/1/?pos=” + str(i))

data = response.read().decode(„ASCII”).split()

s = „”

for item in data:

if len(item) < 12:

s += „.”

else:

s += „-„

for k, v in morsetab.items():

if v == s:

sys.stdout.write(k)

break

print()

And that gives us the flag we were seeking for: s2k14-moredotsmoredots

Najnowsze wpisy