Fast Carmichael
2 minutes to read
We got the Python source code of the server:
from secret import FLAG
from Crypto.Util.number import isPrime
import socketserver
import signal
class Handler(socketserver.BaseRequestHandler):
    def handle(self):
        signal.alarm(0)
        main(self.request)
class ReusableTCPServer(socketserver.ForkingMixIn, socketserver.TCPServer):
    pass
def sendMessage(s, msg):
    s.send(msg.encode())
def receiveMessage(s, msg):
    sendMessage(s, msg)
    return s.recv(4096).decode().strip()
def generate_basis(n):
    basis = [True] * n
    for i in range(3, int(n**0.5) + 1, 2):
        if basis[i]:
            basis[i * i::2 * i] = [False] * ((n - i * i - 1) // (2 * i) + 1)
    return [2] + [i for i in range(3, n, 2) if basis[i]]
def millerRabin(n, b):
    basis = generate_basis(300)
    if n == 2 or n == 3:
        return True
    if n % 2 == 0:
        return False
    r, s = 0, n - 1
    while s % 2 == 0:
        r += 1
        s //= 2
    for b in basis:
        x = pow(b, s, n)
        if x == 1 or x == n - 1:
            continue
        for _ in range(r - 1):
            x = pow(x, 2, n)
            if x == n - 1:
                break
        else:
            return False
    return True
def _isPrime(p):
    if p < 1:
        return False
    if (p.bit_length() <= 600) and (p.bit_length() > 1500):
        return False
    if not millerRabin(p, 300):
        return False
    return True
def main(s):
    p = receiveMessage(s, "Give p: ")
    try:
        p = int(p)
    except:
        sendMessage(s, "Error!")
    if _isPrime(p) and not isPrime(p):
        sendMessage(s, FLAG)
    else:
        sendMessage(s, "Conditions not satisfied!")
if __name__ == '__main__':
    socketserver.TCPServer.allow_reuse_address = True
    server = ReusableTCPServer(("0.0.0.0", 1337), Handler)
    server.serve_forever()
Interacting with the server
The server asks to enter a number:
$ nc 178.62.85.130 30337
Give p: 1337
Conditions not satisfied!
Ncat: Broken pipe.
Basically, the server will give us the flag when we pass this check
    if _isPrime(p) and not isPrime(p):
        sendMessage(s, FLAG)
    else:
        sendMessage(s, "Conditions not satisfied!")
In other words, p must be classified as a prime number by the Miller-Rabin primality test, although it is actually not a prime. Therefore, we must find a number that makes Miller-Rabin test fail.
After searching a bit on the Internet, we find this GitHub Gist, which uses a SageMath script to find such value.
Flag
We can just enter that special number and capture the flag:
$ nc 178.62.85.130 30337
Give p: 99597527340020670697596886062721977401836948352586238797499761849061796816245727295797460642211895009946326533856101876592304488359235447755504083536903673408562244316363452203072868521183142694959128745107323188995740668134018742165409361423628304730379121574707411453909999845745038957688998441109092021094925758212635651445626620045726265831347783805945477368631216031783484978212374792517000073275125176790602508815912876763504846656547041590967709195413101791490627310943998497788944526663960420235802025853374061708569334400472016398343229556656720912631463470998180176325607452843441554359644313713952036867
HTB{c42m1ch431_15_f457_8u7_50m371m35_f457_15_n07_7h3_8357}
Ncat: Broken pipe.