asisctf 2013: PPPCry

PPPCry

Challenge description:

Tears and Smiles = Decode and Flag

An ip address and port was given and if you connected to it, it gave you a long string of the form (note that the challenge was changed during the competition):

1
46x11^*11x10^*+5x9^*+6x8^*+41x7^*+10x6^*+48x5^*+37x4^*+4x3^*+39x2^*+16x1^*+21+x,14x11^*...

Those were 32 equations in reverse polish notation, each followed by either a single letter or digit and seperated by a comma. The goal was to find the value for x that results in the following letter/digit. For a letter, you had to use the index of the letter in the alphabet and take the result modulo 26. For digits, you had to take the result modulo 10 instead.

The final python script:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
#!/usr/bin/env python

import re
import string
import sock as s

def parse_line(line):
  stack = []

  while len(line) > 0:
    match = re.search("(^\d+)(.+)", line)
    if match:
      num, line = match.group(1), match.group(2)
      stack.append(num)
    elif line[0] in "*+^":
      op = line[0]
      if len(stack) > 1:
        stack, a, b = stack[:-2], stack[-2], stack[-1]
      else:
        stack, a, b = stack[:-1], stack[-1], "0"
      stack.append("{}{}{}".format(a, op, b))
      line = line[1:]
      if op == "+" and all(c.isdigit() for c in b):
        result, line = line[0], line[1:]
        stack, a = stack[:-1], stack[-1]
        stack.append("{}{}{}".format(a, "=", result))
    else:
      stack.append(line[0])
      line = line[1:]
  assert len(stack) == 1
  return stack[0]

def solve(x, coeffs, mod):
  ret = 0
  assert(len(coeffs) == 12)
  for e in range(len(coeffs)):
    ret += coeffs[e]*pow(x,len(coeffs)-e-1)
    ret %= mod
  return ret % mod

def get_coeffs(term):
  ret = []
  term += "*x^0"
  summands = term.split("+")
  i = 11
  for summand in summands:
    coeff, rest = summand.split("*")
    x, exp = rest.split("^")
    while i > int(exp):
      ret.append(0)
      i -= 1
    ret.append(int(coeff))
    i -= 1
  while i >= 0:
    ret.append(0)
    i -= 1
  return ret

def calc(term, x, mod):
  coeffs = get_coeffs(term)
  for i in range(mod):
    result = solve(i, coeffs, mod) % mod
    if result == x:
      return i

f = s.Sock("asis-ctf.ir", 65433, timeout=10)
print f.read_until("START")
while True:
  line = f.read_until("END")[:-4]

  stack = [parse_line(term) for term in line.split(",")[:32]]

  num_count = len([item.split("=")[1] for item in stack if item.split("=")[1].isdigit()])

  res = []
  for item in stack:
    term,var = item.split("=")
    if var.isdigit():
      charset = string.digits
    else:
      charset = string.ascii_lowercase
    new = calc(term, (charset.find(var)), len(charset))
    res.append(charset[new])

  f.send("".join(res))
  f.read_until("OK\n")
print f.read_all()
print "finished"