--- title: 'Cyber Apocalypse 2023 - The Cursed Mission | Writeup' --- Cyber Apocalypse 2023 - The Cursed Mission Writeup === ![Awesome](https://awesome.re/badge.svg) ![Cyber Apocalypse 2023](https://ctf.hackthebox.com/storage/ctfs/banner-821.jpg) ## Reversing / Hunting License >STOP! Adventurer, have you got an up to date relic hunting license? If you don't, you'll need to take the exam again before you'll be allowed passage into the spacelanes! [rev_hunting_license.zip](rev_hunting_license.zip) --- #### Analysis This challenge gave us an Interactive license binary. ![Interactive Binary](https://i.imgur.com/3zanQ9P.png) ```C= ptr = sym.imp.readline("Okay, first, a warmup - what\'s the first password? This one\'s not even hidden: "); iVar1 = sym.imp.strcmp(ptr, "PasswordNumeroUno"); if (iVar1 != 0) { sym.imp.puts("Not even close!"); // WARNING: Subroutine does not return sym.imp.exit(0xffffffff); } ``` For the first `password` is `PasswordNumeroUno`. ```C= s2 = 0; var_ch = 0; sym.reverse(&s2, "0wTdr0wss4P", 0xb); ptr = sym.imp.readline("Getting harder - what\'s the second password? "); iVar1 = sym.imp.strcmp(ptr, &s2); if (iVar1 != 0) { sym.imp.puts("You\'ve got it all backwards..."); // WARNING: Subroutine does not return sym.imp.exit(0xffffffff); } ``` ```shell # echo "0wTdr0wss4P" | rev P4ssw0rdTw0 ``` For the second `password` are flipping. Now we just flip it with `rev` in terminal and we’re done with the second password `P4ssw0rdTw0`. ```C= var_30h = 0; var_28h = 0; var_20h._0_1_ = 0; sym.xor(&var_30h, "G{zawR}wUz}r\x7f222\x13", 0x11, 0x13); ptr = sym.imp.readline("Your final test - give me the third, and most protected, password: "); iVar1 = sym.imp.strcmp(ptr, &var_30h); if (iVar1 != 0) { sym.imp.puts("Failed at the final hurdle!"); // WARNING: Subroutine does not return sym.imp.exit(0xffffffff); } ``` We can see the third password is based on an `XOR` check. The `sym.xor` call where `t2` is stored. ```asm │ 0x00401357 b913000000 mov ecx, 0x13 ; 19 ; int64_t arg4 │ 0x0040135c ba11000000 mov edx, 0x11 ; 17 ; uint32_t arg3 │ 0x00401361 be70404000 mov esi, obj.t2 ; 0x404070 ; "G{zawR}wUz}r\x7f222\x13" ; int64_t arg2 │ 0x00401366 4889c7 mov rdi, rax ; int64_t arg1 │ 0x00401369 e8c9feffff call sym.xor │ 0x0040136e bfe8214000 mov edi, str.Your_final_test___give_me_the_third__and_most_protected__password:_ ; 0x4021e8 ; "Your final test - give me the third, and most protected, password: " ``` I use `radare2` to check this. ```asm [0x00401090]> x/s obj.t2 0x00404070 G{zawR}wUz}r222 ``` then using `python` to reveal the string. ```python= t2 = "G{zawR}wUz}r\x7f222\x13" xor = 0x13 password = "" for char in t2: password += chr(ord(char) ^ xor) print(password.strip('\x00')) ``` For the third `password` is `ThirdAndFinal!!!`. Next working on the remote service. ![Answer](https://i.imgur.com/WMKVWrR.png) #### Solver script :::spoiler Click to show details ```python= #!/usr/bin/env python3 from pwn import * jawab = { '1' : b"elf", '2' : b"x86-64", '3' : b"libreadline.so.8", '4' : b"0x401172", '5' : b"5", '6' : b"PasswordNumeroUno", '7' : b"0wTdr0wss4P", '8' : b"0wTdr0wss4P"[::-1], '9' : b"0x13", } t2 = "G{zawR}wUz}r222" xor = 0x13 final = "" for char in t2: final += chr(ord(char) ^ xor) # print(final) r = remote('142.93.38.14', 32115) print(r.recvuntil(b'?').decode()) r.sendline(jawab['1']) print(r.recvuntil(b'?').decode()) r.sendline(jawab['2']) print(r.recvuntil(b'?').decode()) r.sendline(jawab['3']) print(r.recvuntil(b'?').decode()) r.sendline(jawab['4']) print(r.recvuntil(b'?').decode()) r.sendline(jawab['5']) print(r.recvuntil(b'?').decode()) r.sendline(jawab['6']) print(r.recvuntil(b'?').decode()) r.sendline(jawab['7']) print(r.recvuntil(b'?').decode()) r.sendline(jawab['8']) print(r.recvuntil(b'?').decode()) r.sendline(jawab['9']) print(r.recvuntil(b'?').decode()) r.sendline(str(final).strip('\x00').encode()) r.recv() print(r.recvS()) print(r.recvS()) ``` ::: :::success Flag:`HTB{l1c3ns3_4cquir3d-hunt1ng_t1m3!}` ::: --- ## Reversing / Cave System >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... [rev_hunting_license.zip](rev_hunting_license.zip) --- #### Analysis After opening the executable `cave` in `Ghidra` we are presented with an huge `if` expression. > DECOMPILED CODE - main : :::spoiler Click to show details ```asm= ulong main(void) { int32_t iVar1; ulong s1; ulong var_78h; ulong var_70h; ulong var_68h; ulong var_60h; ulong var_58h; ulong var_50h; ulong var_48h; ulong var_40h; ulong var_38h; ulong var_30h; ulong var_28h; ulong var_20h; ulong var_18h; ulong var_10h; ulong var_8h; s1 = 0; var_78h = 0; var_70h = 0; var_68h = 0; var_60h = 0; var_58h = 0; var_50h = 0; var_48h = 0; var_40h = 0; var_38h = 0; var_30h = 0; var_28h = 0; var_20h = 0; var_18h = 0; var_10h = 0; var_8h = 0; sym.imp.printf("What route will you take out of the cave? "); sym.imp.fgets(&s1, 0x80, _reloc.stdin); iVar1 = sym.imp.memcmp(&s1, "HTB{", 4); if (((((((iVar1 == 0) && (var_70h._5_1_ * var_50h == '\x14')) && (var_60h - var_60h._4_1_ == -6)) && ((((((var_60h._5_1_ - var_68h._2_1_ == -0x2a && (var_70h - var_50h == '\b')) && ((var_50h._7_1_ - var_78h == -0x2b && ((var_68h._2_1_ * s1._7_1_ == -0x13 && (s1._4_1_ * var_68h == -0x38)))))) && ((var_60h._2_1_ ^ var_68h._4_1_) == 0x55)) && ((((var_68h._6_1_ - var_50h._7_1_ == '4' && (var_48h._3_1_ + var_50h._2_1_ == -0x71)) && (var_58h._4_1_ + var_68h._3_1_ == -0x2a)) && (((var_70h._1_1_ ^ var_78h._6_1_) == 0x31 && (var_48h * var_70h._4_1_ == -0x54)))))) && ((((var_48h._2_1_ - var_68h._2_1_ == -0x3e && (((var_68h._2_1_ ^ s1._6_1_) == 0x2f && ((var_78h._6_1_ ^ var_60h._7_1_) == 0x5a)))) && ((var_58h._4_1_ ^ var_60h._7_1_) == 0x40)) && (((((var_58h == var_68h._2_1_ && (var_70h._7_1_ + var_50h._1_1_ == -0x68)) && (var_70h._7_1_ * var_48h._3_1_ == 'h')) && ((s1._1_1_ - var_68h._4_1_ == -0x25 && (var_68h - var_68h._5_1_ == -0x2e)))) && ((var_60h._6_1_ - var_68h == '.' && (((var_60h ^ var_70h._6_1_) == 0x1a && (var_58h._4_1_ * s1._4_1_ == -0x60)))))))))))) && (((((var_60h._6_1_ * var_68h._3_1_ == '^' && (((var_78h._7_1_ - var_58h == -0x38 && ((var_50h._1_1_ ^ var_50h._5_1_) == 0x56)) && ((var_68h._2_1_ ^ var_58h._5_1_) == 0x2b)))) && ((((((var_50h._6_1_ ^ var_78h._1_1_) == 0x19 && (var_68h._4_1_ - var_58h._7_1_ == '\x1a')) && ((var_50h._2_1_ + var_70h._3_1_ == -0x5f && ((var_60h._5_1_ + var_48h._1_1_ == 'V' && ((var_68h._5_1_ ^ var_70h._2_1_) == 0x38)))))) && ((var_58h._4_1_ ^ var_48h._4_1_) == 9)) && (((((var_78h._7_1_ * var_60h._6_1_ == 'y' && ((var_60h._5_1_ ^ var_68h._6_1_) == 0x5d)) && (s1._2_1_ * var_60h == '\\')) && ((var_78h._2_1_ * var_70h._2_1_ == '9' && (var_68h._5_1_ == var_70h._5_1_)))) && ((var_60h._3_1_ * var_70h._5_1_ == '/' && ((var_78h * var_60h._5_1_ == -0x55 && (var_60h._7_1_ + var_68h._2_1_ == -0x6d)))))))))) && (((((((var_68h._2_1_ ^ var_60h._2_1_) == 0x73 && ((((var_70h._4_1_ ^ var_68h._7_1_) == 0x40 && (var_68h._1_1_ + var_70h == -0x57)) && ((var_60h._7_1_ ^ var_48h._3_1_) == 0x15)))) && (((s1 + var_48h._3_1_ == 'i' && (var_60h._2_1_ + var_58h._6_1_ == -0x5b)) && (((var_68h._6_1_ ^ var_50h._4_1_) == 0x37 && ((s1 * var_68h._4_1_ == '\b' && (var_60h._2_1_ - var_48h == -0x3b)))))))) && (var_70h._2_1_ + var_48h._4_1_ == -0x1c)) && (((((var_60h._3_1_ ^ var_58h) == 0x6e && (var_48h * var_70h == -0x54)) && (var_50h._6_1_ - var_58h._7_1_ == '\r')) && (((var_68h._6_1_ + var_50h._7_1_ == -100 && (s1._6_1_ + var_60h._1_1_ == -0x2c)) && ((s1._7_1_ * var_68h._5_1_ == -0x13 && (((var_48h ^ var_68h._5_1_) == 0x38 && (s1._1_1_ * var_60h._5_1_ == 'd')))))))))) && (((var_48h ^ var_48h._2_1_) == 0x46 && ((((((s1._2_1_ * var_70h._3_1_ == '&' && ((var_68h._2_1_ ^ var_70h._6_1_) == 0x2b)) && (s1._1_1_ + s1._7_1_ == -0x79)) && (((var_68h._3_1_ ^ s1) == 0x2a && (var_70h._5_1_ - s1._1_1_ == '\v')))) && (var_68h._3_1_ + var_50h._6_1_ == -0x32)) && (((var_70h._1_1_ ^ var_78h._5_1_) == 0x3b && (var_70h._3_1_ - var_48h._2_1_ == '\x12')))))))))) && ((((var_70h._1_1_ == var_78h._2_1_ && (((var_78h._6_1_ - var_48h._2_1_ == 'M' && (var_58h._2_1_ * var_50h._4_1_ == 'N')) && (var_50h._2_1_ == var_60h)))) && (((var_58h._7_1_ ^ var_50h._3_1_) == 0x38 && (var_60h._6_1_ + var_68h._1_1_ == -0x6c)))) && (var_58h._1_1_ + var_50h._4_1_ == -0x31)))))) && ((((var_58h._4_1_ == var_70h._4_1_ && (var_78h._4_1_ + var_68h._1_1_ == 'f')) && ((var_48h._4_1_ + var_60h._4_1_ == -0xf && (((var_58h._1_1_ - var_70h._5_1_ == '\x11' && (var_60h._4_1_ - var_50h._1_1_ == 'D')) && (var_78h._1_1_ - var_60h._3_1_ == 'D')))))) && ((((var_50h._5_1_ ^ var_50h._3_1_) == 1 && ((var_60h._2_1_ ^ var_48h._1_1_) == 0xd)) && (((var_78h._3_1_ - var_68h._4_1_ == -0x15 && ((((var_70h._7_1_ + var_68h == -0x67 && (var_68h + var_78h._5_1_ == -0x6b)) && ((var_78h._4_1_ - s1 == -0x17 && ((((var_60h._2_1_ + var_68h._7_1_ == '`' && (s1._5_1_ + var_50h._5_1_ == -0x6a)) && (var_50h._1_1_ * var_58h._2_1_ == '`')) && ((var_50h * var_70h._5_1_ == '\x14' && (var_68h._3_1_ - var_50h._4_1_ == '\x03')))))))) && (var_48h._1_1_ + var_70h._4_1_ == -0x6b)))) && (((var_78h._2_1_ * var_50h._5_1_ == -0x26 && (s1._1_1_ + var_58h._1_1_ == -0x3c)) && ((var_58h._7_1_ - s1._1_1_ == '\v' && (((var_58h._3_1_ == var_70h._3_1_ && (var_60h._7_1_ + var_58h._7_1_ == -0x6d)) && (var_78h._4_1_ * var_48h._2_1_ == 'Q')))))))))))))) && ((((var_78h * var_68h._2_1_ == 'A' && (var_58h._6_1_ - var_68h._7_1_ == 'E')) && (s1._7_1_ + var_60h._5_1_ == 'h')) && ((((var_60h._4_1_ + s1._4_1_ == -0x44 && (var_68h._7_1_ + var_60h == -0x5e)) && ((var_68h._1_1_ + s1._5_1_ == 'e' && (((var_58h._3_1_ * var_68h._5_1_ == -0x13 && ((var_78h._5_1_ ^ var_58h._5_1_) == 0x10)) && (var_50h - var_78h._4_1_ == ';')))))) && ((((var_70h._7_1_ - var_78h == '\t' && ((s1._7_1_ ^ var_58h._2_1_) == 0x41)) && (s1._5_1_ - var_58h._3_1_ == -3)) && (((((var_48h._4_1_ ^ var_70h._2_1_) == 0x1a && ((s1._1_1_ ^ s1._3_1_) == 0x2f)) && ((var_70h._1_1_ - var_60h._7_1_ == '+' && ((((var_78h + var_70h._4_1_ == -0x2d && (var_78h._3_1_ * var_50h._5_1_ == -0x28)) && (var_68h._3_1_ + s1._6_1_ == -0x2e)) && ((s1._5_1_ + s1._3_1_ == -0x55 && (var_60h._3_1_ - var_58h._7_1_ == -0x2e)))))))) && ((var_70h ^ var_60h._1_1_) == 0x10)))))))))) { sym.imp.puts("Freedom at last!"); } else { sym.imp.puts("Lost in the darkness, you\'ll wander for eternity..."); } return 0; } ``` ::: The whole thing can then be solved be replacing known values and calculating other unknowns until we have all values solved. So, I decide to make automation solver for this. #### Solver script > solver_cave1.py : :::spoiler Click to show details ```python= import sys import angr import pwn def run_binary(solution, path_to_binary): if type(solution) == str: solution = bytes(solution, "utf-8") print(f"[+] Found: {solution.decode()}".strip('\x00')) elf = pwn.ELF(path_to_binary, checksec=False) pty = pwn.process.PTY io = elf.process(stdin=pty, stdout=pty, level="warn") io.recvuntil(b"? ") io.sendline(solution) output = io.recvline().decode().splitlines()[0].strip('\x00') print(f"[+] {output}") def main(): path_to_binary = "./cave" project = angr.Project(path_to_binary, main_opts={'base_addr': 0}) initial_state = project.factory.entry_state( add_options={ angr.options.SYMBOL_FILL_UNCONSTRAINED_MEMORY, angr.options.SYMBOL_FILL_UNCONSTRAINED_REGISTERS, }, ) simulation = project.factory.simgr(initial_state) target = 0x00001aba notgood = 0x00001ac8 simulation.explore(find=target, avoid=notgood) if simulation.found: solution_state = simulation.found[0] solution = solution_state.posix.dumps(sys.stdin.fileno()) run_binary(solution, path_to_binary) else: raise Exception("Could not find the solution") if __name__ == "__main__": main() ``` ::: :::success Flag:`HTB{H0p3_u_d1dn't_g3t_th15_by_h4nd,1t5_4_pr3tty_l0ng_fl4g!!!}` ::: --- ## Reversing / Needle in a Haystack >You've obtained an ancient alien Datasphere, containing categorized and sorted recordings of every word in the forgotten intergalactic common language. Hidden within it is the password to a tomb, but the sphere has been worn with age and the search function no longer works, only playing random recordings. You don't have time to search through every recording - can you crack it open and extract the answer? [rev_needle_haystack.zip](rev_needle_haystack.zip) --- #### Analysis > DECOMPILED CODE - main : ```asm= ulong main(void) { uint uVar1; int32_t iVar2; ulong var_4h; sym.imp.setbuf(_reloc.stdout, 0); sym.imp.printf("Hit enter to select a recording: "); sym.imp.getchar(); uVar1 = sym.imp.time(0); sym.imp.srand(uVar1); for (var_4h._0_4_ = 0; var_4h < 3; var_4h._0_4_ = var_4h + 1) { sym.imp.putchar(0x2e); sym.imp.sleep(1); } iVar2 = sym.imp.rand(); sym.imp.printf("\"%s\"\n", *(obj.words + (iVar2 + (iVar2 / 0xcb) * -0xcb) * 8)); return 0; } ``` The program is fairly simple. First it does a `small processing` animation and then chooses a random word out of a word list. The assumption is that inside the `word` list the flag might be hidden. Trying to find with `strings` command, found what we want. :tada: ``` └─# strings haystack | grep 'HTB' HTB{d1v1ng_1nt0_th3_d4tab4nk5} ``` :::success Flag:`HTB{d1v1ng_1nt0_th3_d4tab4nk5}` ::: --- ###### tags: `Cyber Apocalypse 2023` `HTB` `Writeup` `Documentation`