Wednesday, 15 January 2014

[HACKYOU 2014] Crypto200 - HashMe

So in this challenge, you are given a python script and the address of a server where the script is being run. Upon inspecting the script you can see that it allows you to register and also to log in. When a user logs in as an administrator, the script will automatically spit out the password. However, in the script given to us, the KEY and SALT have both been removed.



When a user registers, he is given a certificate that he can use to log in to the system; this certificate consisting of the user's username, role, hash of the username and role, all xor'ed against the KEY and then base64 encoded. Since we have access to the certificate post-xor and we do know what part of the plaintext was ( the username and the role ), we can recover the KEY. We can manipulate the username to be as long as necessary in order to recover the entire key.



Using this method, I found the key to be 28c1150dac6704583d6c1125a72d3c87241e7f5497e9b80c78f4ce2b08dcab2b0df20be0abde0b17512a935bc765607cf5e5 when hex encoded.

Now that we have the key, we're free to obtain the auth string and the hash from any certificate issued by that particular script on that server. Looking closely at the code, it doesn't seem likely that obtaining the SALT is possible but we do see that the code searches for "administrator" in the data['role'] list. What this tells us is that it is possible for us to successfully pass the admin check even if we have more than one role present in our auth string.

It turns out that we can simply add "&role=administrator" to the end of the auth string and that works out fine. However, we still have to deal with the md5 hash of that auth string. Luckily for us, the hashing algorithm used in the script is vulnerable to a length extension attack. We can use the hash given to us in the certificate ( using the key we recovered earlier ), to discover the internal state ( A,B,C,D ) of the hashing function at the end of its operation. We can then use that state in place of the IV we usually start with and continue to hash our newly added role.

However, the hashing algorithm state, depends not only on A,B,C and D but also on the number of bytes processed up to that point (modulo 32). At this point, this value is purely dependent on the length of the SALT which is still unknown to us but we know that there are only 32 possible values for this aspect of the state and therefore it can be bruteforced.



Using the script above, I was able to generate the 32 candidate certificates for a particular user that comply with the SALT and KEY present in the script on the server. The 21st certificate was accepted, meaning that the length of the SALT was 20 bytes and then the flag was spit out.


[HACKYOU 2014] crypto300 - Matrix

So for this crypto challenge, you're given a file, encrypter.py, and another file, flag.wmv.out. Looking at the encrypter file, you can tell that the original file is broken up into blocks of 16 bytes each which are then transformed into 4x4 matrices. Each of these matrices is then multiplied by a key that was generated earlier and the resulting matrix is turned back into a string of bytes and written to the output file.



The key to this challenge was to take a look at the specification for the file format.
WMV file is in most circumstances encapsulated in the Advanced Systems Format (ASF) container format.
Looking at the ASF specification, these types of file usually start with a 16 byte GUID that identifies the file type. This hints at a known-plaintext attack. Using some basic linear algebra, given the plaintext and the ciphertext for the first 16 bytes of the file, it is possible to recover the key matrix. Once this key matrix is recovered, the rest of the file can be decrypted and the original wmv file can be recovered. The details of the steps involving the calculations are explained in comments in the code below.



Once the file has been decrypted, the wmv file is playable and it reveals the flag.



Wednesday, 24 April 2013

What's in that wchar_t*?!

So I came across a situation yesterday where I wanted the contents of a wide character string while in GDB. Now for regular strings, GDB has the command 'x' which allows you to examine memory and it also takes a format specifier, so x/s usually does the job. Unfortunately, GDB doesn't have builtin support for printing wide character strings ( or maybe I missed it while going through the docs ). A quick search led me to results like this. Seemed a bit like overkill to me.


So looking at the disassembly of main, we can see that one of the command line arguments passed to the program is converted to a wide character string and stored in a global ( I guess it could be static as well.. but meh ) buffer @ 0x80491a0. The contents of that string is already known so it isn't too interesting. Looking a little further down..


Now we see a comparison taking place between our string and another string. What is it comparing it against? We can see that the string in question is stored in a global as well. In a small program, not containing many wide character strings, there's a very simple but imprecise way of finding out.
There is the linux strings command. Now by default strings will only print regular character strings that are at least 4 characters long and followed by an unprintable character. Luckily for us strings takes a switch which allows us to specify the type of encoding of the strings that we're looking for.

 -e --encoding={s,S,b,l,B,L} Select character size and endianness: s = 7-bit, S = 8-bit, {b,l} = 16-bit, {B,L} = 32-bit

So a strings -el <binary> will get us our answer, somewhere inside a mess of other things. In a small program, this shouldn't be too troublesome but in larger programs this could become annoying.

The better, more precise way to get this done is from within gdb itself. If they're using those wide char functions then libc is probably loaded and available and so the respective functions for printing wide character strings should also be available; wprintf in particular.

So all that is really necessary is to call wprintf and pass the address of the string as the argument.


Now since the output stream is buffered and is flushed when things like newlines are printed, to see our output we either have to print a newline or we can just flush stdout. A call to fflush() does the trick and we can see the contents of the string.

Sunday, 21 April 2013

PlaidCTF - Crypto200 - Compression

  It's been a long time since I've blogged. I've been really busy with school and well... life in general.. as well a shift in focus. It sucks because before this weekend I can't remember the last time I actually made time for doing/learning anything sec-related. I feel like if I need a large chunks of uninterrupted free time to be able to get anything done and I just haven't had many of those chunks lately. I can't say I've been wasting time though. I've been investing a lot of time into developing my programming/problem solving skills in general ( mostly via competitive programming ) and that's always a good thing. I've been so out of the loop that I didn't even know that the Plaid CTF was starting until about an hour before it did ( so much for any kind of preparation! ). However, I can successfully say that I dedicated the vast majority of my time this weekend ( while ignoring assignments and the fact that finals are a little less than 2 weeks away) to participating in the CTF. I don't regret it at all. I can't remember the last time I've learned so much in such a short space of time and enjoyed it this much.

  I've been meaning to write something here for a while and now and I figured with me completing a few challenges in the CTF that I could do a writeup or two and see how it turns out. I mostly focused on the crypto challenges this time around. It's always been an area that I've been interested in but I've never really devoted a lot of time to so this was the perfect time; I was participating just for the fun of it and to learn, not competitively. Out of the few that I did solve, the one I enjoyed the most was the crypto200 - compression challenge. So here is my writeup :)


  The script above was provided to us for the challenge. The script in question was actually being run on one of their servers but with the PROBLEM_KEY and ENCRYPT_KEY set to their real values. From reading the comment on line 11, it becomes obvious what the goal is.

GOAL: Determine the PROBLEM_KEY

  At this point in time, the only information that we have about the problem key is that it's 20 characters long and it consists only of lowercase letters and underscores. The fact that they provided us witha charset hints towards the solution requiring some enumeration/bruteforcing.

  The next step, after determining what the goal was, was to take a closer look at the script and determine what it was doing.
Moving further down the script, the next thing we see is the encrypt function.



  Looking at the encrypt function we can see that it uses AES. A little background on that:
AES ( Advanced Encryption Standard ) is a symmetric block cipher. That just means that it operates on a fixed amount of data at a time and that it uses the same key for both encryption and decryption as opposed to asymmetric key encryption like RSA which uses different keys for encrypting and decrypting.

  Now with block ciphers, by design, they are only supposed to operate on one block of a fixed size. So what happens if there is more than one block?

Let's call our encryption function E, our key, K and we have two messages M1 and M2 and two ciphertexts C1 and C2
Ek(M1) = C1
Ek(M2) = C2

  The AES algorithm on its own is completely deterministic; for a given input ( k,Mx ) it will always produce the same output Cx. This is undesirable because it allows us to deduce something about the plaintext given the ciphertext. Just as an example( I'm not going to bother being technically accurate here ), consider that I had to encrypt the string "abccdeabcdef" and that a block size of 3 bytes was being used.

String: "abccdeabcdef"

Separated into 3 byte blocks: ["abc"]["cde"]["abc"]["def"]

  Now given that the block ["abc"] occurs twice, and the algorithm being completely deterministic, when looking at the ciphertexts for the different blocks, it will be quite noticeable that the same ciphertext occurs twice. Seeing that, we might not be able to deduce what exactly the plaintext was but we are able to deduce that whatever the plaintext was, it occurred at least twice within the string. Therefore we can deduce something about the plaintext by looking at the ciphertext and this is semantically insecure.

To combat this, when a block cipher with a particular key needs to be applied to more than one block, a mode of operation is necessary.

Taken from Wikipedia:
 A mode of operation describes how to repeatedly apply a cipher's single-block operation to securely transform amounts of data larger than one block.[
  Looking at the second parameter passed to AES.new(), we see AES.MODE_CTR otherwise known as Counter. This mode of operation essentially turns the block cipher into a stream cipher. The Counter is just required to produce a sequence which is guaranteed not to repeat for a long time ( usually an increment by 1 scheme is used ). In addition to the Counter, a Cryptographic Nonce is used. The nonce is usually generated from some random generator suitable for crypto purposes. The nonce is combined with the value from the counter because of the value from the counter being predictable.

  Now instead of encrypting the actual plaintext, the combination of the nonce and the counter value is encrypted instead to form the keystream. This keystream is then combined, in this case XOR'd, with the plaintext to form the ciphertext. The two diagrams below, taken from Wikipedia, illustrate the encryption and decryption processes.





However, one thing to keep in mind is that the plaintext passed to the encrypt function is compressed before it is actually encrypted. Keeping in mind that the name of the challenge was compression, it would probably be a good thing to keep in mind.

So far we know:

  • The program uses AES encryption with a 256 bit key
  • It uses CTR as it's mode of operation so it can be used on multiple blocks.
    • The nonce used comes from Python's os.urandom().The Python doc's state that this function is suitable for generating random bytes of data suitable for cryptographic use. 
  • The PROBLEM_KEY is 20 bytes long with a charset consisting of lowercase letters and underscores.
  • The plaintext is compressed before it is encrypted.
It isn't feasible to bruteforce the key. 2^256 different permutations isn't something we want to bruteforce.
It isn't feasible to bruteforce the PROBLEM_KEY. 27^20 permutations also isn't something we want to bruteforce. This naive approach is not going to work.

Moving further down the script we see:


As we connect to the server, the nonce is sent to us.

Does this help us at all?

We have the nonce and we can predict the counter values and therefore we know what's being encrypted to form the keystream. If we could figure out what the keystream was, it would be trivial to recover the plaintext. However, we don't have the AES encryption key so that isn't possible. I'm not sure why the nonce was sent but I think it was only really meant as a red herring but I could be very wrong.


Looking at the rest of the script we see mostly trivial details. The first four bytes we send to the server is interpreted as the length of the message about the follow. The server appends the PROBLEM_KEY to the data we send then first sends its length back to us followed by the encrypted version of the compressed data. The detail that really stands out here is that the PROBLEM_KEY is appended to the data.


Looking at what we have so far, it's probably not feasible to perform any sort of AES related attack. What do we have left? We know that the plaintext has the PROBLEM_KEY appended to it  before it is compressed and then encrypted.


We've already looked at the encryption so now let's take a look at the compression. The program uses the zlib library for compression. How does zlib work? A quick google search and some looking around will get you to this page which contains a sufficient explanation of the DEFLATE algorithm which zib uses. Essentially the DEFLATE algorithm uses Huffman Trees and LZ77 compression. The LZ77 compression algorithm looks for repeated substrings foward in the sequence up to a certain point and takes advantage of this redundancy. Instead of storing the same substring multiple times, at every repeated occurrence, it simply stores how far back the identical substring occurred and its length.

zlib.compress(our_data+PROBLEM_KEY) is what is taking place.

From the description of the DEFLATE algorithm, and with the knowledge of what is being compressed, it becomes clear what needs to be done. We can use the fact that the data is being compressed before it is encrypted to gather information about the plaintext and ultimately, the PROBLEM_KEY.

The length of the returned ciphertext should differ depending on how much of PROBLEM_KEY occurs in our string. Assuming that I had the first x-1 characters of the PROBLEM_KEY correct, appending the xth character would cause the length of the ciphertext take one of two values depending on whether the xth character was correct or not. The character that produced the shorter ciphertext would be the correct one.

I wrote that small script solely for the purpose of experimenting to see how the length of the compressed data varied with my input. The first thing I tried to do was to determine the first character of the PROBLEM_KEY. I realized that the compression really only started kicking in when the string I chose had all four of its characters matching the first character of the PROBEM_KEY.


After a little more experimentation, I realized that until I had at least the first four characters right I had to follow a certain pattern and from then on I could just append single characters and work with the respective lengths of the ciphertexts.

For example, if I knew c matched the first character. I would try cacacaca , cbcbcbcb, ... , c_c_c_c_ and pick the one that produced the smallest ciphertext. I did this until I had the first four characters and from there I took a much more straightforward route.

Below is the script I used to automate the process:




Now the explanation above about zlib was just a general idea of how it works. The script mostly works but it does get stuck at a few points. For instance, it successfully deduced crime_somet but another candidate string crime_some_ received the same length ciphertext and as the script continued the latter candidate moved forward while the first did not. That's the reason for me having the option to pass the first candidate as a command line argument. I had to coach the script along a bit but it did do most of the work and after about 2 pushes in the direction at the points where it started failing, it successfully produced the PROBLEM_KEY.

All that work just for "crime_sometimes_pays" :)

It was definitely an awesome challenge and I learned a lot from it. It was also a nice opportunity to brush up on my python.


Tuesday, 16 October 2012

Sale Problem

Problem Description:

In a shop each kind of product has a price. For example, the price of a flower is $2 and the price of a vase is $5. In order to attract more customers, the shop runs a promotion in which special offers are made. A special offer consists of one or more product items for a reduced price. Examples: Three flowers for $5 instead of $6, or two vases and one flower for $10 instead of $12.

Write a program that calculates the price a customer has to pay for a certain set of items, making optimal use of the special offers. That is, the price should be as low as possible. You are not allowed to add items, even if that would lower the price.

For the prices and offers given above, the lowest price for 3 flowers and 2 vases is $14: 2 vases and 1 flower for the reduced price of $10 and 2 flowers for the regular price of $4.

Input:

The first line of input.txt contains the number b of different kinds of products in the basket ( 0 <= b <= 5 ). Each of the next b lines contains three values c,k and p. The value c is the (unique) product code ( 1 <= c <= 999 ). The value k indicates how many items of this product are in the basket ( 1 <= k <= 5 ). The value p is the regular price of the item ( 1 < = p <= 999 ).

The next line contains the number s of special offers ( 0 <= s <= 99 ). Each of the next s lines describes one offer by giving its structure and its reduced price. The first number n on such a line is the number of different kinds of products that are part of the offer ( 1 <= n <= 5 ). The next n pairs of numbers (c,k) indicate that k items ( 1 <= k <= 5 ) with product code c ( 1 <= c <= 999 ) are involved in the offer. The last number p on the line stands for the reduced price ( 1 <= p <= 9999 ). The reduced price of an offer is less than the sum of the regular prices.


Output:

Write to the output file output.txt one line with the lowest possible price to be paid for the items in the basket.


Solution:


Sunday, 22 April 2012

libavformat_p1: A first look at libavutil's memory allocator

   libavformat is one of the libraries that make up the FFmpeg project. FFmpeg is one of the leading mutimedia frameworks out right now and is used  in lot of different projects . It's used in VLCMPlayerHandBrakeBlenderGoogle Chrome and many others. libavformat, in particular, is a muxing/demuxing library which means it's responsible for reading and separating streams of data from within a/v containers. My interest is isn't in creating some of kind a/v related application using FFmpeg or libavformat, but instead I'm just auditing libavformat's code base. Why? Because it's fun. :)


   Now within FFmpeg, there's a library called libavutil. libavutil contains a number of general utilities which are used by various libraries within the project itself, including libavformat. Memory allocation undoubtedly plays a big part in these kinds of libraries and the memory allocator used in libavformat is actually included from libavutil. So I'm tackling this part of the library first.



   So here is the the bread and butter of the memory allocator. Immediately, you can notice that there's some conditional compilation going on.


  • CONFIG_MEMALIGN_HACK
  • HAVE_POSIX_MEMALIGN
  • HAVE_MEMALIGN

 CONFIG_MEMALIGN_HACK is only used when the OS doesn't have one of the two supported memalign functions. So in this scenario, the memory alignment is done manually. HAVE_MEMALIGN and HAVE_POSIX_MEMALIGN are similar. HAVE_POSIX_MEMALIGN takes precedence over HAVE_MEMALIGN since posix_memalign() is more compliant on systems that have certain alignment restrictions. For all intents and purposes, posix_memalign() and memalign() can just be thought of as two functions that return pointers to blocks of memory which are aligned on the boundaries specified.

Now with that aside, we can take a closer look at the code.


Here we see that it's doing a check to make sure that the size is at least 32 bytes less than the maximum size of data allowed to be allocated. The first question one should ask when seeing this is, why 32 bytes? The answer is memory alignment. ALIGN is defined as either 16 or 32 depending on whether or not the processor supports AVX instructions. Here, it seems, they got lazy and instead of just checking to see whether it was 16 or 32, they just assumed the larger. So if the size requested was too large, it just returns NULL and that's the end of it. However, if the size wasn't too large, things become more interesting. Now the memory is allocated and aligned. If HAVE_MEMALIGN or HAVE_POSIX_MEMALIGN is defined then the corresponding function is called. However, when CONFIG_MEMALIGN_HACK is defined, some interesting code is run.



  The first thing this section of code does is that it makes a call to malloc() and allocates size+ALIGN bytes of memory. From our previous observations it should be obvious that the extra bytes allocated is so that alignment can take place. Now if the allocation fails, ptr is set to NULL and the value of ptr is returned. 

  
  Now let's look at what happens if the allocation was successful. It sets diff to the result of an expression containing ptr and ALIGN that involves some bitwise operations. The first part of the expression is ((-(long)ptr - 1). Now the '-' operator at the front tells us that it's computing the negative equivalent of ptr. Now, thinking about that in terms of bits and integer representation, it finds the two's complement of the original value. In other words, it flips all of the bits and then adds one. This is immediately followed by the subtraction of one which effectively cancels out the addition at the end of the two's complement, making it the one's complement of the original value. i.e All the bits were flipped. 


   After all of that occurs, it performs a bitwise AND against ALIGN-1. Keeping in mind that ALIGN is a power of 2, this means that all the bits { b_0, b_1, ... , b_n } before b_x where b^x = ALIGN are being considered during the bitwise AND. Now since the bits being considered against the bitwise AND are the ones before b_x in ptr that are 0 ( since the bits were flipped ), we get the  sum needed to be added to ptr to cause the bits before b_x to add up to ALIGN-1. Now to that sum, 1 is added. We now have the sum necessary to increase the pointer to a multiple of ALIGN. The result of the expression is stored in diff, and then diff is added to ptr.  Now this alignment is guaranteed to increase ptr by at least 1 byte, so it's perfectly safe in the next line when they store the value of diff at an offset of -1 from ptr. In case you're wondering why this is done, it's so that when freeing the memory later on, and the original value returned from malloc() is needed, you can compute it by subtracting diff from the value of ptr.

Saturday, 21 April 2012

Fiddling with ASN.1/DER and X.509 Certificates

   So it turns out that one of the most popular formats, if not THE most popular format, for storing X.509 certificates is base64 encoded DER; DER being an acronym for Distinguished Encoding Rules which is a concrete derivative of ASN.1 ( Abstract Syntax Notation One ). Well after a few days of reading about these two things and how they are related, I'm surprised at how little I'm actually able to understand. There is a fair amount of documentation about ASN.1 and a fair amount about X.509 certificates but there seems to be very little about how they relate. It also doesn't help that what little does exist is extremely vague...

  Luckily for me, I seemed to have stumbled upon ( literally stumbled, not the service ), two articles which seem to discuss both and how they intertwine. It is unfortunate that both articles are extremely long but maybe their density will prove to be a time saver in the long run.


  Now I can truly appreciate the value of .NET. They've simplified the entire process down to a single function call. I don't like the fact that I don't understand exactly what's going on but I have deadlines so it'll have to do for now. I hope to start my own   decoder implementation soon and hopefully finish it within the month.

Tuesday, 17 April 2012

Recursion

What is Recursion?

   Recursion is an algorithmic method for solving problems where the problem itself consists of a number of sub-problems that are near identical, but smaller to itself. These sub-problems, however, must eventually reach a base case.( Else we just end up trying to solve an infinite number of sub-problems which will probably just end up in the process's stack space being exhausted ) Since the method involves solving similar, smaller sub-problems, it shouldn't be surprising that recursion involves a function calling itself with modified arguments.
  1. Recursion involves solving a problem by solving similar but smaller sub-problems.
  2. There must be a base case, so at some point, the decomposition of problems into sub-problems will stop.
  3. Recursive solutions to problems involve functions calling themselves with modified arguments.

Note: This isn't the only situation in which recursion is applicable and this isn't the only way to think about recursion. In my opinion, this is just the best way to introduce the concept.

   Finding the nth number of the Fibonacci sequence is an easy way to demonstrate how recursion works. Since recursion involves solving a problem by solving similar sub-problems, then it would seem logical that the function would call itself.


 In the Fibonacci sequence, each term is the result of the sum of the previous two terms. The exception to this would be the first two terms where they are both 1.


 Let's say we wanted to find the nth number of the Fibonacci sequence. If n was 1 or 2, then we would just return the number, 1. However, if n > 2, then we would return the sum of the two previous fibonacci numbers, Fib(n-1) and Fib(n-2).


Fib(n) is the original problem. We find it's solution by solving similar, smaller sub-problems, Fib(n-1) and Fib(n-2).


In this code snippet, we can clearly see all the characteristics of a recursive solution.
  1. The problem of finding the nth Fibonacci number is computed using the solutions to similar sub-problems;  Fib(n-1), Fib(n-2)
  2. The base case exists when n=1,2 for Fib(n)
  3. The Fib function indeed does call itself with modified arguments.