This is my writeup for the twenty-fifth challenge in the PlaidCTF 2011 competition. The information for the challenge was:

“Amalgamated has banned the use of Solitaire due to loss of productivity.
The only employee who would write a new game for everyone only likes ‘retro’ games, and has placed a text-adventure version of pacman on a company server.
We don’t believe he could have coded this securely, and the server contains a vital key.
Connect to the game here and find the key.
nc a9.amalgamated.biz 60123”

There was no binary available for download, so this level was actually completely blind. Connecting to the game we get:

Typing “help” gave a list of valid commands:

Walking around in the maze eating pills we notice a few things. First I noticed that after eating 16 pills, each increasing my number of points with 8, my number of points suddenly becomes -128. This indicates that a signed 8-bit integer is used to store the number of points.

We also notice that the number of power points seem to determine which item that is listed in our status. Each time we eat a powerpill the number of points increase with 40, and each time we make a move the number of power points (if any) decreases with one. The somehow odd “t” command which is described as “wait” decreases the number of power points with one as well, and surely this must be for a reason.

Moving on through the game eating pills and powerpills as we encounter them, we will sooner or later run into this situation:

We cannot eat another powerpill since this would put us over the maximum of 128. However, if a signed 8-bit integer is used for the number of powerpoints as well the real maximum should be 127. Let’s use the “t” command a few times until our number of powerpoints is 88, and let’s see what happens when we eat the powerpill:

Notice anything strange? Well, besides now having a negative number of powerpoints (which we sort of expected) we can also see that our item-string is now “n” instead of “The some-name Trophy!”. Since the number of powerpoints seemed to determine the item that is listed, it seems likely that the number of powerpoints is used as an index into an array of items/trophies and that when a negative index is being used (-128) it will point into some other string that in this case happened to be “n”.

So where does this “n” come from? Well, since “n” is one of the commands for this game it could certainly be a command string that I’ve entered earlier. It’s not the last command I’ve entered though, so if “n” is indeed a command it is probably stored in a command history buffer. Looking back through the scrollback we also notice:

See the “Command limit reached.” message? We got that on our 127:th command, which probably means that a history of 127 commands are being stored in an array. If this array is stored right before the item string array, the “n” listed as our item after getting a negative number of powerpoints may be taken from this command history array. So, let’s see what happens if we use “t” or some other command until the command limit is reached again:

As you can see I’ve jumped ahead a bit to show you how this bug is turned into an exploitable vulnerability. The very first command entered after the command limit has been reached is used as the item-string, and is actually being passed directly as the format string argument to presumably printf(). This allows to read memory, like we do above, or even write to arbitrary addresses using the %n format string specifier.

After the number of power points have been wrapped into -128, it stays there, so now we can just wrap around the command history buffer each time we want to use a different format string. I developed the following script for being able to play around, enter different format strings and examine their output:

Note that {32-bit-integer-in-hex} will be converted to a raw binary 32-bit integer by the script. This can be used to embed addresses to dump or overwrite. Here’s a sample session:

Here I first determine the offset to the command buffer, by dumping the stack four bytes at a time with %x. The first three bytes are overwritten by a command that has been sent later (‘t’ = 0x74), so if we want to embed an address to read from or write into we should prepend some padding. My second attempt uses %12$s to dereference the pointer that I embed at offset 12 (0x08048000). Obviously NUL-bytes are not a problem, as long as they are located after the format string. The format string buffer is actually located in dynamically allocated memory on the heap, while the command buffer is located in a local variable on the stack. The only problematic byte is 0x0a (e.g. ‘\n’ = line-feed), which ends the command string and is converted to a NUL-byte in the buffer.

Using this technique we are able to dump the entire binary for the level, except for a few bytes at addresses containg 0x0a that we assume are NUL-bytes. This produces a valid enough binary for further analysis in IDA Pro, where we can clearly see the vulnerable call to printf:

We also see that fgets() is used to read our commands, and that the line-feed character is converted to a NUL-byte, just as we have observed:

If we use the format string vulnerability to overwrite a GOT-entry, fgets() seems to be the perfect choice since its first argument happens to be a pointer to our buffer. If we embed a command line to be executed there and point fgets() into system() instead, our command line will be executed.

Now we just need to find system(), either by bruteforcing it or by reading a GOT-entry and calculating the address to system() based on its offset from the function whose GOT-entry we’ve read. We used the latter method to find system() based on its offset from fgets(). Since we had not yet noticed that a5 and a9 used the same libc, we first dumped libc using this vulnerability as well. :) Finally, we ended up with the following exploit:

And finally, here is a sample run:

This is my writeup for the thirty-sixth challenge in the PlaidCTF 2011 competition. The information for the challenge was:

“AED came up with a secret sharing program that looks like innocent food ordering program.
However, there is an information that if you are able to order the following set of food, you can get the secret key.

IMPORTANT: SOUND is VERY VERY IMPORTANT for this mission!!!! MAKE THE VOLUME LARGE before you actually do stuff…

Reverse the program to find out the key!

10 Regular Hamburgers
5 Cheeseburgers
17 French Fries
8 Hot Dogs
20 Regular Coke”

Taking a quick look at the challenge with IDA Pro and OllyDbg respectively I could see that it’s packed, and that it uses miscellaneous anti-debugging and anti-dumping techniques. To get acquainted with the application I tried to make the order, which gave me the following error message after adding 10 regular hamburgers, 5 cheeseburgers and 11 french fries: “You cannot have more than 25 items in your cart.”

When clicking OK and then the Order-button, I got: “Your order confirmation code is Th3m1d4_iS_s!cK”. At this point I couldn’t imagine that I’ve already found the real key, so I continued with trying to reverse-engineer the program for a while before attempting a different order, which resulted in a completely scrambled string as the order confirmation code. Turns out that the developers for this mission messed something up bigtime, and that “Th3m1d4_iS_s!cK” was the actual key. Might look into actually reversing the program someday, but for now I settle with the key. :D

I’m glad that our team would have won the competition even without these 250 points though, wouldn’t have felt fair if this would have been the difference between winning and losing. :)

This is my writeup for the twenty-sixth challenge in the PlaidCTF 2011 competition. The information for the challenge was:

“nc a9.amalgamated.biz 10241”

The binary for the server listening on this port was also available for download. Turns out that this challenge is almost identical with hashcalc1, except for the fact that it is executed through inetd instead of using its own socket handling. Also, in this case the call to strlen() in the function that calculates the hash is inlined. There is another call to strlen() in the function that writes to the socket though. Since the string has been prepended with “<hash> (” before our buffer we need to make sure that this string can be interpreted as valid instructions as well, without triggering a crash.

To change the hash I could simply append to the string, and ended up with the following:

In another tty:

This is my writeup for the twenty-third challenge in the PlaidCTF 2011 competition. The information for the challenge was:

“It seems like AED also has some plans to raise hacker force!
We found this binary as an exploitation practice program in the office, but they forgot to remove the setgid flag on the program.
So we can get the secret key!
ssh username@a5.amalgamated.biz”

Using IDA Pro I see that the binary contains a deliberate stackbased buffer overflow, designed to allow us to overwrite a pointer that is later dereferenced and written into with a user defined value. The decompiled code is as follows:

The pointer p is located directly after the 64-bytes buffer we’re overflowing, and the value we’re writing into the pointer is taken from our second command line argument. Since exit() is called directly after this, we use this to overwrite its GOT-entry. As you can see below, this is located at 0x80497f4.

Since a pointer to our buffer is located at offset 8 on the stack when exit() is called, due to the previous strncpy() call, we can use a pop-pop-ret trampoline to jump there. I found one at address 0x80484d2, and could use this for the following exploit:

If NX would have been effective this challenge would have required some further digging to find a suitable ROP gadget for running system() or execve() for instance. In this case I settled with this to get the key: K3Ys_t0_15_M1nUtEs_0f_F4mE

This is my writeup for the twenty-second challenge in the PlaidCTF 2011 competition. The information for the challenge was:

“nc a9.amalgamated.biz 30001”

The binary for the server listening on this port was also available for download. Simply running strings on the binary reveals that it is a forking socket server that spawns a new process to handle each incoming connection. Connecting to the service we get:

This is an example of connecting to the service on my own machine:

The service prompts for a string, calculates its hash and prints it out to the user before closing the connection. If we send a string slightly larger than 256 bytes the connection is abruptly closed though, which most likely indicates a buffer overflow. Since the binary is compiled with stack canaries enabled this may not be all that useful though. A quick inspection in IDA Pro reveals that the buffer overflow is due to a vsprintf() in the function at 0x08048b22, which I’ve named sock_printf() to be a bit more descriptive. The decompiled version is as follows:

There is also code for checking a stack cookie, but since that is automatically inserted by the compiler I’ve omitted that part from my decompiled code. As you can see, there really isn’t much of value to overwrite on the stack unless we can predict (or bruteforce) the stack cookie, or patch the GOT-entry for ___stack_chk_fail(). Luckily for us there is another issue to exploit, that is revealed by simply sending a “%n” as the string to hash. This will also result in the connection being abruptly closed, which indicates a format string vulnerability.

In IDA Pro we can see that the format string vulnerability is triggered by an fprintf(log_fp, buf), right before the hash is calculated. Since the output is written to a logfile and not back to the socket we are not able to use this to read data from the stack. Since our buffer is on the stack we can easily use this to achieve arbitrary writes to arbitrary addresses though, by embedding the addresses we want to write to in our buffer and finding the offset to the buffer by, for instance, embedding a known writable address in our buffer, using %n at different offsets and switching to a known invalid / non-writable address for each offset that does not trigger a crash. Since we have access to the binary we can determine the offset (5) directly by analyzing the code though.

Due to a mistake by the PlaidCTF organizers, NX was not effective and ordinary shellcode could be used, which saved me some time. Since strlen() is called directly after the vulnerable fprintf(), in the function that calculates the hash, I chose to use the format string vulnerability to overwrite the GOT-entry for strlen() at address 0x0804a41c. Since the stack is randomized it would require bruteforcing to find our shellcode in the stack, but since a pointer to our buffer is passed as the first argument to strlen() we can just stuff our shellcode into the beginning of the buffer and use a suitable trampoline from the binary itself, which is mapped on a fixed address. A pop-ret or a call eax would be good for this purpose, since the buffer pointer is copied into eax before being passed as an argument. I chose the latter, at address 0x080491cb.

At this point of the competition I felt pretty lazy, so ended up exploiting it with a one-liner instead of creating a script for it:

The file cb.bin is an 80 bytes connectback shellcode. Since the beginning of our buffer was at offset 5 I add 20 to this now when I’ve embedded the addresses to write to after the shellcode (20*4=80), and end up with %25$hn and %26$hn to overwrite the two most significant bytes and the two least significant bytes of the GOT-entry respectively.

In another tty I’ve set up a netcat listener on the port that the connectback shellcode connects to:

This is my writeup for the twenty-first challenge in the PlaidCTF 2011 competition. The information for the challenge was:

“We have obtained the binary for AED’s internal data encryption service, running at a9.amalgamated.biz:10240.
Obtain AED’s data encryption key.”

The binary for this level was available for download, so that it could be inspected in IDA Pro and debugged with GDB. Turns out it is executed through inetd, or some similar service. It reads its input data from stdin and writes to stdout, and does not contain any socket handling code by itself.

By analyzing the code in IDA Pro I noticed that snprintf() is called with 80 as its length argument. The problem with this is that the stack buffer it writes to is only 64 bytes large. This is not enough to overwrite the saved return address, we will however overwrite the FILE-pointer for a log file right before fwrite() and fclose() is called. I knew that the FILE-struct contains a pointer to an array of function pointers, and first wrote an exploit that used this to achieve code execution. Since the buffer we control is much smaller than the FILE-struct it was a bit tricky to get right, but I finally ended up with this piece of code to achieve code execution on my own system with ASLR deactivated:

Note that I overwrite the FILE-pointer with the address to my buffer minus 18. This is to make sure I am able to control the _vtable_offset variable and make fwrite() use the function pointer I’m placing in the beginning of the buffer. It is also critical that the word that happens to be at this stack address is a negative number (e.g most significant bit set), to avoid a crash due to dereferencing a value out of my control.

If you want to try this on your own system, deactivate ASLR with sysctl kernel.randomize_va_space=0 (as root) and determine the buffer address with:

If your libc is stripped from symbols, you can disassemble system() to find the do_system() function pointer. Example:

This depends on two addresses though, the address to the buffer and the offset to the libc internal do_system() function. Although it’s certainly possible, it could potentially take a pretty long time to bruteforce both of these values when ASLR is active and unfortunately this is the case on a9.amalgamated.biz where the keyleak server resides. So, let’s figure out another way to exploit this bug.

Reading the description for this mission we learn that we only need to obtain the encryption key used by the program, we don’t actually need code execution. So, let’s see how the key is used by the program and if there is some way we could retrieve it without actually executing code or getting a shell.

The file descriptor to the key file has not yet been opened when the vulnerability is triggered, so finding a way to directly dump the contents of the key file seems unlikely. However, this piece of code indicates that we may do something else that might be useful:

A buffer is read from file descriptor 0 = stdin = the socket descriptor in this case. Then the key is read from the key file descriptor. If we overflow the FILE-pointer with the address of stdin we can close this descriptor, which will make the next call to open() to reuse file descriptor 0. The next call to open() opens the key file in this case, which means that the key will be read into usr_buf (user input buffer). Since this read() will consume everything in the keyfile, the read() into key_buf will result in an empty string.

So, how would this help us? Well, analyzing the rest of the code it derives an AES encryption key from the contents of the keyfile combined with a 32 byte random salt.

The key would in this case be an empty string. :)

Then it continues with generating a random IV for initializing the AES-256 cipher along with the key.

It finishes off by encrypting the user input buffer (containing the real key), and then writing the salt, the iv and the encrypted buffer to stdout.

So, if we manage to close stdin we will get the real key encrypted with a key we will be able to determine by using the known salt, iv and an empty string as key.

Since ASLR is used we had to use bruteforce to find the stdin pointer. Only 12 bits of the glibc base is randomized, and empirically it seems as if some addresses are much more likely to be used than others. The most effective way to bruteforce is therefore to use a static “guess”, based on the address taken from a previous execution. Since we had not yet realized that the a5 box where we already had shell access through SSH used the same glibc version as the a9 box, we used our PC Rouge exploit to upload the keyleak program and the following small library:

By loading this library with LD_PRELOAD when executing keyleak we get to know the address of stdin for this particular execution attempt, and can use this value to bruteforce with.

To perform the bruteforce I developed the following script:

This is the output from a sample execution:

The first 32 bytes is the salt used when deriving the encryption key. This is followed by the 16 bytes IV buffer that is used when initializing AES, and finally by 32 bytes representing the encrypted key.

To get the plaintext key we can now feed this into the following program, originally developed by Kaliman while I worked on the stdin bruteforce script:

This is my writeup for the twentieth challenge in the PlaidCTF 2011 competition. The information for the challenge was:
“They have an update for the vulnerable C++ program trying to fix the bug.
However, the coders at AED suck and introduced another stupid mistake.
Get a shell (and the key, too.)
ssh username@a5.amalgamated.biz”

Since this challenge is about exploiting a C++ program, I immediately think vtable overwrite. Not surprisingly, I’m right. :) For those of you not familiar with how C++ code looks at the assembly level I can tell you that it can be quite a mess to reverse-engineer, due to lots of indirection. When calling virtual member functions, e.g. functions that may be overridden by classes that inherit from a base class, a vtable (virtual table) is used to determine what function to call. The vtable is simply a pointer to an array of function pointers, with each offset into the array representing a different virtual function, stored in the beginning of the object.

Even though this may make things slightly less straight forward when debugging and reverse-engineering, it can actually be quite handy for an exploit developer such as myself. Let’s say we have a heapbased overflow in an application with a reasonably secure malloc() implementation (e.g. most ones on mainstream desktop/server operating systems nowadays). In the good old days we could easily turn a heapbased overflow into a write-4-anywhere primitive by overwriting malloc chunk headers.

Nowadays we have to deal with a lot of integrity checks, such as the safe unlink() that ensures that p->next->prev == p && p->prev->next == p when taking a chunk p off its current free chunk list. Small chunks are usually stored in a single-linked list instead, which makes this kind of check impossible. :) With all the heap integrity checks and exploit mitigation mechanisms of today we are usually better off using an application dependent attack than trying to defeat all of them though.

In this case, we have two classes (Awesomeness and EpicFailure), inheriting from the same base class with one virtual function. It just so happens that before the call to this virtual function there is an overflow due to a sprintf() to a buffer in the Awesomeness-object. There is also another overflow, due to a strcpy() to a buffer in the EpicFailure object but this will not be needed. This is the decompiled code of the relevant function:

Note that the data is also copied into a static global buffer (buf_in_bss), this will come very much in handy due to the nature of a vtable overflow. Remember that a vtable pointer is actually a pointer to an array of function pointers? That means we will have to overwrite it with not just the pointer to our shellcode, but to a pointer that at a certain offset (in this case zero) contains the pointer to our shellcode. We have an extra level of indirection, which makes attacker controlled data at fixed addresses very useful.

Anyway. Let’s see what happens after the overflow. First, in the main() function:

Then in the decompiled EpicFailure_Report() function:

Since the EpicFailure object is allocated after the Awesomeness object, the sprintf() overflow will overwrite the vtable pointer in the EpicFailure object, we can use the call via failObj->vtbl to control EIP by now. To do this we simply need to overflow the vtable pointer with the address to buf_in_bss+strlen(“Uploading… [“), put the address to buf_in_bss+strlen(“Uploading… [“)+4 in the beginning of the first command line argument and the address of something we want to be executed right after it.

The executable has NX enabled, but since the box does not have any hardware NX support we can still execute code in the .bss segment for instance, so in this case we can simply put our shellcode in the buf_in_bss buffer as well. My final exploit looks like this:

For completeness, I also solved it without relying on being able to execute code in the .bss segment. In this case I had to bruteforce the libc-base and call an internal function called by system(), that contains the command line to be executed in the eax register.

This is my writeup for the nineteenth challenge in the PlaidCTF 2011 competition. The information for the challenge was:

“This time, let’s attack /opt/pctf/z2/exploitme.
ssh username@a5.amalgamated.biz”

Using IDA Pro I can see that the program takes one command line argument, which is interpreted as an unsigned integer representing the number of bytes to read from stdin into a 512 byte buffer. There is a length check that is meant to ensure that we don’t pass more than 512 as the length argument. However, it also checks the return value of a function that is supposed to log the error.

Examining the log_error() function (named by me) we see that the logfile is /home/z2/logs/assert.log, and that the function returns zero if the file could not be opened. If we can somehow get fopen() to fail, that would mean the length check in the main() function becomes ineffective.

Normally, I would accomplish this by using

to set the maximum number of open file descriptors before executing the vulnerable program, in this case we don’t even need that though. :D Turns out the /home/z2/logs directory does not exist, and thus the fopen() will always fail. Since the buffer that is read into is located on the stack, we end up with a vanilla stackbased buffer overflow.

No stack canaries are used, and the executable does not even have NX enabled. The only hurdles we need to overcome are that the stack is randomized and that the binary is statically linked (so we can’t jump to system() in libc). To deal with this, I chose to use a return-to-text based attack. For completeness I chose to make two variants of my exploit, one that relies on .bss being executable and one that would work with full NX too.

My first exploit simply returns into read(), with the stack set up to read from file descriptor 0 (stdin) to the .bss segment, and then return into that buffer. It relies on the following addresses, easily retrieved from the binary using IDA Pro:

  • 0x80489d8 – read()
  • 0x804b510 – .bss

My second exploit returns into mmap() to map a writable and executable buffer at a fixed location, then into an address with six pops and a ret instruction to go past the mmap() arguments on the stack and return into read() just as in the previous exploit. This relies on the address of:

  • 0x8049abc – mmap()
  • 0x8048529 – pop * 6 + ret
  • 0x80489d8 – read()

This is the source of my second exploit:

Note the call to sleep between the exploit buffer and the shellcode. This is to make sure that only the exploit buffer is read by the fgets(), and the shellcode by the call to read() that I set up.

This is my writeup for the eighteenth challenge in the PlaidCTF 2011 competition. The information for the challenge was:
“Get access to the key using /opt/pctf/z1/exploitme.
ssh username@a5.amalgamated.biz”

/opt/pctf/z1/exploitme is an SGID binary, which is executed with the privileges of the z1key group. Using IDA Pro to analyze the code I quickly spot an exploitable race condition. The tempnam() function is called to generate a temporary filename in /tmp/chal_XXXXXX, where XXXXXX is a random string. To make sure that such a file does not already exist, stat() is used. Then fopen() is used to create the file and write the string given as a command line argument to it.

This means that if we are able to create a symbolic link from the filename generated by tempnam() to a file we want to create or overwrite before the vulnerable application before the call to fopen(), we are able to create a file owned by the z1key group. So, let’s log in the server and see what this allows us to do:

Ah, perfect. The /opt/pctf/z1key/cron.d directory is writable to by the z1key group, and any filename ending with ‘.sh’ will be interpreted as a shellscript and executed once a minute. The key we need is stored in /opt/pctf/z1key/key, so let’s use the vulnerability to execute something like ‘mail z1_201 < /opt/pctf/z1key/key' to mail us the key. We don't want to copy it to /tmp or something like that, since that would mean other teams would be able to read it as well. z1_201 is our personal team account for this challenge. In this case, IDA Pro was actually quite superfluous. The bug would have been obvious just by running the application:

By using strace, for instance, we would have seen that the data written to the temporary file is our command line argument and the level can be easily completed as follows:

This is my writeup for the eighth challenge in the PlaidCTF 2011 competition. The information for the challenge was:

“We found the mobile phone that’s left in one of the office.
Out of all applications, The Color Game App seemed suspicious.

We believe the solution to this game is the password of the user for the computer next to it.
Solve it! and get the password!

Key is the color sequence of the buttons in all lower case with no spaces [e.g. redyellowbluegreenred]

These are the screenshot of the game:
b94b784100fe411b2909988c074c947faad85237.jpg
ecdc0a5406ccfeb142b0bd8d4d7523e87cf2bcf0.jpg

This challenge consisted of an iPhone application, compiled for x86 so it can be used with the iPhone Simulator included with the iOS SDK. To analyze the code I used IDA Pro and for dynamically analyzing in runtime I used GDB, attaching to it after I’ve started it with the iPhone Simulator.

With IDA Pro I started with analyzing the viewDidLoad() method for the reverseMeViewController object. Scattered throughout the function are calls to the addObject() method of the NSMutableArray object named holyHandGrenadeOfAntioch. The objects added to the array are of type CFConstantStringClassReference and the strings that are added are, in order:
Blue, Green, Yellow, Blue, Red, Red, Red, Blue, Purple, Yellow, Green, Orange, Blue, Blue

By listing the cross references to the holyHandGrenadeOfAntioch object I find the reverseMeViewController.sheepFucker() method, which compares the contents of holyHandGrenadeofAntioch array with the contents of another array named donkeyFucker, and by listing the cross references to the donkeyFucker object I find that it is referenced in the following methods:

  • allDayHomeBoy
  • myLifeBeLike
  • weAreTheKnightsWhoSayNi
  • blameGalgara
  • bitch
  • brooooooooo

These are the handlers that are called when the addRed/addYellow/etc buttons are pressed in the applications, and by analyzing them in IDA I can see that they add the following strings to the donkeyFucker object:

  • allDayHomeBoy – Blue
  • myLifeBeLike – Green
  • weAreTheKnightsWhoSayNi – Yellow
  • blameGalgara – Purple
  • bitch – Red
  • brooooooooo – Orange

Now, a hasty assumption would be that the strings added to the array equals the actual colors that are seen in the GUI. In this case our key would be:
bluegreenyellowblueredredredbluepurpleyellowgreenorangeblueblue

Of course, this is not the case. We have a little bit of work to do still, and I chose to take the easy route of simply using GDB and setting a breakpoint on each of the button press handlers.

First, we start the application in the iPhone Simulator:

Second, we attach to the process in GDB and set our breakpoints:

I now simply press each button in order (red, yellow, green, purple, blue, orange) and see which handler is called for which color:

This gives me the following mapping between handler and color:

  • allDayHomeBoy – Red
  • myLifeBeLike – Yellow
  • weAreTheKnightsWhoSayNi – Green
  • blameGalgara – Purple
  • bitch – Blue
  • brooooooooo – Orange

Combined with the knowledge of which string is added to the donkeyFucker array in each of these handlers, I can now map a string in the array to the actual color in the GUI:

  • Blue -> Red
  • Green -> Yellow
  • Yellow -> Green
  • Purple -> Purple
  • Red -> Blue
  • Orange -> Orange

Applying this to the list of strings in the holyHandGrenadeOfAntioch array we get:
Blue, Green, Yellow, Blue, Red, Red, Red, Blue, Purple, Yellow, Green, Orange, Blue, Blue
->
Red, Yellow, Green, Red, Blue, Blue, Blue, Red, Purple, Green, Yellow, Orange, Red, Red

Trying this in the emulator, we get a picture of a happy face. :)

Thus, our key is:
redyellowgreenredblueblueblueredpurplegreenyelloworangeredred