(){}
blacklist by using asm()
to spoof free function and a gadget in #<netipx/ipx.h>
Discovered that help()
can import any module to the program and also we can import jail again with it.
We can use the prototype pollution to define variables which we can later access in the jsfuck part using undefined
. In the jsfuck part we can use [][[]] -> undefined
to access the values from the prototype pollution, and use the constructor
function to execute code.
Flag: SECCON{prototype_po11ution_turns_JavaScript_into_a_puzzle_game}
there is a format string bug in printf(name);
. so if use this can overwrite lower 2byte of got. if change the printf
got scanf
address. then can get a overflow in printf(" answered, a bit confused.\n\"Welcome to SECCON,\" the cat greeted %s warmly.\n", name);
.
FLAG : SECCON{The_cat_seemed_surprised_when_you_showed_this_flag.}
The mmio device has no out-of-bounds checks, so we can use it to read/write anywhere in QEMU memory. By this, we can first leak the addresses of QEMU heap and binary, then craft a fake vtable in the heap and overwrite an existing vtable pointer to execute system("/bin/sh")
.
For this we use the following gadgets
With that we can control the content of rdi
putting an address to a /bin/sh
string in it, which we'll also put on the heap.
Since executing system
directly from the first gadget would result in a segfault on movaps
due to a misaligned stack, we'll just chain another call
, which will fix the stack alignment and then execute system
.
Flag: SECCON{q3mu_35c4p3_15_34513r_7h4n_y0u_7h1nk}
The range check in stt
has a off-by-one error, which allows us to overwrite one byte outside of _mem
. With this, we can overwrite the _mem
pointer itself, moving the vm memory range up- and downwards. This lets us overwrite the size of the vm memory and also access the vtable pointer of the vm.
We can then read the original vtable pointer and calculate the address of main
, create a fake vtable, which will jump back into main
when vm->dump_registers()
gets called. Raising an exception before with executing an illegal
instruction (opcode 7) will put an exception object on the heap, which will contain pointers to libstdc++
.
So when returning back into main, we can again overwrite size
and _mem
pointer to read/write outside of the vm. Thus we can get the libstdc++
pointer, calculate libc
base and then create a fake vtable, which will then execute system("/bin/sh")
when vm->dump_registers()
is called again.
Flag: SECCON{Im4g1n3_pWn1n6_1n51d3_a_3um_CM0S}
A user can send their balance to another user putting their name into the form. The following database query will be executed:
This can be exploited if there are multiple accounts with the same name, because this query will increase the balance of all accounts with the given name.
The register endpoint, which is responsible to prevent this from happening, looks indeed weird:
There is the names
map variable, which is used to handle the duplication check. Although this weird logic makes race condition impossible (due to the single-threaded nature of Node.js), but still… this logic is weird.
The type of the users
table's name
field is TEXT
. The maximum length of a TEXT
field is 65,535 bytes. If the value exceeds this threshold, well, it will be truncated, emitting a warning. Honestly I think this is insane engineering decision; This is database and you don't want your database to screw up your data, but that's what they're doing here exactly. Yeah, it emits the warning, which will be silently ignored on the backend since no one knows this abnormal behavior and generally no one writes the code handling the 'warning' from the database after your seemingly successful query. Good job, MySQL.
By the way, this behavior is exploitable in this codebase. You can generate some 65535-byte random username and register the accounts with the names username
, username + "A"
, username + "A" * 2
, …, username + "A" * 15
. Now when someone transfers their money to username
, all of the accounts we created will receive the amount sent.
The following code exploits this behavior to get the flag:
And the flag obtained with the above code is SECCON{The_Greedi3st_Hackers_in_th3_W0r1d:1,000,000,000,000}
.
We have a /ssrf
endpoint that will locally send a request to /flag
with the correct flag
but there's a middleware that checks that req.query.flag
exists so the request will end up looking like flag={user input}&flag=SECCON{realflag}
which will not pass the code below.
Since passing in two different flag
will make req.query.flag
into an array.
After a bit of digging, we found out that /ssrf?flag[=]=1
will output flag due to the code initially recognizing flag
correctly as an object but whenever it tries to append it, it treats the flag object differently and uniquely adds the real flag as it can be seen in the log below.
Congratz! The flag is 'SECCON{Which_whit3space_did_you_u5e?}'
.
For the markdown function, it has less validation. So, we could do inject onerror
on the img tag.
We could get some XSS on there and manage to solve this challenge.
SECCON{Firefox Link = Kitsune Udon <-> Chrome Speculation-Rules = Tanuki Udon}
There a trivial XSS in this challenge but there are two problems. First one is that we need currentId and the second one is that all users use a unique key so we can't easily alert() other users.
We can solve the first problem by iframing the payload, the currentId then won't be overwritten because chrome will isolate the localstorage.
For the second problem, we can use the prototype pollution vulnerablity that exists in purl.
The following code is part of CryptoJS.enc.Base64
. If we pollute the first 4 indexes of words
array with 0xffffffff then the key will always be \xff*16
.
We then crafted a "ciphertext" and "iv" that after decrypted, will contain the following payload (kinda tricky since the first 32 bytes of iv and ciphertext will also be 0xff, but since it is AES CBC we can workaround it):
Then hosted the following code at that webhook URL.
Finally triggering the exploit
htmlparser2 doesn't handle xmp tags well… We can use this to craft a payload.
To bypass CSP we can use HTML comments. In Javascript (<!--
) behaves somehow like //
.
The code has uninitialized pointer reuse vulnerability. So, we could overwrite the slot address where we want to ovewrite.
Ref: https://github.com/ethereum/solidity/issues/14021
After some research, I realized that the integer overflow/underflow will be detected but it's not reveretd when it calculates the internal slot address. So, I could abuse this and manage to solve this challenge.
My payload will overwrite balance as msg.sender
. So, we could drain the balance of the contract.
payload:
SECCON{unb3l13-bubb13_64362072f002c1ea}
Related message attack
Profit
we know ct11 + ct12 because ct11 + ct12 = pt1 + pt2
we also know ct21 + ct22 = pt1 + pt2 too.
so we can recover a1^2, a2^2
now put final_pt = pt1 + x
where x = (t11 + t21) / (a1^2 + a2^2)
then
so final_tag1 = final_tag2
Square of alphas are given, and we have determinant of submatrixes of G, which is Vandermonde matrix.
I expressed dets with alphas, and just rungroebner_basis
with them.
Then we can gain every alphas are expressed with alphas[35].
Since alpha_sum_rsa
is given, we can recover all the alphas.
After that, we can recover pvec with LLL.
But since first row of G is [1, 1, ..., 1]
, we cannot recover first value of pvec.
We can recover p with just running small_roots
.
In modulo p (or q), the make_random_vector2
has 14 non-zero values out of 36 total elements.
Since 22C8/36C8 = 0.010567.., we can simply use a brute-force method to identify the 8 indices where make_random_vector2
is zero.
Should not unpack by upx -d
and see main. Function main
is fake.
Simple xor encryption.
There is a switch case thing in 0x4009EC. But the binary call functions in different way.
Puyopuyo without display.