Having
*
before challenge name means I solved that challenge after the competition.
Find LCG parameters by some easy math then generate the fixed CSRF token for admin, then make it access addadmin.php
to add a new admin user. Login as the new admin user to get flag.
Read the framework source code we see that there is a filesystem access with .Echo.Filesystem
, but to read the file you need to have a []byte
buffer to write to. Fortunately, we see .Get "template"
will be our template in []byte
, so we can just reuse it to store the flag.
then you will get
decode it with bytes(eval(s.replace(' ',',')))
: ACSC{h0w_did_y0u_leak_me}
The challenge use a nginx config enables byte-ragne caching like this:
But the .mp4
regex misuse the .
character, so it will also match path like /notes/834dc5a4-45c9-45a7-ba4e-a5969a18fb8b-amp4
.
By playing with the byte caching with a custom server locally, we know that nginx will try to parse your Range
header and align it to 4096
blocks, so $slice_range
will always be 0-4095
or 4096-8191
. Also, since $slice_range
is used in cache key so different ranges will have different cache.
So if we create a new note and only access its second block (4096-8191
), then we edit the note content to be shorter and then access the first block (0-4095
). So if we access the note regularly, nginx will concat the content of the two cached block and send it to client. Therefore, with a carefully block alignment (like heap pwn) it is possible to make the first block end with <
, and the start of second block will be user-controlled content. Then we can get xss with <img onerror=alert() src=x:
.
This challenge implemented a Merkle–Hellman knapsack cryptosystem and encrypt the flag char by char, and the private key is also given.
But the length of public key is just 7, so for implementation simplicity we can simply bruteforce possibilities for each char to decrypt the flag.
We are given a bunch of that satisify:
then we have:
Then we CRT to get for , so .
Since is slightly less than the size of , we bruteforce to find and factor by solving a quadratic equation.
We have two modular equation:
Take F = crt([f1, f2], [q2, q2])
, then .
Since is much larger then the size of , we use LLL to find it.
First we fix the PEM formatting by adding newlines appropriately, then replace ?
with A
. By the file length we know it is a corrupted RSA-2048 private key. We can generate another key with ssh-keygen -t rsa -b 2048 -m pem
for comparison.
Decode the base64 into a binary and open a hex editor (like ImHex), so we can attempt to decode the data with ASN.1 parsing rules and RSA key format:
Taken from RECOVERING A FULL PEM PRIVATE KEY WHEN HALF OF IT IS REDACTED
After that, we know the start byte index and the byte length for each integer, but only is fully given (not corrupted) and is partially corrupted but it is easy to guess . All the other parameters from have random bits being corrupted.
I used the method given in section 4.3.2 in Recovering cryptographic keys from partial
information, by example.
We have:
and satisfy . So we only have to guess at most pairs of .
Then we iteratively search the unknown bits using branch-and-prune to find integers simultaneously satisfying the following equations:
Then we generate the fixed key with:
Implemented the cipher in Z3 during the competition but can't get it work efficiently. Thanks @deuterium for telling me I need to minimize the number of bits of chosen plaintexts.
The encryptor choose random 2 primes and encrypt the flag with textbook RSA, then encrypt the RSA ciphertext with xor cipher.
Since the xor key is known, simply xor it back and do RSA decryption to get flag.
There is a Galois LFSR is the binary, and it is generating the flag by xoring the -th output with some known numbers where .
Since the max length of that LFSR is , computing -th output is enough.
BOF to rop leak puts@GOT
and go back to main
, then BOF again to do ret2libc.
sol2.py
:
sol3.py
:
and we prepare a static binary to opendir
for us and write a wrapper script:
source code of main
:
Then we get the flag filename with:
Then read the flag with print((f:=open('flag-0479f1dcda629bbe833598bce876a647.txt')).read())
.
ACSC{bl4ckL1st_ruL3_1s_4lw4y5_d4ng3r0uS!}