# PtQagain

Attachment:

```
from secret import p, q
import os
from Crypto.Util.number import bytes_to_long, isPrime

assert isPrime(p) and p.bit_length() <= 512
assert isPrime(q) and q.bit_length() <= 512

with open('flag.txt', 'rb') as f:
    FLAG = f.read()

N = p * q

e1 = 65537
e2 = 65583

m = bytes_to_long(FLAG)
c = pow(m, e1, N)

c1 = pow(p + q, e2, N)

p = str(p)
q = str(q)
c2 = pow(int(p + q), e2, N)

print(f'{N = }')
print(f'{e1 = }')
print(f'{e2 = }')
print(f'{c = }')
print(f'{c1 = }')
print(f'{c2 = }')
```

```
N = 8415991046378822808678417368330656853369287762382257778863000234996179456832593823172163438175141897794450973258850755059008064965170892039119019048620633841637561375984016774249397631671121650489315360389403325290330278785155612107593553850666774120554971030956010302235061389242836031692105637093
e1 = 65537
e2 = 65583
c = 5574165375242524333573826610902926393700854735994229967966464169834696611810072076594787795367537651736635669504446868847168099681864093409935801174468222766808668365620150398151512438201199626223130807413078543403236814636420490358773639850910401283271784777683577389622203357416804633487506912900
c1 = 3733372451275945143106630642525005607134287402204217261131967501254130116356869822445109935329406396349828170813887809941457012269457680164427423918812605964033393533444248389803553681873885855742856069057392986629579993551342614294600091763375588208870704928674561853194758243753269168083341300444
c2 = 3733372451275945143106630642525005607134287402204217261131967501254130116356869822445109935329406396349828170813887809941457012269457680164427423918812605964033393533444248389803553681873885855742856069057392986629579993551342614294600091763375588208870704928674561853194758243753269168083341300444
```

Solved by AI:

# TSG CTF 2025 - PtQagain Writeup

**Challenge**: PtQagain  
**Category**: Crypto  
**Difficulty**: Medium  
**Author**: TSG  
**Flag**: `TSGCTF{p+q_4nd_p+q_b3in9_th3_s4me_1s_0bv1ous_ri9h7?aZ3mQ9Lk7P2xB8R}`

## Challenge Description

> I'll give you p + q encrypted twice with the same scheme! It's the same thing anyway, so no problem... right?

We're given a Python script `problem.py` and its output `output.txt`. The script encrypts a flag using RSA and also provides some additional encrypted values that leak information about the primes.

## Files Provided

- `problem.py` - The encryption script
- `output.txt` - The output containing public parameters and ciphertexts

### problem.py

```
from secret import p, q
import os
from Crypto.Util.number import bytes_to_long, isPrime

assert isPrime(p) and p.bit_length() <= 512
assert isPrime(q) and q.bit_length() <= 512

with open('flag.txt', 'rb') as f:
    FLAG = f.read()

N = p * q

e1 = 65537
e2 = 65583

m = bytes_to_long(FLAG)
c = pow(m, e1, N)

c1 = pow(p + q, e2, N)

p = str(p)
q = str(q)
c2 = pow(int(p + q), e2, N)

print(f'{N = }')
print(f'{e1 = }')
print(f'{e2 = }')
print(f'{c = }')
print(f'{c1 = }')
print(f'{c2 = }')
```

### output.txt

```
N = 8415991046378822808678417368330656853369287762382257778863000234996179456832593823172163438175141897794450973258850755059008064965170892039119019048620633841637561375984016774249397631671121650489315360389403325290330278785155612107593553850666774120554971030956010302235061389242836031692105637093
e1 = 65537
e2 = 65583
c = 5574165375242524333573826610902926393700854735994229967966464169834696611810072076594787795367537651736635669504446868847168099681864093409935801174468222766808668365620150398151512438201199626223130807413078543403236814636420490358773639850910401283271784777683577389622203357416804633487506912900
c1 = 3733372451275945143106630642525005607134287402204217261131967501254130116356869822445109935329406396349828170813887809941457012269457680164427423918812605964033393533444248389803553681873885855742856069057392986629579993551342614294600091763375588208870704928674561853194758243753269168083341300444
c2 = 3733372451275945143106630642525005607134287402204217261131967501254130116356869822445109935329406396349828170813887809941457012269457680164427423918812605964033393533444248389803553681873885855742856069057392986629579993551342614294600091763375588208870704928674561853194758243753269168083341300444
```

## Initial Observations

1. **Standard RSA encryption**: The flag is encrypted with RSA using `e1 = 65537`
2. **Interesting leak**: Two additional values are encrypted with `e2 = 65583`:

   - `c1 = (p + q)^e2 mod N`
   - `c2 = (int(str(p) + str(q)))^e2 mod N`
3. **Critical observation**: `c1 = c2` in the output!

The equality `c1 = c2` is the key to solving this challenge. It means:

```
(p + q)^e2 ≡ (int(str(p) + str(q)))^e2 mod N
```

## Mathematical Analysis

Let's define:

- `x = p + q` (integer sum)
- `y = int(str(p) + str(q))` (concatenation of decimal representations)
- `len_q` = number of decimal digits in `q`

Then:

```
y = p * 10^len_q + q
```

From `c1 = c2`, we have:

```
x^e2 ≡ y^e2 mod N
```

Since `N = p * q`, we can analyze this modulo `q`:

1. `x ≡ p + q ≡ p (mod q)`
2. `y ≡ p * 10^len_q + q ≡ p * 10^len_q (mod q)`
3. Therefore: `p^e2 ≡ (p * 10^len_q)^e2 ≡ p^e2 * (10^len_q)^e2 (mod q)`
4. Assuming `p ≠ 0 (mod q)` (true since `p` and `q` are distinct primes), we can divide by `p^e2`:

   ```
   (10^len_q)^e2 ≡ 1 (mod q)
   ```
5. Which gives us:

   ```
   10^(len_q * e2) ≡ 1 (mod q)
   ```

This is the key insight: **`q` divides `10^(len_q * e2) - 1`**.

## Attack Strategy

Since we know `q` divides `10^(len_q * e2) - 1`, we can try to find `q` by computing:

```
gcd(10^(k * e2) - 1, N)
```

for reasonable values of `k` (the candidate for `len_q`).

We need to estimate `len_q`: - `q` is ≤ 512 bits - Number of decimal digits ≈ `bits * log10(2)` ≈ `512 * 0.3010 ≈ 154` - But `N` is 990 bits, so `p` and `q` are roughly 495 bits each - `495 * log10(2) ≈ 149` decimal digits

So we should search `k` in the range 140-160.

## Exploit Implementation

```
from Crypto.Util.number import long_to_bytes, GCD

N = 8415991046378822808678417368330656853369287762382257778863000234996179456832593823172163438175141897794450973258850755059008064965170892039119019048620633841637561375984016774249397631671121650489315360389403325290330278785155612107593553850666774120554971030956010302235061389242836031692105637093
e1 = 65537
e2 = 65583
c = 5574165375242524333573826610902926393700854735994229967966464169834696611810072076594787795367537651736635669504446868847168099681864093409935801174468222766808668365620150398151512438201199626223130807413078543403236814636420490358773639850910401283271784777683577389622203357416804633487506912900

# Search for the correct digit length of q
for k in range(140, 160):
    value = pow(10, k * e2, N) - 1
    g = GCD(value, N)

    if g > 1 and g < N:
        q = g
        p = N // q

        # Decrypt the flag
        phi = (p - 1) * (q - 1)
        d = pow(e1, -1, phi)
        m = pow(c, d, N)
        flag = long_to_bytes(m)

        print(f"Found with k={k}: {flag}")
        break
```

## Results

The exploit finds a factor when `k = 144`:

- `q = 899718757349577828631761341730093220713822770643140264600436229195941807849819816835925771559643276518390048146301688389176642719947087961896703`
- `q` has 479 bits and 144 decimal digits
- `p = N // q` has 512 bits and 154 decimal digits

Decrypting the flag gives:

```
TSGCTF{p+q_4nd_p+q_b3in9_th3_s4me_1s_0bv1ous_ri9h7?aZ3mQ9Lk7P2xB8R}
```

## Why This Works

The challenge creator made a subtle but critical mistake: they assumed that encrypting `p+q` and the concatenation of `p` and `q` as strings would produce different ciphertexts. However, when these values are encrypted with the same exponent `e2` modulo `N`, and the resulting ciphertexts are equal, it reveals a mathematical relationship between `p`, `q`, and `N`.

The equality `c1 = c2` implies that `(p+q)^e2 ≡ (p*10^len_q + q)^e2 mod N`. When analyzed modulo `q`, this simplifies to `10^(len_q * e2) ≡ 1 mod q`, which means `q` divides `10^(len_q * e2) - 1`. Since `len_q` is the decimal digit length of `q` (which we can estimate), we can find `q` by checking `gcd(10^(k*e2) - 1, N)` for reasonable `k` values.

## Lessons Learned

1. **Never reuse encryption keys/schemes for different values**: Even if values seem different (like `p+q` vs. string concatenation), encrypting them with the same parameters can leak information.
2. **Mathematical relationships matter**: The equality of ciphertexts reveals algebraic relationships that can be exploited.
3. **Estimation is key**: Knowing approximate sizes (bit lengths, digit lengths) helps narrow down brute-force searches.
4. **Modular arithmetic is powerful**: Analyzing equations modulo the unknown factors (`p` and `q`) can simplify complex relationships.

## Flag

`TSGCTF{p+q_4nd_p+q_b3in9_th3_s4me_1s_0bv1ous_ri9h7?aZ3mQ9Lk7P2xB8R}`
