⏪
CTFs
TwitterGithub
  • 👋Introduction
  • 📚Write Up
    • 2024
      • 📖1337UP LIVE CTF
        • Reverse Engineering
        • Mobile
        • Forensic
        • Misc
      • 📖HKCERT CTF Quals
        • Reverse Engineering
        • Binary Exploitation
      • 📖Flare-On 11
        • Challenge #1 - frog
      • 📖Intechfest
        • Reverse Engineering
        • Forensic
        • Cryptography
        • Mobile
      • 📖Cyber Breaker Competition (1v1)
        • Reverse Engineering
        • Web Exploitation
        • Cryptography
        • Binary Exploitation
      • 📖Cyber Breaker Competition Quals
        • Reverse Engineering
        • Web Exploitation
        • Cryptography
      • 📖BlackHat MEA Quals
        • Reverse Engineering
        • Forensic
      • 📖TFC CTF
        • Reverse Engineering
        • Forensic
        • Misc
      • 📖DeadSec CTF
        • Reverse Engineering
        • Web Exploitation
      • 📖Aptos - Code Collision CTF
        • Reverse Engineering
        • Misc
      • 📖DownUnder CTF
        • Reverse Engineering
      • 📖JustCTF
        • Reverse Engineering
        • Forensic
        • Misc
      • 📖Akasec CTF
        • Reverse Engineering
        • Forensic
      • 📖Codegate CTF Preliminary
        • Reverse Engineering
      • 📖NahamCon CTF
        • Cryptography
        • Reverse Engineering
        • Malware
        • Misc
        • Mobile
        • Scripting
        • Web Exploitation
        • Forensic
      • 📖SAS CTF Quals
        • Reverse Engineering
      • 📖SwampCTF
        • Reverse Engineering
        • Misc
        • Cryptography
      • 📖UNbreakable International
        • Reverse Engineering
        • Network
        • Cryptography
      • 📖ACSC
        • Reverse Engineering
        • Hardware
        • Web Exploitation
      • 📖0xL4ugh
        • Mobile
    • 2023
      • 📖BlackHat MEA Final
        • Reverse Engineering
        • Web Exploitation
      • 📖Flare-On 10
        • Challenge #1 - X
        • Challenge #2 - ItsOnFire
        • Challenge #3 - mypassion
        • Challenge #4 - aimbot
        • Challenge #5 - where_am_i
        • Challenge #6 - FlareSay
        • Challenge #7 - flake
        • Challenge #8 - AmongRust
        • Challenge #9 - mbransom
        • Challenge #10 - kupo
        • Challenge #11 - over_the_rainbow
        • Challenge #12 - HVM
        • Challenge #13 - y0da
      • 📖LakeCTF Quals
        • Reverse Engineering
        • Cryptography
      • 📖TSG CTF
        • Reverse Engineering
        • Cryptography
      • 📖ISITDTU Quals
        • Web Exploitation
        • Misc
        • Reverse Engineering
      • 📖BlackHat MEA Quals
        • Reverse Engineering
      • 📖ASCIS Final
        • Reverse Engineering
        • Web Exploitation
        • Cryptography
      • 📖ASCIS Quals
        • Reverse Engineering
        • Forensic
        • Cryptography
      • 📖IFest
        • Reverse Engineering
        • Cryptography
        • Misc
      • 📖Cyber Jawara International
        • Reverse Engineering
        • Forensic
        • Cryptography
        • Web Exploitation
      • 📖Intechfest
        • Reverse Engineering
        • Forensic
        • Cryptography
        • Mobile
      • 📖CSAW Quals
        • Reverse Engineering
      • 📖SECCON Quals
        • Reverse Engineering
      • 📖CTFZone Quals
        • Reverse Engineering
      • 📖Securinets Quals
        • Reverse Engineering
      • 📖Compfest Final (Attack Defense)
        • Web Exploitation
        • Cryptography
      • 📖Compfest Quals
        • Reverse Engineering
        • Cryptography
        • Forensic
        • Misc
      • 📖Tenable
        • Reverse Engineering
        • Cryptography
        • Steganography
      • 📖ASCWG Quals
        • Reverse Engineering
        • Cryptography
      • 📖Gemastik Quals
        • Reverse Engineering
      • 📖BSides Indore
        • Reverse Engineering
        • Cryptography
      • 📖NahamCon CTF
        • Cryptography
      • 📖HSCTF
        • Reverse Engineering
        • Cryptography
        • Web Exploitation
        • Misc
      • 📖ACSC
        • Reverse Engineering
      • 📖HackTM Quals
        • Reverse Engineering
    • 2022
      • 📖Intechfest
        • Reverse Engineering
        • Mobile
        • Cryptography
      • 📖NCW Final
        • Reverse Engineering
      • 📖NCW Quals
        • Reverse Engineering
        • Misc
        • Cryptography
      • 📖Compfest Final
        • Reverse Engineering
        • Forensic
      • 📖Compfest Quals
        • Reverse Engineering
        • Cryptography
      • 📖IFest
        • Reverse Engineering
        • Cryptography
        • Forensic
    • 2021
      • 📖Cyber Jawara Final
        • Reverse Engineering
      • 📖Cyber Jawara Quals
        • Reverse Engineering
        • Cryptography
      • 📖DarkCon CTF
        • Reverse Engineering
      • 📖Wreck IT Quals
        • Mobile
      • 📖MDT4.0 Final
        • Reverse Engineering
        • Cryptography
        • Forensic
      • 📖MDT4.0 Quals
        • Reverse Engineering
        • Cryptography
      • 📖IFest
        • Reverse Engineering
        • Cryptography
      • 📖Compfest Final
        • Reverse Engineering
      • 📖Compfest Quals
        • Reverse Engineering
        • Cryptography
    • 2020
      • 📖Deep CTF
        • Reverse Engineering
  • 🚩Lifetime CTF
    • 📖Hack The Box
      • Reverse Engineering
        • TBU
Powered by GitBook
On this page
  • Can You Revela Me (50 pts)
  • Description
  • Solution
  • Sage (212 pts)
  • Description
  • Solution
  • U Can't Touch This (372 pts)
  • Description
  • Solution
  1. Write Up
  2. 2024
  3. Aptos - Code Collision CTF

Reverse Engineering

PreviousAptos - Code Collision CTFNextMisc

Last updated 10 months ago

Challenge
Link

Can You Revela Me (50 pts)

Sage (212 pts) 🥉

U Can't Touch This (372 pts) - UPSOLVE

Can You Revela Me (50 pts)

Description

A friend of mine found this on his PC, but he can't remember what it is. It should contain juicy information. Can you recover it?

Solution

Given mv file which is a compiled move code. Trying to find disassembler or decompiler for given file i found this . The repository name has the same name with the challenge, so i tried to use the the released binary from the repository to decompile the mv file.

./revela-aarch64-apple-darwin -b source.mv
module 0x1337::source {
    public fun a0() : vector<u8> {
        magic_1(b"etpkw", 4)
    }
    
    public fun a1() : vector<u8> {
        magic_2(b"zrn^", 1)
    }
    
    public fun a10() : vector<u8> {
        magic_2(b"bW", 10)
    }
    
    public fun a2() : vector<u8> {
        magic_3(b"_uoy")
    }
    
    public fun a3() : vector<u8> {
        magic_1(b"i`wd", 1)
    }
    
    public fun a4() : vector<u8> {
        magic_2(b"]jc_", 2)
    }
    
    public fun a5() : vector<u8> {
        magic_1(b"qmfg", 3)
    }
    
    public fun a6() : vector<u8> {
        magic_3(b"woh_")
    }
    
    public fun a7() : vector<u8> {
        magic_2(b"^sn^", 1)
    }
    
    public fun a8() : vector<u8> {
        magic_1(b"qwa[", 4)
    }
    
    public fun a9() : vector<u8> {
        magic_3(b"ever")
    }
    
    public fun get_flag() : vector<u8> {
        let v0 = a0();
        0x1::vector::append<u8>(&mut v0, a1());
        0x1::vector::append<u8>(&mut v0, a2());
        0x1::vector::append<u8>(&mut v0, a3());
        0x1::vector::append<u8>(&mut v0, a4());
        0x1::vector::append<u8>(&mut v0, a5());
        0x1::vector::append<u8>(&mut v0, a6());
        0x1::vector::append<u8>(&mut v0, a7());
        0x1::vector::append<u8>(&mut v0, a8());
        0x1::vector::append<u8>(&mut v0, a9());
        0x1::vector::append<u8>(&mut v0, a10());
        0x1::vector::append<u8>(&mut v0, b"}");
        v0
    }
    
    fun magic_1(arg0: vector<u8>, arg1: u8) : vector<u8> {
        let v0 = 0x1::vector::empty<u8>();
        let v1 = 0;
        while (v1 < 0x1::vector::length<u8>(&arg0)) {
            0x1::vector::push_back<u8>(&mut v0, *0x1::vector::borrow<u8>(&arg0, v1) ^ arg1);
            v1 = v1 + 1;
        };
        v0
    }
    
    fun magic_2(arg0: vector<u8>, arg1: u8) : vector<u8> {
        let v0 = 0x1::vector::empty<u8>();
        let v1 = 0;
        while (v1 < 0x1::vector::length<u8>(&arg0)) {
            0x1::vector::push_back<u8>(&mut v0, *0x1::vector::borrow<u8>(&arg0, v1) + arg1);
            v1 = v1 + 1;
        };
        v0
    }
    
    fun magic_3(arg0: vector<u8>) : vector<u8> {
        let v0 = 0x1::vector::empty<u8>();
        let v1 = 0x1::vector::length<u8>(&arg0);
        while (v1 > 0) {
            let v2 = v1 - 1;
            v1 = v2;
            0x1::vector::push_back<u8>(&mut v0, *0x1::vector::borrow<u8>(&arg0, v2));
        };
        v0
    }
    
    // decompiled from Move bytecode v6
}

From code above we can see that there are some function and there is one function named get_flag. So in this case our objective is to find the flag that created from get_flag function. My approach is to rewrite above code in python and then print out the flag.

def magic_1(a, b):
	res = []
	for i in a:
		res.append(i ^ b)
	return bytes(res)

def magic_2(a, b):
	res = []
	for i in a:
		res.append(i + b)
	return bytes(res)

def magic_3(a):
	return a[::-1]

flag = []
flag.append(magic_1(b"etpkw", 4))
flag.append(magic_2(b"zrn^", 1))
flag.append(magic_3(b"_uoy"))
flag.append(magic_1(b"i`wd", 1))
flag.append(magic_2(b"]jc_", 2))
flag.append(magic_1(b"qmfg", 3))
flag.append(magic_3(b"woh_"))
flag.append(magic_2(b"^sn^", 1))
flag.append(magic_1(b"qwa[", 4))
flag.append(magic_3(b"ever"))
flag.append(magic_2(b"bW", 10))

print(b''.join(flag) + b"}")

Flag: aptos{so_you_have_learned_how_to_use_revela}

Sage (212 pts)

Description

I lost my code to unlock the flag. HELP ME

Solution

Given an archive file that consist of framework and framework-solve folder. Take a look on framework folder to get information about the challenge. We can see the challenge in file framework/challenge/sources/sage.move.

module challenge::sage {

    use aptos_framework::signer;
    use std::vector;

    struct ChallengeStatus has key {
        is_solved: bool,
    }

    public entry fun initialize(account: &signer) {
        let account_address = signer::address_of(account);
        assert!(account_address == @challenger, 0);
        move_to(account, ChallengeStatus { is_solved: false })

    }

    public entry fun challenge(m: vector<u64>, n: vector<u64>) acquires ChallengeStatus {

        let flag: vector<u64> = vector[
            12, 0, 7, 7, 37, 20, 25, 39, 10, 6, 35, 25, 43, 43, 26, 12, 28, 34, 37, 5, 22,
            9, 25, 4, 31, 8, 40, 38, 3, 27, 3, 24, 8, 0, 23, 38, 10, 5, 2, 16, 11, 37, 28,
            0, 18, 2, 12, 27, 40, 3, 11, 32, 24, 14, 2, 20, 12, 38, 30, 17, 21, 37, 26, 37,
            12, 28, 12, 27, 34, 24, 18, 32];

        if (build(m, n) == flag) {
            let challenge_status = borrow_global_mut<ChallengeStatus>(@challenger);
            challenge_status.is_solved = true;
        }
    }

    fun build(m: vector<u64>, n: vector<u64>): vector<u64> {
        let m1 = copy m;
        let text = &mut m1;
        let text_length = vector::length(text);
        assert!(text_length > 3, 0);

        if (text_length % 3 != 0) {
            if (3 - (text_length % 3) == 2) {
                vector::push_back(text, 0);
                vector::push_back(text, 0);
                text_length = text_length + 2;
            } else {
                vector::push_back(text, 0);
                text_length = text_length + 1;
            }
        };

        let next_text = vector::empty<u64>();
        vector::push_back(&mut next_text, 42);
        vector::push_back(&mut next_text, 11);
        vector::push_back(&mut next_text, 13);
        vector::push_back(&mut next_text, 16);
        vector::push_back(&mut next_text, 13);
        vector::push_back(&mut next_text, 62);
        vector::push_back(&mut next_text, 72);
        vector::push_back(&mut next_text, 13);
        vector::push_back(&mut next_text, 12);
        vector::append(&mut next_text, *text);
        text_length = text_length + 9;

        let n2 = copy n;
        let r = &mut n2;
        let x11 = *vector::borrow(r, 0);
        let x12 = *vector::borrow(r, 1);
        let x13 = *vector::borrow(r, 2);
        let x21 = *vector::borrow(r, 3);
        let x22 = *vector::borrow(r, 4);
        let x23 = *vector::borrow(r, 5);
        let x31 = *vector::borrow(r, 6);
        let x32 = *vector::borrow(r, 7);
        let x33 = *vector::borrow(r, 8);

        assert!(vector::length(r) == 9, 0);
        let i: u64 = 0;
        let end_text = vector::empty<u64>();
        while (i < text_length) {
            let y11 = *vector::borrow(&mut next_text, i + 0);
            let y21 = *vector::borrow(&mut next_text, i + 1);
            let y31 = *vector::borrow(&mut next_text, i + 2);

            let z11 = ((x11 + y31) * (x12 + y11) + (x13 * y21)) % 44;
            let z21 = ((x21 + y31) * (x22 + y11) + (x23 * y21)) % 44;
            let z31 = ((x31 + y31) * (x32 + y11) + (x33 * y21)) % 44;

            vector::push_back(&mut end_text, z11);
            vector::push_back(&mut end_text, z21);
            vector::push_back(&mut end_text, z31);

            i = i + 3;
        };

        end_text
    }

    public entry fun is_solved() acquires ChallengeStatus {
        let challenge_status = borrow_global_mut<ChallengeStatus>(@challenger);
        assert!(challenge_status.is_solved, 2);
    }
}

initialize is the first function executed in main.rs, after that we will interact with the server (framework folder). In this case we can call public function and because this is a RE challenge so our objective is to call the challenge function and send the valid m and n array. Build function seems like solvable using z3 but in this case we can directly solve by just putting the build function on z3. To solve the challenge we need to add some constraint and put the correct bit length. Below is my approach to solve it using z3

  • Information we know

    • next_text consist of static values (42,11,13,16,13,62,72,13,12) + m

    • array m and n consist of value with maximum size u64 (64 bit/8 bytes)

  • From information above i try to find value for m and n with size u8 (8 bit/1 byte)

  • If i put bit length of m and n in z3 with size of 8 it will automatically wrapped to 1 byte, so i can't use 8 as the bit length

  • The maximum bit length from the operation should be 19, we know it from below operation

    • Lets use maximum 1 byte value for each variable

    • ((x11 + y31) * (x12 + y11) + (x13 * y21))

So to utilize approach above we will use bit length 19 and use ULE function to make sure that the value will be 1 byte.

from z3 import *

bit_length = 19

a = [BitVec("x{}".format(i), bit_length) for i in range(72)]
r = [BitVec("y{}".format(i), bit_length) for i in range(9)]

known = [42,11,13,16,13,62,72,13,12]

x11 = r[0]
x12 = r[1]
x13 = r[2]
x21 = r[3]
x22 = r[4]
x23 = r[5]
x31 = r[6]
x32 = r[7]
x33 = r[8]

target = [12, 0, 7, 7, 37, 20, 25, 39, 10, 6, 35, 25, 43, 43, 26, 12, 28, 34, 37, 5, 22, 9, 25, 4, 31, 8, 40, 38, 3, 27, 3, 24, 8, 0, 23, 38, 10, 5, 2, 16, 11, 37, 28, 0, 18, 2, 12, 27, 40, 3, 11, 32, 24, 14, 2, 20, 12, 38, 30, 17, 21, 37, 26, 37, 12, 28, 12, 27, 34, 24, 18, 32]

s = Solver()

for i in range(len(known)):
	s.add(a[i] == known[i])

length = 72

for i in range(length):
	s.add(ULE(a[i], 2**8-1))

for i in range(9):
	s.add(ULE(r[i], 2**8-1))

for i in range(0, length, 3):
	y11 = a[i]
	y21 = a[i+1]
	y31 = a[i+2]
	z11 = (((x11 + y31) * (x12 + y11)) + (x13 * y21))
	z21 = (((x21 + y31) * (x22 + y11)) + (x23 * y21))
	z31 = (((x31 + y31) * (x32 + y11)) + (x33 * y21))
	s.add(z11 % 44 == target[i])
	s.add(z21 % 44 == target[i+1])
	s.add(z31 % 44 == target[i+2])

# print(s)
print(s.check())
model = s.model()

m = []
for i in a:
	m.append(model[i].as_long())
n = []
for i in r:
	n.append(model[i].as_long())

next_text = m[:]

x11 = n[0]
x12 = n[1]
x13 = n[2]
x21 = n[3]
x22 = n[4]
x23 = n[5]
x31 = n[6]
x32 = n[7]
x33 = n[8]

end_text = []

for i in range(0, len(next_text), 3):
	y11 = next_text[i]
	y21 = next_text[i+1]
	y31 = next_text[i+2]

	z11 = ((x11 + y31) * (x12 + y11) + (x13 * y21)) % 44;
	z21 = ((x21 + y31) * (x22 + y11) + (x23 * y21)) % 44;
	z31 = ((x31 + y31) * (x32 + y11) + (x33 * y21)) % 44;

	end_text.append(z11)
	end_text.append(z21)
	end_text.append(z31)

assert(end_text == target)

print(f"{m=}")
print(f"{n=}")

After getting the valid value for m and n lets put it in move and send run the solver.

module solution::exploit {
    use challenge::sage;
    use std::vector;

    public entry fun solve() {
        let m: vector<u64> = vector[112, 58, 255, 94, 17, 220, 195, 108, 55, 132, 107, 52, 162, 29, 162, 130, 186, 72, 22, 252, 103, 254, 150, 112, 69, 217, 91, 243, 193, 136, 146, 134, 101, 239, 104, 5, 96, 231, 187, 156, 8, 37, 7, 224, 105, 183, 88, 46, 54, 151, 183, 92, 227, 44, 244, 142, 68, 241, 215, 230, 113, 60, 39];
        let n: vector<u64> = vector[3, 11, 28, 230, 233, 25, 144, 107, 2];
        sage::challenge(m, n);
    }
}

Flag: AptosCTF{a7ed0e353b00f098d765c11605fa807b78cf4b80c8879afb5aa17351760f9787}

U Can't Touch This (372 pts)

Description

I'll keep this short for you. The flag is inside the Safe Deposit Box. Only the admin can access it. Good Luck!

Solution

TBU

📚
📖
repository
Here
Here
Here