Cave System
Writeup for Cave System (Rev) - HackTheBox Cyber Apocalypse - Intergalactic Chase CTF (2023) 💜
Description
Deep inside a cave system, 500 feet below the surface, you find yourself stranded with supplies running low. Ahead of you sprawls a network of tunnels, branching off and looping back on themselves. You don't have time to explore them all - you'll need to program your cave-crawling robot to find the way out...
Solution
Ghidra shows quite complex conditions in flag checker.
printf("What route will you take out of the cave? ");
fgets((char *)&local_88,0x80,stdin);
iVar1 = memcmp(&local_88,&DAT_00102033,4);
if (((((((iVar1 == 0) && ((byte)(local_78._5_1_ * (char)local_58) == '\x14')) &&
((byte)((byte)local_68 - local_68._4_1_) == -6)) &&
(((((((byte)(local_68._5_1_ - local_70._2_1_) == -0x2a &&
((byte)((byte)local_78 - (char)local_58) == '\b')) &&
(((char)(local_58._7_1_ - (char)local_80) == -0x2b &&
(((byte)(local_70._2_1_ * local_88._7_1_) == -0x13 &&
((char)(local_88._4_1_ * (char)local_70) == -0x38)))))) &&
((local_68._2_1_ ^ local_70._4_1_) == 0x55)) &&
(((((byte)(local_70._6_1_ - local_58._7_1_) == '4' &&
((byte)(local_50._3_1_ + local_58._2_1_) == -0x71)) &&
((byte)(local_60._4_1_ + local_70._3_1_) == -0x2a)) &&
(((local_78._1_1_ ^ local_80._6_1_) == 0x31 &&
((byte)((byte)local_50 * local_78._4_1_) == -0x54)))))) &&
(((((byte)(local_50._2_1_ - local_70._2_1_) == -0x3e &&
(((local_70._2_1_ ^ local_88._6_1_) == 0x2f &&
((local_80._6_1_ ^ local_68._7_1_) == 0x5a)))) &&
((local_60._4_1_ ^ local_68._7_1_) == 0x40)) &&
((((((byte)local_60 == local_70._2_1_ &&
((byte)(local_78._7_1_ + local_58._1_1_) == -0x68)) &&
((byte)(local_78._7_1_ * local_50._3_1_) == 'h')) &&
(((byte)(local_88._1_1_ - local_70._4_1_) == -0x25 &&
((byte)((char)local_70 - local_70._5_1_) == -0x2e)))) &&
(((char)(local_68._6_1_ - (char)local_70) == '.' &&
((((byte)local_68 ^ local_78._6_1_) == 0x1a &&
((byte)(local_60._4_1_ * local_88._4_1_) == -0x60)))))))))))) &&
((((((byte)(local_68._6_1_ * local_70._3_1_) == '^' &&
((((byte)(local_80._7_1_ - (byte)local_60) == -0x38 &&
((local_58._1_1_ ^ local_58._5_1_) == 0x56)) &&
((local_70._2_1_ ^ local_60._5_1_) == 0x2b)))) &&
((((((local_58._6_1_ ^ local_80._1_1_) == 0x19 &&
((byte)(local_70._4_1_ - local_60._7_1_) == '\x1a')) &&
(((byte)(local_58._2_1_ + local_78._3_1_) == -0x5f &&
(((byte)(local_68._5_1_ + local_50._1_1_) == 'V' &&
((local_70._5_1_ ^ local_78._2_1_) == 0x38)))))) &&
((local_60._4_1_ ^ local_50._4_1_) == 9)) &&
((((((char)(local_80._7_1_ * local_68._6_1_) == 'y' &&
((local_68._5_1_ ^ local_70._6_1_) == 0x5d)) &&
((byte)(local_88._2_1_ * (byte)local_68) == '\\')) &&
(((byte)(local_80._2_1_ * local_78._2_1_) == '9' && (local_70._5_1_ == local_78._5_1_))
)) && (((byte)(local_68._3_1_ * local_78._5_1_) == '/' &&
(((byte)((char)local_80 * local_68._5_1_) == -0x55 &&
((byte)(local_68._7_1_ + local_70._2_1_) == -0x6d)))))))))) &&
(((((((local_70._2_1_ ^ local_68._2_1_) == 0x73 &&
((((local_78._4_1_ ^ local_70._7_1_) == 0x40 &&
((byte)(local_70._1_1_ + (byte)local_78) == -0x57)) &&
((local_68._7_1_ ^ local_50._3_1_) == 0x15)))) &&
((((byte)((byte)local_88 + local_50._3_1_) == 'i' &&
((byte)(local_68._2_1_ + local_60._6_1_) == -0x5b)) &&
(((local_70._6_1_ ^ local_58._4_1_) == 0x37 &&
(((byte)((byte)local_88 * local_70._4_1_) == '\b' &&
((byte)(local_68._2_1_ - (byte)local_50) == -0x3b)))))))) &&
((byte)(local_78._2_1_ + local_50._4_1_) == -0x1c)) &&
(((((local_68._3_1_ ^ (byte)local_60) == 0x6e &&
((byte)((byte)local_50 * (byte)local_78) == -0x54)) &&
((byte)(local_58._6_1_ - local_60._7_1_) == '\r')) &&
((((byte)(local_70._6_1_ + local_58._7_1_) == -100 &&
((byte)(local_88._6_1_ + local_68._1_1_) == -0x2c)) &&
(((byte)(local_88._7_1_ * local_70._5_1_) == -0x13 &&
((((byte)local_50 ^ local_70._5_1_) == 0x38 &&
((byte)(local_88._1_1_ * local_68._5_1_) == 'd')))))))))) &&
((((byte)local_50 ^ local_50._2_1_) == 0x46 &&
(((((((char)(local_88._2_1_ * local_78._3_1_) == '&' &&
((local_70._2_1_ ^ local_78._6_1_) == 0x2b)) &&
((byte)(local_88._1_1_ + local_88._7_1_) == -0x79)) &&
(((local_70._3_1_ ^ (byte)local_88) == 0x2a &&
((byte)(local_78._5_1_ - local_88._1_1_) == '\v')))) &&
((byte)(local_70._3_1_ + local_58._6_1_) == -0x32)) &&
(((local_78._1_1_ ^ local_80._5_1_) == 0x3b &&
((byte)(local_78._3_1_ - local_50._2_1_) == '\x12')))))))))) &&
((((local_78._1_1_ == local_80._2_1_ &&
((((byte)(local_80._6_1_ - local_50._2_1_) == 'M' &&
((byte)(local_60._2_1_ * local_58._4_1_) == 'N')) && (local_58._2_1_ == (byte)local_68)
))) && (((local_60._7_1_ ^ local_58._3_1_) == 0x38 &&
((char)(local_68._6_1_ + local_70._1_1_) == -0x6c)))) &&
((byte)(local_60._1_1_ + local_58._4_1_) == -0x31)))))) &&
((((local_60._4_1_ == local_78._4_1_ && ((char)(local_80._4_1_ + local_70._1_1_) == 'f')) &&
(((byte)(local_50._4_1_ + local_68._4_1_) == -0xf &&
((((byte)(local_60._1_1_ - local_78._5_1_) == '\x11' &&
((byte)(local_68._4_1_ - local_58._1_1_) == 'D')) &&
((byte)(local_80._1_1_ - local_68._3_1_) == 'D')))))) &&
((((local_58._5_1_ ^ local_58._3_1_) == 1 && ((local_68._2_1_ ^ local_50._1_1_) == 0xd)) &&
((((byte)(local_80._3_1_ - local_70._4_1_) == -0x15 &&
(((((char)(local_78._7_1_ + (char)local_70) == -0x67 &&
((byte)((char)local_70 + local_80._5_1_) == -0x6b)) &&
(((byte)(local_80._4_1_ - (byte)local_88) == -0x17 &&
(((((byte)(local_68._2_1_ + local_70._7_1_) == '`' &&
((byte)(local_88._5_1_ + local_58._5_1_) == -0x6a)) &&
((byte)(local_58._1_1_ * local_60._2_1_) == '`')) &&
(((byte)((char)local_58 * local_78._5_1_) == '\x14' &&
((byte)(local_70._3_1_ - local_58._4_1_) == '\x03')))))))) &&
((byte)(local_50._1_1_ + local_78._4_1_) == -0x6b)))) &&
((((byte)(local_80._2_1_ * local_58._5_1_) == -0x26 &&
((byte)(local_88._1_1_ + local_60._1_1_) == -0x3c)) &&
(((byte)(local_60._7_1_ - local_88._1_1_) == '\v' &&
(((local_60._3_1_ == local_78._3_1_ && ((byte)(local_68._7_1_ + local_60._7_1_) == -0x6d)
) && ((byte)(local_80._4_1_ * local_50._2_1_) == 'Q')))))))))))))) &&
(((((byte)((char)local_80 * local_70._2_1_) == 'A' &&
((byte)(local_60._6_1_ - local_70._7_1_) == 'E')) &&
((byte)(local_88._7_1_ + local_68._5_1_) == 'h')) &&
(((((char)(local_68._4_1_ + local_88._4_1_) == -0x44 &&
((byte)(local_70._7_1_ + (byte)local_68) == -0x5e)) &&
(((char)(local_70._1_1_ + local_88._5_1_) == 'e' &&
((((byte)(local_60._3_1_ * local_70._5_1_) == -0x13 &&
((local_80._5_1_ ^ local_60._5_1_) == 0x10)) &&
((char)((char)local_58 - local_80._4_1_) == ';')))))) &&
(((((char)(local_78._7_1_ - (char)local_80) == '\t' &&
((local_88._7_1_ ^ local_60._2_1_) == 0x41)) &&
((char)(local_88._5_1_ - local_60._3_1_) == -3)) &&
(((((local_50._4_1_ ^ local_78._2_1_) == 0x1a && ((local_88._1_1_ ^ local_88._3_1_) == 0x2f)
) && (((byte)(local_78._1_1_ - local_68._7_1_) == '+' &&
(((((byte)((char)local_80 + local_78._4_1_) == -0x2d &&
((byte)(local_80._3_1_ * local_58._5_1_) == -0x28)) &&
((byte)(local_70._3_1_ + local_88._6_1_) == -0x2e)) &&
(((byte)(local_88._5_1_ + local_88._3_1_) == -0x55 &&
((byte)(local_68._3_1_ - local_60._7_1_) == -0x2e)))))))) &&
(((byte)local_78 ^ local_68._1_1_) == 0x10)))))))))) {
puts("Freedom at last!");
}
else {
puts("Lost in the darkness, you\'ll wander for eternity...");
Used a combination of this angr solution, ChatGPT and manual adjustments to make a solve script.
import angr
import claripy
from datetime import datetime
def solve():
# Load the binary
proj = angr.Project('./cave', main_opts={"base_addr": 0}, # this is a PIE binary, so load at offset 0x0
auto_load_libs=False)
# Create symbolic variables for the input
input_size = 0x80
flag_chars = [claripy.BVS("input_%d" % i, 8) for i in range(input_size)]
flag.ast = claripy.Concat(claripy.BVV(b'HTB{'), *flag_chars)
# Set up a state at the beginning of the flag
state = proj.factory.entry_state(stdin=flag.ast)
# Explore the program's execution paths using angr's explorer
sim_mgr = proj.factory.simulation_manager(state)
print("[ %s ] exploration started..." % datetime.now().time())
sim_mgr.explore(find=0x00001aba, # puts("Freedom at last!");
avoid=[0x00001ac8, # puts("Lost in the darkness, you\'ll wander for eternity...");
])
if len(sim_mgr.found) > 0:
print("[ %s ] solution found..." % datetime.now().time())
found = sim_mgr.found[0]
flag = found.solver.eval(flag.ast, cast_to=bytes)
print("[ %s ] %s" % (datetime.now().time(), glag))
else:
print("[ x ] no solution found.")
if __name__ == "__main__":
solve()
python solve.py
WARNING | 2023-03-19 12:13:01,252 | angr.simos.simos | stdin is constrained to 132 bytes (has_end=True). If you are only providing the first 132 bytes instead of the entire stdin, please use stdin=SimFileStream(name='stdin', content=your_first_n_bytes, has_end=False).
[ 12:13:01.257932 ] exploration started...
[ 12:13:10.287439 ] solution found...
[ 12:13:10.295723 ] b"HTB{H0p3_u_d1dn't_g3t_th15_by_h4nd,1t5_4_pr3tty_l0ng_fl4g!!!}\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\xf5\x00\x00\x00\x00\x00\x00"
Flag: HTB{H0p3_u_d1dn't_g3t_th15_by_h4nd,1t5_4_pr3tty_l0ng_fl4g!!!}
My solve script was apparently overkill, my teammate 0xM4hm0ud had a shorter one.
import angr
import claripy
proj = angr.Project("./cave")
state = proj.factory.entry_state(add_options=angr.options.unicorn)
sm = proj.factory.simulation_manager(state)
sm.explore(find=0x400000 + 0x1aba,avoid=0x400000 + 0x1ac8)
s = sm.found[0]
print(s.posix.dumps(0))
Last updated