CyberStudents Wordmark

interns

Category

Points

Author

Cryptography

150

q

qvipin

Solves (9)

1Profile Picture for minipifminipif12/19 7:56 pm
2Profile Picture for zenobia9082zenobia908212/20 7:36 pm
3Profile Picture for trixaitrixai12/21 8:25 am
4Profile Picture for infernosalexinfernosalex12/21 10:52 am
5Profile Picture for connorm2821connorm282112/21 2:48 pm
6Profile Picture for pligonsteinpligonstein12/23 6:33 am
7Profile Picture for zarnex__zarnex__12/25 12:38 am
8Profile Picture for monstermanyana_47633monstermanyana_4763312/25 3:05 pm
9Profile Picture for verbalverbalverbalverbal12/25 5:36 pm

Description

Interns often struggle with creating things, but Elf Bharmeesh insists you give it a shot. You refused initially, but he went behind your back and encrypted your password, wrapping it in csd{}. Now, you’ve forgotten the password. It’s time to crack it!

Attachments

Hint

There are no penalties for viewing hints. Hints are released 12 hours and 24 hours after the challenge releases.

Submit flag

Discuss this challenge with others in #🎄丨advent-of-ctf on our Discord server.

Write-up

zarnex's write-up was selected as the best write-up submitted for this challenge.

View this write-up on GitHub

I first thought there was a more graceful way to factor N with leaked p and q, but I gave up and just factored with Cado-NFS which is as simple as:

$ make
$ ./cado-nfs.py <N>

Then wait forever for it to factor... Eventually I got 3 primes: p, q, r

p = 196193902291230366369929504455328667247
q = 330729152607130810754863800538364851519
r = 321006911433478242108053772673636286011

I did confirm it was correct by doing 2 things

  • Adding/Asserting the primes, checking if they multiply to N.
  • Comparing the partial salt to my own generated salt from the primes

At this point, we have N factored. Now, we need a mask. Using Python for brute-forcing would take too long, so I had GPT write a C script to brute-force step 3 following the requirements needed for the task.

The biggest issue I encountered was with the first iteration of the mask script. I initially instructed GPT to make a script, which I fixed up, but it was designed to quit after finding a mask. The problem was that there were more than one mask. It wasn’t until later that I realized the mask was wrong but met the requirements. I had to rewrite the script to brute-force all the way through to find all the masks that met the requirements.

After generating the masks, I used my script to check if they were correct. This iterative process helped me identify the correct masks. Below is the output of the script that generated the 3 masks.

Since the script is around 170 lines, I won't paste it here. You can download it here.

$ time ./brute_mask 14148786803331853127777889559896138396417219981773502601578745985604370779076393473723769040986523787622227351205298

# Truncated for brevity

[*] Progress: 4286000000 / 4294967296 (99.79%)
[*] Progress: 4287000000 / 4294967296 (99.81%)
[*] Progress: 4288000000 / 4294967296 (99.84%)
[*] Progress: 4289000000 / 4294967296 (99.86%)
[*] Progress: 4290000000 / 4294967296 (99.88%)
[*] Progress: 4291000000 / 4294967296 (99.91%)
[*] Progress: 4292000000 / 4294967296 (99.93%)
[*] Progress: 4293000000 / 4294967296 (99.95%)
[*] Progress: 4294000000 / 4294967296 (99.98%)

[*] Finished brute force. Found 3 matches.

[Match #1]
    mask_candidate = 0x8252FF0B (2186477323)
    step3 = 14148786803331853127777889559896138396417219981773502601578745985604370779076393473723769040986523787622229537622713

[Match #2]
    mask_candidate = 0x966321D0 (2523079120)
    step3 = 14148786803331853127777889559896138396417219981773502601578745985604370779076393473723769040986523787622229740049506

[Match #3]
    mask_candidate = 0xC51C22A3 (3306955427)
    step3 = 14148786803331853127777889559896138396417219981773502601578745985604370779076393473723769040986523787622228375918353
./brute_mask   4645.55s user 3.10s system 99% cpu 1:17:28.97 total

Now we have our mask, I then started to extract the flag out now. My script is below with comments:

from sage.all import Zmod, pari
from Crypto.Util.number import long_to_bytes

# modular arithmetic setup
N = 20829189282001863372322428196733308195464709019397028562940874561583326274287129648306568901830962480022928679678123
p = 196193902291230366369929504455328667247
q = 330729152607130810754863800538364851519
r = 321006911433478242108053772673636286011

# step3 values from our brute force
step3_values = [
    14148786803331853127777889559896138396417219981773502601578745985604370779076393473723769040986523787622229537622713,
    14148786803331853127777889559896138396417219981773502601578745985604370779076393473723769040986523787622229740049506,
    14148786803331853127777889559896138396417219981773502601578745985604370779076393473723769040986523787622228375918353,
]

# pari primes used for optimization, pari seems to speed up sage like crazy which i came across from https://doc.sagemath.org/html/en/reference/spkg/pari.html
x = pari.addprimes([p, q, r])

# salt is the sum of p and q
salt = p + q

# get modular roots (nth root of value mod mod), returns all roots
def get_roots(value, n, mod):
    try:
        return list(Zmod(mod)(value).nth_root(n, all=True))
    except:
        return []

# process a single step3 value to find the flag
def find_flag(step3):
    step2_roots = get_roots(step3, 5, N)
    if not step2_roots:
        return None
    for step2 in step2_roots:
        step1 = (step2 - salt) % N
        step1_roots = get_roots(step1, 3, N)
        if not step1_roots:
            continue
        for m in step1_roots:
            try:
                plaintext = long_to_bytes(int(m))
                if b'csd' in plaintext:  # flag format starts with 'csd'
                    return plaintext.decode()
            except:
                pass
    return None

# loop through step3 values to find the flag
for step3 in step3_values:
    flag = find_flag(step3)
    if flag:
        print(f"Flag: {flag}")
        break

Running it...

$ sage sol2.sage
Flag: csd{dH4R_dh4r_4Nt1_P1R4cY_5CR33n}

Flag: csd{dH4R_dh4r_4Nt1_P1R4cY_5CR33n}

Need help with a challenge? Is a challenge broken? DM @ModMail in our Discord server.