I thought that perfect blue CTF that sponsored by Zellic(web3) would release blockchain challenges
However, the challenge turned out to be VM reversing.. :(
https://coinmarketcap.com/currencies/aptos/
I heard that Aptos was created by Facebook developers and was heavily promoted.
It seemed to use a different kind of VM compared to the traditional EVM and Sealevel, and Move was used in it.
Anyway, we were given a VM and we had to analyze it to extract the flag.
I had no idea how to start analyzing it, so I looked for a disassembler.
Luckily, I found it on the Move official GitHub, and since it wasn't installed on WSL, I ran it on VMWare.
https://github.com/move-language/move
I tried "move disassemble message.mv" but it doesn't work.
Ref)
https://leoq7.com/2023/02/PBCTF-Move-VM/
https://github.com/move-language/move/tree/main/language/tools/move-disassembler
https://github.com/pr0cf5/move-bytecode-llvm-compiler/tree/main/compiler
https://github.com/Knightz1/CTF/tree/main/pbctf_2022
We can modify the official Move code to enable disassembling. (It seems like other CTF teams did the same.)
https://gist.github.com/kangsangsoo/8de518ec9ef364269776c04b349f8797
result of disassemble
https://gist.github.com/kangsangsoo/2096d25bf2a500dd049e7f08faf6ee82
move's instructions
https://github.com/move-language/move/blob/main/language/move-vm/runtime/src/interpreter.rs
B0
The check_flag function takes Arg0 as an argument,
and checks whether its length equals to 58, which is the length of the flag.
B2
Assuming Arg is copied into a vector, we expect the following formula to hold true:
If we solve this formula, we can determine that the flag starts with "pbctf{" and ends with '}'.
(((vec[0] << 48) | (vec[1] << 40) | (vec[2] << 32) | (vec[3] << 24) | (vec[4] << 16) | (vec[5] << 8) | vec[length-1]) ^ 29670774015617385) == 7049012482871828
B4
LdConst instruction is used to convert bytecode to a vector.
The first two values of LdConst (252 and 1) are metadata.
Starting from LdConst[2:], 64 bytes are packed, which creates 252 elements.
Then, initialize index(loc44) to 0.
B5
If len(bytecodes) <= index, the function exits.
B7
bytecode is an 8-byte size value.
It is split into two 4-byte halves:
opcode(loc43) = (bytecode >> 32) & 255
arg(loc41) = bytecode & (2**32-1)
112line ~
Since we know the specific opcodes that will be executed based on the packed vector produced by the LdConst instruction,
we don't need to analyze every opcode defined in the VM.
We only need to focus on the opcodes that will be executed next.
LdConst = [252, 1, 1, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 120, 59, 246, 255, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 20, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 7, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 9, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 250, 24, 177, 131, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 11, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 13, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 127, 123, 156, 239, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 15, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 16, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 159, 135, 179, 149, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 19, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 20, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 21, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 101, 155, 55, 49, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 24, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 171, 83, 202, 163, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 27, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 28, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 29, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 185, 145, 23, 145, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 31, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 32, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 33, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 161, 133, 218, 233, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 35, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 37, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 45, 118, 11, 90, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 39, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 40, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 41, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 1, 110, 10, 218, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 43, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 44, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 45, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 133, 137, 39, 72, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 47, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 49, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 190, 135, 104, 172, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 51, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 53, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 188, 209, 165, 38, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 55, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 56, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 57, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 238, 181, 61, 151, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0]
import struct
bytecodes = struct.unpack("<252Q", bytes(LdConst[2:]))
for i in range(len(bytecodes):
bytecodes[i] = (bytecodes[i] >> 32) & 255
bytecodes = [0, 64, 57, 0, 57, 48, 0, 21, 57, 0, 19, 0, 17, 0, 16, 0, 19, 17, 57, 0, 16, 48, 0, 22, 58, 66, 57, 56, 57, 67, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 0, 2, 17, 68, 2, 17, 68, 2, 17, 68, 2, 17, 68, 0, 23, 65, 255, 69]
print(set(bytecodes))
{0, 2, 16, 17, 19, 21, 22, 23, 48, 56, 57, 58, 64, 65, 66, 67, 68, 69, 255}
VM.py
'''
StLoc(idx): pop하고 local[idx]에 저장
LdU64(value): value를 push
CopyLoc(idx): local[idx]를 push (복사)
moveLoc(idx): local[idx]를 push (이동)
'''
LdConst = [252, 1, 1, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 64, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 21, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 255, 255, 255, 255, 0, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 120, 59, 246, 255, 0, 0, 0, 0, 0, 0, 0, 0, 19, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 1, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 16, 0, 0, 0, 0, 0, 0, 0, 48, 0, 0, 0, 8, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 0, 0, 0, 0, 58, 0, 0, 0, 20, 0, 0, 0, 66, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 56, 0, 0, 0, 0, 0, 0, 0, 57, 0, 0, 0, 0, 0, 0, 0, 67, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 6, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 7, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 8, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 9, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 250, 24, 177, 131, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 10, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 11, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 12, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 13, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 127, 123, 156, 239, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 14, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 15, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 16, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 159, 135, 179, 149, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 18, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 19, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 20, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 21, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 101, 155, 55, 49, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 22, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 24, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 25, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 171, 83, 202, 163, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 26, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 27, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 28, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 29, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 185, 145, 23, 145, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 30, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 31, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 32, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 33, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 161, 133, 218, 233, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 34, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 35, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 36, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 37, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 45, 118, 11, 90, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 38, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 39, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 40, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 41, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 1, 110, 10, 218, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 42, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 43, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 44, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 45, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 133, 137, 39, 72, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 46, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 47, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 48, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 49, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 190, 135, 104, 172, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 50, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 51, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 52, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 53, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 188, 209, 165, 38, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 54, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 55, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 56, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 57, 0, 0, 0, 2, 0, 0, 0, 0, 0, 0, 0, 17, 0, 0, 0, 2, 0, 0, 0, 68, 0, 0, 0, 238, 181, 61, 151, 0, 0, 0, 0, 0, 0, 0, 0, 23, 0, 0, 0, 2, 0, 0, 0, 65, 0, 0, 0, 0, 0, 0, 0, 255, 0, 0, 0, 0, 0, 0, 0, 69, 0, 0, 0]
import struct
bytecodes = struct.unpack("<252Q", bytes(LdConst[2:]))
flag=b"b"*64
class VM:
def __init__(self, bytecodes: list):
self.stack = []
self.bytecodes = bytecodes
self.idx = 0
def pop(self):
return self.stack.pop()
def push(self, value):
self.stack += [value]
def go(self):
while True:
opcode = (self.bytecodes[self.idx] >> 32) & 255
arg = self.bytecodes[self.idx] & (2**32 - 1)
# print(f"opcode: {opcode}, arg: {arg}")
# input()
self.execute(opcode, arg)
def execute(self, opcode, arg):
#b8
if opcode == 0:
self.push(arg)
print(f"push({arg})")
self.idx += 1
#b15
elif opcode == 2:
self.push(flag[arg])
print(f"push(flag[{arg}])")
self.idx += 1
#b17
elif opcode == 16:
a = self.pop()
b = self.pop()
c = a + b
c = c & (2**64 - 1)
self.push(c)
print(f"push(pop({a}) + pop({b}) = {c})")
self.idx += 1
#b19
elif opcode == 17:
a = self.pop()
b = self.pop()
c = a ^ b
self.push(c)
print(f"push(pop({a}) ^ pop({b}) = {c})")
self.idx += 1
#b23
elif opcode == 19:
a = self.pop()
b = self.pop()
c = a & b
self.push(c)
print(f"push(pop({a}) & pop({b}) = {c})")
self.idx += 1
#b27
elif opcode == 21:
a = self.pop()
a = a & 255
b = self.pop()
c = b >> a
# c = a >> b
self.push(c)
print(f"push(pop({b}) >> pop({a}) = {c})")
self.idx += 1
#b29
elif opcode == 22:
a = self.pop()
a = a & 255
b = self.pop()
c = b < a
# c = b != a
if c == True:
self.push(1)
c = 1
else:
self.push(0)
c = 0
print(f"push(pop({b}) < pop({a}) = {c})")
self.idx += 1
#b34
elif opcode == 23:
a = self.pop()
b = self.pop()
c = b == a
if c == True:
self.push(1)
c = 1
else:
self.push(0)
c = 0
print(f"push(pop({b}) == pop({a}) = {c})")
self.idx += 1
#b39
elif opcode == 48:
a = self.pop()
self.push(a)
self.push(a)
print(f"dup(top)")
self.idx += 1
#b47
elif opcode == 56:
self.pop()
print(f"pop()")
self.idx += 1
#b49
elif opcode == 57:
a = self.pop()
b = self.pop()
self.push(a)
self.push(b)
print(f"swap(top)")
self.idx += 1
#b51
elif opcode == 58:
a = self.pop()
b = self.pop()
c = self.pop()
self.push(b)
self.push(c)
self.push(a)
print(f"swap(top-1)")
self.idx += 1
#b53
elif opcode == 64:
a = self.pop()
if a == 0:
self.idx += 1
else:
self.idx = arg
#b57
elif opcode == 65:
a = self.pop()
if a == 0:
self.idx += 1
else:
self.idx += arg
#b61
elif opcode == 66:
a = self.pop()
if a == 0:
# local[45] -= local[42]
self.idx += 1
else:
self.idx -= arg
#b65
elif opcode == 67:
self.idx = self.pop()
#b67
elif opcode == 68:
# self.push(local[45] + 1)
# local[42] = local[46]
# local[45] = local[42]
self.push(self.idx+1)
self.idx = arg
#b69
elif opcode == 69:
# self.push(local[45] + 1)
# local[46] = 0
# local[41] = 0
# local[39] = 0
print("exit")
exit(-1)
#b72
elif opcode == 70:
idx += 1
else:
self.idx += 1
print("?")
vm = VM(bytecodes=bytecodes)
vm.go()
python3 VM.py >> result
result
https://gist.github.com/kangsangsoo/5f079e9fb580398bb7381e49d3d23dfb
The observed pattern is that the flag is read four times and then the opcode 255 ("?" output) is encountered after the "==operator.
Therefore, it was thought that putting the==` operator in z3 would work.
The analysis of lines ~678 of the result is as follows:
Since the flag was initialized to b"b" = 98, we can focus on the operations and analyze them as follows:
It seems that the same logic is repeated over and over again, which is fortunate.
flag = b"bbbb"
tmp = flag[0] ^ 0
# 8번 반복
for _ in range(8):
tmp2 = tmp >> 1
tmp3 = tmp & 1
tmp4 = tmp3 ^ 4294967295
tmp5 = tmp4 + 1
tmp6 = 4294327160 & tmp5
tmp7 = tmp2 ^ tmp6
tmp = tmp7
tmp = flag[1] ^ tmp
# 8번 반복
for _ in range(8):
tmp2 = tmp >> 1
tmp3 = tmp & 1
tmp4 = tmp3 ^ 4294967295
tmp5 = tmp4 + 1
tmp6 = 4294327160 & tmp5
tmp7 = tmp2 ^ tmp6
tmp = tmp7
tmp = flag[2] ^ tmp
# 8번 반복
for _ in range(8):
tmp2 = tmp >> 1
tmp3 = tmp & 1
tmp4 = tmp3 ^ 4294967295
tmp5 = tmp4 + 1
tmp6 = 4294327160 & tmp5
tmp7 = tmp2 ^ tmp6
tmp = tmp7
tmp = flag[3] ^ tmp
# 8번 반복
for _ in range(8):
tmp2 = tmp >> 1
tmp3 = tmp & 1
tmp4 = tmp3 ^ 4294967295
tmp5 = tmp4 + 1
tmp6 = 4294327160 & tmp5
tmp7 = tmp2 ^ tmp6
tmp = tmp7
2209421562 == tmp
solve.py
from z3 import *
def enc(g, f):
tmp = 0
for i in range(4):
tmp = tmp ^ g[i]
for j in range(8):
tmp2 = tmp >> 1
tmp3 = tmp & 1
tmp4 = tmp3 ^ 4294967295
tmp5 = tmp4 + 1
tmp6 = 4294327160 & tmp5
tmp7 = tmp2 ^ tmp6
tmp = tmp7
s.add(tmp == f)
flag = [BitVec(f'flag{i}', 64) for i in range(58)]
ans = [2209421562, 4020009855, 2511570847, 825727845, 2747945899, 2434240953, 3923412385, 1510700589, 3658116609, 1210550661, 2892531646, 648401340, 2537403886]
s = Solver()
s.add((((flag[0] << 48) | (flag[1] << 40) | (flag[2] << 32) | (flag[3] << 24) | (flag[4] << 16) | (flag[5] << 8) | flag[len(flag)-1]) ^ 29670774015617385) == 7049012482871828)
for i in flag:
s.add(i>=0x20)
s.add(i<=0x7f)
for i in range(13):
enc(flag[6+4*i:6+4*i+4], ans[i])
print(s.check())
A = (s.model())
for i in flag:
print(chr(A[i].as_long()), end="")
pbctf{enjoy haccing blockchains? work for Zellic:pepega:!}
'writeups' 카테고리의 다른 글
DaVinciCTF 2023 - blockchain (0) | 2023.03.13 |
---|---|
SUITF (0) | 2023.03.03 |
Idek CTF 2023 pwn - (baby blockchain 1,2,3) (0) | 2023.02.19 |
HackTM CTF Quals 2023 - smart contract(Dragon Slayer, Diamond Heist) (0) | 2023.02.19 |
LA CTF 2023 - pwn(breakup, evmvm, sailor) (0) | 2023.02.13 |