You can not select more than 25 topics
Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.
234 lines
8.2 KiB
234 lines
8.2 KiB
from __future__ import absolute_import
|
|
|
|
import ast
|
|
import re
|
|
import operator as op
|
|
import pyparsing
|
|
|
|
from ..exceptions import CloudflareSolveError
|
|
from . import JavaScriptInterpreter
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
_OP_MAP = {
|
|
ast.Add: op.add,
|
|
ast.Sub: op.sub,
|
|
ast.Mult: op.mul,
|
|
ast.Div: op.truediv,
|
|
ast.Invert: op.neg,
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
|
|
class Calc(ast.NodeVisitor):
|
|
|
|
def visit_BinOp(self, node):
|
|
return _OP_MAP[type(node.op)](self.visit(node.left), self.visit(node.right))
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
def visit_Num(self, node):
|
|
return node.n
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
def visit_Expr(self, node):
|
|
return self.visit(node.value)
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
@classmethod
|
|
def doMath(cls, expression):
|
|
tree = ast.parse(expression)
|
|
calc = cls()
|
|
return calc.visit(tree.body[0])
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
|
|
class Parentheses(object):
|
|
|
|
def fix(self, s):
|
|
res = []
|
|
self.visited = set([s])
|
|
self.dfs(s, self.invalid(s), res)
|
|
return res
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
def dfs(self, s, n, res):
|
|
if n == 0:
|
|
res.append(s)
|
|
return
|
|
for i in range(len(s)):
|
|
if s[i] in ['(', ')']:
|
|
s_new = s[:i] + s[i + 1:]
|
|
if s_new not in self.visited and self.invalid(s_new) < n:
|
|
self.visited.add(s_new)
|
|
self.dfs(s_new, self.invalid(s_new), res)
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
def invalid(self, s):
|
|
plus = minus = 0
|
|
memo = {"(": 1, ")": -1}
|
|
for c in s:
|
|
plus += memo.get(c, 0)
|
|
minus += 1 if plus < 0 else 0
|
|
plus = max(0, plus)
|
|
return plus + minus
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
|
|
class ChallengeInterpreter(JavaScriptInterpreter):
|
|
|
|
def __init__(self):
|
|
super(ChallengeInterpreter, self).__init__('native')
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
def eval(self, body, domain):
|
|
|
|
operators = {
|
|
'+': op.add,
|
|
'-': op.sub,
|
|
'*': op.mul,
|
|
'/': op.truediv
|
|
}
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
def flatten(lists):
|
|
return sum(map(flatten, lists), []) if isinstance(lists, list) else [lists]
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
def jsfuckToNumber(jsFuck):
|
|
# "Clean Up" JSFuck
|
|
jsFuck = jsFuck.replace('!+[]', '1').replace('!![]', '1').replace('[]', '0')
|
|
jsFuck = jsFuck.lstrip('+').replace('(+', '(').replace(' ', '')
|
|
jsFuck = Parentheses().fix(jsFuck)[0]
|
|
|
|
# Hackery Parser for Math
|
|
stack = []
|
|
bstack = []
|
|
|
|
for i in flatten(pyparsing.nestedExpr().parseString(jsFuck).asList()):
|
|
if i == '+':
|
|
stack.append(bstack)
|
|
bstack = []
|
|
continue
|
|
bstack.append(i)
|
|
stack.append(bstack)
|
|
|
|
return int(''.join([str(Calc.doMath(''.join(i))) for i in stack]))
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
def divisorMath(payload, needle, domain):
|
|
jsfuckMath = payload.split('/')
|
|
if needle in jsfuckMath[1]:
|
|
expression = re.findall(r"^(.*?)(.)\(function", jsfuckMath[1])[0]
|
|
|
|
expression_value = operators[expression[1]](
|
|
float(jsfuckToNumber(expression[0])),
|
|
float(ord(domain[jsfuckToNumber(jsfuckMath[1][
|
|
jsfuckMath[1].find('"("+p+")")}') + len('"("+p+")")}'):-2
|
|
])]))
|
|
)
|
|
else:
|
|
expression_value = jsfuckToNumber(jsfuckMath[1])
|
|
|
|
expression_value = jsfuckToNumber(jsfuckMath[0]) / float(expression_value)
|
|
|
|
return expression_value
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
def challengeSolve(body, domain):
|
|
jschl_answer = 0
|
|
|
|
try:
|
|
jsfuckChallenge = re.search(
|
|
r"setTimeout\(function\(\){\s+var.*?f,\s*(?P<variable>\w+).*?:(?P<init>\S+)};"
|
|
r".*?\('challenge-form'\);.*?;(?P<challenge>.*?a\.value)\s*=\s*\S+\.toFixed\(10\);",
|
|
body,
|
|
re.DOTALL | re.MULTILINE
|
|
).groupdict()
|
|
except AttributeError:
|
|
raise CloudflareSolveError('There was an issue extracting "jsfuckChallenge" from the Cloudflare challenge.')
|
|
|
|
kJSFUCK = re.search(r'(;|)\s*k.=(?P<kJSFUCK>\S+);', jsfuckChallenge['challenge'], re.S | re.M)
|
|
if kJSFUCK:
|
|
try:
|
|
kJSFUCK = jsfuckToNumber(kJSFUCK.group('kJSFUCK'))
|
|
except IndexError:
|
|
raise CloudflareSolveError('There was an issue extracting "kJSFUCK" from the Cloudflare challenge.')
|
|
|
|
try:
|
|
kID = re.search(r"\s*k\s*=\s*'(?P<kID>\S+)';", body).group('kID')
|
|
except IndexError:
|
|
raise CloudflareSolveError('There was an issue extracting "kID" from the Cloudflare challenge.')
|
|
|
|
try:
|
|
r = re.compile(r'<div id="{}(?P<id>\d+)">\s*(?P<jsfuck>[^<>]*)</div>'.format(kID))
|
|
|
|
kValues = {}
|
|
for m in r.finditer(body):
|
|
kValues[int(m.group('id'))] = m.group('jsfuck')
|
|
|
|
jsfuckChallenge['k'] = kValues[kJSFUCK]
|
|
except (AttributeError, IndexError):
|
|
raise CloudflareSolveError('There was an issue extracting "kValues" from the Cloudflare challenge.')
|
|
|
|
jsfuckChallenge['challenge'] = re.finditer(
|
|
r'{}.*?([+\-*/])=(.*?);(?=a\.value|{})'.format(
|
|
jsfuckChallenge['variable'],
|
|
jsfuckChallenge['variable']
|
|
),
|
|
jsfuckChallenge['challenge']
|
|
)
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
if '/' in jsfuckChallenge['init']:
|
|
val = jsfuckChallenge['init'].split('/')
|
|
jschl_answer = jsfuckToNumber(val[0]) / float(jsfuckToNumber(val[1]))
|
|
else:
|
|
jschl_answer = jsfuckToNumber(jsfuckChallenge['init'])
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
for expressionMatch in jsfuckChallenge['challenge']:
|
|
oper, expression = expressionMatch.groups()
|
|
|
|
if '/' in expression:
|
|
expression_value = divisorMath(expression, 'function(p)', domain)
|
|
else:
|
|
if 'Element' in expression:
|
|
expression_value = divisorMath(jsfuckChallenge['k'], '"("+p+")")}', domain)
|
|
else:
|
|
expression_value = jsfuckToNumber(expression)
|
|
|
|
jschl_answer = operators[oper](jschl_answer, expression_value)
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
# if not jsfuckChallenge['k'] and '+ t.length' in body:
|
|
# jschl_answer += len(domain)
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
return '{0:.10f}'.format(jschl_answer)
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
return challengeSolve(body, domain)
|
|
|
|
# ------------------------------------------------------------------------------- #
|
|
|
|
|
|
ChallengeInterpreter()
|