STANDCON CTF 2021

Space Papaya

Description

I’ve been promised some space payayas but Thanos has encrypted it Could you help me get to my delicious goodness??

Solution

Here’s the source code provided for this challenge:

#!/usr/bin/env python3
from gmpy2 import *

p = next_prime(mpz_random(random_state(), 2**4096))
q = next_prime(p)
e = 65537
n = p * q

m = int(MESSAGE.encode().hex(), 16)

c = powmod(m, e, n)

print('n: {}'.format(n))
print('e: {}'.format(e))
print('c: {}'.format(c))

# n: 194362842708866084215953237234010601714782092068657017473036251138431676671868175490629669131293355984477767779272247912898398003959598463151066327473719826508089049945621257750848449074932994104814837269421034757100838633416457369464538335095187107124930210006276919089438600966775135156325573710403062047464883549122709123350776456864832248893144446427550937016055121611927144077249211333929866196092711899598411273258926755835322801924551532228696950330545602277465654578138211698686416187777914526845287508335161840449532353167124783735356724420020001236913091469220071460852670489823002071147052631961616736127542307774553784213551564976856862680192529610361702034557744944910925361832621941572380183550749898722374949514136460106207058164376880193895095890731337592265596372136311383975505976373658539080754488958878095929819739059683095890843634555379298978595862942651406158169690562827703147073676346785373790083861582561563008369782363501250191378992269292876039816066218709174263118734404595822048356915079954616024173040278438553459313402270428924062608121168067858306151528190868319071885225544108264749925782330560592480215417954913785029628131817946912132072071073028544681943321456215850218595253490676706062275706321000094834565990465219492368108372464243357854880130522009562946069931768188707691738519573044108555326442766896720655950851164470011771898369770346952788889888492093755625144273054698093660448370077627755233718711030066641378367031583966313900302554231887044203179482191397744246669757016815802390996997529280308699365864745829622981997926231390088157384772877949116604666434897004907955030386342793095734076042434116395844774091900169206181916763238708781774871037938343903139193853528470631949004956352291136030226647324347066772397900205516364442447205487793826622232753382956298250824843380629813818205906683470702831540412778050975345092843721732642350276838457054614948850604421694716563394223556916722778907958340426218859169271015308289476166790080639494788055226687567028709925056618087462428702997126125118029596845966551281440608686343474201915276278183257597304657555212877310709706767336727956810525772106318192285540421821451870148936086342692819049576874727321875121881832817291330007033448759929549864436079804109819708577707453355419019938895736691749711920076052148328406790627343167155687496921184728015803775794667189219633138620080230753579310617631968722793155536612115409526848248365291605624225739530401807631
# e: 65537
# c: 48231407940727026948672141996334529816590411386860292966914913052426047651987115807777841403140631401701249166736959609586369772130907665951564333728016264781106888841594995016670252351243877043043931895035854072541505075903498699581537787497372368026387461275304338678342882903111473016523122047215378268701734813840019057484437006734608455311609606589978328615886021809161996407351531878330820646743145761284259656979198538333302516658013752884296025359474328771699801774792040803990528899610805832697102960908000990289217674047087227612100839918274810879461691566218620108094572187135662070276143399213929056836949141685032577775184655073850890039819023606342148129988769179828433133474387630746462392553047809255640475195268000075192882984582676932698012626821016591108454125334943555006192200330741124548142161400440375747011531508789850387965540536344945763731373457679082441847778497960038863971948480784876442032472906163053089647045655001542030261262366802020060960874257117691078438824193893194414541917317612137645261904776291223687776582045621554101330940016490033717225542054294550509723890916037908439996672821935786685430873048747351042450242135132402651504861858744900133079753832853206911330310597784333649988497694115288456557414447667162368132351388665881330668539885411369791455480519780053033578607022175215519541686716889470249651075696960031469802931249420275612918537198815728307645858921412505760969514941201413816870042202876699227452040578555623960644754047671586444437939411346055325470287778116774616418405759426544629377975037932066699294880194784853999718178319023949211409048595187949438738050805752373774067262605283342238201172745181281789835988232279319864794958750383869310348406998847103542030538181779060240394157480926897363819838099821784624349518599583933815726206097153419947746982886708613483961746873726951138409665385485483718572380938222490388874840647413038783667362727632810016605914301245603947756748870162828963959855754534784070704364459621393050570977513255324417271658411232766634623398123418984633536742998705987579502631970588156672642641810685697296750600811292822227321148497030230391951474359873858004455962186711399896745959043999926635601785091266948248579919445401875107488515808513058701412149085423586181153312241453589223174436758137842395277731895460702559785230565410482969668156381371880091047940473364486255074425419927756644949215073309100344302265133911935519226690015718699828070058406043592623

As we can see from the source code, \(p\) and \(q\) are consecutive primes, as the function next_prime was used on \(p\) to find \(q\). Since the values of \(p\) and \(q\) are pretty close together, we can thus use the Fermat’s Theorem to factorise out \(p\) and \(q\).

The Math

The fermat algorithm uses the fact that:

\[\begin{equation} n = pq =(\frac{q-p}{2})^2 - (\frac{p+q}{2})^2 = x^2 - y^2\end{equation}\]

where:

\[\begin{equation} x = \frac{q-p}{2}, y = \frac{p+q}{2}\end{equation}\]

The algorithm works like this:

  1. \(a = \sqrt{n}\)
  2. \(b = a^2 - n\)
  3. While b is not a perfect square: increment a
  4. Return \(p = a - \sqrt{b}\)

Here’s my code:

n = 194362842708866084215953237234010601714782092068657017473036251138431676671868175490629669131293355984477767779272247912898398003959598463151066327473719826508089049945621257750848449074932994104814837269421034757100838633416457369464538335095187107124930210006276919089438600966775135156325573710403062047464883549122709123350776456864832248893144446427550937016055121611927144077249211333929866196092711899598411273258926755835322801924551532228696950330545602277465654578138211698686416187777914526845287508335161840449532353167124783735356724420020001236913091469220071460852670489823002071147052631961616736127542307774553784213551564976856862680192529610361702034557744944910925361832621941572380183550749898722374949514136460106207058164376880193895095890731337592265596372136311383975505976373658539080754488958878095929819739059683095890843634555379298978595862942651406158169690562827703147073676346785373790083861582561563008369782363501250191378992269292876039816066218709174263118734404595822048356915079954616024173040278438553459313402270428924062608121168067858306151528190868319071885225544108264749925782330560592480215417954913785029628131817946912132072071073028544681943321456215850218595253490676706062275706321000094834565990465219492368108372464243357854880130522009562946069931768188707691738519573044108555326442766896720655950851164470011771898369770346952788889888492093755625144273054698093660448370077627755233718711030066641378367031583966313900302554231887044203179482191397744246669757016815802390996997529280308699365864745829622981997926231390088157384772877949116604666434897004907955030386342793095734076042434116395844774091900169206181916763238708781774871037938343903139193853528470631949004956352291136030226647324347066772397900205516364442447205487793826622232753382956298250824843380629813818205906683470702831540412778050975345092843721732642350276838457054614948850604421694716563394223556916722778907958340426218859169271015308289476166790080639494788055226687567028709925056618087462428702997126125118029596845966551281440608686343474201915276278183257597304657555212877310709706767336727956810525772106318192285540421821451870148936086342692819049576874727321875121881832817291330007033448759929549864436079804109819708577707453355419019938895736691749711920076052148328406790627343167155687496921184728015803775794667189219633138620080230753579310617631968722793155536612115409526848248365291605624225739530401807631
e = 65537
c = 48231407940727026948672141996334529816590411386860292966914913052426047651987115807777841403140631401701249166736959609586369772130907665951564333728016264781106888841594995016670252351243877043043931895035854072541505075903498699581537787497372368026387461275304338678342882903111473016523122047215378268701734813840019057484437006734608455311609606589978328615886021809161996407351531878330820646743145761284259656979198538333302516658013752884296025359474328771699801774792040803990528899610805832697102960908000990289217674047087227612100839918274810879461691566218620108094572187135662070276143399213929056836949141685032577775184655073850890039819023606342148129988769179828433133474387630746462392553047809255640475195268000075192882984582676932698012626821016591108454125334943555006192200330741124548142161400440375747011531508789850387965540536344945763731373457679082441847778497960038863971948480784876442032472906163053089647045655001542030261262366802020060960874257117691078438824193893194414541917317612137645261904776291223687776582045621554101330940016490033717225542054294550509723890916037908439996672821935786685430873048747351042450242135132402651504861858744900133079753832853206911330310597784333649988497694115288456557414447667162368132351388665881330668539885411369791455480519780053033578607022175215519541686716889470249651075696960031469802931249420275612918537198815728307645858921412505760969514941201413816870042202876699227452040578555623960644754047671586444437939411346055325470287778116774616418405759426544629377975037932066699294880194784853999718178319023949211409048595187949438738050805752373774067262605283342238201172745181281789835988232279319864794958750383869310348406998847103542030538181779060240394157480926897363819838099821784624349518599583933815726206097153419947746982886708613483961746873726951138409665385485483718572380938222490388874840647413038783667362727632810016605914301245603947756748870162828963959855754534784070704364459621393050570977513255324417271658411232766634623398123418984633536742998705987579502631970588156672642641810685697296750600811292822227321148497030230391951474359873858004455962186711399896745959043999926635601785091266948248579919445401875107488515808513058701412149085423586181153312241453589223174436758137842395277731895460702559785230565410482969668156381371880091047940473364486255074425419927756644949215073309100344302265133911935519226690015718699828070058406043592623

from Crypto.Util.number import long_to_bytes
import gmpy2
import math

## Fermat Implemenation

a = gmpy2.isqrt(n) 
b2 = a*a - n
b = gmpy2.isqrt(n) 
count = 0
while b*b != b2:
    a += 1
    b2 = a*a - n
    b = gmpy2.isqrt(b2) # int(b2**0.5)
    count += 1
p=a+b
q=a-b
assert n == p * q

totient = (p-1)*(q-1)
d = gmpy2.invert (e, totient) # d = e^(-1) mod totient
m = gmpy2.powmod (c, d, n)

print (long_to_bytes(m))

And here’s the flag: STC{where_did_my_space_papayas_go}.

Rocket Ship Academy

Description

Oracle: a person or thing regarded as an infallible authority on something.

Do we have one of those here?

Solution

This is a chosen ciphertext attack against RSA. Hence, we can send a certain ciphertext to decipher the message.

I sent this:

\[\begin{equation} c' = c * 2^e\end{equation}\]

as a ciphertext to get the flag.

How the Math Works

Basically if you send \(c’\) as a ciphertext, this is what happens.

\[\begin{aligned}(c * 2^e)^d \mod n &= c^d *2^{ed} \mod n \\ &=(c^d \mod n)(2^{ed} \mod n) \mod n \\ &= m *(2^{1+kϕ(n)}) \mod n \\ &= m* 2* (2^{1+kϕ(n)}) \mod n \\ &= 2m * 1 \mod n \end{aligned}\]

The Script

from pwn import *
from Crypto.Util.number import long_to_bytes



p = remote('20.198.209.142', 55002)
n = int(p.recvline().strip().lstrip(b"n = "))
e = int(p.recvline().strip().lstrip(b"e = "))
c = int(p.recvline().strip().lstrip(b"c = "))

ct = c * pow(2, e, n) % n
p.recvuntil(b"Enter ciphertext:")
p.sendline(str(ct))

decrypt = int(p.recvline().strip().decode().lstrip("Decrypted: "))

log.info(f"flag is {long_to_bytes(decrypt//2).decode().strip()}")

And the flag is, STC{ch0s3n_c1ph3rt3xt_d7b593cd54baba9e2ffa49215d33e4c657cf230a}.

Mind Reader

Description

So you think you are the best mind reader in the entire galaxy? We shall see about that…

#!/usr/bin/env python

import random
from string import ascii_lowercase

life = 3

def printFlag():
    print("[REDACTED]")
    exit(0)
    
def banner():
    print(" ╔══════════════════════════════════════════════════════╗")
    print(" ║                                                      ║")
    print(" ║           ███╗   ███╗██╗███╗   ██╗██████╗            ║")
    print(" ║           ████╗ ████║██║████╗  ██║██╔══██╗           ║")
    print(" ║           ██╔████╔██║██║██╔██╗ ██║██║  ██║           ║")
    print(" ║           ██║╚██╔╝██║██║██║╚██╗██║██║  ██║           ║")
    print(" ║           ██║ ╚═╝ ██║██║██║ ╚████║██████╔╝           ║")
    print(" ║           ╚═╝     ╚═╝╚═╝╚═╝  ╚═══╝╚═════╝            ║")
    print(" ║                                                      ║")                                            
    print(" ║   ██████╗ ███████╗ █████╗ ██████╗ ███████╗██████╗    ║")
    print(" ║   ██╔══██╗██╔════╝██╔══██╗██╔══██╗██╔════╝██╔══██╗   ║")
    print(" ║   ██████╔╝█████╗  ███████║██║  ██║█████╗  ██████╔╝   ║")
    print(" ║   ██╔══██╗██╔══╝  ██╔══██║██║  ██║██╔══╝  ██╔══██╗   ║")
    print(" ║   ██║  ██║███████╗██║  ██║██████╔╝███████╗██║  ██║   ║")
    print(" ║   ╚═╝  ╚═╝╚══════╝╚═╝  ╚═╝╚═════╝ ╚══════╝╚═╝  ╚═╝   ║")
    print(" ║                                                      ║")
    print(" ╚══════════════════════════════════════════════════════╝")     

def printMenu():     
    print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=")
    print("1. Play game")
    print("2. View Top players")
    print("3. Exit")
    print("Please select your option")
    choice=input("> ")
    print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=")
    return choice

def readFile():
    firstName = open('./first-names.txt', 'r')
    lastName = open('./last-names.txt', 'r')
    game = open('./game.txt', 'r')

    fnArr = firstName.read().splitlines()
    lnArr = lastName.read().splitlines()
    gameArr = game.read().splitlines()

    return fnArr, lnArr, gameArr

def gen(initSeed, fnArr, lnArr, gameArr):
    genNameArr = []
    genHighscoreArr = []
    toPredictArr = []
    seed = []
    r = random.Random()
    r.seed(initSeed)
    for count in range(730):

        x = r.getrandbits(32)
        seed.append(x)
        if (count < 350):

            fnIndex = x & 0xFFFF
            lnIndex = x >> 16
            Name = " ".join([fnArr[fnIndex], lnArr[lnIndex]])
            genNameArr.append(Name)

        if (count >= 350 and count < 700):
            genHighscoreArr.append(x)

        elif(count >= 700):

            toPredictArr.append(gameArr[x & 0xFFFF])
    return genNameArr, genHighscoreArr, toPredictArr

def hangman(tried_letters):
    print ("LIFE: {0}".format(life))
    if len(tried_letters) >= 1:
        print("Used letters: {}".format(", ".join(letters for letters in tried_letters)))

def print_error(why):
    print(why)

def gameLogic(letters, random_word):
    global life
    tried_letters = []

    while True:
        try:
            hangman(tried_letters)

            for letter in letters:
                print(letter, end=' ')
            letter_input = str(input("\nLetter: ")).lower()

            if  letter_input == random_word:
                print('\nThe word is {0}. You win!'.format(random_word))
                return

            if len(letter_input) > 1 or letter_input not in ascii_lowercase:
                print_error("Invalid Input\nOnly single letters")
                continue

            if letter_input in tried_letters:
                print_error("You already tried this letter!")
                continue 
    
            else:
                tried_letters.append(letter_input)
                if letter_input in random_word:
                    
                    letter_index = [index for index, value in enumerate(random_word) if value == letter_input]
                    
                    if len(letter_index) > 1:
                        for i in letter_index:
                            letters[i] = letter_input
                    elif letter_input in letters:
                        print_error("You already tried this letter!")
                        continue
                    else:
                        letters[letter_index[0]] = letter_input
                else:
                    life -= 1
                
                if ''.join(letters) == random_word:
                    hangman(tried_letters)                
                    for letter in letters:
                        print(letter, end=' ')                
                    print('\nThe word is {0}. You win!'.format(random_word))
                    return

                elif life == 0:
                    hangman(tried_letters)
                    print('\nThe word is {0}'.format(random_word))
                    print("The Hangman was hanged. You loose!")
                    exit(0)

        except IndexError:
            print_error("Invalid Input\nOnly single Letters")
            continue

def playGame(toPredictArr):
    print("So you think you are a mind reader huh!?")
    print("Try to guess what i am thinking 30 times in a row and win the grand prize")
    print("I am even going to give u 3 chances, not like that would help >:)")
    
    for idx, words in enumerate(toPredictArr):
        letters = []
        for _ in range(len(words)):
                letters.append("_")
        print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
        print("Round " + str(idx+1))
        print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
        gameLogic(letters,words)

    if idx == 29:
        printFlag()

def viewTopPlayers(nameArr, highscoreArr):
    print("Now showing the top 350 players!!!")
    print("══════════════════════════════════")
    print('{0:25} {1}'.format("Name", "ID"))
    print("=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-")
    for x in range (len(nameArr)):
        print('{0:25} {1}'.format(nameArr[x], highscoreArr[x]))

def main():
    initSeed = random.getrandbits(32)
    fnArr, lnArr, gameArr = readFile()
    nameArr, highscoreArr, toPredictArr = gen(initSeed, fnArr, lnArr, gameArr)

    banner()

    while True:

        choice = printMenu()
        if(choice == '1'):
            playGame(toPredictArr)
        elif(choice == '2'):
            viewTopPlayers(nameArr, highscoreArr)
        elif(choice == '3'):
            exit()

if __name__ == "__main__":
    main()
    

Solution

(I didn’t do this challenge, my teammate did.)

Basically, we are making use of the library randcrack, to exploit the weaknesses of the Python random module.

from pwn import *
from warnings import filterwarnings as fw
from randcrack import RandCrack
fw('ignore', category=BytesWarning)


def answer():
    xx = rc.predict_randrange(0, 4294967295)
    FnIndex = xx & 0xFFFF
    LnIndex = xx >> 16
    Name = " ".join([firstName[FnIndex], lastName[LnIndex]])
    Game = game[xx & 0xFFFF]
    return Game

rc = RandCrack()

p = remote('20.198.209.142', 55003)
# p = process('python3 ./mind.py', shell=True)
p.sendline('2')
p.recvuntil('=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-=-\n')
FnArray = []
LnArray = []
HsArray = []
firstName = open('./first-names.txt', 'r').read().splitlines()
lastName = open('./last-names.txt', 'r').read().splitlines()
game = open('./game.txt', 'r').read().splitlines()
x = []

for i in range(350):
    leaderboard = p.recvline().strip().decode().split(' ')
    FnArray.append(leaderboard[0])
    LnArray.append(leaderboard[1])
    HsArray.append(leaderboard[-1])

for i in range(350):
    fn = firstName.index(FnArray[i])
    ln = lastName.index(LnArray[i]) << 16
    x.append(ln | fn)


for i in range(350):
    x.append(HsArray[i])

# print(x)
for i in range(700 - 624, 700):
    rc.submit(int(x[i]))

p.sendline('1')

for i in range(30):
    Game = answer()
    for x in list(set(Game)):
        p.sendline(x)

p.interactive()

Here’s the flag: STC{w0w_wHa7_An_UneXPec7eD_7Wi57}.