Rating:
# Parallel-The-M0le
This programs is a flag-hasher that spawns 14 threadss and apply operations to the flag (in a random order), it print the results, then it re-apply these operation in a slightly different order to a new copy of the flag and print it.
In particular if at the first step it executes the functions:
```
result1 = f1 f2 f3 f4 f5 f6 f7 f8 f9 f10 f11 f12 f13 f14
```
then the second step it execute:
```
result2 = f1 f2 f3 f4 f5 f6 f14 f13 f12 f11 f10 f9 f8 f7
```
Since the functions are all injective, we can write the inverse and bruteforce the sequence of 7 functions that applied in a reverse order on result1 and result2 produces the same result.
Once we found this ""collision"" we can bruteforce the last 7 functions until we find a string that starts with `ptm{`.
```rust
// All the functions reversed from the binary, and their inverse.
fn func1(mut flag: Vec<u8>) -> Vec<u8> {
let mut buffer = vec![0, 0, 0, 0];
let mut i = 0;
while i < flag.len() {
for j in 0..4 {
buffer[j] = flag[j + i];
}
buffer.swap(3, 0);
buffer.swap(1, 0);
for j in 0..4 {
flag[j + i] = buffer[j];
}
i += 4;
}
flag
}
fn func1_inv(mut flag: Vec<u8>) -> Vec<u8> {
let mut buffer = vec![0, 0, 0, 0];
let mut i = 0;
while i < flag.len() {
for j in 0..4 {
buffer[j] = flag[j + i];
}
buffer.swap(1, 0);
buffer.swap(3, 0);
for j in 0..4 {
flag[j + i] = buffer[j];
}
i += 4;
}
flag
}
fn func2(mut flag: Vec<u8>) -> Vec<u8> {
let l = flag.len();
for i in 0..(l / 2) {
flag[i] ^= flag[l - i - 1];
}
flag
}
fn func2_inv(mut flag: Vec<u8>) -> Vec<u8> {
let l = flag.len();
for i in 0..(l / 2) {
flag[i] ^= flag[l - i - 1];
}
flag
}
fn func3(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
let t = i & 7;
flag[i] = flag[i] << t | flag[i] >> (8 - t);
}
flag
}
fn func3_inv(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
let t = 8 - (i & 7);
flag[i] = flag[i] << t | flag[i] >> (8 - t);
}
flag
}
fn func4(mut flag: Vec<u8>) -> Vec<u8> {
let l = flag.len();
for i in 0..(l / 2) {
flag[l - i - 1] ^= flag[i];
}
flag
}
fn func4_inv(mut flag: Vec<u8>) -> Vec<u8> {
let l = flag.len();
for i in 0..(l / 2) {
flag[l - i - 1] ^= flag[i];
}
flag
}
fn func5(mut flag: Vec<u8>) -> Vec<u8> {
let buffer = b"{reverse_fake_flag}ptm".to_vec();
for i in 0..16 {
flag[i] ^= buffer[i];
}
flag
}
fn func5_inv(mut flag: Vec<u8>) -> Vec<u8> {
let buffer = b"{reverse_fake_flag}ptm".to_vec();
for i in 0..16 {
flag[i] ^= buffer[i];
}
flag
}
fn func6(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] = !flag[i];
}
flag
}
fn func6_inv(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] = !flag[i];
}
flag
}
fn func7(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
let mut val = flag[i];
let mut j = 7;
let mut tmp = val >> 1;
while tmp != 0 {
val = val << 1 | tmp & 1;
tmp = tmp >> 1;
j -= 1;
}
flag[i] = val << (j & 0x1F);
}
flag
}
fn func7_inv(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
let mut val = flag[i];
let mut j = 7;
let mut tmp = val >> 1;
while tmp != 0 {
val = val << 1 | tmp & 1;
tmp = tmp >> 1;
j -= 1;
}
flag[i] = val << (j & 0x1F);
}
flag
}
fn func8(mut flag: Vec<u8>) -> Vec<u8> {
flag.reverse();
flag
}
fn func8_inv(mut flag: Vec<u8>) -> Vec<u8> {
flag.reverse();
flag
}
fn func9(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] += 42;
}
flag
}
fn func9_inv(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] -= 42;
}
flag
}
fn func10(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] = flag[i] >> 4 & 0xf | flag[i] << 4;
}
flag
}
fn func10_inv(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] = (flag[i] >> 4) & 0xf | flag[i] << 4;
}
flag
}
fn func11(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] = (
((flag[i] & 1) << 4)
| flag[i] * 2 & 0x80
| ((flag[i] & 0x10) << 2)
| ((flag[i] & 0x4) << 3)
) >> 4
| ((flag[i] & 0x2) << 3)
| flag[i] & 0x80
| flag[i] * 2 & 0x40
| ((flag[i] & 0x8) << 2);
}
flag
}
// we are lazy so to invert the Func11 we just save every input-output pair
const FUNC11MAP: [u8; 256] = [0, 1, 4, 5, 16, 17, 20, 21, 64, 65, 68, 69, 80, 81, 84, 85, 2, 3, 6, 7, 18, 19, 22, 23, 66, 67, 70, 71, 82, 83, 86, 87, 8, 9, 12, 13, 24, 25, 28, 29, 72, 73, 76, 77, 88, 89, 92, 93, 10, 11, 14, 15, 26, 27, 30, 31, 74, 75, 78, 79, 90, 91, 94, 95, 32, 33, 36, 37, 48, 49, 52, 53, 96, 97, 100, 101, 112, 113, 116, 117, 34, 35, 38, 39, 50, 51, 54, 55, 98, 99, 102, 103, 114, 115, 118, 119, 40, 41, 44, 45, 56, 57, 60, 61, 104, 105, 108, 109, 120, 121, 124, 125, 42, 43, 46, 47, 58, 59, 62, 63, 106, 107, 110, 111, 122, 123, 126, 127, 128, 129, 132, 133, 144, 145, 148, 149, 192, 193, 196, 197, 208, 209, 212, 213, 130, 131, 134, 135, 146, 147, 150, 151, 194, 195, 198, 199, 210, 211, 214, 215, 136, 137, 140, 141, 152, 153, 156, 157, 200, 201, 204, 205, 216, 217, 220, 221, 138, 139, 142, 143, 154, 155, 158, 159, 202, 203, 206, 207, 218, 219, 222, 223, 160, 161, 164, 165, 176, 177, 180, 181, 224, 225, 228, 229, 240, 241, 244, 245, 162, 163, 166, 167, 178, 179, 182, 183, 226, 227, 230, 231, 242, 243, 246, 247, 168, 169, 172, 173, 184, 185, 188, 189, 232, 233, 236, 237, 248, 249, 252, 253, 170, 171, 174, 175, 186, 187, 190, 191, 234, 235, 238, 239, 250, 251, 254, 255];
fn func11_inv(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] = FUNC11MAP[flag[i] as usize];
}
flag
}
fn func12(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] = !(flag[i] ^ i as u8);
}
flag
}
fn func12_inv(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] = !(flag[i] ^ i as u8);
}
flag
}
fn func13(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] += i as u8;
}
flag
}
fn func13_inv(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
flag[i] -= i as u8;
}
flag
}
fn func14(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
if (flag[i] < b'A') || (b'Z' < flag[i]) {
if (b'`' < flag[i]) && (flag[i] < b'{') {
flag[i] = flag[i] - 0x20;
}
} else {
flag[i] = flag[i] + 0x20;
}
}
flag
}
fn func14_inv(mut flag: Vec<u8>) -> Vec<u8> {
for i in 0..16 {
if (flag[i] < b'A') || (b'Z' < flag[i]) {
if (b'`' < flag[i]) && (flag[i] < b'{') {
flag[i] = flag[i] - 0x20;
}
} else {
flag[i] = flag[i] + 0x20;
}
}
flag
}
const INV_FUNCTIONS: [fn(Vec<u8>) -> Vec<u8>; 14] = [
func1_inv,
func2_inv,
func3_inv,
func4_inv,
func5_inv,
func6_inv,
func7_inv,
func8_inv,
func9_inv,
func10_inv,
func11_inv,
func12_inv,
func13_inv,
func14_inv
];
// the two results that are used to bruteforce the flag
const RESULT1: [u8; 16] = [
42,
173,
46,
90,
73,
251,
45,
154,
219,
144,
141,
208,
14,
180,
140,
138,
];
const RESULT2: [u8; 16] = [
102,
7,
171,
97,
159,
117,
176,
39,
47,
60,
30,
179,
63,
233,
237,
175,
];
// find the sequence of seven functions that in opposite order yields the same output using result1 and result2 as inputs
fn brute_step_1() -> Vec<u8> {
for i1 in 0..14 {
for i2 in 0..14 {
for i3 in 0..14 {
for i4 in 0..14 {
for i5 in 0..14 {
for i6 in 0..14 {
for i7 in 0..14 {
let mut x: u64 = 0;
x |= 1 << i1;
x |= 1 << i2;
x |= 1 << i3;
x |= 1 << i4;
x |= 1 << i5;
x |= 1 << i6;
x |= 1 << i7;
if x.count_ones() != 7 {
continue;
}
let r1 = INV_FUNCTIONS[i1](RESULT1.to_vec());
let r1 = INV_FUNCTIONS[i2](r1);
let r1 = INV_FUNCTIONS[i3](r1);
let r1 = INV_FUNCTIONS[i4](r1);
let r1 = INV_FUNCTIONS[i5](r1);
let r1 = INV_FUNCTIONS[i6](r1);
let r1 = INV_FUNCTIONS[i7](r1);
let r2 = INV_FUNCTIONS[i7](RESULT2.to_vec());
let r2 = INV_FUNCTIONS[i6](r2);
let r2 = INV_FUNCTIONS[i5](r2);
let r2 = INV_FUNCTIONS[i4](r2);
let r2 = INV_FUNCTIONS[i3](r2);
let r2 = INV_FUNCTIONS[i2](r2);
let r2 = INV_FUNCTIONS[i1](r2);
if r1 == r2 {
println!("{:?}", r1);
println!("{:?}", vec![i1, i2, i3, i4, i5, i6, i7]);
return r1;
}
}
}
}
}
}
}
}
panic!("Brute force step 1 failed");
}
// bruteforce the remaining sequence of 7 functions that yields an ouput that starts with `ptm{`
fn brute_step_2(reference: Vec<u8>){
for i1 in 0..14 {
for i2 in 0..14 {
for i3 in 0..14 {
for i4 in 0..14 {
for i5 in 0..14 {
for i6 in 0..14 {
for i7 in 0..14 {
let mut x: u64 = 0;
x |= 1 << i1;
x |= 1 << i2;
x |= 1 << i3;
x |= 1 << i4;
x |= 1 << i5;
x |= 1 << i6;
x |= 1 << i7;
if x.count_ones() != 7 {
continue;
}
let r1 = INV_FUNCTIONS[i1](reference.to_vec());
let r1 = INV_FUNCTIONS[i2](r1);
let r1 = INV_FUNCTIONS[i3](r1);
let r1 = INV_FUNCTIONS[i4](r1);
let r1 = INV_FUNCTIONS[i5](r1);
let r1 = INV_FUNCTIONS[i6](r1);
let r1 = INV_FUNCTIONS[i7](r1);
if r1.starts_with(b"ptm{") {
for x in r1 {
print!("{}", x as char);
}
print!("\n");
}
}
}
}
}
}
}
}
}
fn main() {
// check that the setup is sane
let r1 = RESULT1.to_vec();
assert_eq!(r1, func1_inv(func1(r1.clone())));
assert_eq!(r1, func2_inv(func2(r1.clone())));
assert_eq!(r1, func3_inv(func3(r1.clone())));
assert_eq!(r1, func4_inv(func4(r1.clone())));
assert_eq!(r1, func5_inv(func5(r1.clone())));
assert_eq!(r1, func6_inv(func6(r1.clone())));
assert_eq!(r1, func7_inv(func7(r1.clone())));
assert_eq!(r1, func8_inv(func8(r1.clone())));
assert_eq!(r1, func9_inv(func9(r1.clone())));
assert_eq!(r1, func10_inv(func10(r1.clone())));
assert_eq!(r1, func11_inv(func11(r1.clone())));
assert_eq!(r1, func12_inv(func12(r1.clone())));
assert_eq!(r1, func13_inv(func13(r1.clone())));
assert_eq!(r1, func14_inv(func14(r1.clone())));
// run the bruteforce
brute_step_2(brute_step_1());
}
```
Which gives the result:
```
$ time cargo run --release
[159, 91, 170, 77, 137, 115, 185, 199, 151, 69, 59, 246, 125, 88, 183, 111]
[7, 8, 13, 3, 9, 11, 6]
ptm{brut¸Õëöñ¸ò
ptm{brut¸Õëöñ¸ò
ptm{brut¸Õëöñ¸ò
ptm{brut¸Õëöñ¸ò
ptm{brut3_f0rc3}
ptm{brut3_f0rc3}
ptm{brut¸Õëöñ¸ò
ptm{brut¸Õëöñ¸ò
ptm{brut¸Õëöñ¸ò
ptm{brut¸Õëöñ¸ò
ptm{brut3_f0rc3}
ptm{brut¸Õëöñ¸ò
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
ptm{brut3_f0rc3}
ptm{brut3_f0rc3}
ptm{brut3_f0rc3}
ptm{brut¸Õëöñ¸ò
ptm{brut¸Õëöñ¸ò
ptm{brut3_f0rc3}
ptm{brut¸Õëöñ¸ò
ptm{brutÌ ÏÌ
ptm{brut3_f0rc3}
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
ptm{brutÌ ÏÌ
cargo run --release 5.36s user 0.01s system 99% cpu 5.390 total
```