Padding Oracle attack and its applications on ASP.NET

ASP.NET is a group of Web development technologies created by
Microsoft, which offers developers an easy way to create dynamic web
sites, web applications, or XML web services. To use it, a compatible
web server is needed (like Microsoft IIS for example).
ASP.NET is part of Microsoft.NET platform and represents an evolution
of ASP.
ASP.NET uses cryptography in order to encrypt sensitive data. This
way, it is hidden and protected against attacks and unwanted
modifications.
In mid-September, a vulnerability on ASP.NET was reported. It
corresponds to CVE-2010-3332.
It reveals that ASP.NET is vulnerable to a "padding oracle"
attack. In this article, we will describe this type of attack, show why
it is possible on ASP.NET, and how to resolve it.

The padding oracle cryptographic attack

Definitions

PKCS7 Padding

Padding is an important notion in cryptography. The cryptographic
primitives of block ciphers proceed on fixed size bytes blocks.
If the size of the text to encrypt is not a multiple of the block size
used by the cryptographic primitive in question, then we need to fill
the last block with padding bytes.
There are many standards for padding, each one with its own rules for
the byte values. As per the PKCS7 standard (RFC 3852, section 6.3),
the value of every padding byte is the total length of the padding.
Figure 1.1 illustrates this rule.

Figure 1.1: PKCS7 padding

In the first case, 3 bytes are missing in the plain text to form a
complete block. So we have to add 3 padding bytes which will have 0x3 as
value.
In the second example, the first block is filled, and therefore does
not contain padding. On the other hand, 5 bytes are missing in the
second block, therefore we need 5 padding bytes with the 0x5 value.
In the case of a 8 byte single block, or a 8 byte final block, the
standard specify that we need to add another block full of padding,
therefore with 0x8 value.
It is important to remember that our article only deals with symmetric
ciphering primitives, with 8 bytes blocks, and using the PKCS7 padding.

The oracle

An "oracle" is a black box system which can solve a decision
problem in one operation. We send it a question and it answers it.
In our case, a padding oracle is a mechanism which, when we submit a
cipher text, will return a response that tells us whether the padding is
correct with respect to the PKCS7 standard.
In this article, we will simulate the attack presented by Vaudenay in
his paper.

CBC mode

We will attack a ciphering system using the "Cipher Block
Chaining" (CBC) mode to encrypt and decrypt data. We will first
briefly explain how the CBC mode works. Figure 1.2 shows how the mode
works.

Figure 1.2: CBC mode (source: Wikipedia)

As shown in figure 1.3, the encryption of a plain text occurs after a
"exclusive or" (XOR) between this text and a 8 bytes vector. This
vector is called the initialization vector (IV). It is chosen by the
user or generated randomly. For the following blocks, the vector used is
the ciphertext of the previous block: IV(block n) = cipher(block n-1).

Figure 1.3: encryption in CBC mode

Decryption is illustrated in figure 1.4. It is symmetric, each block
goes first in the cryptographic system, and then goes through a XOR with
an 8 bytes vector. This vector is obtained in the same way as the
encryption.

Figure 1.4: decryption in CBC mode

Simulation of the attack

We remind you that this attack is possible only on a block symmetric
ciphering using PKCS7 padding.

Decryption

In order to ease the writing and understanding of this article, we
have coded with Python a program that simulates the oracle in a class,
and implements the attack in an other class.
We start by forging a cipher text giving a plain text to the program:

When the padding is bad, a "bad padding" exception error is
raised, and this constitutes the padding oracle.
We will put ourselves in the shoes of an attacker which does not know
the key used for the encryption.
He just knows that the cryptographic system is an 8 bytes CBC block
ciphering and that the blocks are padded with PKCS7, and will only use
the padding oracle.
He intercepts a cookie containing a ciphered message that he wants to
decrypt: 1712180527255516ff7ac1e450628bcc.
In order to succeed, he uses this class in Python to simulate the
attack:

At first, the attacker begins by recovering the original IV and the
ciphered text.
Then, he decrypts each block, not forgetting that in each new block,
the new IV is the last ciphered block.
Here is the heart of the attack, the decryption of a block by
exploiting the padding oracle:

classVeryBadHacker:defuncipherBlock(self,block):if(self.verbose):print"Ciphered block: "+block.encode('hex')print"Original IV: "+self.IVoriginal.encode('hex')currentByte=0x8goodPaddingValue=0x1while(currentByte>0):if(self.verbose):print"Deciphering byte "+str(currentByte)print"Good padding value: "+str(goodPaddingValue)format="%0"+str(2*currentByte)+"x"+self.iv[2*currentByte:2*DES3.block_size]# bruteforce the IV until good padding response from the oracleforself.ivin[format%iforiinxrange(0,0xFF+1)]:try:self.oracle.decrypt(self.iv+block.encode('hex'))exceptException:continueelse:break;if(self.verbose):print"IV found to generate valid padding value: "+self.iv# we know the plain text thanks to the valid padding responsevectPadd=("%02x"%(goodPaddingValue))*goodPaddingValue# we can calculate the intermediate valuesself.intVal="%016x"%(int(self.iv,16)^int(vectPadd,16))if(self.verbose):print"Intermediates Values vector: "+self.intValprint'Deciphering byte '+str(currentByte)+' done!'# prepare data for next bytecurrentByte=currentByte-1goodPaddingValue=goodPaddingValue+1vectPadd=("%02x"%(goodPaddingValue))*(goodPaddingValue-1)# we calculate last byte of bruteforced IVself.iv="%016x"%(int(self.intVal,16)^(int(vectPadd,16)))if(self.verbose):print"New IV for next round: "+self.ivprint"\n"# all block deciphered - XORing intVal with the original IV to find the plain textunciphered="%016x"%((int(self.IVoriginal.encode('hex'),16))^(int(self.intVal,16)))if(self.verbose):print"Intermediate values found for this block: "+self.intValreturnunciphered

For each block, we have to find the IV generating a correct padding.
It is the work of the while loop which put all values from 0x00 to 0xFF
in a byte of the IV.We start with the last byte: its value is correct
whenever the generated plain text byte is 0x1, and that is exactly what
the oracle can tell us.
Once we know the last byte of the IV, we can find the previous one,
knowing that the oracle will reveal us when the plain text is 0x2 for
the last 2 bytes.
Once this IV is found, we just have to progressively recover to the
plain bytes for the following blocks by following the CBC mode
decryption. By XORing the byte found for this IV with the good padding
value, we retrieve the good intermediate value. And finally, by XORing
this intermediate value with the original IV (we know it because it
transmitted with the cipher) we recover the plain value.
For clarity purposes, we detailed the results for bytes 8 and 7 in the
following tables.
IV* represents the IV which will be brute forced, and the plain
text* is the out plain text with a valid padding.

Decryption of the 8th byte

Our brute force stops when IV*(8) = 0x16

Once the 8th byte is decrypted, we can pass to the 7th.

Decryption of the 7th byte

For it, we must keep in mind that the good value for all bytes of the
padding changes, now it is 0x2.
Therefore we have to fix the 8th byte of the new IV* so that the
plain text gives 0x2 (this way we only brute force the 7th byte). That
is possible because we have previously found the 8th byte of the
intermediate values vector. By XORing it with 0x2 we find the correct
value for the 8th byte of the IV*, and now we only need to proceed in
the same way to decrypt the 7th byte.

Our brute force finds value 0x66 for the 7th byte of the IV*, that
allows us to decrypt the cipher 7th byte.
By iterating these operations on every byte, our program retrieves the
whole plain text.

The cookie is decrypted!
The value of the cookie is: ""admin=0"
Since he is not administrator of the site, the attacker can assume
that this value would be 1 for an admin. He wants to encrypt a new
cookie with value "admin=1". But he has no idea of the encryption
key used by the server. We will see that he can however use the oracle
to correctly encrypt any message.

Encryption

Our decryption technique allows us, in addition to recovering the
plain text, to put our hands on the intermediate values vector. And if
we observe the decryption schema of CBC, we see that the discovery of
this vector is sufficient to cipher any plain text without knowing the
key, because we control the IV too.
Here is the code in Python of the function which allows us to
correctly encrypt a plain text. We just need a valid cipher in order to
retrieve the IV and the first intermediate values.

classVeryBadHacker:defcipherPlainText(self,plain,cipher):IVoriginal=cipher[0:DES3.block_size]plain=self.oracle.pkcs7(plain,DES3.block_size)nbBlock=len(plain)/(DES3.block_size)blocks=[plain[DES3.block_size*i:DES3.block_size*i+DES3.block_size]foriinrange(len(plain)/DES3.block_size)]res=cipher[DES3.block_size:2*DES3.block_size]# actual cipher = iv + first block encryptedactualCipher=cipher[0:2*DES3.block_size]while(nbBlock>0):# we decrypt the cipher to recuperate the intermediate valuesself.uncipherValue(actualCipher)# we calculate the iv necessary for the plain text blocknewIV="%016x"%(int(self.intVal,16)^int(blocks[nbBlock-1].encode('hex'),16))if(self.verbose):print'Actual cipher: '+actualCipher.encode('hex')print'IV found for this cipher: '+newIVres=newIV.decode('hex')+res# now the cipher becomes iv original + last iv foundactualCipher=IVoriginal+newIV.decode('hex')nbBlock=nbBlock-1return"Ciphered value for \""+plain+"\" is "+res.encode('hex')

The function begins by retrieving the valid ciphered block and the IV
used for this block encryption.
Then it recovers the intermediate values vector by decrypting this
cipher with the attack by padding oracle.
Next, we calculate the IV necessary for the plain data block to
encrypt (by beginning with the last block). To find this IV, we just
need to XOR the intermediate values vector with the plain block.
These operations are repeated for the following block, with the valid
cipher block becoming the original IV accompanied by the new IV that we
just found. And the process will run on until it reaches the plain text
first block.
Here is the (light) output of our function on a 2 blocks plain text:

The program starts by finding the good IV to obtain
"rld!\\x03\\x03\\x03" from the intermediate values found
thanks to the valid cipher passed in parameter
1712180527255516ff7ac1e450628bcc.
This new IV becomes the new valid cipher: 041a114c681b6614.
The program now looks for the good IV to obtain "Hello Wo" from
the intermediate values found thanks to the valid cipher found
previously: 1712180527255516041a114c681b6614.
Once this IV is found, and since we were at the first plain text
block, our algorithm is finished.
We verify our cipher using the decryption function of our server:

By changing his cookie from a ciphered value to
"1712180527255416ff7ac1e450628bcc", he becomes administrator on the
website.

ASP.NET vulnerable to padding oracle attacks

The vulnerability: the padding oracle

If we rely on the Juliano Rizzo and Thai Duong conference, on the
CVE out a little later, and on the other many blog posts, ASP.NET is
vulnerable to padding oracle attacks.
As we have seen in the first part of this article, it means that an
oracle informing us on the accuracy of the padding is present, and that
the padding scheme used is PKCS7.
We will verify that information with the tool Reflector which
allows us to browse the source code of .NET libraries.
After a quick research on the key word "padding", we retrieve the
method which returns a very clear exception on the invalidity of the
padding received.

Still with Reflector, we quickly realize that this function is used by
every Web functions of .NET using cryptography. Indeed, ASP.NET offers
applications developers the possibility to cipher their sensitive data
(cookie, form fields, VIEWSTATE, and so on). It is therefore all .NET
security models that are impacted by this vulnerability.
It is for instance the case for the method

FormsAuthentication.Decrypt(String)

from the namespace

System.Web.Security

which is the heart of ASP.NET security.
We will recover the code of this method with the Reflector tool.To
decrypt the string in the parameter, it uses the method

which is vulnerable to attacks by padding oracle, and forwards this
oracle information to the end user.

Possible applications

From CVE and the different posts on Internet, it would be possible to:

decrypt/encrypt sensible data (cookies, VIEWSTATE, and so on)

download the web.config of the application

We will summarise these different processes and explain for each them,
why they are possible.

The Decryption/encryption of sensible data

This is a basic simulation of an attack by padding oracle as seen in a
previous part.
Thanks to the oracle, an attacker who intercepts a cookie or another
type of encrypted data (other than the VIEWSTATE which is a special case
that we will explain later) can decrypt it, modify it at will and
re-encrypt it properly in order to hedge the vulnerable Web server.

The Downloading of web.config

When looking at an ASP.NET page, we can frequently see this type of
code:

The JavaScript file and its path are hidden, and can only be
accessible by using WebResource.axd. The value of the parameter "d"
is encoded in a modified version of base64 by .NET. It seems that this
value can be encrypted, and therefore may be the path and the name of
the file.
WebResource.axd is indeed a HTTP handler. When the ASP.NET server
receives a request for this file, it uses the AssemblyResourceLoader
HTTP handler located in the System.WebHandlers namespace.
Reading the code of this handler, and specially that of the

ProcessRequest(HttpContextcontext)

method, we recover a call to the function

DecryptString(str)

which is as previously seen vulnerable to padding oracle attacks.
An attacker would be able to decrypt parameter values of
WebResource.axd, and change the name of the requested file by
substituting it to web.config. Once the value is well encrypted and
injected in the URL, he will obtain the file.
It is quite important to note that there is moreover a brute force to
be performed on the first block, because we do not control the IV. To
get more information on this attack, you can read this great article.

VIEWSTATE case

In the CVE, we read that it is also possible with attack the padding
attack to decrypt, modify, and crypt the VIEWSTATE (a real tote bag for
ASP.NET developers).
But the VIEWSTATE is protected by a MAC which prevents from
modifying its value as we would do with a cookie or another type of
data.
To successfully decrypt the VIEWSTATE, we need the key used for the
MAC, key that is located in the web.config file. And , as just seen,we
are able to get it! ;)

Conclusion

Although quite simple, padding oracle attacks can be very effective
and destructive. ASP.NET has made the cost because their security model
is based on the possibility for developers to easily encrypt their data
and the VIEWSTATE.
Our tool was not developed in order to simulate the attack on real
environment. For this, we should code ways to recognize the padding
oracle program with the observation of the return code of the Web page,
the observation of eventual error messages on the page, or the
difference between display times when there is a padding error or not.
PadBuster, a quite effective software for identifying and
exploiting padding oracles in any web application is available on the
Internet.