Hello I am Arsalan. Offensive Security Engineer, I blog about Cyber security, CTF writeup, Programming, Blockchain and more about tech. born and raised in indonesia, currently living in indonesia
[AsisCTF Quals 2023] Attacking Javascript Engine libjs SerenityOS
Published on 27 Sep 2023
This is a writeup for night.js pwn challenge
Background
Asis CTF 2023 Quals has ended. I solved 3 out of 4 challenges, including night.js. Unfortunately, I solved night.js after the event had ended and read a write-up on Discord ;). This challenge helped me a lot to learn more about pwning the JavaScript interpreter. It’s a JavaScript interpreter pwn challenge, which means we are attacking the JS binary/interpreter, not a JS web application.
Challenge Analysis
We were given several filess, including Dockerfile, challenge patch, commit information, and more. After analyzing the challenge patch, I discovered that the patch will disables the assertion and from_size checks, allowing us to call the function copy_data_block_bytes with smaller size from the destination array. this can lead to arbitrary read or write.
With this information, we can trigger the vulnerability by using ArrayBuffer.prototype.transfer(),
which allow us to copy the contents of the array to a new array. In this case, we can create several array buffer, then modifying the appropriate array with a size of 0x100 then call .transfer to trigger the vulnerability.
now the value at buffer_array[0x2f] will stored at the memory location array_address+0x0040
Yellow represents the address of the array, blue represents the value stored in the array. When we trigger ArrayBuffer.prototype.transfer(), the size of the copied array will 0x100.
The new array will be created with size 0x100, which is equivalent to 256. and this array will contains memory leaks.
We can use the memory leak to obtain __libc_system and the Array address. Our plan is to overwrite the free@GOT with the address of the one_gadget.
Before that, we can perform array spray-ing, which can be useful for performing a GOT overwrite.
Now we can initialize a new array to read the stored memory inside buffer2 which is the array with small size but has a size of 0x100.
Not only read. We can also performing GOT overwrite by inserting the free@got address into leak[16]. Then create a new big uint64 array using our spray_array[0] finally, we can modify free@got using the first index of the new big uint64 array.
We are overwriting free@got because the liblagom-js.so.0.0.0 library lacks RELRO protection. Allowing us to freely overwrite the values in the GOT addresses within this library. Additionally, free() is used extensively throughout the binary.
Now, we can overwrite the free@got with __libc_system. In this case, I use one_gadget to simplify the process. Here is my full exploit code to achieve remote code execution (RCE).