We can get source code from hint
array_push($users, "guest:".hash("sha256", "guest"));
in login.php
So we can login as guest
In index.php, javascript has show_detail()
show_detail() can read xml data and we can do xpath injection
Use the Following python script to parse the the HTML page and solve the equation using the following logic:
Pillow 8.2.0 has vulnerability (CVE-2022-22817)
We have SSTI with {% %}
and limitation for the character length. Note that server is using FastAPI / Not Flask. As you know, we can use set
, include
and arbitrary function execution (but sadly no output) with {% %}
. So, I made some chaining for RCE using set
and include
. While I suffer from char lenght limitation, I found this on starlette's document.
So, I used request.headers['x']
for bypassing command length limitation.
My payload is like below:
send the above request. It will send jwt
cookie as an object. WE can achieve RCE with this controlled object when its passed to express template. refer to blog https://eslam.io/posts/ejs-server-side-template-injection-rce/
The workflow to solve the challenge goes as follows:
finish_rate=-1
that will immediately finish all the lectures. As a result, it will print out the session cookie value to be reused inside the browser.^$
check (i.e check to perform complete match), we can add extra characters after or before a valid email. For example, email can be:pingu@gmail.com<script>x=new XMLHttpRequest();x.open('GET','file:///etc/hostname',false);x.send(); document.write(x.responseText);</script>
/etc/hostname
file inside the generated "certificate".config.py
file which contains the secret_key
:secret_key
to create custom cookie value with is_admin=True
, so we could send it to /flag
endpoint to receive the flag.Create attachment.ipynb with the following code to get XSS
Exfil the admin's username. It's sub-admin.
then import the following script
Then we have the admin api token. then get the flag with that. why sub-admin token works with admin? idk
curl 'http://datasciencecls.sstf.site/user/admin/api/contents/flag' -X 'GET' -H 'Authorization: token 35e595c2253a45e2b19051e05db95a74'
The server checks the structure of decrypted $_cookie["SESSION"]
is iv(16byte)|id|sig
and if the id
is admin
, sends flag.
So, it uses aes-128-ctf
for encrypting the cookie with fixed key.
Therefore, we xored cookie["SESSION"][17:22]
by "admin" ^ "guest"
after login as guest and gt the flag.
The service provides a game-like interface. One of the features is to load a game. Reverse engineering this feature lead to the conclusion that the provided game script is base64 decoded and passed to the lua_Load
API. In other words, it is possible to execute arbitrary lua code.
However, attempting to execute malicious code io.popen
or os.system
is infeasible because the io
and os
globals are not initialized. Taking a look at the initialization code reveals that two functions, start
and load
are installed.
Analyzing the load implementation instantly reveals an obvious vulnerability. One of its argument is a raw pointer subject to write. The address of the write(arg1
) can be fully controlled. The value of the write (arg2
) is a char *
where its contents can be fully controlled. In conclusion, we can write a char *
to any address where the contents of the char *
can be controlled.
We selected the Table
structure for the victim of the write. The Table
structure represents an instance of the table
data structure in lua, which is conceptually equivalent to objects or dictionaries in Python and JavaScript.
We constructed a strategy based on the following intutition: values in the table are actually stored inside TValue *array
, and its integer index can be calculated without its contents. Thus, if we change array
to a pointer whose contents we can control, we can forge arbitrary TValue
structures by reading fields from the victim table.
Below is a minimal PoC that forges a CClosure
value and calls it, resulting in an invalid jump to address 0xdeadbeef. Now we have RIP control.
To leak libc, we constructed an aribtrary read primitive using lua String types. Then, using the RIP control primitive, we called an one-gadget.
The service is an one-time encryption service, where the key of the encryption is the flag. I noticed two 'peculiarities'.
ord
method to yield values larger than 255. For example, ord("ׯ")
is equal to 1519.By using these two properties, I devised a timing based side channel attack to leak the encryption key byte by byte. The vulnerability is caused due to the following lines of code:
The execution time of the last line is highly dependent on e
. If p
and e
are large, its execution time becomes considerably high. However, if e
is 0, its execution time becomes negligible. e
becomes 0 if ord(KEY[ i % len(KEY) ]) ^ p == 500009
. Therefore, if we iterate over c
and attempt all decryptions such that p = 500009 ^ c
and select c
with the smallest execution time, we can leak the flag.
Below is the exploit code ran on the remote box, which leaks a byte of the flag. We couldn't leak the entire flag due to timeouts in the remote box.
The service has a simple buffer overflow, reading 64 bytes into a 4 byte stack variable.
From here, it's just to provide a x86 ropchain, to call r(buf_in_bss, 128, 0)
to read /bin/sh
to a known address and then call x(buf_in_bss)
to finally call system("/bin/sh")
.
PoW + wordle challenge
Since it is pwnable challenge, first we have to find a vector to get the shell or read the flag.
This is done by achieving >3000 score and setting email like '";/bin/sh;"' + '@'*0x780 + '.'*0x780 + ' 1'
.
The front part ('";/bin/sh;"'
) is to inject command into os.system
and the remaining part is used to trigger catastrophic backtracking to get timeout so that command injection actually occurs.
There is no other ways to bypass PoWdle puzzle… So I quickly implemented PoWdle solver.
The final exploit code is as below. I've run this code multiple times to get proper score.
Use 1-day exploit -> link
Or…
watch -n 1 ps -aux
on the server.echo $(realpath /proc/[pid]/cwd)
/tmp/wotmdtit
)The binary is simple command runner with RSA signing.
Both version have same vulnerability introduced: FSB to overwrite 8 bytes with 0 into heap address.
There are GMP number values in the heap, so we can overwrite some cryptographic numbers used in RSA w/ CRT such as n
, p
, q
, d_p
, …
It uses RSA w/ CRT without any sort of sanity checks.
So we can do fault attack by overwrite d_p
or d_q
In this case, we cannot forge p
, q
, d_p
, d_q
since it checks pre-evaluated xor value with the current evaulation result before signing.
But there is another room for fault attack: link
It says a fault in modulus can be harmful.
The algorithm to find the reduced basis for the orthogonal basis is from here
Just easy ROP on riscv:64
Whatever the encoding algorithm the binary has, we can get encoding result by breakpoint to 0x29EF
and see *(rdi+0x20)
. After some guessing with various input, only 1~2 bytes of input effects to 1~2 bytes of output.
So, we solved by making table of input-output pair by bruteforce.
Have you ever stolen 3d asset rendered in WebGL?
First, we deobfuscated the Javascript code and started analysing it. After lots of "digging", we found a routine used for drawing triangles and reduced the hardcapped number of objects to be drawn to half:
As a result, we got a partially rendered model, with a readable flag:
Finally, I developed 3d maze adventure game. I think it will be very difficult to defeat final stage without any game cheat or wonderful maze solving skill.
There were 3 levels of a maze, where it was virtually impossible to pass all of them (to get the flag) without any cheating. Started with the GameConqueror
and had some partial success in passing the requirements (e.g. increasing the time limit and money), but we were stuck on how to actually pass the last (3rd) level.
After lots of in-memory digging, we started to analyse the game files and files being written to the disk. At the end, we found out that game is using LevelDB to store current game status (for resume on game reload).
For "cheating" purposes, we did the following:
Notice that additionally to obvious MONEY
, WALK_SPEED
and TIME_LIMIT
we also modified the values for PATHFINDER_ENABLED
(boolean value enabling nice "pathfinder" functionality inside the game) and STG_ACCESS_INFO
(array of boolean values giving playing access to levels). At the end, we reloaded the game, started to play the 3rd level and just followed the path-finding route.
At the end, we succeeded in getting the flag from the menu (by having all requirements satisfied):
After extracting the docs file by magic of 7zip, there is a suspicious file word\embeddings\oleObject1.bin
. After 7zip magic once more, there is EMF file called Open-Me.bin
in [1]Ole10Native
. We can see the flag after removing some data for proper emf file.
This wav file has two channel and two channel's wave is different. Let's mix these two channel and remove the common part.
https://cdn.discordapp.com/attachments/816203216751558678/1011563880679481365/unreal_mix.wav
In each interval, make sound is 1 otherwise 0.
11001010110000100010101001100010110111101010101001110110010011101100110010000110001100101111101001001100111110101110011000001100111110100100011000000010110001101101011011111010100101100111001011111010001010101001011010110110110011001011111
Revert and int to bytes give us flag.
SCTF{Unr3aL_2_g0_b@ck_iN_Tim3}
Auto reverse the class file and find the correct path.
But there are some wrong path, 1) results "Nope" Exception, 2) unreachable path because of impossible key.
With simple code parser for decompiled code from jd-gui and perform DFS, we can get flag.
M(X) -> load
A(X) -> double
R(a, b) -> check (now buffer size + a & 0xFF == 0)
After a little analysis, we found out the aboves.
We wrote a parse code for defines and arguments by python.
There is an Out-Of-Bound
on the free game index in the free game select
If you select higher index than 3 of free game type A and change to the free game type B, free game index points other global variable.
When the difficult is 50000, score can be lower than zero.
After make score be negative, we repeated to lose until could buy the flag.
Since we can move at most 11 times, there are at most 4^11 move sequences to consider.
However, removing the cases where we move to the spot we were just before, there are at most 4 * 3^10 move sequences.
We can precompute everything before connecting to the remote server, solving the challenge.
I made a chat bot with the Big(not that big) data based on me!
Once connected to the chat server, we were presented with the simple interface where we chatted with some kind of AI bot. After couple of replies, we started searching for the corpus used for learning it and successfully found it at Baidu. Then, tried to find out a logic in responses by going through different questions used in corpus.
To our luck, it seems that during our deduction phase we got the flag in most inconspicuous way by asking incomplete question "who wrote the
":