Rating: 5.0

## Golf.so

> Thinking that Bovik’s flags might be hidden in plain sight, you find on your minimap of the Inner Sanctum that there’s a golf course tucked into the northeast corner. Before other people catch on to the idea, you sneak off towards the rolling grassy hills of the golf course.
>
> As you walk up to the entrance, a small man with pointy ears pops up out of the ground.
>
> “Would you like to play? Right now almost nobody is here, so it’ll only be a thousand checkers to play a round.“
>
> You wave your hand and send the man the required amount. It’s not a lot of money, but your balance dwindles to a paltry sum.
>
> “Thanks! Enjoy your game.“
>
> A golf club appears in your hand along with a ball that looks suspiciously too large. The start of the first hole beckons, and you stride over to tee off.
>
> http://golf.so.pwni.ng

On the website we see a scoreboard with team names and file sizes. There is an
“Upload” button which shows us the rules of the game:
> Upload a 64-bit ELF shared object of size at most 1024 bytes. It should
> spawn a shell (execute `execve("/bin/sh", ["/bin/sh"], ...)`) when used like
>
> `LD_PRELOAD=<upload> /bin/true`

Ok, we have to write a tiny ELF file which spawns a shell when `LD_PRELOAD`ed.
Unfortunately a simple C program produces a 16kB large .so file, which is
clearly too big.

## ELF: Dynamic Shared Objects

First of all we need to know how ELF files work.

Every 64bit ELF file starts with an `Elf64_Ehdr` struct. It contains a magic
word, as well as general information about the file like the CPU architecture,
entry point, and endianess. It also has pointers to two other structures, the
Program Headers and Section Headers.

Program Headers (`Elf64_Phdr`) describe which *segments* are stored in the ELF
file. There are two important types of segments: `PT_LOAD` segments and
`PT_DYNAMIC` segments. Only `PT_LOAD` segments are loaded into memory. They
usually contain code, data, and other information that has to be available at
runtime. A `PT_DYNAMIC` segment has to overlap with a `PT_LOAD` segment, and it
holds information for the dynamic linker.

Section Headers (`Elf64_Shdr`) describe *sections* in the ELF file, like e.g.
the `.text` section. They contain additional information about which data is
stored where *within* a segment. A single segment can contain multiple
sections.

Since we want to create a library, we need both code and a `PT_DYNAMIC`
segment. We will see that sections are not important at all.

## ELF: The DYNAMIC Segment
The DYNAMIC segment consists of a list of `Elf64_Dyn` entries, which contain a
tag and a value. According to [some webpage](https://docs.oracle.com/cd/E19455-01/816-0559/chapter6-42444/index.html)
a few entries are mandatory and all other entries are optional.

One entry is particularly interesting, the `DT_INIT` entry. It has a pointer to
an initializer function which is executed after the library is loaded. It is
supposed to initialize the library. We will use this function to spawn the
shell.

## Writing An ELF File By Hand
With this information, we can start to write an ELF file by hand. To start
“small”, we write a fully compliant ELF file with all necessary information.

The shellcode is rather simple, it's an off the shelf msfvenom shellcode.

First Attempt: [golf.c](https://www.sigflag.at/assets/posts/golf.so/golf.v1.c)

Running this program produces a `golf.so` which is 821 bytes large. We can
upload this to the website, but it tells us that we have to shrink the file in
order to get the flag.

![Troll](https://www.sigflag.at/assets/posts/golf.so/troll.png)

## Making It Smaller

Now let's remove all unnecessary things. First of all the section headers are
totally irrelevant, we can just remove them. With the section headers removed,
we can also remove the strings for the string tables, since they were only
referenced from section headers.

We can also remove most of the segments and only leave a single `PT_LOAD`
segment which holds the code and the DYNAMIC data. And we can remove most of
the `Elf64_Dyn` entries, since they are not needed.

Now we only have two Program Headers:
- `PT_LOAD` which loads the whole file to address 0
- `PT_DYNAMIC` which points to the `Elf64_Dyn` list

We still have a few `Elf64_Dyn` entries:
- `DT_INIT` for our initializer
- `DT_STRTAB`
- `DT_SYMTAB`

But this is still too large.

We can start to overlap data. The last Program Header doesn't need a value in
`p_memsz` since it is not a `PT_LOAD` segment. We could start our `Elf64_Dyn`
list in this field, such that it overlaps with the Program Header.

We can save 6 more bytes by overlapping the first Program Header with the end
of the ELF header, since the last 3 fields in the ELF header are not used.

We *cannot* remove any more `Elf64_Dyn` records, since removing any of them
makes the linker crash.

Seccond Attempt: [golf.c](https://www.sigflag.at/assets/posts/golf.so/golf.v2.c)

We now have 229 bytes, but it is still too large.

## Making It Even Smaller

Running `/bin/true` with `golf.so` preloaded in a debugger reveals that our
initializer is called via `call rax`, so `rax` has a pointer to the shellcode.
Now it's time to write some custom shellcode which is smaller. Since we have
the address to our shellcode, we can store the `/bin/sh` string in some unused
ELF header field and directly reference it. Since the offset to the Section
Headers `e_shoff` is not used, we can just store the string in there.

The shellcode now looks like this:

```
401000: 48 8d b8 5e ff ff ff lea rdi,[rax-0xa2]
401007: 31 c0 xor eax,eax
401009: 50 push rax
40100a: 57 push rdi
40100b: 48 89 e6 mov rsi,rsp
40100e: 50 push rax
40100f: 48 89 e2 mov rdx,rsp
401012: b0 3b mov al,0x3b
401014: 0f 05 syscall
```

Third Attempt: [golf.c](https://www.sigflag.at/assets/posts/golf.so/golf.v3.c)

This generates a 224 byte large golf.so, but that's *still* too big. We have to
reach 192 bytes!

## Creating A Tiny But Totally Broken ELF File

Shrinking the file even further is no longer trivial. We now have to overlap
everything, and completely get rid of the shellcode section.

There are a few pointers within the ELF header as well as within Program
Headers that are never used. We can overwrite them with arbitrary values and
the linker won't care. We can use this to split the shellcode and embed it in
those pointers.

Unfortunately the first command (`lea rdi,[rax-0xa2]`) not only depends on the
address of the initializer function, it also consists of 7 bytes, so we cannot
add a jump afterwards. Luckily there are some pointers where the exact value of
the next field doesn't matter that much, so we can enter something slightly too
big. More specifically, if we put this command into the `p_addr` field of the
DYNAMIC segment, we can put the offset for a `jmp` into the `p_filesz` field.
The `DT_INIT` has to point to the `p_addr` field. The `lea` is executed, and
then the jump is taken.

The next shellcode fragment can be placed in the `e_entry` field of the ELF
header. We can fit 4 shellcode instructions in there, but then we only have one
byte left for the next jump. This time we *cannot* change the next byte, but
since the ELF header is 0x3A bytes large, we conveniently jump to address 0x90.
This is right into the `p_filesz` field of the `PT_LOAD` header. We put another
jump there, back to the `p_addr` field of the same Program Header. In this
field we can finally put the remaining commands of the shellcode.

Since we don't need the shellcode at the end of the file anymore, the last
`Elf64_Dyn` entry ends with a NULL value. We don't have to store that in our
ELF file, since everything past the file end is implicitly zero. We don't even
have to store the whole `d_tag` of the last `Elf64_Dyn` record, since only the
first byte is nonzero.

With those optimizations, the final ELF file is only 187 bytes large.

Final Solution: [golf.c](https://www.sigflag.at/assets/posts/golf.so/golf.c)

Interestingly enough, IDA Pro refuses to load this ELF file.

With this `golf.so` file, we finally get our flags:

![Flag](https://www.sigflag.at/assets/posts/golf.so/flag.png)

Original writeup (https://www.sigflag.at/blog/2020/writeup-plaidctf2020-golfso/).