Phat Contract Runtime - 0xTheC0der's results

Coprocessor for blockchains

General Information

Platform: Code4rena

Start Date: 01/03/2024

Pot Size: $60,500 USDC

Total HM: 4

Participants: 18

Period: 21 days

Judge: Lambda

Total Solo HM: 3

Id: 344

League: POLKADOT

Phala Network

Findings Distribution

Researcher Performance

Rank: 3/18

Findings: 2

Award: $12,619.42

🌟 Selected for report: 1

🚀 Solo Findings: 1

Findings Information

🌟 Selected for report: 0xTheC0der

Labels

2 (Med Risk)
bug
primary issue
satisfactory
selected for report
sponsor confirmed
sufficient quality report
edited-by-warden
M-01

Awards

12549.5721 USDC - $12,549.57

External Links

Lines of code

https://github.com/code-423n4/2024-03-phala-network/blob/a01ffbe992560d8d0f17deadfb9b9a2bed38377e/phala-blockchain/crates/pink/runtime/src/runtime/extension.rs#L270

Vulnerability details

Impact

According to the documentation (online and in-line), the availability of the balance_of(...) method (see code below) should be any contract instead of system only which is caused by the present ensure_system check.

fn balance_of(
    &self,
    account: ext::AccountId,
) -> Result<(pink::Balance, pink::Balance), Self::Error> {
    self.ensure_system()?;    // @audit Availability should be 'any contract' instead of 'system only'
    let account: AccountId32 = account.convert_to();
    let total = crate::runtime::Balances::total_balance(&account);
    let free = crate::runtime::Balances::free_balance(&account);
    Ok((total, free))
}

The ensure_system(...) method returns a BadOrigin error in case the caller/origin is not the system contract.

fn ensure_system(&self) -> Result<(), DispatchError> {
    let contract: AccountId32 = self.address.convert_to();
    if Some(contract) != PalletPink::system_contract() {
        return Err(DispatchError::BadOrigin);
    }
    Ok(())
}

Consequence:
The availability of the balance_of(...) method is limited to the system contract instead of being accessible to anyone. Therefore, user contracts relying on this method will inevitably fail.

For comparison:
The import_latest_system_code(...) method has consistent system only availability according to the implementation and documentation.

Proof of Concept

Please add the test case below to phala-blockchain/crates/pink/runtime/tests/test_pink_contract.rs and run it with cargo test test_balance_of -- --nocapture.

#[test]
fn test_balance_of() {
    const TEST_ADDRESS: AccountId32 = AccountId32::new([255u8; 32]);

    let (mut cluster, checker) = create_cluster();

    let balance = 114514;

    cluster.tx().deposit(TEST_ADDRESS.clone(), balance);

    let result = checker
        .call()
        .direct_balance_of(TEST_ADDRESS.convert_to())
        .query(&mut cluster);
    assert_eq!(result.unwrap(), (balance, balance));
}

The test will fail with a BadOrigin error as discussed above.

called `Result::unwrap()` on an `Err` value: Failed to execute call: BadOrigin

Tools Used

Manual review

Remove the ensure_system check from the balance_of(...) method to ensure availability for any contract.

Assessed type

Invalid Validation

#0 - c4-pre-sort

2024-03-25T04:45:12Z

141345 marked the issue as primary issue

#1 - c4-pre-sort

2024-03-25T04:45:24Z

141345 marked the issue as sufficient quality report

#2 - 141345

2024-03-25T04:52:39Z

contradicts doc specs

#3 - c4-sponsor

2024-03-26T06:02:45Z

kvinwang (sponsor) confirmed

#4 - c4-judge

2024-03-27T18:34:01Z

OpenCoreCH marked the issue as satisfactory

#5 - c4-judge

2024-03-27T18:35:41Z

OpenCoreCH marked the issue as selected for report

Findings Information

🌟 Selected for report: Cryptor

Also found by: 0xTheC0der, Bauchibred, Daniel526, XDZIBECX, ihtishamsudo, zhaojie

Labels

bug
grade-b
QA (Quality Assurance)
sufficient quality report
Q-02

Awards

69.8535 USDC - $69.85

External Links

Summary

  • L-1: js_eval is not implemented for queries
  • L-2: Contract address derivation does not include input_data
  • R-1: Flush LimitedWriter after use

L-1: js_eval is not implemented for queries

According to the documentation (online and in-line #1,#2), the js_eval(...) method should be available in queries (only in queries because it's non-deterministic). However, it's actually not implemented in the chain-extension.

fn js_eval(&self, _codes: Vec<JsCode>, _args: Vec<String>) -> Result<JsValue, Self::Error> {
    Ok(JsValue::Exception("No Js Runtime".into()))
}

L-2: Contract address derivation does not include input_data

Pink's contact address derivation overrides the one of pallet-contracts as follows.

fn contract_address(
    deploying_address: &AccountIdOf<T>,
    code_hash: &HashOf<T>,
    _input_data: &[u8],   // @audit _input_data ignored
    salt: &[u8],
) -> AccountIdOf<T> {
    let cluster_id = <ClusterId<T>>::get();
    let buf = phala_types::contract::contract_id_preimage(
        deploying_address.as_ref(),
        code_hash.as_ref(),
        cluster_id.as_ref(),
        salt,
    );
    UncheckedFrom::unchecked_from(<T as frame_system::Config>::Hashing::hash(&buf))
}

Although compatibility with pallet contracts v9 is ensured, it's not in line with the latest address derivation.

Consequence: Multiple instances of the same contract with different arguments/input_data cannot be created as expected failing with a DuplicateContract error. It requires to explicitly alter the salt in order to obtain distinct addresses in such a case.

R-1: Flush LimitedWriter after use

The async_http_request(...) method uses the LimitedWriter to write the body chunk by chunk from the HTTP response.

...

let mut writer = LimitedWriter::new(&mut body, MAX_BODY_SIZE);

while let Some(chunk) = response
    .chunk()
    .await
    .or(Err(HttpRequestError::NetworkError))?
{
    writer
        .write_all(&chunk)
        .or(Err(HttpRequestError::ResponseTooLarge))?;
}

...

However, the LimitedWriter is never flushed.

Recommendation: Add writer.flush().or(Err(HttpRequestError::ResponseTooLarge))?; below the above snipppet to ensure that the writer is correctly flushed.

#0 - c4-pre-sort

2024-03-25T04:58:05Z

141345 marked the issue as sufficient quality report

#1 - 141345

2024-03-25T09:11:03Z

63 0xTheC0der l r nc 2 1 0

L 1 l L 2 l L 3 r

#2 - c4-judge

2024-03-27T15:31:23Z

OpenCoreCH marked the issue as grade-b

AuditHub

A portfolio for auditors, a security profile for protocols, a hub for web3 security.

Built bymalatrax © 2024

Auditors

Browse

Contests

Browse

Get in touch

ContactTwitter