TUCTF 2022 - Rapid Arithmetic
Rapid Arithmetic is a misc challenge that was part of the 2022 TUCTF. To get the flag, we had to solve various math problems in a short amount of time. The description of the challenge was:
Hope you are fluent in braille.
nc chals.tuctf.com 30200
But no braille was used…
Solution
We were prompted with math problems that we had to solve. The problems were in the form of text, Morse code, Roman numerals, and even a “picture”.
Challenge #1
The first challenges were easy, just some basic math equations. The first one was:
(100 * 25)
Answer:
Then these got more complex but were still manageable:
((((5575663 / 107 ) + 16271 ) - 68379 ) / 1 )
Answer:
To solve these we just had to use the eval()
function in python. The code for this was:
from pwn import remote
r = remote('chals.tuctf.com', 30200)
while True:
try:
line = r.recvuntil(b"Answer: ").decode()
except:
print(r.recvall().decode())
break
line = line.split("\n")[-2]
answer = eval(line)
answer = round(answer)
r.sendline(str(answer).encode())
After ~125 equations, the response changed from:
b'Correct!\n'
...
to
b'Correct! exec('\nimport os\nscript_path = os.path.realpath( \
__file__ )\nnew_program = ""\nwith open( script_path, r" ) \
as f:\n
...
which was a python script that we didn’t want to execute. But we already bypassed this by using line.split("\n")[-2]
.
Challenge #2
The next challenge started after ~338 equations.
b'Correct!\n'
b'one thousand, four hundred and sixty-eight minus twenty-four \
minus two hundred and ninety-five plus two hundred and \
sixteen minus five hundred and ninety-eight minus \
five hundred and ninety-four \n'
b'Answer: '
This was a bit more complex:
from word2number import w2n
if line[0] != "(":
line = line.replace("plus", "+").replace("minus", "-") \
.replace("times", "*") .replace("divided by", "/") \
.replace(",", "")
tmp_line = []
tmp_number = []
for word in line.split(' '):
if word == '+' or word == '-' or word == '*' or word == '/':
if tmp_number[0] == "negative":
tmp_line.append("-" + str(w2n.word_to_num( \
" ".join(tmp_number[1:]))))
else:
tmp_line.append(str(w2n.word_to_num( \
" ".join(tmp_number))))
tmp_number = []
tmp_line.append(word)
else:
tmp_number.append(word)
tmp_line.append(str(w2n.word_to_num(" ".join(tmp_number))))
line = " ".join(tmp_line)
Challenge #3
Then, after ~506 equations, the response changed to:
b'Correct!\n'
b'((CCXLVIII + III) - XIII) \n'
b'Answer: '
The solution for this was:
def roman_to_int(s):
rom_val = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, \
'M': 1000}
int_val = 0
for i in range(len(s)):
if i > 0 and rom_val[s[i]] > rom_val[s[i - 1]]:
int_val += rom_val[s[i]] - 2 * rom_val[s[i - 1]]
else:
int_val += rom_val[s[i]]
return int_val
romanReplace = []
tmp_number = []
for i in range(len(line)):
if line[i] in "IVXLCDM":
tmp_number.append(line[i])
else:
if tmp_number:
romanReplace.append("".join(tmp_number))
tmp_number = []
for i in range(len(romanReplace)):
line = line.replace(romanReplace[i], \
str(roman_to_int(romanReplace[i])), 1)
After ~674 equations the response changed back to #1.
Challenge #4
The 4th challenge was:
b'Correct!\n'
b'(((.---- .---- ..--- --... ..--- ...-- / .---- ...-- ) - \
--... ) / ...-- ---.. ) \n'
b'Answer: '
after ~842 equations. Morse code:
MORSECODE = {
'.----': '1',
'..---': '2',
'...--': '3',
'....-': '4',
'.....': '5',
'-....': '6',
'--...': '7',
'---..': '8',
'----.': '9',
'-----': '0'
}
for i in MORSECODE:
line = line.replace(f"{i} ", MORSECODE[i])
Challenge #5
The last challenge started after ~1010 equations.
b'Correct!\n'
b'\n'
b' 2222 2222 8888 44 44 2222 0000 3333 777777 \n'
b'22 22 22 22 88 88 44 44 22 22 00 00 33 33 77 \n'
b' 22 22 8888 444444 ==== 22 00 00 333 77 \n'
b' 22 22 88 88 44 22 00 00 33 33 77 \n'
b'222222 222222 8888 44 222222 0000 3333 77 \n'
b'\n'
b'Answer: '
This wasn’t fun:
x = line.split("\n")[-2]
if x == "":
lines = []
for l in line.split("\n")[2:-2]:
lines.append(l)
chars = []
for i in range(len(lines[0])):
char = ""
for l in lines:
if l[i] != " ":
char += l[i]
chars.append(char)
if chars[0] == "":
del chars[0]
tmp_line = []
tmp_tmp_char = chars[0]
tmp_char = tmp_tmp_char[0]
tmp_line.append(tmp_char)
for i in range(len(chars)):
if chars[i].startswith(tmp_char) and chars[i].endswith(tmp_char):
pass
else:
if chars[i] == "":
tmp_line.append(" ")
tmp_char = " "
else:
tmp_tmp_char = chars[i]
tmp_char = tmp_tmp_char[0]
tmp_line.append(tmp_char)
line = "".join(tmp_line)
line = line.replace(" ", "").replace("=", "-")
Final code
#!/usr/bin/env python
from pwn import remote
from word2number import w2n
MORSECODE = {
'.----': '1',
'..---': '2',
'...--': '3',
'....-': '4',
'.....': '5',
'-....': '6',
'--...': '7',
'---..': '8',
'----.': '9',
'-----': '0'
}
def roman_to_int(s):
rom_val = {'I': 1, 'V': 5, 'X': 10, 'L': 50, 'C': 100, 'D': 500, \
'M': 1000}
int_val = 0
for i in range(len(s)):
if i > 0 and rom_val[s[i]] > rom_val[s[i - 1]]:
int_val += rom_val[s[i]] - 2 * rom_val[s[i - 1]]
else:
int_val += rom_val[s[i]]
return int_val
r = remote('chals.tuctf.com', 30200)
run = 0
while True:
run += 1
print(f"run: {run}")
line = ""
try:
line = r.recvuntil(b"Answer: ").decode()
except:
print(r.recvall().decode())
break
x = line.split("\n")[-2]
if x == "":
lines = []
for l in line.split("\n")[2:-2]:
lines.append(l)
chars = []
for i in range(len(lines[0])):
char = ""
for l in lines:
if l[i] != " ":
char += l[i]
chars.append(char)
if chars[0] == "":
del chars[0]
tmp_line = []
tmp_tmp_char = chars[0]
tmp_char = tmp_tmp_char[0]
tmp_line.append(tmp_char)
for i in range(len(chars)):
if chars[i].startswith(tmp_char) and chars[i] \
.endswith(tmp_char):
pass
else:
if chars[i] == "":
tmp_line.append(" ")
tmp_char = " "
else:
tmp_tmp_char = chars[i]
tmp_char = tmp_tmp_char[0]
tmp_line.append(tmp_char)
line = "".join(tmp_line)
line = line.replace(" ", "").replace("=", "-")
else:
line = x
if line[0] != "(":
line = line.replace("plus", "+").replace("minus", "-") \
.replace("times", "*").replace("divided by", "/") \
.replace(",", "")
tmp_line = []
tmp_number = []
for word in line.split(' '):
if word == '+' or word == '-' or word == '*' \
or word == '/':
if tmp_number[0] == "negative":
tmp_line.append("-" + str(w2n.word_to_num( \
" ".join(tmp_number[1:]))))
else:
tmp_line.append(str(w2n.word_to_num( \
" ".join(tmp_number))))
tmp_number = []
tmp_line.append(word)
else:
tmp_number.append(word)
tmp_line.append(str(w2n.word_to_num(" ".join(tmp_number))))
line = " ".join(tmp_line)
romanReplace = []
tmp_number = []
for i in range(len(line)):
if line[i] in "IVXLCDM":
tmp_number.append(line[i])
else:
if tmp_number:
romanReplace.append("".join(tmp_number))
tmp_number = []
for i in range(len(romanReplace)):
line = line.replace(romanReplace[i], \
str(roman_to_int(romanReplace[i])), 1)
for i in MORSECODE:
line = line.replace(f"{i} ", MORSECODE[i])
answer = eval(line)
answer = round(answer)
r.sendline(str(answer).encode())
Output:
$ ./solve-rapid.py
[+] Opening connection to chals.tuctf.com on port 30200: Done
run: 1
run: 2
run: 3
...
run: 1174
run: 1175
run: 1176
[+] Receiving all data: Done (140B)
[*] Closed connection to chals.tuctf.com port 30200
Correct!
You got everything correct!
Here is your flag: TUCTF{7h3_k1n6_0f_7h3_m47h_c457l3_15_m3_425927}
Was there something else in there??
Flag
The flag is “TUCTF{7h3_k1n6_0f_7h3_m47h_c457l3_15_m3_425927}”.