├── .gitignore ├── Makefile ├── README.md ├── bitflip.c └── examples ├── source.f8d0839dd728cb9a723e32058dcc386070d5e3b5.cpp ├── source.f8d0839dd728cb9a723e32058dcc386070d5e3b5.cpp.1flip.z ├── test.dddd6ee7cc3af6ca5e814d9522acf57bb7b7cdc1.txt ├── test.dddd6ee7cc3af6ca5e814d9522acf57bb7b7cdc1.txt.2flips.z ├── test2.e35151d8fd6445f9361e4237bb282e707efe4090.txt └── test2.e35151d8fd6445f9361e4237bb282e707efe4090.txt.3flips.z /.gitignore: -------------------------------------------------------------------------------- 1 | # Object files 2 | *.o 3 | *.ko 4 | *.obj 5 | *.elf 6 | 7 | bitflipper 8 | 9 | # Precompiled Headers 10 | *.gch 11 | *.pch 12 | 13 | # Libraries 14 | *.lib 15 | *.a 16 | *.la 17 | *.lo 18 | 19 | # Shared objects (inc. Windows DLLs) 20 | *.dll 21 | *.so 22 | *.so.* 23 | *.dylib 24 | 25 | # Executables 26 | *.exe 27 | *.out 28 | *.app 29 | *.i*86 30 | *.x86_64 31 | *.hex 32 | 33 | # Debug files 34 | *.dSYM/ 35 | -------------------------------------------------------------------------------- /Makefile: -------------------------------------------------------------------------------- 1 | src = $(wildcard *.c) 2 | obj = $(src:.c=.o) 3 | 4 | LDFLAGS = -lz -lcrypto 5 | 6 | bitflipper: $(obj) 7 | $(CC) -O3 -Wall -Werror -o $@ $^ $(LDFLAGS) 8 | 9 | clean: 10 | rm -f $(obj) bitflipper 11 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # bitflipper 2 | Combinatorially flip bits by brute force until a file is no longer corrupted. 3 | 4 | ## Building 5 | 6 | You need to have openssl and zlib development packages installed. 7 | 8 | Then just run `make` 9 | 10 | ## Running 11 | 12 | Examples: 13 | 14 | ```bash 15 | # 1 flip in corrupted file. 16 | $ ./bitflipper examples/source.f8d0839dd728cb9a723e32058dcc386070d5e3b5.cpp.1flip.z f8d0839dd728cb9a723e32058dcc386070d5e3b5 17 | Checking the uncompressed version 18 | Found original file. Saving to out.bin 19 | 20 | # 2 flips in corrupted file. 21 | $ ./bitflipper examples/test.dddd6ee7cc3af6ca5e814d9522acf57bb7b7cdc1.txt.2flips.z dddd6ee7cc3af6ca5e814d9522acf57bb7b7cdc1 22 | Checking the uncompressed version 23 | Found original file. Saving to out.bin 24 | 25 | # 3 bit flips in corrupted file. This one takes ~ 10 seconds 26 | $ ./bitflipper examples/test2.e35151d8fd6445f9361e4237bb282e707efe4090.txt.3flips.z e35151d8fd6445f9361e4237bb282e707efe4090 27 | Checking the uncompressed version 28 | Found original file. Saving to out.bin 29 | ``` 30 | 31 | Make sure you are passing the zlib compressed version of the file as the first argument and just 32 | the SHA1 hash in the second argument. 33 | 34 | You can see the "corrupted" contents of each example: 35 | 36 | * [Example 1](https://github.com/conorpp/bitflipper/blob/master/examples/source.f8d0839dd728cb9a723e32058dcc386070d5e3b5.cpp) 37 | * [Example 2](https://github.com/conorpp/bitflipper/blob/master/examples/test.dddd6ee7cc3af6ca5e814d9522acf57bb7b7cdc1.txt) 38 | * [Example 3](https://github.com/conorpp/bitflipper/blob/master/examples/test2.e35151d8fd6445f9361e4237bb282e707efe4090.txt) 39 | 40 | 41 | 42 | -------------------------------------------------------------------------------- /bitflip.c: -------------------------------------------------------------------------------- 1 | /* 2 | * Started by Jean-Philippe 3 | * Rewritten by Conor Patrick 4 | */ 5 | 6 | #include 7 | #include 8 | #include 9 | 10 | #include 11 | #include 12 | #include 13 | 14 | #include 15 | #include 16 | #include 17 | #include 18 | #include 19 | #include 20 | #include 21 | #include 22 | #include 23 | #include 24 | 25 | // Use a mask to flip N bits 26 | struct bitsaver_mask 27 | { 28 | int bits; 29 | int done; 30 | size_t max; 31 | size_t* pos; 32 | }; 33 | 34 | // Max size to decompress into 35 | size_t MAX_MEM_SIZE = (1<<29); 36 | 37 | struct bitsaver 38 | { 39 | char* inflated; 40 | char* data; 41 | 42 | off_t data_size; 43 | size_t inflated_max_size; 44 | size_t inflated_size; 45 | 46 | uint8_t goodhash[SHA_DIGEST_LENGTH]; 47 | }; 48 | 49 | int bs_check_hash(const struct bitsaver* bs) 50 | { 51 | unsigned char hash[SHA_DIGEST_LENGTH]; 52 | SHA1(bs->inflated, bs->inflated_size, hash); 53 | return (bcmp(bs->goodhash, hash, SHA_DIGEST_LENGTH) == 0); 54 | } 55 | 56 | 57 | // Flips N bits according to mask 58 | void BITFLIP(uint8_t* buf, struct bitsaver_mask* bm) 59 | { 60 | int i; 61 | for (i = 0; i < bm->bits; i++) 62 | { 63 | if(bm->pos[i])buf[bm->pos[i]/8] ^= 1<<(bm->pos[i] % 8); 64 | } 65 | } 66 | 67 | typedef enum 68 | { 69 | BS_SUCCESS = 0, 70 | BS_FAIL, 71 | } bitsaver_t; 72 | 73 | // init a mask with N bits 74 | struct bitsaver_mask* bs_mask_init(int bits) 75 | { 76 | struct bitsaver_mask* bm = malloc(sizeof(struct bitsaver_mask)); 77 | if (bm == NULL) 78 | { 79 | err(1,"malloc"); 80 | } 81 | bm->pos = malloc(sizeof(size_t)*bits); 82 | if (bm->pos == NULL) 83 | { 84 | err(1,"malloc"); 85 | } 86 | 87 | bm->bits = bits; 88 | bm->done = 0; 89 | 90 | return bm; 91 | } 92 | 93 | void bs_mask_destroy(struct bitsaver_mask* bm) 94 | { 95 | free(bm->pos); 96 | free(bm); 97 | } 98 | 99 | void bs_mask_set_max(struct bitsaver_mask* bm,int max) 100 | { 101 | bm->max = max; 102 | } 103 | void bs_mask_clear(struct bitsaver_mask* bm) 104 | { 105 | memset(bm->pos, 0, sizeof(size_t)*bm->bits); 106 | } 107 | 108 | 109 | // For testing 110 | void bs_mask_print(struct bitsaver_mask* bm) 111 | { 112 | int i; 113 | for (i=0; i < bm->bits; i ++) 114 | printf(" %zd ",bm->pos[i]); 115 | printf("\n"); 116 | } 117 | 118 | 119 | void bs_mask_inc(struct bitsaver_mask* bm) 120 | { 121 | int i; 122 | size_t* lsd = bm->pos; 123 | size_t* end = lsd + bm->bits - 1; 124 | do 125 | { 126 | (*lsd)++; 127 | if (*lsd == bm->max) 128 | { 129 | *lsd = 0; 130 | if (lsd == end) 131 | { 132 | bm->done = 1; 133 | return; 134 | } 135 | } 136 | if (lsd == end) 137 | { 138 | break; 139 | } 140 | lsd++; 141 | } 142 | while(*(lsd-1)==0); 143 | } 144 | 145 | // Flip all bits in inflated buffer 146 | bitsaver_t bs_check_bits_inflate(struct bitsaver* bs, struct bitsaver_mask* bm) 147 | { 148 | 149 | // Check without any flipping 150 | if (bs_check_hash(bs)) 151 | { 152 | return BS_SUCCESS; 153 | } 154 | 155 | int j; 156 | bm->max = bs->inflated_size*8; 157 | for(bs_mask_clear(bm); !bm->done; bs_mask_inc(bm)) 158 | { 159 | BITFLIP(bs->inflated, bm); 160 | if (bs_check_hash(bs)) 161 | { 162 | 163 | return BS_SUCCESS; 164 | } 165 | BITFLIP(bs->inflated, bm); 166 | 167 | } 168 | return BS_FAIL; 169 | } 170 | 171 | // Flip all bits in compressed buffer and bit flip each new uncompressed version 172 | bitsaver_t bs_check_bits_compressed(struct bitsaver* bs, int bits) 173 | { 174 | 175 | struct bitsaver_mask* bm_inflate = bs_mask_init(bits); 176 | 177 | // Check without any flips 178 | size_t len = bs->inflated_max_size; 179 | int i,j; 180 | 181 | bs->inflated_size = bs->inflated_max_size; 182 | 183 | printf("Checking the uncompressed version\n"); 184 | int ec; 185 | while(ec=(uncompress(bs->inflated, &bs->inflated_size, bs->data, bs->data_size)) == Z_BUF_ERROR) 186 | { 187 | free(bs->inflated); 188 | bs->inflated_max_size *= 0x10; 189 | if (bs->inflated_max_size > MAX_MEM_SIZE) 190 | { 191 | errx(1,"decompressing file exceeded %zd bytes.\n",MAX_MEM_SIZE); 192 | } 193 | bs->inflated = malloc(bs->inflated_max_size); 194 | if(bs->inflated == NULL) 195 | { 196 | err(1,"malloc"); 197 | } 198 | bs->inflated_size = bs->inflated_max_size; 199 | } 200 | if( ec == Z_OK ) 201 | { 202 | if (bs_check_bits_inflate(bs, bm_inflate) == BS_SUCCESS) 203 | { 204 | return BS_SUCCESS; 205 | } 206 | } 207 | 208 | 209 | printf("Checking the compressed version\n"); 210 | struct bitsaver_mask* bm = bs_mask_init(bits); 211 | bs_mask_set_max(bm, bs->data_size*8); 212 | for(bs_mask_clear(bm); !bm->done; bs_mask_inc(bm)) 213 | { 214 | BITFLIP(bs->data, bm); 215 | 216 | bs->inflated_size = bs->inflated_max_size; 217 | 218 | if( uncompress(bs->inflated, &bs->inflated_size, bs->data, bs->data_size) == Z_OK ) 219 | { 220 | if (bs_check_bits_inflate(bs,bm_inflate) == BS_SUCCESS) 221 | { 222 | return BS_SUCCESS; 223 | } 224 | 225 | } 226 | 227 | BITFLIP(bs->data, bm); 228 | 229 | } 230 | 231 | bs_mask_destroy(bm); 232 | bs_mask_destroy(bm_inflate); 233 | 234 | return BS_FAIL; 235 | } 236 | 237 | 238 | int bs_init(struct bitsaver* bs, uint8_t* fn, uint8_t* hash) { 239 | struct stat sbuf; 240 | uint8_t* data; 241 | int fd; 242 | BIGNUM *bn = NULL; 243 | memset(bs, 0, sizeof(struct bitsaver)); 244 | 245 | printf("loading file %s hash %s\n",fn,hash); 246 | 247 | if (BN_hex2bn(&bn, hash) == 0) 248 | { 249 | unsigned long e = ERR_get_error(); 250 | errx(1, "BN_bex2bn: %s\n", ERR_error_string(e,NULL)); 251 | } 252 | 253 | if(BN_num_bytes(bn) != SHA_DIGEST_LENGTH) 254 | { 255 | errx(1, "Must provide a SHA1 hash\n"); 256 | } 257 | 258 | if (BN_bn2bin(bn, bs->goodhash) == 0) 259 | { 260 | unsigned long e = ERR_get_error(); 261 | errx(1, "BN_bn2bin: %s\n", ERR_error_string(e,NULL)); 262 | } 263 | 264 | BN_free(bn); 265 | 266 | if ((fd = open(fn, O_RDONLY)) == -1) 267 | { 268 | err(1, "open"); 269 | } 270 | 271 | if (fstat(fd, &sbuf) == -1) 272 | { 273 | err(1, "stat"); 274 | } 275 | 276 | if ((data = mmap(NULL, sbuf.st_size, PROT_READ | PROT_WRITE, MAP_PRIVATE, fd, 0)) == MAP_FAILED) 277 | { 278 | err(1, "mmap"); 279 | } 280 | 281 | if(close(fd) != 0) 282 | { 283 | err(1,"close"); 284 | } 285 | 286 | bs->data = data; 287 | bs->data_size = sbuf.st_size; 288 | bs->inflated_max_size = sbuf.st_size; 289 | 290 | bs->inflated = malloc(bs->inflated_max_size); 291 | 292 | if (bs->inflated == NULL) 293 | { 294 | err(1,"malloc"); 295 | } 296 | 297 | return 0; 298 | } 299 | 300 | void bs_destroy(struct bitsaver* bs) 301 | { 302 | free(bs->inflated); 303 | if(munmap(bs->data, bs->data_size) != 0) 304 | { 305 | err(1,"munmap"); 306 | } 307 | } 308 | 309 | void bs_save(struct bitsaver* bs, const char* fn) 310 | { 311 | int fd = open(fn, O_CREAT | O_WRONLY | O_TRUNC, S_IRUSR | S_IWUSR | S_IRGRP | S_IWGRP | S_IROTH | S_IWOTH); 312 | 313 | if (fd < 0) 314 | { 315 | err(1,"open"); 316 | } 317 | 318 | if (write(fd, bs->inflated, bs->inflated_size) < 0) 319 | { 320 | err(1,"write"); 321 | } 322 | 323 | if (close(fd) < 0) 324 | { 325 | err(1,"close"); 326 | } 327 | } 328 | 329 | int main(int argc, char *argv[]) 330 | { 331 | 332 | int fd,bits=3; 333 | struct bitsaver bs; 334 | const char* output_fn = "out.bin"; 335 | 336 | int opt,offset=0; 337 | while ((opt = getopt (argc, argv, "b:h")) != -1) 338 | { 339 | switch (opt) 340 | { 341 | case 'b': 342 | bits = atoi(optarg); 343 | offset += 2; 344 | break; 345 | case 'h': 346 | fprintf(stderr,"bitsaver\n" 347 | " -h print help\n" 348 | " -b the upper bound for the number of bits to try permuting on the file (default 3).\n" 349 | ); 350 | exit(0); 351 | break; 352 | } 353 | } 354 | if (argc + offset< 3) 355 | { 356 | errx(1, "usage: %s [ -b | -h ] \n", argv[0]); 357 | } 358 | 359 | 360 | bs_init(&bs, argv[1+offset], argv[2+offset]); 361 | 362 | bitsaver_t r = bs_check_bits_compressed(&bs,bits); 363 | 364 | if (r == BS_SUCCESS) 365 | { 366 | printf("Found original file. Saving to %s\n",output_fn); 367 | bs_save(&bs, output_fn); 368 | } 369 | else 370 | { 371 | printf("Could not find original file after checking all combinations ( ~ %zd choose %d + %zd choose %d ).\n", 372 | bs.inflated_size, bits, bs.data_size,bits); 373 | } 374 | 375 | bs_destroy(&bs); 376 | 377 | return 0; 378 | } 379 | -------------------------------------------------------------------------------- /examples/source.f8d0839dd728cb9a723e32058dcc386070d5e3b5.cpp: -------------------------------------------------------------------------------- 1 | blob 1880#include 2 | #include 3 | #include 4 | 5 | #include 6 | #include 7 | #include 8 | 9 | using namespace std; 10 | 11 | std::string calculate_flag( 12 | std::string &part1, 13 | int64_t part2, 14 | std::string &part4, 15 | uint64_t factor1, 16 | uint64_t factor2) 17 | { 18 | 19 | std::transform(part1.begin(), part1.end(), part1.begin(), ::tolower); 20 | std::transform(part4.begin(), part4.end(), part4.begin(), ::tolower); 21 | 22 | SHA_CTX ctx; 23 | SHA1_Init(&ctx); 24 | 25 | unsigned int mod = factor1 % factor2; 26 | for (unsigned int i = 0; i < mod; i+=2) 27 | { 28 | SHA1_Update(&ctx, 29 | reinterpret_cast(part1.c_str()), 30 | part1.size()); 31 | } 32 | 33 | 34 | while (part2-- > 0) 35 | { 36 | SHA1_Update(&ctx, 37 | reinterpret_cast(part4.c_str()), 38 | part1.size()); 39 | } 40 | 41 | unsigned char *hash = new unsigned char[SHA_DIGEST_LENGTH]; 42 | SHA1_Final(hash, &ctx); 43 | 44 | std::string rv; 45 | for (unsigned int i = 0; i < SHA_DIGEST_LENGTH; i++) 46 | { 47 | char *buf; 48 | asprintf(&buf, "%02x", hash[i]); 49 | rv += buf; 50 | free(buf); 51 | } 52 | 53 | return rv; 54 | } 55 | 56 | int main(int argc, char **argv) 57 | { 58 | (void)argc; (void)argv; //unused 59 | 60 | std::string part1; 61 | cout << "Part1: Enter flag:" << endl; 62 | cin >> part1; 63 | 64 | int64_t part2; 65 | cout << "Part2: Input 31337:" << endl; 66 | cin >> part2; 67 | 68 | std::string part3; 69 | cout << "Part3: Watch this: https://www.youtube.com/watch?v=PBwAxmrE194" << endl; 70 | cin >> part3; 71 | 72 | std::string part4; 73 | cout << "Part4: C.R.E.A.M. Get da _____: " << endl; 74 | cin >> part4; 75 | 76 | uint64_t first, second; 77 | cout << "Part5: Input the two prime factors of the number 270031727027." << endl; 78 | cin >> first; 79 | cin >> second; 80 | 81 | uint64_t factor1, factor2; 82 | if (first < second) 83 | { 84 | factor1 = first; 85 | factor2 = second; 86 | } 87 | else 88 | { 89 | factor1 = second; 90 | factor2 = first; 91 | } 92 | 93 | std::string flag = calculate_flag(part1, part2, part4, factor1, factor2); 94 | cout << "flag{"; 95 | cout << flag; 96 | cout << "}" << endl; 97 | 98 | return 0; 99 | } 100 | 101 | -------------------------------------------------------------------------------- /examples/source.f8d0839dd728cb9a723e32058dcc386070d5e3b5.cpp.1flip.z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conorpp/bitflipper/a346a6040ede2145b2367c9fe2c27825d0b25455/examples/source.f8d0839dd728cb9a723e32058dcc386070d5e3b5.cpp.1flip.z -------------------------------------------------------------------------------- /examples/test.dddd6ee7cc3af6ca5e814d9522acf57bb7b7cdc1.txt: -------------------------------------------------------------------------------- 1 | this &ile has two bit &lips 2 | -------------------------------------------------------------------------------- /examples/test.dddd6ee7cc3af6ca5e814d9522acf57bb7b7cdc1.txt.2flips.z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conorpp/bitflipper/a346a6040ede2145b2367c9fe2c27825d0b25455/examples/test.dddd6ee7cc3af6ca5e814d9522acf57bb7b7cdc1.txt.2flips.z -------------------------------------------------------------------------------- /examples/test2.e35151d8fd6445f9361e4237bb282e707efe4090.txt: -------------------------------------------------------------------------------- 1 | this file has 3 bitflips in it! 2 | ffhewo&hlsihjf;lweh;lfghes;dlfg 3 | -------------------------------------------------------------------------------- /examples/test2.e35151d8fd6445f9361e4237bb282e707efe4090.txt.3flips.z: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/conorpp/bitflipper/a346a6040ede2145b2367c9fe2c27825d0b25455/examples/test2.e35151d8fd6445f9361e4237bb282e707efe4090.txt.3flips.z --------------------------------------------------------------------------------