<-->

 

 

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:!}

+ Recent posts