Skip to content

Commit c0a2e10

Browse files
committed
Add support for two different paddings: PKCS7 and ANSIX923
1 parent e471816 commit c0a2e10

File tree

4 files changed

+95
-40
lines changed

4 files changed

+95
-40
lines changed

PaddingOracleDecryptor.cs

Lines changed: 61 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,61 @@
1+
using System;
2+
using System.Text;
3+
using System.Reflection;
4+
using System.Security.Cryptography;
5+
6+
namespace Padding_Oracle_Attack
7+
{
8+
class PaddingOracleDecryptor
9+
{
10+
private RemoteServerMock oracle;
11+
private PaddingValueProvider paddingValueProvider;
12+
13+
public PaddingOracleDecryptor(RemoteServerMock oracle)
14+
{
15+
this.oracle = oracle;
16+
17+
PaddingMode paddingMode = oracle.Padding;
18+
// TODO: ugly, but works!
19+
paddingValueProvider = (int pos, int paddingLength, int blockLength) => (byte)(typeof(PaddingValueProviders).GetMethod(Enum.GetName(typeof(PaddingMode), paddingMode)).Invoke(null, new object[] { pos, paddingLength, blockLength }));
20+
}
21+
22+
23+
public string DecryptBlock(byte[] block, byte[] previousBlock)
24+
{
25+
byte[] decrypted = new byte[block.Length];
26+
byte[] manipulatedPrevious = new byte[16];
27+
28+
for (int currentPosition = block.Length - 1; currentPosition >= 0; --currentPosition)
29+
{
30+
var paddingLength = block.Length - currentPosition;
31+
32+
for (int pos = block.Length - 1; pos > currentPosition; --pos)
33+
{
34+
manipulatedPrevious[pos] ^= (byte)(paddingValueProvider(pos, paddingLength - 1, block.Length) ^ paddingValueProvider(pos, paddingLength, block.Length));
35+
}
36+
37+
var found = false;
38+
39+
for (byte v = byte.MinValue; v <= byte.MaxValue; ++v)
40+
{
41+
manipulatedPrevious[currentPosition] = v;
42+
43+
if (oracle.IsPaddingCorrect(ByteUtils.Concatenate(manipulatedPrevious, block)))
44+
{
45+
found = true;
46+
decrypted[currentPosition] = (byte)(previousBlock[currentPosition] ^ paddingValueProvider(currentPosition, paddingLength, block.Length) ^ v);
47+
break;
48+
}
49+
}
50+
51+
if (!found)
52+
{
53+
throw new Exception("Decryption not possible");
54+
}
55+
}
56+
57+
return Encoding.UTF8.GetString(decrypted, 0, decrypted.Length);
58+
}
59+
60+
}
61+
}

PaddingValueProviders.cs

Lines changed: 17 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,17 @@
1+
namespace Padding_Oracle_Attack
2+
{
3+
public delegate byte PaddingValueProvider(int pos, int paddingLength, int blockLength);
4+
5+
class PaddingValueProviders
6+
{
7+
public static byte PKCS7(int pos, int paddingLength, int blockLength)
8+
{
9+
return (byte)paddingLength;
10+
}
11+
12+
public static byte ANSIX923(int pos, int paddingLength, int blockLength)
13+
{
14+
return (byte)((pos == blockLength - 1) ? paddingLength : 0);
15+
}
16+
}
17+
}

Program.cs

Lines changed: 9 additions & 38 deletions
Original file line numberDiff line numberDiff line change
@@ -1,4 +1,5 @@
1-
using System.Net.Mime;
1+
using System.Security.Cryptography;
2+
using System.Net.Mime;
23
using System.Linq;
34
using System.Diagnostics;
45
using System;
@@ -9,7 +10,8 @@ namespace Padding_Oracle_Attack
910
{
1011
class PaddingOracleAttack
1112
{
12-
private static RemoteServerMock server = new RemoteServerMock();
13+
private static RemoteServerMock server = new RemoteServerMock(PaddingMode.PKCS7);
14+
private static PaddingOracleDecryptor decryptor = new PaddingOracleDecryptor(server);
1315

1416
public static void Main(String[] args)
1517
{
@@ -36,7 +38,7 @@ public static void Main(String[] args)
3638
{
3739
stopwatch.Start();
3840

39-
string decryptedPlaintext = DecryptBlock(blocks[blockIndex], blocks[blockIndex - 1]);
41+
string decryptedPlaintext = decryptor.DecryptBlock(blocks[blockIndex], blocks[blockIndex - 1]);
4042

4143
stopwatch.Stop();
4244

@@ -57,15 +59,17 @@ private static void HandleConfigurationArguments(String[] args)
5759
{
5860
OptionSet arguments = new OptionSet();
5961
arguments.Add("d|delay=", "oracle delay in milliseconds for each padding request", (uint d) => server.OracleDelayMilliseconds = d);
60-
arguments.Add("h|help", "displays this message", _ => {
62+
arguments.Add("h|help", "displays this message", _ =>
63+
{
6164
arguments.WriteOptionDescriptions(Console.Out);
6265
Environment.Exit(0);
6366
});
6467

6568
try
6669
{
6770
var rest = arguments.Parse(args);
68-
if (rest.Count == 0) {
71+
if (rest.Count == 0)
72+
{
6973
return;
7074
}
7175
Console.WriteLine("Unrecognized arguments: {0}", String.Join(",", rest));
@@ -78,38 +82,5 @@ private static void HandleConfigurationArguments(String[] args)
7882
arguments.WriteOptionDescriptions(Console.Out);
7983
Environment.Exit(1);
8084
}
81-
82-
private static string DecryptBlock(byte[] block, byte[] previousBlock)
83-
{
84-
byte[] decrypted = new byte[block.Length];
85-
byte[] manipulatedPrevious = new byte[16];
86-
87-
// in case of PKCS7 padding value is same as padding length
88-
for (int paddingLength = 1; paddingLength <= block.Length; ++paddingLength)
89-
{
90-
for (int pos = block.Length - 1; pos >= block.Length - paddingLength; --pos)
91-
{
92-
int previousPaddingLength = paddingLength - 1;
93-
manipulatedPrevious[pos] ^= (byte)(previousPaddingLength ^ paddingLength);
94-
}
95-
var found = false;
96-
for (byte v = byte.MinValue; v <= byte.MaxValue; ++v)
97-
{
98-
manipulatedPrevious[block.Length - paddingLength] = v;
99-
if (server.IsPaddingCorrect(ByteUtils.Concatenate(manipulatedPrevious, block)))
100-
{
101-
found = true;
102-
decrypted[block.Length - paddingLength] = (byte)(previousBlock[block.Length - paddingLength] ^ paddingLength ^ v);
103-
break;
104-
}
105-
}
106-
if (!found)
107-
{
108-
throw new Exception("Decryption not possible. This function supports only AES/CBC/PKCS7");
109-
}
110-
}
111-
112-
return Encoding.UTF8.GetString(decrypted, 0, decrypted.Length);
113-
}
11485
}
11586
}

RemoteServerMock.cs

Lines changed: 8 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -9,11 +9,17 @@ class RemoteServerMock
99
private Aes aesAlg = Aes.Create();
1010
public uint OracleDelayMilliseconds { get; set; } = 0;
1111

12-
public RemoteServerMock()
12+
public PaddingMode Padding {
13+
get {
14+
return aesAlg.Padding;
15+
}
16+
}
17+
18+
public RemoteServerMock(PaddingMode paddingMode = PaddingMode.PKCS7)
1319
{
1420
aesAlg.BlockSize = 128;
1521
aesAlg.Mode = CipherMode.CBC;
16-
aesAlg.Padding = PaddingMode.PKCS7;
22+
aesAlg.Padding = paddingMode;
1723
}
1824

1925
public byte[] Encrypt(string plaintext)

0 commit comments

Comments
 (0)