@mrexcessive WHA

## Trend Micro CTF 2015 Programming 200

#### The problem

nc ctfquest.trendmicro.co.jp 51740

Do all the sums.

#### The solution

We need to repeatedly evaluate sums provided by challenge server and send back the answer.

It starts simply enough with sums like:

```1 + 4 =
4 * 0 =
```

Then it starts to escalate.

First of by including brackets/braces, then using numbers with commas every three digits to separate thousands; then roman numerals are used; then words - so we get sums such as:

```two * 5 =
CXIV + 174 =
7 - three hundred * 5 =
```

1. Socket receive - get the sum
2. Calculate using Python eval()

Parsing the input to deal with Roman numerals and words. Words are tricky because there are space separators in numbers like three thousand two hundred twenty one but it's still fairly straightforward.

I found Python code on the interwebs for Roman numbers and numbers as words, it just needed joining in and the spaces dealing with. Links are in the code below.

At the end the server starts mixing up the types of input, so normal decimals; Roman numbers and words.

The final question before flag given - preceeded by about 80 other questions - was:

```67095485 * 478545 - 41558 * ( 714363 + 3 ) - twenty one thousand eight hundred sixty two * ( 845 - 82751 ) * 622576 + 9419063254 * four
Going to eval [67095485*478545-41558*(714363+3)-21862 *(845-82751)*622576+9419063254*4]
--> 1146918820371985
Congratulations!
The flag is TMCTF{U D1D 17!}
```

The code I used was:

```#!/usr/bin/python
# calc.py for TrendJP 2015 CTF Pwn 200 - challenge response calculator
#@mrexcessive

import os, sys, code
import socket
import time
import struct
import telnetlib

SERVER = "ctfquest.trendmicro.co.jp"      # the actual challenge server
PORT = 51740

pauseDebugging = False     # use this when debugging locally
goTelnetAtEnd = True       # enable once you have some kind of expectation that it isn't all screwed up
# and you might actually get a shell

localtest = False
if localtest:
#TESTING LOCALLY
SERVER = "localhost"
PORT = 7777

debug = True
loweralphas = "abcdefghijklmnopqrstuvwxyz"
alphanums = "0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZabcdefghijklmnopqrstuvwxyz"
printables =   alphanums + ".,<>?/!\$%^&*()_-+=@'#][{}`#"
s = None

numwords = None

def DoConnect():
global s
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((SERVER,PORT))
assert(s <> None)

def GetResponse(timeout=2):
global s
s.setblocking(0)
total_data=""
begin = time.time()
while True:
if total_data and time.time() - begin > timeout:      # wait timeout sec if we have something
break
elif time.time() - begin > timeout * 2:               # wait 2xtimeout if nothing
break
try:
data = s.recv(1024)
if data:
total_data += data
if "=" in total_data:         # specific to this CTF
break
begin = time.time()
else:
time.sleep(0.1)
except:
pass

def rom_to_int(string):    # from http://codereview.stackexchange.com/questions/5091/converting-roman-numerals-to-integers-and-vice-versa
table=[['M',1000],['CM',900],['D',500],['CD',400],['C',100],['XC',90],['L',50],['XL',40],['X',10],['IX',9],['V',5],['IV',4],['I',1]]
returnint=0
for pair in table:
continueyes=True
while continueyes:
if len(string)>=len(pair):
if string[0:len(pair)]==pair:
returnint+=pair
string=string[len(pair):]
else:
continueyes=False
else:
continueyes=False
return returnint

def int_to_roman (integer):
returnstring=''
table=[['M',1000],['CM',900],['D',500],['CD',400],['C',100],['XC',90],['L',50],['XL',40],['X',10],['IX',9],['V',5],['IV',4],['I',1]]
for pair in table:
while integer-pair>=0:
integer-=pair
returnstring+=pair
return returnstring

def text2int(textnum):    # from http://www.tutorialspoint.com/python/string_isalpha.htm
global numwords
if not numwords:       # only setup once
numwords = {}
print "Setting up numwords"
units = [
"zero", "one", "two", "three", "four", "five", "six", "seven", "eight",
"nine", "ten", "eleven", "twelve", "thirteen", "fourteen", "fifteen",
"sixteen", "seventeen", "eighteen", "nineteen",
]

tens = ["", "", "twenty", "thirty", "forty", "fifty", "sixty", "seventy", "eighty", "ninety"]

scales = ["hundred", "thousand", "million", "billion", "trillion"]

numwords["and"] = (1, 0)
for idx, word in enumerate(units):    numwords[word] = (1, idx)
for idx, word in enumerate(tens):     numwords[word] = (1, idx * 10)
for idx, word in enumerate(scales):   numwords[word] = (10 ** (idx * 3 or 2), 0)
current = result = 0
for word in textnum.split():
if word not in numwords:
raise Exception("Illegal word: " + word)
scale, increment = numwords[word]
current = current * scale + increment
if scale > 100:
result += current
current = 0
return result + current

def DoMaths():
while True:
r = GetResponse()
print r

r,drop = r.split(" =")
if "," in r:
print "*** removed commas"
r = r.replace(",","")
print r

bits = r.split(" ")
r = ""
# these state things for text number parsing
state = 0      # state 0 is not got a word yet, state 1 is last one was a word
joining = ""   # joining words together here
prevans = 0

for onebit in bits:
notdone = True
try:     # try parsing text to numbers first
test = text2int(joining + " " + onebit)
state = 1         # success from text2int() if gets to here without exception
joining = joining + " " + onebit
prevans = test
notdone = False      # we used this chunk
except:
if state == 0:    # no previous word, so just number
pass           # so fall through, with notdone == True
else:             # was a previous word, but this one failed
r += str(prevans) + " "    # so put previous word translation there...
state = 0
joining = ""               # and reset state
# and drop through again with notdone == True for current chunk

if notdone:    # if not used it yet, either roman or symbol or "digits" number
if "L" in onebit or "I" in onebit or "V" in onebit or "X" in onebit or "C" in onebit or "D" in onebit or "M" in onebit: # ROMAN ?
r += "%i" % rom_to_int(onebit)
else:
r += onebit    # just use the bit

# after loop through bits
if state == 1:    # may need to dump out final thing...
r += str(prevans)

print "Going to eval [%s]" % r

# now eval
ans = eval(r)
# and send
print "--> %d" % ans
s.send("%d\n" % ans)
#      else:
#         reroman = int_to_roman(ans)
#         print "--[ROMAN %i]--> %s" % (ans,reroman)
#         s.send("%s\n" % reroman)

if __name__ == "__main__":
vars = globals()
vars.update(locals())
shell = code.InteractiveConsole(vars)
# any startups

if True:
DoConnect()
DoMaths()
#print text2int("fifty two")  # testing

if goTelnetAtEnd:
t = telnetlib.Telnet()
t.sock = s
t.interact()

# go interactive
#shell.interact()    # exit... cos... reasons
```