# golf.so Writeup
## Problem Details
[golf.so.pwni.ng](http://golf.so.pwni.ng/)
## Solution
The linked site contains some leaderboard table with the ability to upload new solutions:
![](https://i.imgur.com/cNrhzC5.png)
When we try to upload we get the following message:
![](https://i.imgur.com/sBGKwLg.png)
Means we must create shared object which should run `execve` upon loading, and be small enough.
Lets start working.
### Sources
- [https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html](https://www.muppetlabs.com/~breadbox/software/tiny/teensy.html) - great tutorial minimizing 32 bit linux executable.- [https://stackoverflow.com/questions/53382589/smallest-executable-program-x86-64](https://stackoverflow.com/questions/53382589/smallest-executable-program-x86-64) - good exmaple porting the previous tutorial to 64 bit.- [https://github.com/elemeta/elfloader](https://github.com/elemeta/elfloader) - Linux loader wasn't informative about it's issues, so I used external small loader to debug the ELF.- [http://osr507doc.sco.com/en/topics/ELF_dynam_section.html](http://osr507doc.sco.com/en/topics/ELF_dynam_section.html) - Greate documentation of the dynamic section.
### Naive Try - Using GCC
First of all what is ELF ?
**Executable and Linkable Format** is a common standard in Unix environment which includes the following:
- Executable files (for example `/bin/bash`).- Object code (for example `.o` file created by a compiler).- Shared libraries (equivalent to `.dll` files).- Core dumps.
The reason we use shared libraries  is mainly to reduce amount of code and data of the executables, and allow sharing common resources between multiple executables. Best example is the common `libc` shared library.
So lets compile a simple shared library which runs execve:
```cint main() {    execve("/bin/sh", 0, 0);    return 0;}```
```bashalex@ubuntu:~/Desktop/writeup$ LD_PRELOAD=./sample_gcc /bin/true```
Nothing happens.
Apparently the Linux loader will not run anything (unlike Windows dllmain) unless it was commanded to with special annotation:
```c__attribute__((constructor)) main() {    execve("/bin/sh", 0, 0);}```
```bashalex@ubuntu:~/Desktop/writeup$ LD_PRELOAD=./sample_gcc /bin/true$ whoamialexalex@ubuntu:~/Desktop/writeup$ wc -c sample_gcc7904 sample_gcc```
Now it works, but the file is too big.
### Compiling ELF in NASM
I searched the web for template of a minimized ELF file so I can use NASM - a popular assembler and disassembler for Linux. So I used the following template:
```wasmbits 64            org 0x08048000
ehdr:                                           ; Elf64_Ehdr            db  0x7F, "ELF", 2, 1, 1, 0         ;   e_ident    times 8 db  0            dw  2                               ;   e_type            dw  62                              ;   e_machine            dd  1                               ;   e_version            dq  _start                          ;   e_entry            dq  phdr - $$                       ;   e_phoff            dq  0                               ;   e_shoff            dd  0                               ;   e_flags            dw  ehdrsize                        ;   e_ehsize            dw  phdrsize                        ;   e_phentsize            dw  1                               ;   e_phnum            dw  0                               ;   e_shentsize            dw  0                               ;   e_shnum            dw  0                               ;   e_shstrndx
ehdrsize    equ $ - ehdr
phdr:                                           ; Elf64_Phdr            dd  1                               ;   p_type            dd  5                               ;   p_flags            dq  0                               ;   p_offset            dq  $$                              ;   p_vaddr            dq  $$                              ;   p_paddr            dq  filesize                        ;   p_filesz            dq  filesize                        ;   p_memsz            dq  0x1000                          ;   p_align
phdrsize    equ     $ - phdr
_start:    xor eax, eax    mul esi    push rax    mov rdi, "/bin//sh"    push rdi    mov rdi, rsp    mov al, 59    syscall
filesize      equ     $ - $$```
We can compile it with NASM and run it:
```bashalex@ubuntu:~/Desktop/writeup$ nasm -f bin -o try2 try2.asmalex@ubuntu:~/Desktop/writeup$ ./try2bash: ./try2: Permission deniedalex@ubuntu:~/Desktop/writeup$ chmod 777 try2alex@ubuntu:~/Desktop/writeup$ ./try2$alex@ubuntu:~/Desktop/writeup$ wc -c try2143 try2```
We can observe the ELF using `readelf` command:
```bashalex@ubuntu:~/Desktop/writeup$ readelf -h try2ELF Header:  Magic:   7f 45 4c 46 02 01 01 00 00 00 00 00 00 00 00 00   Class:                             ELF64  Data:                              2's complement, little endian  Version:                           1 (current)  OS/ABI:                            UNIX - System V  ABI Version:                       0  Type:                              EXEC (Executable file)  Machine:                           Advanced Micro Devices X86-64  Version:                           0x1  Entry point address:               0x8048078  Start of program headers:          64 (bytes into file)  Start of section headers:          0 (bytes into file)  Flags:                             0x0  Size of this header:               64 (bytes)  Size of program headers:           56 (bytes)  Number of program headers:         1  Size of section headers:           0 (bytes)  Number of section headers:         0  Section header string table index: 0```
So lets analyze the assembly file:
- `bits 64` - 64 bit architecture- `org 0x08048000` - Sets base address.- `ehdr` - Defines ELF header. It is a must for every ELF file. We will soon see that not all the fields are mendatory.    - `e_shoff` - Value for section header table is zero. This means the file has no sections like `.text`, `.data`, `.reloc` and more.    - `e_phnum` - equals to 1. This means we have one program header.    - A popular misconception is that executable must have sections. Sections are created by the compiler only for organizing the file and helping the loader to map each section to the appripriate memory region (R/W/X).- `phdr` - The only program header in file.    - `p_type` - Equals to 1. We can see using `readelf` it is of `LOAD` type.
    ```bash    alex@ubuntu:~/Desktop/writeup$ readelf -l try2
    Elf file type is EXEC (Executable file)    Entry point 0x8048078    There is 1 program header, starting at offset 64
    Program Headers:      Type           Offset             VirtAddr           PhysAddr                     FileSiz            MemSiz              Flags  Align      LOAD           0x0000000000000000 0x0000000008048000 0x0000000008048000                     0x000000000000008f 0x000000000000008f  R E    0x1000    ```
- `_start` - This symbol will be run upon file execution. In our case is contains shellcode running `execve`.
Now lets fix the file so it will be defined as shared object and not as executable:
- we change `dw  2                               ;   e_type` into `dw  3`.
```bashalex@ubuntu:~/Desktop/writeup$ nasm -f bin -o try2 try2.asm alex@ubuntu:~/Desktop/writeup$ file try2try2: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), statically linked, corrupted section header sizealex@ubuntu:~/Desktop/writeup$ LD_PRELOAD=./try2 /bin/trueERROR: ld.so: object './try2' from LD_PRELOAD cannot be preloaded (object file has no dynamic section): ignored.```
So we can see the file is indeed `shared object` but when we try to load it we receive `object file has no dynamic section`. Now the fun part begins.
### Creating Dynamic Program Header
According to [http://man7.org/linux/man-pages/man5/elf.5.html](http://man7.org/linux/man-pages/man5/elf.5.html):
> PT_DYNAMICThe array element specifies dynamic linking information.
Not helping. Lets open in IDA the first GCC compiled version and dissect it.
**Program Header for the Dynamic data:**
![](https://i.imgur.com/zcfC2Yh.png)
It's `Virtual Address` field points to mapped address which contains more information about dynamic linking.
**Dynamic Linking Struct:**
![](https://i.imgur.com/qV6j1vX.png)
Digging into [http://osr507doc.sco.com/en/topics/ELF_dynam_section.html](http://osr507doc.sco.com/en/topics/ELF_dynam_section.html) led me into the next information:
- Each entry has 16 bytes size -    - `dq code`    - `dq value/ptr`- `DT_NEEDED` - Linking to dependable libc. the value is index at string table.- `DT_INIT` - Virtual address  to function which should run on loading.- `DT_GNU_HASH` - Not sure about it's meaning. Because it was a must for a proper loading, I copied the values form this executable.- `DT_STRTAB` - String table pointer. Contains important strings for loading and linking
![](https://i.imgur.com/tBc6B3Y.png)
- `DT_SYMTAB` - Symbol table pointer.
![](https://i.imgur.com/COiM6fs.png)
- `DT_STRSZ` - String table size.- `DT_SYMENT` - Symbol table entry size.- `DT_NULL` - Ending field.- Rest weren't important for the challenge.
Using the attached sources, mandatory fields are: `DT_NULL`, `DT_HASH`, `DT_STRTAB`, `DT_SYMTAB`, `DT_STRSZ`, `ST_SYMENT`.
So lets create a custom dynamic section/table (`try3.asm`):
```wasmbits 64            org 0x08048000
ehdr:                                           ; Elf64_Ehdr            db  0x7F, "ELF", 2, 1, 1, 0         ;   e_ident    times 8 db  0            dw  3                               ;   e_type            dw  62                              ;   e_machine            dd  1                               ;   e_version            dq  _start                          ;   e_entry            dq  0x40                            ;   e_phoff            dq  0                               ;   e_shoff            dd  0                               ;   e_flags            dw  0x40                            ;   e_ehsize            dw  0x38                            ;   e_phentsize            dw  2                               ;   e_phnum            dw  0                               ;   e_shentsize            dw  0                               ;   e_shnum            dw  0                               ;   e_shstrndx
phdr_load:                                           ; Elf64_Phdr            dd  1                               ;   p_type            dd  7                               ;   p_flags            dq  0                               ;   p_offset            dq  $$                              ;   p_vaddr            dq  $$                              ;   p_paddr            dq  filesize                        ;   p_filesz            dq  filesize                        ;   p_memsz            dq  0x1000                          ;   p_align
phdr_dyn:                                           ; Elf64_Phdr            dd  2                               ;   p_type            dd  6                               ;   p_flags            dq  dyn_offset                      ;   p_offset            dq  _dyn                            ;   p_vaddr            dq  _dyn                            ;   p_paddr            dq  dyn_size                        ;   p_filesz            dq  dyn_size                        ;   p_memsz            dq  0x8                          ;   p_align
_start:    xor eax, eax    mul esi    push rax    mov rdi, "/bin//sh"    push rdi    mov rdi, rsp    mov al, 59    syscall
dyn_offset equ $ - $$
_dyn:    dq 6FFFFEF5h, _gnu_hash     ; DT_GNU_HASH    dq 5, 0                     ; DT_STRTAB    dq 6, 0                     ; DT_SYMTAB    dq 0Ah, 0h                 ; DT_STRSZ    dq 0Bh, 0h                 ; DT_SYMENT    dq 0Ch, _start              ; DT_INIT    dq 0,0
dyn_size    equ $ - _dyn
_gnu_hash:    dd 1    dd 1    dd 1    dd 0    dq 0    dd 0    dd 0
filesize      equ     $ - ehdr```
When we run it we get:
```bashalex@ubuntu:~/Desktop/writeup$ LD_PRELOAD=./try3 /bin/true$ alex@ubuntu:~/Desktop/writeup$ wc -c try3343 try3```
Great Progression !
In order to get the first flag, we need to be below 300.
Playing a bit with the assembly we can see `DT_GNU_HASH` isn't really mandatory, and can be removed.
This will give us:
```bashalex@ubuntu:~/Desktop/writeup$ wc -c try3295 try3```
And the first flag:
![](https://i.imgur.com/4N29Vvw.png)
### Reducing to the Minimum
Lets try to find more unnecessary dynamic fields. 
We can remove `DT_SYMENT` and `DT_STRSZ`, but anything beyond that will give us SegFault.
Next effort will be finding unused fields inside program header, and elf header.
I iterated all the fields to find all the unused one. I also changed the the shellcode:
```wasmbits 64            org 0x08048000
ehdr:                                           ; Elf64_Ehdr            db  0x7F, "ELF", 2, 1, 1, 0         ;   e_ident    times 8 db  0            dw  3                               ;   e_type            dw  62                              ;   e_machine            dd  1                               ;   e_version            dq  _start                          ;   e_entry            dq  0x40                            ;   e_phoff            times 12 db 9   ; FREE SPACE            dw  0x40                            ;   e_ehsize            dw  0x38                            ;   e_phentsize            dw  2                               ;   e_phnum            times 6 db 9    ; FREE SPACE
phdr_load:                                           ; Elf64_Phdr            dd  1                               ;   p_type            dd  7                               ;   p_flags            dq  0                               ;   p_offset            dq  $$                              ;   p_vaddr            times 16 db 9   ; FREE SPACE            dq  filesize                        ;   p_memsz            dq  0x1000                          ;   p_align
phdr_dyn:                                           ; Elf64_Phdr            dd  2                               ;   p_type            dd  6                               ;   p_flags            dq  dyn_offset                      ;   p_offset            dq  _dyn                            ;   p_vaddr            times 32 db 9   ; FREE SPACES
_start:    xor eax, eax    mov rbx, 0xFF978CD091969DD1    neg rbx    push rbx    push rsp    pop rdi    cdq    push rdx    push rdi    push rsp    pop rsi    mov al, 0x3b    syscall
dyn_offset equ $ - $$
_dyn:    dq 5, 0                     ; DT_STRTAB    dq 6, 0                     ; DT_SYMTAB    dq 0Ch, _start              ; DT_INIT    dq 0,0
dyn_size    equ $ - _dyn
filesize      equ     $ - ehdr```
When we assemble the shellcode, we can split it, and use `jmp short` between the parts (2 byte instruction).
```wasmpart 1 - 10 bytes48 bb d1 9d 96 91 d0    movabs rbx,0xff978cd091969dd18c 97 ff
part 2 - 3 bytes48 f7 db                neg    rbx
part 3 - 14 bytes31 c0                   xor    eax,eax53                      push   rbx54                      push   rsp5f                      pop    rdi99                      cdq52                      push   rdx57                      push   rdi54                      push   rsp5e                      pop    rsib0 3b                   mov    al,0x3b0f 05                   syscall```
So lets insert the shellcode into the spaces, and the dynamic table into the last 32 bytes space.
Final code (`try4.asm`):
```wasmbits 64            org 0x08048000
ehdr:                                           ; Elf64_Ehdr            db  0x7F, "ELF", 2, 1, 1, 0         ;   e_ident    times 8 db  0            dw  3                               ;   e_type            dw  62                              ;   e_machine            dd  1                               ;   e_version            dq  _start                          ;   e_entry            dq  pht_start                       ;   e_phoff    ; part 1    _start:             mov rbx, 0xFF978CD091969DD1            jmp part_2            dw  0x40                            ;   e_ehsize            dw  0x38                            ;   e_phentsize            dw  2                               ;   e_phnum    part_2:            neg rbx            jmp part_3
pht_start equ $ - $$
phdr_load:                                           ; Elf64_Phdr            dd  1                               ;   p_type            dd  7                               ;   p_flags            dq  0                               ;   p_offset            dq  $$                              ;   p_vaddr    part_3:            xor eax, eax            push rbx            push rsp            pop rdi            cdq            push rdx            push rdi            push rsp            pop rsi            mov al, 0x3b            syscall            db 0,0 ; remainder            dq  filesize                        ;   p_memsz            dq  0x1000                          ;   p_align
phdr_dyn:                                           ; Elf64_Phdr            dd  2                               ;   p_type            dd  6                               ;   p_flags            dq  dyn_offset                      ;   p_offset            dq  _dyn                            ;   p_vaddr    ; dynamic table in free space    dyn_offset equ $ - $$    _dyn:            dq 0Ch, _start ; DT_INIT            dq 5, 0   ; DT_STRTAB            dq 6, 0    ; DT_SYMTAB
filesize      equ     $ - ehdr```
```wasmalex@ubuntu:~/Desktop/writeup$ nasm -f bin -o try4 try4.asm alex@ubuntu:~/Desktop/writeup$ LD_PRELOAD=./try3 /bin/true$ alex@ubuntu:~/Desktop/writeup$ wc -c try4191 try4```
When we submit it we get the second flag:
![](https://i.imgur.com/7fJmzpA.png)