---
# System prepended metadata

title: writeups

---


## Challenge[0] = Social Media Hunt
Difficulty - Easy
Find it in PClub Y25 discord group description.
Flag: `pclub{J01N_TH3_ADV3NTUR3}`

## Challenge[1] = Hidden and Protected
**Difficulty** - Medium
Hints were given in Tools section of Resources and then explicitly on the challenge page.
Most of the times, Aperisolve would be good enough for such challenges. But one of the methods I like best is to search the hexdump of the file. IEND is PNG chunk that occurs towards the end of the PNG file. If one would have compared the challenge's hexdump to any normal PNG and used the File Signature link in the Resources, you would have been able to identify the RIFF WAVEfmt chunk. Then, either copy-paste the bits into a new .wav file and use an online morse audio to ascii decoder. 
Flag: `pclub{GENERALKENOBI}`

## Challenge[2] = It’s Not What It Looks Like
**Difficulty** - Medium
Kinda difficult, again if one were to analyse the hexdump, he/she would have been able to identify flipped up JFIF bits which signify JPEG image header. 
Then in any linux shell, run `dd if=ikka.bin of=image.jpg conv=swab`.
You would get a JPEG image with the flag written on it.
Flag: `pclub{dd_is_overpowered}`

## Challenge[3] = The Logfather
**Difficulty** - Hard
The only challenge to remain unsolved (partially solved)... 
Working on Windows, .evtx is a Windows Event Log File. This was kind of a miscellaneous problem, so no unique way of doing this. Without the hint, one would have to go through every event, looking in description for something encoded/suspicious. With the hint given being the flag is encoded, you can search `==` (as suggested in Resources by bold in Base64 part) to find a part of the flag. 
`X1ZpM3czcl80dGhlX3dpbiEhfQ==` which decodes to `_Vi3w3r_4the_win!!}`
The other part was `cGNsdWJ7RXZlbnQ=` which decodes to `pclub{Event`. One can look this up by searching the starting letters `cGN` of the Base64 encoded form of `pclub` as all flags started with that.
So the flag is `pclub{Event_Vi3w3r_4the_win!!}`

##  Challenge[4] = Really Strong... Allegedly
**Difficulty:** Easy–Medium

We are given:
```
n = 6683814830832741157291901781085817018264769266205413441602841973210814994530431066126387585451170356360654316076701293885868331196720763203247280519617823
e = 3
c =5199646040860775002185575024744819137179187280040791631246957765082798553820697418805249953956474067855501281156499677464030941324268438173152296036906968 
```

**RSA Basics**

RSA public encryption:

- Public key: `(n, e)`
- Private key: `d`, where `d ≡ e⁻¹ mod φ(n)`
- Encryption: `c = m^e mod n`
- Decryption: `m = c^d mod n`

To decrypt, we need `d`, which means we need `φ(n)`, which requires factoring `n`.

**Hint Analysis**

The challenge words hint on **close** — this is a direct hint to use **Fermat’s factorization**, which is effective when `p ≈ q`.

**Fermat’s Factorization**

Fermat's method expresses `n` as: ```n = a² - b² = (a - b)(a + b)```
We know,`n=p.q`, if p and q are close this method finds them to break the RSA encryption.

To learn more about Fermat attack to break RSA see [here](https://medium.com/@phiphatchomchit/fermat-factorization-algorithm-can-break-poor-rsa-encryption-3c657848cc87).

**Python Code:**


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

n = 6683814830832741157291901781085817018264769266205413441602841973210814994530431066126387585451170356360654316076701293885868331196720763203247280519617823
e = 3
c = 5199646040860775002185575024744819137179187280040791631246957765082798553820697418805249953956474067855501281156499677464030941324268438173152296036906968        

#  Taking integer sqrt
def isqrt(n):
    x = n
    y = (x + 1) // 2
    while y < x:
        x = y
        y = (x + n // x) // 2
    return x

# Fermat's Factorization
def fermat_factor(n):
    a = isqrt(n) + 1
    b2 = a * a - n
    while True:
        b = isqrt(b2)
        if b * b == b2:
            break
        a += 1
        b2 = a * a - n
    p = a - b
    q = a + b
    return p, q

# Solve RSA
p, q = fermat_factor(n)
phi = (p - 1) * (q - 1)
d = inverse(e, phi)
m = pow(c, d, n)
flag = long_to_bytes(m)
print("Flag:", flag.decode())
```

Which gives us the Flag:`PClub{E4sy_Rs4_Ferm4t}`

## Challenge[5] = Mirror Mirror
**Dificulty** - Medium
This was a hash collision challenge as it was hinted by the Hash Functions link which contained the only link to Collisions Resistance.

```
import hashlib
from utils import listener

FLAG = "pclub{???????????????????????????????}"

class Challenge():
    def __init__(self):
        self.before_input = "Give me a document to store\n"
        self.documents = {
            "508dcc4dbe9113b15a1f971639b335bd": b"Particle {REDACTED} behaviour.",
            "cb07ff7a5f043361b698c31046b8b0ab": b"The Large {REDACTED} countries.",
        }

    def challenge(self, msg):
        if "document" not in msg:
            self.exit = True
            return {"error": "You must send a document"}

        document = bytes.fromhex(msg["document"])
        document_hash = hashlib.md5(document).hexdigest()

        if document_hash in self.documents.keys():
            self.exit = True
            if self.documents[document_hash] == document:
                return {"error": "Document already exists in system"}
            else:
                return {"error": f"Document system crash, leaking flag: {FLAG}"}

        self.documents[document_hash] = document

        if len(self.documents) > 5:
            self.exit = True
            return {"error": "Too many documents in the system"}

        return {"success": f"Document {document_hash} added to system"}

"""
When you connect, the 'challenge' function will be called on your JSON
input.
"""
```

In `collide.py`, what is being done that you supply a json with {"document":"\<text>"}, the document parameter is necessary as hinted by `if "document" not in msg:`. 
After you provide the json, the `<text>`, gets md5 hashed and stored in a dictionary with it being the key and the `<text>` being the value. 
`if document_hash in self.documents.keys():` this line suggests that if two documents with same hashes were to exist in the dictionary, the flag would be outputted.
Now, if one were to search `md5 collision` in Google, this [site](https://www.mscs.dal.ca/~selinger/md5collision/) would be one of the firsts. 
So the solution was to submit both the strings on the site in the correct json format to the nc challenge.

{"document": "d131dd02c5e6eec4693d9a0698aff95c2fcab50712467eab4004583eb8fb7f8955ad340609f4b30283e4888325f1415a085125e8f7cdc99fd91dbd7280373c5bd8823e3156348f5bae6dacd436c919c6dd53e23487da03fd02396306d248cda0e99f33420f577ee8ce54b67080280d1ec69821bcb6a8839396f965ab6ff72a70"}
then,
{"document": "d131dd02c5e6eec4693d9a0698aff95c2fcab58712467eab4004583eb8fb7f8955ad340609f4b30283e488832571415a085125e8f7cdc99fd91dbdf280373c5bd8823e3156348f5bae6dacd436c919c6dd53e2b487da03fd02396306d248cda0e99f33420f577ee8ce54b67080a80d1ec69821bcb6a8839396f9652b6ff72a70"}
Flag: `pclub{i_broke_md5_with_dark_magic_lol}`

## Challenge[6] = Not the Edible Kind
**Difficulty** - Easy
I'm sure you guys would have heard the term `cookie` while browsing. 
Searching for cookies in the Developers Panel, one would have to search under Application tab. The initial cookie, `eyJhZG1pbiI6IDB9`, decodes to `{"admin": 0}`. Now 1 and 0 represent True and False respectively. So one would only have to replace the 0 with 1, encode it and then replace the cookie and then reload the page.
Flag: `pclub{c00kie_priv1leg3_escalat3d}`

## Challenge[7] = Behind the Curtains
**Difficulty** - Medium
The flag can only be obtained by `pclub_browser`
User-Agent is a header that identifies the user agent which is a program that represents a person, for eg, a browser in a Web context.
So, one would just have to switch the User-Agent to `pclub_browser`. 
There are quite a few ways of doing this but many have done this using some browser extension.
Flag: `pclub{cur10us_br0wser_h34der}`

## Challenge[8] = Rev Me If You Can
**Difficulty** - Medium
In the ruins of an old hard drive, one program still runs. It asks for a password. No GUI, no logs, just a challenge. It's been waiting... for you to reverse it.


We are given an ELF binary (a linux binary) named legacy,

```
$ chmod +x legacy 
$ file legacy
legacy: ELF 64-bit LSB pie executable, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=6f6fe34864b752814b43ae6cdfd37786b4035fcc, for GNU/Linux 4.4.0, not stripped
$ ./legacy
Get me my flag: hello_chooms          
Access denied. [CODE: 0xDEAD]
$ ./legacy
Get me my flag: pclub{is_this_the_flag}
Access denied. [CODE: 0xDEAD]
```

Hmmm, the binary asks for a string ( a flag) from the user and then gives us the access if the flag is correct .

Using ghidra, a popular decompiler , we try to decompile the binary to get to the source code.
> A decompiler is a software which tries to build the source code from a compiled binary



```
------ Main function -------------

undefined8 main(void)

{
  int iVar1;
  size_t sVar2;
  long in_FS_OFFSET;
  char local_58 [72];
  long local_10;
  
  local_10 = *(long *)(in_FS_OFFSET + 0x28);
  printf("Get me my flag: ");
  fgets(local_58,0x40,stdin);
  sVar2 = strcspn(local_58,"\n");
  local_58[sVar2] = '\0';
  iVar1 = check_password(local_58);
  if (iVar1 == 0) {
    puts("Access denied. [CODE: 0xDEAD]");
  }
  else {
    puts("Access granted. [LEGACY FLAG ACCEPTED]");
  }
  if (local_10 != *(long *)(in_FS_OFFSET + 0x28)) {
                    /* WARNING: Subroutine does not return */
    __stack_chk_fail();
  }
  return 0;
}

```

We see that the string we input is stored in local_58 (just some variable name) then its passed to a function named check_password.

We find the check_password function and simplify the decompiled code to get 
```
-------------- The Encrypt Function -------------------------

bool check_password(char *param_1)

{
  int iVar1;
  size_t input_len;
  bool bVar2;
  int i;
  byte encrypted_string [72];
  
  input_len = strlen(param_1);
  if (input_len == 0x19) {
    i = 0;
    while( true ) {
      input_len = strlen(param_1);
      if (input_len <= (ulong)(long)i) break;
      encrypted_string[i] =
           (byte)(param_1[i] ^ KEY[(ulong)(long)i % 11]) >> 5 |
           (param_1[i] ^ KEY[(ulong)(long)i % 11]) * 8;
      i = i + 1;
    }
    iVar1 = memcmp(encrypted_string,CYPHERTEXT,0x19);
    bVar2 = iVar1 == 0;
  }
  else {
    bVar2 = false;
  }
  return bVar2;
}


```

So we infer form this - 
- The check_password function takes a string as parameter
- checks if the length is 25
- for each char in our input performs an operation on it and stores it result in encrypted string
- then it compares the encrypted string with a CIPHERTEXT, and returns 1 if it matches else


Double click on KEY `the key` to get to its data
> You can double click on a global variable (like KEY,CIPHERTEXT) or a function call in ghidra to jump to it

![image](https://hackmd.io/_uploads/rydFVso_eg.png)

Do the same for CIPHERTEXT `the lock`

Now we've all the info we'll need to decrypt the flag. Use python to reverse the logic and decrpyt the flag

```

ct =[0x19, 0x82, 0x79, 0x39, 0x8A, 0x62, 0xC1, 0x21, 0xE2, 0xE9, 0xFA, 0xC9, 0xE2, 0x09, 0x68, 0x0A, 0x92, 0x49, 0x71, 0x0A, 0x51, 0x4B, 0xE9, 0xA2, 0xF1]
key= [0x53,0x33,0x43,0x52,0x33,0x37,0x5f,0x4b,0x33,0x59,0x00]

str = ""

for i in range(len(ct)):
    out = ct[i]
    out = ((out >> 3) | (out << 5) )& 0xFF 
    out = out ^ key[i%11]
    # res.append(out)
    str += chr(out)
 
print(str)
    
```

Running this python script gets us our flag

Flag: `pclub{good_job_reversing`

NOTE: Since some of you reported that inconsistancy in the binaries, We'll be bonusing this question while calculating for top scorers 