---
# System prepended metadata

title: SeaTF-NITK 2025 Writeups

---

# SeaTF-NITK 2025 Writeups

Hello guys, today we are going to look into some of my writeups for the SeaTF Challenge 2025 by NITK.


> The files given for challenges are in my github repo.

 
Challenges were divided into the following categories
### Categories: 
1. Sanity Check
2. Tresure Map
3. Captain's Log
4. Hall of Legends
5. Rules of the Sea


## Sanity Check

### are_u_sane? (10)

> flag was found in comments html source code of `Are you SEAne?` titled page of contest webpage.

---

## Treasure Map
<!-- ### Lost Signal from the Deep (100)


```
A mysterious distress signal has been intercepted from a lost submarine built by the mechanical engineers at NITK. Can you recover the message?

The Electrical Engineers who were eavesdropping on random signals (as usual) were able to find some vague clues regarding the missing signal:

    submarine
    q1.jpg

NOTE: Flag format for this question is SEATF{}
```

#### Solution

First I checked the `submarine` bine

 -->


### Black Pearl's Cursed Cipher (300)

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

#### Solution
I solved it in different way that what was intended. 

From problem statement, it was pretty clear that we have to decode `Hjg_KiFTyVaO_KvMcYHoYg_fReIoHi`. I tried *ROT13*, **didn't work**.

From vigenere cipher using key `BLACKBEARD` I got caesar cipher of plain text `Wow_YoUFoUnD_PlAiNTeXt_uWuWuWu`.
Putting this plain text into `nc` gives us the base64 encoded flag `Q29uZ3JhdHVsYXRpb25zISEKRmxhZyA6IFNlYVRGe2RBbU5fUmVWZXJTZV9lbkdpTmVFcl9Qck99`.

What I did was decompile the `vuln` binary and get the plain text from there directly.

### Bind the strings under the flag poles! (500)
![image](https://hackmd.io/_uploads/BJjcK-J31x.png)

#### Solution

In `Good Stuff` link, there was `Seatify.apk`.
First I ran `strings` to check for any clue.
For making it fast, I ran
```bash=
strings Seatify.apk | grep flag
```
this got me this output 
```
res/layout/activity_flag.xml
flag0=SeaTF{bytecode}
flag1=47hf7ycb
flag2=u7gdcbnv
flag3=wi48cbf
res/layout/activity_flag.xml
activity_flag
res/layout/activity_flag.xmlPK
```
I realised I have to decompile apk. 
Ran `jadx-gui` on apk and traversed to file `res/layout/activity_flag.xml`

got this code inside the `FlagActivity` class
```java
package com.nitk.seatify;

import android.os.Bundle;
import android.view.View;
import android.widget.TextView;
import androidx.appcompat.app.AppCompatActivity;
import kotlin.Metadata;
import kotlin.jvm.internal.Intrinsics;

/* compiled from: FlagActivity.kt */
@Metadata(d1 = {"\u0000\u0018\n\u0002\u0018\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\n\u0002\u0010\u0002\n\u0000\n\u0002\u0018\u0002\n\u0000\u0018\u00002\u00020\u0001B\u0005¢\u0006\u0002\u0010\u0002J\u0012\u0010\u0003\u001a\u00020\u00042\b\u0010\u0005\u001a\u0004\u0018\u00010\u0006H\u0014¨\u0006\u0007"}, d2 = {"Lcom/nitk/seatify/FlagActivity;", "Landroidx/appcompat/app/AppCompatActivity;", "()V", "onCreate", "", "savedInstanceState", "Landroid/os/Bundle;", "app_debug"}, k = 1, mv = {1, 7, 1}, xi = 48)
/* loaded from: classes3.dex */
public final class FlagActivity extends AppCompatActivity {
    @Override // androidx.fragment.app.FragmentActivity, androidx.activity.ComponentActivity, androidx.core.app.ComponentActivity, android.app.Activity
    protected void onCreate(Bundle savedInstanceState) {
        super.onCreate(savedInstanceState);
        setContentView(R.layout.activity_flag);
        View findViewById = findViewById(R.id.settext);
        Intrinsics.checkNotNullExpressionValue(findViewById, "findViewById(R.id.settext)");
        TextView flagTextView = (TextView) findViewById;
        int[] array = {85, 50, 86, 104, 86, 69, 90, 55, 78, 72, 66, 119, 88, 50, 81, 122, 100, 109, 85, 120, 77, 72, 65, 122, 99, 110, 48, 61};
        StringBuilder stringBuilder = new StringBuilder();
        for (int ascii : array) {
            stringBuilder.append((char) ascii);
        }
        flagTextView.setText(stringBuilder.toString());
    }
}
```

and wrote a simple python script to get the actual flag
```py
array = [85, 50, 86, 104, 86, 69, 90, 55, 78, 72, 66, 119, 88, 50, 81, 122, 100, 109, 85, 120, 77, 72, 65, 122, 99, 110, 48, 61]
flag = "".join(chr(c) for c in array)
print(flag)
```
This gave base64 encoded flag
```bash=
echo "U2VhVEZ7NHBwX2QzdmUxMHAzcn0=" | base64 -d
```
Decoding it gave the actual flag

## Captain's Log

### Mystery of the Sunken Cipher (100)
![image](https://hackmd.io/_uploads/ByYDpZyhye.png)

#### Solution
https://marvelous-heliotrope-29e086.netlify.app/
![image](https://hackmd.io/_uploads/HJJqlzJ2ye.png)
This was the homepage. I thought to checked source code and found 3 links (from the Learn more button on homepage). 
```
/coral-reefs
/deep-sea
/ocean-currents
```
visiting 1st gives this page
![image](https://hackmd.io/_uploads/SkfwWMk3kg.png)
The riddle hinted me for [display: none;](https://developer.mozilla.org/en-US/docs/Web/CSS/display) property of css. I inspected the code and found a hidden button, I unhide it by removing the hidden class.
Clicking the unlock button gave a placeholder for entering a flag.
![image](https://hackmd.io/_uploads/BySfMMy3yl.png)


Now I needed a flag.
I also saw there two div elements hidden so therefore checked source code.
![image](https://hackmd.io/_uploads/BkCkffk3Jl.png)
![image](https://hackmd.io/_uploads/rk41L7y2Jg.png)
We got `final.jpg` image which we have to analyse.
I downloaded it and and started analysis.
FIrst checked `file final.jpg` which returned
```bash=
final.jpg: PNG image data, 289 x 180, 8-bit/color RGBA, non-interlaced
```
so the image was actually a PNG file. I changed the extension to .png.
Now I ran `zsteg` since it was a PNG file. 
```bash
b1,rgb,lsb,xy       .. text: "hcyv{Mpm_Irkt_Ba_Ihfmi_221U_JimvpHmzmgk}"
b2,r,msb,xy         .. text: "PQUUUUUUU"
b2,g,msb,xy         .. file: OpenPGP Public Key
b2,b,msb,xy         .. text: "PUUUUUUUU"
b2,rgb,msb,xy       .. text: "EQUUUUUUUUUUUUUUUUUU"
b2,bgr,msb,xy       .. text: ["U" repeated 18 times]
b2,rgba,lsb,xy      .. text: "7gcwgg'g7c#ssg7w333w''g7"
b2,abgr,msb,xy      .. text: "CGGGGGGGGGGGGGGGGGG"
b3,b,msb,xy         .. file: OpenPGP Secret Key
b3,abgr,msb,xy      .. text: "7|C7|C7s?"
b4,r,lsb,xy         .. text: "CGREUUEEEUEDEDT##3\"DEUFdKN}H"
b4,r,msb,xy         .. text: "b0 f&$sw737U37b"
b4,g,lsb,xy         .. text: "fdDd\"Dd&"
b4,g,msb,xy         .. text: "aaf&\"&D\"&dU&"
b4,b,lsb,xy         .. text: "fD\"FffffffffdDDDD\" \""
b4,b,msb,xy         .. text: "w2f5AQsSe&\"D"
b4,rgb,msb,xy       .. text: "e5`1@`!QVcsV#Q"
b4,bgr,msb,xy       .. text: "2ea@0aP!SvcSV!"
b4,rgba,lsb,xy      .. text: "?u?g/F/&"
b4,abgr,msb,xy      .. text: "c?e_!_Ao&"
```
Thats interesting. In the first line itself we got something.
`hcyv{Mpm_Irkt_Ba_Ihfmi_221U_JimvpHmzmgk}`
This actually looks like a flag but I guessed it was shifted by some letters or some other cipher.
Anyways I tried ROT13 brute from [CyberChef](https://gchq.github.io/CyberChef/) but didn't get anything. 
Since there were 4 letters outside of curly brackets, I guessed it might match with `flag`. Just after this Vigenère cipher got into my mind, I thought that I might be able to get the key to cipher by using `flag` keyword.
![image](https://hackmd.io/_uploads/r1FZREy3yg.png)
`cryp` that might be something meaningful
on further trial and error (definitely not the expected way though), I got the key `cryptii` which made the decoded string make sense.
![image](https://hackmd.io/_uploads/HJmt0NJhke.png)
Tried this flag on that webpage and voila it worked.
It then downloaded a `nextclue.txt` which had `{"message":"Correct flag! hey 12345"}` written inside.
It then automatically redirected me to `https://marvelous-heliotrope-29e086.netlify.app/final.html?clue=%7B%22message%22%3A%22Correct%20flag!%20hey%2012345%22%7D` (just URL encoded params of `nextclue.txt`). 
There was a field to enter final code.
![image](https://hackmd.io/_uploads/ryaXxBJ2yl.png)

I tried random string to check what is actually happening, analysed using network tab and found that a post request was being made to /api/flag. Reponse was `Invalid Flag` for all random strings. I then tried `12345` (hinted from `newclue.txt`) and found that in response I got `{"message":"Correct flag! CTF{SURFER_YOU_CRACKED_IT}"}`  which was the correct flag.


### Space Wars (100)

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

#### Solution
The riddle hinted at words `spock` and `prosper`.
Trying these as username and password respectively, it redirected to `/capture`. There was this image ![spock](https://hackmd.io/_uploads/B1dDwBJ3ye.jpg) which on analysis didn't give anything.

I then analysed the the network tab and saw that there is a cookie named `seatf-jwt` with a [jwt token](https://jwt.io) as cookie value. On decoding the jwt, the payload was
```json
{
  "user": "Captain Kirk",
  "password": "Inrh%h3nbP+k",
  "iat": <timestamp>
}
```
On logging in again in that form again with these creds, I got the flag.
![image](https://hackmd.io/_uploads/Bk33uHJ2yl.png)


---
### The Keeper of Secrets (300)

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

#### Solution

Looking at encrypted username, it is base64 encoding which on decoding gives `Trust is given, but never returned. Once inside, all doors are open.`

Now coming to the `challenge2_bin`

executing it asks for a username - tried that string, failed.
It was time to disassemble the binary. I used IDA.
![image](https://hackmd.io/_uploads/HyRZsDJ2yg.png)
Seeing the code, it was clear that it is comparing input username with s2, which is a2 in `decode_string` function
![image](https://hackmd.io/_uploads/S17Pivy2kl.png)

this just perform XOR operation on every character of v7 string `aOXHOXEY` with `42LL`. Doing that manually or with any online tool, I got username `kerberos` which on entering in the binary gave a Encrypted flag. Though I also XORed the string in v8 with 42 according to the logic in binary and it gave the flag directly.
**Flag:** `SeaTF{Kerberos_Ticket}`

---
### Ghost Protocol (300)

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

#### Solution

First I got a zip file (can be found at my github). Extracted it and got ![image](https://hackmd.io/_uploads/ByAm3Gx2Jg.png)
Inside `ghost_protocol_hint.txt` it was this 
```
The ghosts communicate silently,
In space between moments they hide.
Count not the words, but the silence,
For in the emptiness, truth resides.

When windows open to the network,
Even and odd tell a story untold.
ICMP speaks more than it seems,
In its identity, secrets unfold.

DNS records hold the cipher,
But without the key, darkness remains.
Assemble the pieces with precision,
To reveal what the ghost contains.
```
Since the PS clearly says message hidden within ID of ICMP packets. I started analysing `ghost_protocol_final.pcap` for something in ICMP packets. Opened pcap file in wireshark and filtered for ICMP packets.
On Analysing a few packets, I observed that Identification field was oscillating between values 100 and 500.



---

### The Lost Pirate's Code (300)




---
 Thanks for reading the writeups.