Skip to content

add: malicious send calls #401

New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Merged
merged 17 commits into from
May 7, 2025
172 changes: 172 additions & 0 deletions src/components/ppom/eip5792.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,172 @@
import globalContext from '../..';
import { DEFAULT_CALLS } from '../transactions/eip5792/sendCalls';
import { getMaliciousTransactions } from './sharedConstants';

export function ppomMaliciousSendCalls(parentContainer) {
parentContainer.insertAdjacentHTML(
'beforeend',
`<div class="col-xl-4 col-lg-6 col-md-12 col-sm-12 col-12 d-flex align-items-stretch">
<div class="card full-width">
<div class="card-body">
<h4 class="card-title">
EIP 5792 - PPOM - Malicious Send Calls
</h4>
<button class="btn btn-primary btn-lg btn-block mb-3" id="ppomSendMaliciousEthButton">
Send Calls with Malicious ETH Transfer
</button>
<button class="btn btn-primary btn-lg btn-block mb-3" id="ppomSendMaliciousERC20TransferButton">
Send Calls with Malicious ERC20 Transfer (USDC)
</button>
<button class="btn btn-primary btn-lg btn-block mb-3" id="ppomSendMaliciousERC20ApprovalButton">
Send Calls with Malicious ERC20 Approval (BUSDC)
</button>
<button class="btn btn-primary btn-lg btn-block mb-3" id="ppomSendMaliciousSetApprovalForAllButton">
Send Calls with Malicious Set Approval for All
</button>
<button class="btn btn-primary btn-lg btn-block mb-3" id="ppomSendMaliciousContractInteractionButton">
Send Calls with Malicious Contract Interaction
</button>
<hr/>
<div id="ppomRequestIdContainer" hidden>
<label>Request ID:</label>
<input type="text" id="ppomRequestIdInput" class="form-control" readonly />
</div>
<button class="btn btn-primary btn-lg btn-block mb-3" id="ppomGetCallsStatusButton" disabled>
Get Calls Status
</button>
<p class="info-text alert alert-success">
<span class="wrap" id="ppomGetCallsStatusResult">Status</span>
</p>
</div>
</div>
</div>`,
);

const ppomSendMaliciousEthButton = document.getElementById(
'ppomSendMaliciousEthButton',
);

ppomSendMaliciousEthButton.onclick = async () => {
await sendMaliciousCalls('eth');
};

const ppomSendMaliciousERC20TransferButton = document.getElementById(
'ppomSendMaliciousERC20TransferButton',
);

ppomSendMaliciousERC20TransferButton.onclick = async () => {
await sendMaliciousCalls('erc20Transfer');
};

const ppomSendMaliciousERC20ApprovalButton = document.getElementById(
'ppomSendMaliciousERC20ApprovalButton',
);

ppomSendMaliciousERC20ApprovalButton.onclick = async () => {
await sendMaliciousCalls('erc20Approval');
};

const ppomSendMaliciousSetApprovalForAllButton = document.getElementById(
'ppomSendMaliciousSetApprovalForAllButton',
);

ppomSendMaliciousSetApprovalForAllButton.onclick = async () => {
await sendMaliciousCalls('setApprovalForAll');
};

const ppomSendMaliciousContractInteractionButton = document.getElementById(
'ppomSendMaliciousContractInteractionButton',
);

ppomSendMaliciousContractInteractionButton.onclick = async () => {
await sendMaliciousCalls('maliciousContractInteraction');
};

document.addEventListener('globalConnectionChange', function (e) {
if (e.detail.connected) {
// MetaMask is connected, enable the button
ppomSendMaliciousEthButton.disabled = false;
ppomSendMaliciousERC20TransferButton.disabled = false;
ppomSendMaliciousERC20ApprovalButton.disabled = false;
ppomSendMaliciousSetApprovalForAllButton.disabled = false;
ppomSendMaliciousContractInteractionButton.disabled = false;
}
});

document.addEventListener('disableAndClear', function () {
ppomSendMaliciousEthButton.disabled = true;
ppomSendMaliciousERC20TransferButton.disabled = true;
ppomSendMaliciousERC20ApprovalButton.disabled = true;
ppomSendMaliciousSetApprovalForAllButton.disabled = true;
ppomSendMaliciousContractInteractionButton.disabled = true;
});

async function sendMaliciousCalls(type) {
const maliciousTransactions = getMaliciousTransactions(globalContext);

const calls = [];

switch (type) {
case 'eth':
calls.push(maliciousTransactions.eth);
break;
case 'erc20Transfer':
calls.push(maliciousTransactions.erc20Transfer);
break;
case 'erc20Approval':
calls.push(maliciousTransactions.erc20Approval);
break;
case 'maliciousContractInteraction':
calls.push(maliciousTransactions.maliciousContractInteraction);
break;
case 'setApprovalForAll':
calls.push(maliciousTransactions.setApprovalForAll);
break;
default:
// Do nothing
break;
}

calls.push(...DEFAULT_CALLS);

try {
const result = await globalContext.provider.request({
method: 'wallet_sendCalls',
params: [getParams(calls)],
});
document.getElementById('ppomRequestIdInput').value = result.id;
document.getElementById('ppomRequestIdContainer').hidden = false;
document.getElementById('ppomGetCallsStatusButton').disabled = false;
} catch (error) {
console.error(error);
}
}

// Get Calls Status functionality
document.getElementById('ppomGetCallsStatusButton').onclick = async () => {
const requestId = document.getElementById('ppomRequestIdInput').value;
const resultOutput = document.getElementById('ppomGetCallsStatusResult');

try {
const result = await globalContext.provider.request({
method: 'wallet_getCallsStatus',
params: [requestId],
});

resultOutput.innerHTML = JSON.stringify(result, null, 2);
document.getElementById('ppomGetCallsStatusButton').disabled = false;
} catch (error) {
console.error(error);
resultOutput.innerHTML = `Error: ${error.message}`;
}
};

function getParams(calls) {
return {
version: '1.0',
from: globalContext.accounts[0],
chainId: `0x${globalContext.chainIdInt.toString(16)}`,
calls,
};
}
}
3 changes: 2 additions & 1 deletion src/components/ppom/index.js
Original file line number Diff line number Diff line change
@@ -1,3 +1,4 @@
export * from './transactions';
export * from './batching';
export * from './bypasses';
export * from './eip5792';
export * from './transactions';
39 changes: 39 additions & 0 deletions src/components/ppom/sharedConstants.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,39 @@
import { maliciousAddress } from '../../sample-addresses';
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

moved the malicious tx's in this file, so they can be used both in the ppom tx and in the ppom batch txs

import {
ERC20_SAMPLE_CONTRACTS,
ERC721_SAMPLE_CONTRACTS,
MALICIOUS_CONTRACT_ADDRESSES,
} from '../../onchain-sample-contracts';

export const getMaliciousTransactions = (globalContext) => {
const networkName = globalContext.networkName || 'default';

return {
eth: {
to: maliciousAddress,
value: '0x9184e72a000',
},
erc20Transfer: {
to: ERC20_SAMPLE_CONTRACTS[networkName],
data: '0xa9059cbb0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa30000000000000000000000000000000000000000000000000000000000000064',
value: '0x0',
},
erc20Approval: {
to: ERC20_SAMPLE_CONTRACTS[networkName],
data: '0x095ea7b3000000000000000000000000e50a2dbc466d01a34c3e8b7e8e45fce4f7da39e6000000000000000000000000000000000000000000000000ffffffffffffffff',
value: '0x0',
},
setApprovalForAll: {
to: ERC721_SAMPLE_CONTRACTS[networkName],
data: '0xa22cb465000000000000000000000000b85492afc686d5ca405e3cd4f50b05d358c75ede0000000000000000000000000000000000000000000000000000000000000001',
value: '0x0',
},
maliciousContractInteraction: {
to:
MALICIOUS_CONTRACT_ADDRESSES[networkName] ||
MALICIOUS_CONTRACT_ADDRESSES.default,
data: '0xef5cfb8c0000000000000000000000000b3e87a076ac4b0d1975f0f232444af6deb96c59',
value: '0x0',
},
};
};
66 changes: 17 additions & 49 deletions src/components/ppom/transactions.js
Original file line number Diff line number Diff line change
@@ -1,11 +1,7 @@
import globalContext from '../..';
import { maliciousAddress } from '../../sample-addresses';
import {
ERC20_SAMPLE_CONTRACTS,
ERC721_SAMPLE_CONTRACTS,
MALICIOUS_CONTRACT_ADDRESSES,
} from '../../onchain-sample-contracts';
import { isBaseNetworkId, isSepoliaNetworkId } from '../../utils';
import { isSepoliaNetworkId } from '../../utils';
import { getMaliciousTransactions } from './sharedConstants';

export function ppomMaliciousTransactionsAndSignatures(parentContainer) {
parentContainer.insertAdjacentHTML(
Expand Down Expand Up @@ -51,17 +47,17 @@ export function ppomMaliciousTransactionsAndSignatures(parentContainer) {
</button>
<button
class="btn btn-primary btn-lg btn-block mb-3"
id="maliciousContractInteractionButton"
id="maliciousSetApprovalForAll"
disabled
>
Malicious Contract Interaction
Malicious Set Approval For All
</button>
<button
class="btn btn-primary btn-lg btn-block mb-3"
id="maliciousSetApprovalForAll"
id="maliciousContractInteractionButton"
disabled
>
Malicious Set Approval For All
Malicious Contract Interaction
Copy link
Contributor Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

re-ordered these 2 as it makes more sense like this (first all custom contract interactions, and the last one the fallback contract interaction) -- also following this order for the batch

</button>
<h5>Signatures</h5>
<button
Expand Down Expand Up @@ -139,20 +135,17 @@ export function ppomMaliciousTransactionsAndSignatures(parentContainer) {

document.addEventListener('newNetwork', function (e) {
mintSepoliaERC20.hidden = !isSepoliaNetworkId(e.detail.networkId);
maliciousContractInteractionButton.hidden =
isBaseNetworkId(e.detail.networkId) ||
isSepoliaNetworkId(e.detail.networkId);
});

// Malicious raw ETH transfer
maliciousRawEthButton.onclick = async () => {
const { eth } = getMaliciousTransactions(globalContext);
const result = await globalContext.provider.request({
method: 'eth_sendTransaction',
params: [
{
from: globalContext.accounts[0],
to: `${maliciousAddress}`,
value: '0x9184e72a000',
...eth,
},
],
});
Expand Down Expand Up @@ -183,21 +176,13 @@ export function ppomMaliciousTransactionsAndSignatures(parentContainer) {

// Malicious ERC20 transfer
maliciousERC20TransferButton.onclick = async () => {
let erc20Contract;

if (globalContext.networkName) {
erc20Contract = ERC20_SAMPLE_CONTRACTS[globalContext.networkName];
} else {
erc20Contract = '0xA0b86991c6218b36c1d19D4a2e9Eb0cE3606eB48';
}

const { erc20Transfer } = getMaliciousTransactions(globalContext);
const result = await globalContext.provider.request({
method: 'eth_sendTransaction',
params: [
{
from: globalContext.accounts[0],
to: erc20Contract,
data: '0xa9059cbb0000000000000000000000005fbdb2315678afecb367f032d93f642f64180aa30000000000000000000000000000000000000000000000000000000000000064',
...erc20Transfer,
},
],
});
Expand All @@ -206,20 +191,13 @@ export function ppomMaliciousTransactionsAndSignatures(parentContainer) {

// Malicious ERC20 Approval
maliciousApprovalButton.onclick = async () => {
let erc20Contract;

if (globalContext.networkName) {
erc20Contract = ERC20_SAMPLE_CONTRACTS[globalContext.networkName];
} else {
erc20Contract = '0x4fabb145d64652a948d72533023f6e7a623c7c53';
}
const { erc20Approval } = getMaliciousTransactions(globalContext);
const result = await globalContext.provider.request({
method: 'eth_sendTransaction',
params: [
{
from: globalContext.accounts[0],
to: erc20Contract,
data: '0x095ea7b3000000000000000000000000e50a2dbc466d01a34c3e8b7e8e45fce4f7da39e6000000000000000000000000000000000000000000000000ffffffffffffffff',
...erc20Approval,
},
],
});
Expand All @@ -228,18 +206,15 @@ export function ppomMaliciousTransactionsAndSignatures(parentContainer) {

// Malicious Contract interaction
maliciousContractInteractionButton.onclick = async () => {
const contractAddress =
MALICIOUS_CONTRACT_ADDRESSES[globalContext.networkName] ||
MALICIOUS_CONTRACT_ADDRESSES.default;
Copy link
Contributor Author

@seaona seaona Apr 11, 2025

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

we directly use the malicious contract interaction data from the sample contracts. We don't want to hide this button for these networks (unsure why this was added)
The payload for the hex data is what makes Blockaid flag it as malicious, no matter to which address we point, even if it's an EOA (which makes our life easier)

const { maliciousContractInteraction } =
getMaliciousTransactions(globalContext);

const result = await globalContext.provider.request({
method: 'eth_sendTransaction',
params: [
{
from: globalContext.accounts[0],
to: contractAddress,
data: '0xef5cfb8c0000000000000000000000000b3e87a076ac4b0d1975f0f232444af6deb96c59',
value: '0x0',
...maliciousContractInteraction,
},
],
});
Expand All @@ -248,21 +223,14 @@ export function ppomMaliciousTransactionsAndSignatures(parentContainer) {

// Malicious Set Approval For All
maliciousSetApprovalForAll.onclick = async () => {
let erc721Contract;

if (globalContext.networkName) {
erc721Contract = ERC721_SAMPLE_CONTRACTS[globalContext.networkName];
} else {
erc721Contract = '0xbc4ca0eda7647a8ab7c2061c2e118a18a936f13d';
}
const { setApprovalForAll } = getMaliciousTransactions(globalContext);

const result = await globalContext.provider.request({
method: 'eth_sendTransaction',
params: [
{
from: globalContext.accounts[0],
to: erc721Contract,
data: '0xa22cb465000000000000000000000000b85492afc686d5ca405e3cd4f50b05d358c75ede0000000000000000000000000000000000000000000000000000000000000001',
...setApprovalForAll,
},
],
});
Expand Down
2 changes: 1 addition & 1 deletion src/components/transactions/eip5792/sendCalls.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import globalContext from '../../..';

const VERSION = '1.0';

const DEFAULT_CALLS = [
export const DEFAULT_CALLS = [
{
to: '0x0c54FcCd2e384b4BB6f2E405Bf5Cbc15a017AaFb',
data: '0x654365436543',
Expand Down
5 changes: 5 additions & 0 deletions src/index.js
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@ import {
erc721Component,
} from './components/transactions';
import {
ppomMaliciousSendCalls,
ppomMaliciousTransactionsAndSignatures,
ppomMaliciousBatchingAndQueueing,
ppomMaliciousWarningBypasses,
Expand Down Expand Up @@ -178,6 +179,10 @@ erc1155Component(transactionsRow);
eip747Component(transactionsRow);
eip5792Component(transactionsRow);

const ppomEip5792Section = document.createElement('section');
mainContainer.appendChild(ppomEip5792Section);
ppomMaliciousSendCalls(ppomEip5792Section);

const ppomSection = document.createElement('section');
mainContainer.appendChild(ppomSection);
const ppomRow = document.createElement('div');
Expand Down
Loading