1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
|
#include "../lib/lib.h"
#include "../lib/lib2.h"
#include "../lib/lib3.h"
#include "../lib/lib4.h"
#include "../lib/lib5.h"
#include "../lib/lib7.h"
#include <zlib.h>
void generate_random_printable_bytes(char *s, unsigned int length)
{
int i;
for(i=0;i<length;i++)
s[i] = random_number_between(33,127);
}
void format_request(char **res, char *content, char *secret)
{
char *request_template =
"POST / HTTP/1.1\n"
"Host: hapless.com\n"
"Cookie: sessionid=%s\n"
"Content-Length: %i\n\n\n"
"%s";
// 10 for the content length as max
*res = malloc(strlen(request_template)+10+strlen(content)+strlen(secret));
sprintf(*res, request_template, secret, strlen(content), content);
}
void my_compress(char *toCompress, char **compressed, int *compressed_length)
{
// 20 bytes for metadata and bla but normally it should be smaller
// than the uncompressed one
*compressed = malloc(strlen(toCompress)+20);
// compress
z_stream defstream;
defstream.zalloc = Z_NULL;
defstream.zfree = Z_NULL;
defstream.opaque = Z_NULL;
defstream.avail_in = strlen(toCompress);
defstream.next_in = toCompress;
defstream.avail_out = strlen(toCompress)+20;
defstream.next_out = *compressed;
deflateInit(&defstream, Z_BEST_COMPRESSION);
deflate(&defstream, Z_FINISH);
deflateEnd(&defstream);
*compressed_length = defstream.total_out;
//printf("uncompressed/compressed: %i/%i\n", strlen(toCompress), defstream.total_out);
}
int oracle_count = 0;
int compression_oracle(char *content, char* secret, int stream_cipher)
{
oracle_count++;
char *res;
char *compressed;
int compressed_length;
format_request(&res, content, secret);
//printf("%s\n", res);
my_compress(res, &compressed, &compressed_length);
// encrypt
// using AES_CTR, plaintext and ciphertext length are the same
// do it anyway
if(stream_cipher == 1) {
char *encrypted = malloc(compressed_length);
generate_random_bytes(key, 16);
generate_random_bytes(nonce, 16);
aes_ctr(compressed, compressed_length, encrypted, key, nonce);
return compressed_length;
}
else {
// do CBC encryption aka add padding
char *encrypted;
int pad_len = aes_cbc_padded(compressed, compressed_length, &encrypted,
key, iv, 1);
return pad_len;
}
}
int max_int_index(int *numbers, int length)
{
int i;
int max = 0;
int index = 0;
for(i=0;i<length;i++) {
if(numbers[i] > max) {
max = numbers[i];
index = i;
}
}
return index;
}
int main()
{
srand(time(NULL));
char *known_text = "Cookie: sessionid=";
char *secret = "TmV2ZXIgcmV2ZWFsIHRoZSBXdS1UYW5nIFNlY3JldCE=";
// generate password between 33, 127
// assume here that the session id is printable (HTTP is text protocol)
char *t = malloc(strlen(known_text)+50);
memset(t, 0, strlen(known_text)+50);
memcpy(t, known_text, strlen(known_text));
int max, res, i, k;
// assume that the string we wanne find is less than 100 bytes
for(k=0;k<100;k++) {
t[strlen(known_text)+k] = 0x09;
max = compression_oracle(t, secret, 1);
for(i=33;i<128;i++) {
t[strlen(known_text)+k] = (char) i;
res = compression_oracle(t, secret, 1);
if(res < max)
break;
}
if(max == res)
break;
}
printf("found string : %s\n", t);
printf("should string: Cookie: sessionid=%s\n", secret);
// doing binary search above would reduce the calls to the orcale
// significant
printf("calls to the oracle: %i\n", oracle_count);
// with cbc
// length will always be a multiple of the block size
oracle_count = 0;
printf("with CBC mode\n");
// because we only get multiple of 16 as result we have to take
// care that a complete block gets compressed
// we than need control over the size of the request so that
// is is a multiple of 16 BEFORE padding
// otherwise we compress 15 bytes and use one block less
char *kt = malloc(strlen(known_text)+strlen(secret));
memcpy(kt, known_text, strlen(known_text));
memcpy(&kt[strlen(known_text)], secret, strlen(secret));
// one block which should be compressed away
// 6 is the alignment to get a full block
char rand[17+6];
char ttt[18+8];
int j,l;
generate_random_printable_bytes(rand, 16+8);
rand[17] = '\0';
char *raw_request;
char *compressed;
int compressed_length;
int pad;
int counts[127-33];
int times = 8;
char found_sec_cbc[100];
format_request(&raw_request, "AAAAAAAAAAAAAAAA", secret);
my_compress(raw_request, &compressed, &compressed_length);
__pkcs7_padding(compressed, compressed_length, 16, &pad);
int max_ = compression_oracle(rand, secret, 0);
memcpy(&ttt[pad], &known_text[strlen(known_text)-16], 16);
for(l=0;l<strlen(secret);l++) {
memset(&counts, 0, sizeof(int)*(127-33));
generate_random_printable_bytes(ttt, pad);
for(i=0;i<15;i++)
ttt[pad+i] = ttt[pad+i+1];
for(k=0;k<10;k++) {
for(i=33;i<127;i++) {
ttt[pad+15] = i;
ttt[pad+16] = '\0';
int len = compression_oracle(ttt, secret, 0);
if( len < max_)
break;
}
counts[i-33]++;
}
i = max_int_index(counts, 127-33);
found_sec_cbc[l] = (char) (i + 33);
}
found_sec_cbc[l] = '\0';
printf("should: %s\n", secret);
printf("is : %s\n", found_sec_cbc);
printf("calls to the oracle: %i\n", oracle_count);
}
|