Skip to content

Commit e01d7e0

Browse files
committed
Add multi-threading
1 parent 431fe07 commit e01d7e0

File tree

3 files changed

+106
-44
lines changed

3 files changed

+106
-44
lines changed

Makefile

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -3,8 +3,8 @@ CC = gcc
33
OPENSSL = /usr/include/openssl
44
OPENSSL_LIB = -lssl
55

6-
CFLAGS += -I $(OPENSSL) -O3
7-
LDFLAGS += $(OPENSSL_LIB) -lcrypto
6+
CFLAGS += -I $(OPENSSL) -g -std=gnu99 -O3
7+
LDFLAGS += $(OPENSSL_LIB) -lcrypto -lpthread
88

99
NAME = jwtcrack
1010
SRCS = main.c base64.c

README.md

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
# JWT cracker
22

3-
A JWT brute-force cracker written in C. If you are very lucky or have a huge computing power, this program should find the secret key of a JWT token, allowing you to forge valid tokens. This is for testing purposes only, do not put yourself in trouble :)
3+
A multi-threaded JWT brute-force cracker written in C. If you are very lucky or have a huge computing power, this program should find the secret key of a JWT token, allowing you to forge valid tokens. This is for testing purposes only, do not put yourself in trouble :)
44

55
I used the [Apple Base64 implementation](https://opensource.apple.com/source/QuickTimeStreamingServer/QuickTimeStreamingServer-452/CommonUtilitiesLib/base64.c) that I modified slightly.
66

@@ -30,6 +30,5 @@ In the above example, the key is `Sn1f`. It takes approximately 23 seconds to cr
3030

3131
## Caveats
3232

33-
* Not multi-threaded :((
3433
* No progress status
3534
* If you stop the program, you cannot start back where you were

main.c

Lines changed: 103 additions & 40 deletions
Original file line numberDiff line numberDiff line change
@@ -4,7 +4,7 @@
44
#include <openssl/evp.h>
55
#include <openssl/hmac.h>
66
#include <stdbool.h>
7-
#include <time.h>
7+
#include <pthread.h>
88
#include "base64.h"
99

1010
char *g_header_b64 = NULL; // Holds the Base64 header of the original JWT
@@ -26,62 +26,128 @@ size_t g_to_encrypt_len = 0;
2626
char *g_alphabet = NULL;
2727
size_t g_alphabet_len = 0;
2828

29-
// Holds the computed signature at each iteration to compare it with the original
30-
// signature
31-
unsigned char *g_result = NULL;
32-
unsigned int g_result_len = 0;
29+
char *g_found_secret = NULL;
3330

34-
char *g_buffer = NULL;
31+
struct s_thread_data {
32+
EVP_MD *g_evp_md; // The hash function to apply the HMAC to
3533

36-
// The hash function to apply the HMAC to
37-
EVP_MD *g_evp_md = NULL;
34+
// Holds the computed signature at each iteration to compare it with the original
35+
// signature
36+
unsigned char *g_result;
37+
unsigned int g_result_len;
38+
39+
char *g_buffer; // Holds the secret being constructed
40+
41+
char starting_letter; // Each thread is assigned a first letter
42+
size_t max_len; // And tries combinations up to a certain length
43+
};
44+
45+
void init_thread_data(struct s_thread_data *data, char starting_letter, size_t max_len) {
46+
data->max_len = max_len;
47+
data->starting_letter = starting_letter;
48+
// The chosen hash function is SHA-256
49+
data->g_evp_md = (EVP_MD *) EVP_sha256();
50+
// Allocate the buffer used to hold the calculated signature
51+
data->g_result = malloc(EVP_MAX_MD_SIZE);
52+
// Allocate the buffer used to hold the generated key
53+
data->g_buffer = malloc(max_len + 1);
54+
}
55+
56+
void destroy_thread_data(struct s_thread_data *data) {
57+
free(data->g_result);
58+
free(data->g_buffer);
59+
}
3860

3961
/**
4062
* Check if the signature produced with "secret
4163
* of size "secrent_len" (without the '\0') matches the original
4264
* signature.
4365
* Return true if it matches, false otherwise
4466
*/
45-
bool check(const char *secret, size_t secret_len) {
67+
bool check(struct s_thread_data *data, const char *secret, size_t secret_len) {
68+
// If the secret was found by another thread, stop this thread
69+
if (g_found_secret != NULL) {
70+
destroy_thread_data(data);
71+
pthread_exit(NULL);
72+
}
73+
4674
// Hash to_encrypt using HMAC into result
4775
HMAC(
48-
g_evp_md,
76+
data->g_evp_md,
4977
(const unsigned char *) secret, secret_len,
5078
(const unsigned char *) g_to_encrypt, g_to_encrypt_len,
51-
g_result, &g_result_len
79+
data->g_result, &(data->g_result_len)
5280
);
5381

5482
// Compare the computed hash to the given decoded base64 signature.
5583
// If there is a match, we just found the key.
56-
return memcmp(g_result, g_signature, g_signature_len) == 0;
84+
return memcmp(data->g_result, g_signature, g_signature_len) == 0;
5785
}
5886

59-
bool bruteImpl(char* str, int index, int maxDepth)
87+
bool brute_impl(struct s_thread_data *data, char* str, int index, int max_depth)
6088
{
6189
for (int i = 0; i < g_alphabet_len; ++i)
6290
{
91+
// The character at "index" in "str" successvely takes the value
92+
// of each symbol in the alphabet
6393
str[index] = g_alphabet[i];
6494

65-
if (index == maxDepth - 1) {
66-
if (check((const char *) str, maxDepth)) return true;
67-
}
95+
// If just changed the last letter, that means we generated a
96+
// permutation, so we check it
97+
if (index == max_depth - 1) {
98+
// If we found the key, we return, otherwise we continue.
99+
// By continuing, the current letter (at index "index")
100+
// will be changed to the next symbol in the alphabet
101+
if (check(data, (const char *) str, max_depth)) return true;
102+
}
103+
// If the letter we just changed was not the last letter of
104+
// the permutation we are generating, recurse to change the
105+
// letter at the next index.
68106
else {
69-
if (bruteImpl(str, index + 1, maxDepth)) return true;
70-
}
107+
// If this condition is met, that means we found the key.
108+
// Otherwise the loop will continue and change the current
109+
// character to the next letter in the alphabet.
110+
if (brute_impl(data, str, index + 1, max_depth)) return true;
111+
}
71112
}
72113

114+
// If we are here, we tried all the permutations without finding a match
73115
return false;
74116
}
75117

76-
char *bruteSequential(int start, int maxLen)
118+
/**
119+
* Try all the combinations of secret starting with letter "starting_letter"
120+
* and stopping at a maximum length of "max_len"
121+
* Returns the key when there is a match, otherwise returns NULL
122+
*/
123+
char *brute_sequential(struct s_thread_data *data)
77124
{
78-
for (int i = start; i <= maxLen; ++i)
79-
{
80-
if (bruteImpl(g_buffer, 0, i))
81-
return strdup(g_buffer);
125+
// We set the starting letter
126+
data->g_buffer[0] = data->starting_letter;
127+
// Special case for len = 1, we check in this function
128+
if (check(data, data->g_buffer, 1)) {
129+
// If this thread found the solution, set the shared global variable
130+
// so other threads stop, and stop the current thread. Congrats little
131+
// thread!
132+
g_found_secret = strndup(data->g_buffer, 1);
133+
return g_found_secret;
134+
}
135+
136+
// We start from length 2 (we handled the special case of length 1
137+
// above.
138+
for (size_t i = 2; i <= data->max_len; ++i) {
139+
if (brute_impl(data, data->g_buffer, 1, i)) {
140+
// If this thread found the solution, set the shared global variable
141+
// so other threads stop, and stop the current thread. Congrats little
142+
// thread!
143+
g_found_secret = strndup(data->g_buffer, i);
144+
return g_found_secret;
145+
}
82146
}
83147

84-
return NULL;
148+
success:
149+
150+
return NULL;
85151
}
86152

87153
void usage(const char *cmd) {
@@ -131,29 +197,26 @@ int main(int argc, char **argv) {
131197
// is returned by this function
132198
g_signature_len = Base64decode((char *) g_signature, (const char *) g_signature_b64);
133199

134-
// Allocate the buffer used to hold the calculated signature
135-
g_result = malloc(EVP_MAX_MD_SIZE);
136-
g_buffer = malloc(max_len + 1);
137200

138-
// The chosen hash function is SHA-256
139-
g_evp_md = (EVP_MD *) EVP_sha256();
201+
struct s_thread_data *pointers_data[g_alphabet_len];
202+
pthread_t *tid = malloc(g_alphabet_len * sizeof(pthread_t));
140203

141-
clock_t start = clock(), diff;
142-
char *secret = bruteSequential(1, max_len);
143-
diff = clock() - start;
204+
for (size_t i = 0; i < g_alphabet_len; i++) {
205+
pointers_data[i] = malloc(sizeof(struct s_thread_data));
206+
init_thread_data(pointers_data[i], g_alphabet[i], max_len);
207+
pthread_create(&tid[i], NULL, (void *(*)(void *)) brute_sequential, pointers_data[i]);
208+
}
144209

210+
for (size_t i = 0; i < g_alphabet_len; i++)
211+
pthread_join(tid[i], NULL);
145212

146-
if (secret == NULL)
213+
if (g_found_secret == NULL)
147214
printf("No solution found :-(\n");
148215
else
149-
printf("Secret is \"%s\"\n", secret);
150-
151-
free(g_result);
152-
free(g_buffer);
153-
free(secret);
216+
printf("Secret is \"%s\"\n", g_found_secret);
154217

155-
int msec = diff * 1000 / CLOCKS_PER_SEC;
156-
printf("Time taken %d seconds %d milliseconds", msec/1000, msec%1000);
218+
free(g_found_secret);
219+
free(tid);
157220

158221
return 0;
159222
}

0 commit comments

Comments
 (0)