-
Notifications
You must be signed in to change notification settings - Fork 0
/
main.c
179 lines (146 loc) · 5.7 KB
/
main.c
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
// sha1extend - A tool to perform the Length Extension Attack on SHA1
// Copyright (C) 2023 Maciej Sawka (msaw328) <[email protected], [email protected]>
//
// This library is free software; you can redistribute it and/or
// modify it under the terms of the GNU Lesser General Public
// License as published by the Free Software Foundation; either
// version 2.1 of the License, or (at your option) any later version.
//
// This library is distributed in the hope that it will be useful,
// but WITHOUT ANY WARRANTY; without even the implied warranty of
// MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
// Lesser General Public License for more details.
//
// You should have received a copy of the GNU Lesser General Public
// License along with this library; if not, write to the Free Software
// Foundation, Inc., 51 Franklin Street, Fifth Floor, Boston, MA 02110-1301 USA
// main.c - main() function, arg parsing and the bulk of programs functionality
#include <stdio.h>
#include <string.h>
#include <stdint.h>
#include <stddef.h>
#include <stdlib.h>
#include <unistd.h>
#include <endian.h>
#include "hexconv.h"
#include "sha1.h"
// Data supplied by the user below
uint8_t original_hash[20] = { 0 }; // -s
int hash_supplied = 0;
char* append = NULL; // -a
long original_length = 0; // -l
int length_supplied = 0;
// This initializes the SHA1_CTX structure to the intermediate state after consuming original data
void init_SHA1_ctx_intermediate_state(SHA1_CTX* ctx, uint8_t* hash, uint32_t data_len_so_far) {
// Code pasted from implementation to encode string length as those weird counts
uint32_t j = ctx->count[0];
if ((ctx->count[0] += data_len_so_far << 3) < j)
ctx->count[1]++;
ctx->count[1] += (data_len_so_far >> 29);
// Hash is big endian
for(int i = 0; i < 5; i++) {
ctx->state[i] = 0;
for(int j = 0; j < 4; j++) {
ctx->state[i] += hash[4 * i + j] << ((3 - j) * 8);
}
}
}
void print_usage_and_exit() {
puts("sha1extend - tool to perform the Length Extension Attack on SHA1");
puts("Options:");
puts("\t-h: print this usage and exit");
puts("\t-s <signature>: original signature in hex");
puts("\t-l <number>: length of the data which produced the original signature");
puts("\t-a <string>: data to be appended");
exit(0);
}
// Checks if a string contains a valid SHA1 hash
int is_valid_hash(char* str) {
size_t str_len = (size_t) ((uint8_t*) memchr(str, '\0', 41) - (uint8_t*) str);
if(str_len != 40) return 0;
for(size_t i = 0; i < str_len; i++) {
char c = str[i];
if(('A' <= c && c <= 'F') || ('a' <= c && c <= 'f') || ('0' <= c && c <= '9')) continue;
return 0;
}
return 1;
}
// Parses and validates arguments and stores them in gloal variables defined at the very top of this file
void parse_args(int argc, char** argv) {
int c = 0;
while((c = getopt(argc, argv, "hs:l:a:")) != -1) {
switch(c) {
case 'h': {
print_usage_and_exit();
}
case 's': {
if(!is_valid_hash(optarg)) {
puts("Invalid signature");
print_usage_and_exit();
}
hex2bytes(optarg, original_hash, 20);
hash_supplied = 1;
break;
}
case 'l': {
char* endptr = NULL;
long length = strtol(optarg, &endptr, 0);
if(endptr == optarg || *endptr != '\x0') {
puts("Invalid length");
print_usage_and_exit();
}
original_length = (uint32_t) length;
length_supplied = 1;
break;
}
case 'a': {
append = optarg;
break;
}
case '?': {
if (optopt == 'c' || optopt == 'a' || optopt == 'l') {
printf("Option -%c requires an argument.\n", optopt);
print_usage_and_exit();
}
}
}
}
if(hash_supplied == 0 || length_supplied == 0 || append == NULL) {
puts("Missing arguments");
print_usage_and_exit();
}
}
int main(int argc, char** argv) {
parse_args(argc, argv);
SHA1_CTX ctx = { 0 };
size_t padding_length = 64 - (original_length % 64);
if(padding_length < 9) {
padding_length += 64;
}
// Initialize SHA1_CTX to the intermediate state that we know:
// - length of the data consumed is the length of the data + the padding calculated above
// - the state afterwards is the original hash
init_SHA1_ctx_intermediate_state(&ctx, original_hash, original_length + padding_length);
// Update the state with the data we want to append
SHA1Update(&ctx, (const unsigned char*) append, strlen(append));
// Finalize, apply padding and produce a digest
uint8_t digest[20] = { 0 };
SHA1Final(digest, &ctx);
char digest_hex[41] = { 0 };
bytes2hex(digest, digest_hex, 20);
printf("New digest: %s\n", digest_hex);
// Padding is made of unprintable bytes so we print those as \x escape sequences
printf("Full append: \\x80");
for(int i = 0; i < padding_length - 9; i++) {
printf("\\x00");
}
// Afterwards length is printed, also using \x sequences in big endian order
uint64_t biglength = (uint64_t) original_length;
biglength = biglength << 3; // In bits
for(int i = 0; i < 8; i++) {
uint8_t byte = (uint8_t) (biglength >> ((7 - i) * 8)) & 0xff;
printf("\\x%02X", byte);
}
// The append part is most likely printable so we can just print it directly
puts(append);
}