ASIS CTF 2023 - night.js (JavaScript Engine)
jspwnnn
nc 172.86.97.8 1337
- [11 solves / 271 points]
After CTF, I solved it myself without referring to Writeups. It was really fun! xD.
This challenge is related with JavaScript Engine, but it had different exploit solution with the other JavaScript Engine like JSC.
Analysis
chall.txt
1 | commit hash: 799b465fac5672f167d6fec599fe167bce92862d |
chall.patch
1 | diff --git a/./AK/ByteBuffer.h b/../patched-serenity/AK/ByteBuffer.h |
I got errors when I applied it to my source using git apply
. So I modified source codes myself.
By the way, when I checked it I knew this challenge is from serenity
.
Analysis
The important part is array_buffer_copy_and_detach
function in ArrayBuffer.cpp
.
1 | // 25.1.2.14 ArrayBufferCopyAndDetach ( arrayBuffer, newLength, preserveResizability ), https://tc39.es/proposal-arraybuffer-transfer/#sec-arraybuffer.prototype.transfertofixedlength |
I wanted to trigger this function, But I didn’t know how to trigger it. So I read mozilla’s document. The reason I read this part was simply because this function was related to arraybuffer. (guessing with file name ^^)
1 | ArrayBuffer.prototype.transfer() Experimental |
I realized I could use transfer
. I was able to trigger it using the script below.
1 | var buffer = new ArrayBuffer(0x120); |
The important point in this function is how define copy_length
. When size of old buffer(arrray_buffer
) is over 0x100, size of new_buffer
is same as old buffer. It is really important that copy_length
is newly redefined because the argument of transfer
is passed to malloc as a size, which creates a new buffer.
In the JavaScript above is original copy_length
is 0x20, but size of array_buffer
is over 0x100, so it is redefined as 0x120!
Now let’s analyze the function below. (copy_data_block_bytes
)
1 | // 6.2.9.3 CopyDataBlockBytes ( toBlock, toIndex, fromBlock, fromIndex, count ), https://tc39.es/ecma262/#sec-copydatablockbytes |
There is no to_index + count <= to_size
check. So I can trigger overflow in this line.
1 | // ii. Set toBlock[toIndex] to fromBlock[fromIndex]. |
It was natural, since length(size) of to_block
is smaller than count
.
This is the end of the vulnerabilities I analyzed. Now Let’s go exploit. ^^
Exploit
Exploit Scenario
libc leak
- I leaked address of
liblagom-js.so.0
. - During this process, I conducted various experiments.
- I knew the return value of
transfer
can made type array. So I tried it and then I can did it. (Actually I think I was lucky. ^^) - However, in this process, it was necessary to set values such as view[4], view[5], and view[6]. I relied on dynamic analysis(gdb).
view[4]
: it means size of victim. when this had too low value, I can’t made type array.view[5]
: whether it is an inline buffer. (I didn’t know what this meant when I solved.)view[6]
: whether it is detached.
- I leaked address of
GOT overwriting
- I checked the mapping memory(vmmap instruction in gef debugger) to see if this was possible or not.
- When I called
transer
, I checked thefree
is called. To be exact, it is called when the old buffer is detached. (detach_array_buffer
) - So I decided to overwrite free’s got with a system function!
- Actually, I had concerns about how to give an argument to the system.
- The answer was to write to an Array Buffer.
- In this challenge, I need to execute
/readflag
instead ofsh
to read flag.
Exploit Code
1 | function hex(x) { |