019bab3b-aba8-711d-8ff4-ada11975c85e
Open this run to answer one question: is this real attacker-relevant behavior with real impact, or just normal contract plumbing?
| ID | Type | Status | Attempts | Heartbeat |
|---|---|---|---|---|
| 38604 | artifact.fetch | complete | 1 / 3 | 3 months ago |
| 38605 | proxy.resolve | complete | 1 / 3 | 3 months ago |
| 38606 | tool.cast_disasm | complete | 1 / 3 | 3 months ago |
| 38607 | analysis.bundle | complete | 1 / 3 | 3 months ago |
| 38608 | capability.graph | complete | 1 / 3 | 3 months ago |
| 38609 | detector.run | complete | 1 / 3 | 3 months ago |
| 38610 | validation.fork | complete | 1 / 3 | 3 months ago |
| 39087 | report.consolidate | complete | 1 / 6 | 3 months ago |
{
"status": "not_proxy"
}
No slither job recorded yet.
{"no_findings":false,"summary":"Two low-level CALL sites exist with computed target/value; one appears owner/time-locked, while the reachability/guarding of the pc:0x44a CALL is unclear from the bytecode graph and could allow arbitrary external calls if exposed.","findings":[{"severity":"medium","category":"access_control","title":"Potentially unguarded arbitrary CALL with ETH value (reachability unclear)","exploit_scenario":"If the code path containing the CALL at pc:0x44a (1098) is reachable from a public selector and its target/value are derived from calldata or other user-controlled inputs, any caller could trigger arbitrary external calls (and potentially transfer ETH) from this contract. This could allow draining ETH, token approvals/transfers, or interaction with attacker-controlled contracts. However, the control-flow graph does not clearly link this block to the dispatcher, and one dynamic trace observed a constant target (self), so actual exploitability is uncertain.","recommendation":"Confirm whether the pc:0x44a call path is reachable from any external selector and whether its target/value are user-controlled. If reachable, add explicit access control (owner/time-lock/allowlist) and validate target/value. If the call is intended to be a self-call or a fixed target, hardcode it or add assertions to prevent user-controlled targets.","evidence":"Bytecode analysis shows CALL at pc 1098 with target/value classified as computed (`callsites`), and no CALLER-based guard appears in nearby blocks (pc 953/1004/1080). Detector flagged both \"Untrusted CALL target/value reachable\" and \"ETH value transfer possible\" for this callsite; validation trace observed a constant target (self), so control is uncertain.","locations":["pc:0x44a","pc:0x3b8","pc:0x3ec"]}]}
OpenAI Codex v0.77.0 (research preview)
--------
workdir: /tmp/codex-86npeb2q
model: gpt-5.2-codex
provider: openai
approval: never
sandbox: read-only
reasoning effort: high
reasoning summaries: auto
session id: 019babb8-52c4-7d61-9b07-2f7c161f6450
--------
user
You are a security auditor. You ONLY have runtime bytecode + analysis artifacts.
You MUST read `BYTECODE_BUNDLE.json` before concluding; it contains the bytecode summary,
evidence bundle, callsites, storage slots, and detector findings.
Special cases when reasoning from bytecode (be explicit about uncertainty):
- Selector inference is best-effort; missing selectors are common.
- Fallback/receive paths may be the only entrypoints.
- CALL vs DELEGATECALL semantics differ; DELEGATECALL executes in caller storage.
- Proxy patterns (EIP-1967, minimal proxies, beacon) change which storage is authoritative.
- Storage slots are raw; mappings/arrays appear via keccak patterns, names are unknown.
- Guard detection is heuristic; absence of guards does NOT mean unguarded.
- Returndata checking is unknown unless explicitly shown in evidence.
- Precompiles (0x1..0x9) and common ERC20 transfer selectors may appear; do not over-claim.
Output MUST be valid JSON matching the provided schema.
- If you find nothing meaningful: set no_findings=true and findings=[].
- Otherwise: set no_findings=false and include findings with severity/category/title.
- Locations can be pc references like "pc:0x123" or selectors like "selector:0xa9059cbb".
Focus on: reentrancy, access control, upgradeability/proxy issues, signature validation,
accounting/invariant breaks, dangerous delegatecall, and untrusted external calls.
Be conservative: if evidence is weak, mark severity low/medium and explain why.
mcp startup: no servers
thinking
**Reading BYTECODE_BUNDLE.json**
exec
/bin/bash -lc ls in /tmp/codex-86npeb2q succeeded in 5ms:
BYTECODE_BUNDLE.json
codex-output-schema.json
exec
/bin/bash -lc 'cat BYTECODE_BUNDLE.json' in /tmp/codex-86npeb2q succeeded in 11ms:
{
"analysis_artifact": {
"address": "0x367ba7d034abf0b97bccdf07fd95217410c9dbe8",
"artifact_hash": "0xc67fdf84eaee7efdf3e172f1d56ca90619532781164a797ad694bc528fdb2089",
"artifact_json": {
"auth": {
"guards": [
{
"pc": 416,
"type": "msg_sender_eq_const",
"value": "0xffffffffffffffffffffffffffffffffffffffff"
},
{
"pc": 786,
"type": "msg_sender_eq_const",
"value": "0xffffffffffffffffffffffffffffffffffffffff"
}
]
},
"basic_blocks": [
thinking
**Investigating callsites and bytecode details**
I’m focusing on understanding the suspicious CALL instructions at pc 584 and 1098 by parsing the artifact’s opcode blocks, especially around pc 502–596. There’s complexity around how addresses and values like SELFBALANCE are computed, with guarding logic that might be degenerate or unreachable. The large JSON artifact might require using jq or Python to locate and analyze these callsites more precisely, since some parts seem truncated. I want to extract exact bytecode and control flow details to clarify the transfer and target behavior.
exec
/bin/bash -lc "python - <<'PY'
import json
import sys
from pathlib import Path
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# find blocks containing pc 1098 or callsite 1098
for b in blocks:
if b.get('type')=='basic_block':
start=b.get('start_pc')
end=b.get('end_pc')
if start is not None and end is not None and start<=1098<=end:
print('block',start,end,'id',b.get('id'))
print('opcodes',b.get('opcodes'))
break
else:
print('no block covering 1098')
# find block covering 584
for b in blocks:
if b.get('type')=='basic_block':
start=b.get('start_pc')
end=b.get('end_pc')
if start is not None and end is not None and start<=584<=end:
print('block',start,end,'id',b.get('id'))
print('opcodes',b.get('opcodes'))
break
PY" in /tmp/codex-86npeb2q succeeded in 28ms:
no block covering 1098
thinking
**Investigating missing block around address 1098**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=[b for b in j['analysis_artifact']['artifact_json']['basic_blocks'] if b.get('type')=='basic_block']
for b in blocks:
s,e=b.get('start_pc'),b.get('end_pc')
if s is not None and e is not None and s>=1070 and s<=1120:
print('block',s,e,'id',b.get('id'),'hash',b.get('hash'))
PY" in /tmp/codex-86npeb2q succeeded in 28ms:
thinking
**Investigating missing basic blocks**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
found=False
for b in blocks:
if b.get('start_pc')==1104:
print('found block',b)
found=True
break
print('count',len(blocks),'found',found)
PY" in /tmp/codex-86npeb2q succeeded in 76ms:
found block {'end_pc': 1114, 'hash': '0x8ebd94d87699ca2db03662333869d541106e8bbcebe87d832747cd139829d82e', 'opcodes': ['PUSH1', 'MLOAD', 'RETURNDATASIZE', 'PUSH1', 'OP_82', 'RETURNDATACOPY', 'RETURNDATASIZE', 'OP_81', 'REVERT'], 'start_pc': 1104}
count 127 found True
thinking
**Reevaluating block filtering criteria**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
for b in blocks:
s,e=b.get('start_pc'),b.get('end_pc')
if s is not None and e is not None and s<=1098<=e:
print('block',s,e,'opcodes',b.get('opcodes'))
break
else:
print('no block covering 1098')
for b in blocks:
s,e=b.get('start_pc'),b.get('end_pc')
if s is not None and e is not None and s<=584<=e:
print('block',s,e,'opcodes',b.get('opcodes'))
break
PY" in /tmp/codex-86npeb2q succeeded in 35ms:
block 1080 1103 opcodes ['JUMPDEST', 'PUSH1', 'OP_80', 'PUSH1', 'PUSH1', 'OP_84', 'MLOAD', 'PUSH1', 'OP_86', 'ADD', 'PUSH1', 'OP_88', 'GAS', 'CALL', 'OP_80', 'PUSH2', 'JUMPI']
block 502 596 opcodes ['JUMPDEST', 'PUSH1', 'PUSH1', 'OP_90', 'SLOAD', 'OP_90', 'PUSH2', 'EXP', 'OP_90', 'DIV', 'PUSH20', 'AND', 'PUSH20', 'AND', 'PUSH2', 'SELFBALANCE', 'OP_90', 'OP_81', 'ISZERO', 'MUL', 'OP_90', 'PUSH1', 'MLOAD', 'PUSH1', 'PUSH1', 'MLOAD', 'OP_80', 'OP_83', 'SUB', 'OP_81', 'OP_85', 'OP_88', 'OP_88', 'CALL', 'OP_93', 'POP', 'POP', 'POP', 'POP', 'ISZERO', 'OP_80', 'ISZERO', 'PUSH2', 'JUMPI']
thinking
**Analyzing external calls and access controls**
I'm interpreting low-level CALL patterns to see if value (ETH) is transferred and if the call is untrusted. I'm spotting a common pattern where if amount==0, it sends the full balance using SELFBALANCE and ISZERO logic. I'm also checking for reentrancy guard absence and suspicious access control that requires msg.sender to be an unlikely reserved address. Function selectors hint at ownership and withdrawal features, and timestamp checks suggest time-lock behavior. No upgradeability detected, so I’m focusing on external call safety and storage control of addresses. Next, I'll dig into writing/storage instructions to verify if owners or addresses can be set or changed.
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
for b in blocks:
ops=b.get('opcodes')
if ops and 'SSTORE' in ops:
print('block',b.get('start_pc'),b.get('end_pc'),ops)
PY" in /tmp/codex-86npeb2q succeeded in 26ms:
thinking
**Analyzing call targets and storage usage**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
for key in j['analysis_artifact']['artifact_json']:
if 'storage' in key:
print(key)
PY" in /tmp/codex-86npeb2q succeeded in 39ms:
storage
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
import pprint
pprint.pprint(j['analysis_artifact']['artifact_json']['storage'])
PY" in /tmp/codex-86npeb2q succeeded in 77ms:
{'keccak_count': 1,
'reads': 9,
'slots': [{'op': 'SLOAD',
'pc': 286,
'slot': '0x0000000000000000000000000000000000000000000000000000000000000000'},
{'op': 'SLOAD',
'pc': 292,
'slot': '0x0000000000000000000000000000000000000000000000000000000000000000'},
{'op': 'SLOAD',
'pc': 364,
'slot': '0x0000000000000000000000000000000000000000000000000000000000000000'},
{'op': 'SLOAD',
'pc': 508,
'slot': '0x0000000000000000000000000000000000000000000000000000000000000000'},
{'op': 'SLOAD',
'pc': 613,
'slot': '0x0000000000000000000000000000000000000000000000000000000000000000'},
{'op': 'SLOAD',
'pc': 627,
'slot': '0x0000000000000000000000000000000000000000000000000000000000000000'},
thinking
**Analyzing immutable state and access control**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
print(j['analysis_artifact']['artifact_json'].keys())
PY" in /tmp/codex-86npeb2q succeeded in 26ms:
dict_keys(['auth', 'basic_blocks', 'callsites', 'cfg_edges', 'cfg_hash', 'dispatcher_type', 'function_entries', 'functions', 'selectors', 'sinks', 'storage', 'version'])
thinking
**Reviewing callsites list**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
print(j['analysis_artifact']['artifact_json']['callsites'])
PY" in /tmp/codex-86npeb2q succeeded in 44ms:
[{'opcode': 'CALL', 'pc': 584, 'returndata_checked': None, 'target_expression_category': 'computed', 'value_expression_category': 'computed'}, {'opcode': 'CALL', 'pc': 1098, 'returndata_checked': None, 'target_expression_category': 'computed', 'value_expression_category': 'computed'}]
thinking
**Examining callsites and selectors**
exec
/bin/bash -lc "python - <<'PY'
import json, pprint
j=json.load(open('BYTECODE_BUNDLE.json'))
pprint.pprint(j['analysis_artifact']['artifact_json']['selectors'])
PY" in /tmp/codex-86npeb2q succeeded in 57ms:
['0x251c1aa3', '0x3ccfd60b', '0x8380edb7', '0x8da5cb5b', '0xa1db9782']
thinking
**Inspecting function entries**
exec
/bin/bash -lc "python - <<'PY'
import json, pprint
j=json.load(open('BYTECODE_BUNDLE.json'))
pprint.pprint(j['analysis_artifact']['artifact_json']['function_entries'][:20])
PY" in /tmp/codex-86npeb2q succeeded in 57ms:
[{'entry_pc': 1020253707, 'selector': '0x251c1aa3'},
{'entry_pc': 2206264759, 'selector': '0x3ccfd60b'},
{'entry_pc': 2376452955, 'selector': '0x8380edb7'},
{'entry_pc': 2715522946, 'selector': '0x8da5cb5b'},
{'entry_pc': None, 'selector': '0xa1db9782'}]
thinking
**Assessing entry_pc and dispatcher_type values**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
print(j['analysis_artifact']['artifact_json']['dispatcher_type'])
PY" in /tmp/codex-86npeb2q succeeded in 32ms:
selector_jump_table
thinking
**Analyzing potential contract vulnerabilities**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
edges=j['analysis_artifact']['artifact_json']['cfg_edges']
# edges are tuples? let's inspect few
print('edges sample',edges[:10])
# find incoming edges to block start 1080
incoming=[e for e in edges if e[1]==1080]
print('incoming to 1080',incoming)
# find outgoing from 1080
outgoing=[e for e in edges if e[0]==1080]
print('outgoing from 1080',outgoing)
PY" in /tmp/codex-86npeb2q exited 1 in 29ms:
edges sample [{'from': 'bb:0', 'to': 'bb:78', 'type': 'control_flow'}, {'from': 'bb:0', 'to': 'bb:13', 'type': 'control_flow_fallthrough'}, {'from': 'bb:13', 'to': 'bb:90', 'type': 'control_flow'}, {'from': 'bb:13', 'to': 'bb:30', 'type': 'control_flow_fallthrough'}, {'from': 'bb:30', 'to': 'bb:133', 'type': 'control_flow'}, {'from': 'bb:30', 'to': 'bb:41', 'type': 'control_flow_fallthrough'}, {'from': 'bb:41', 'to': 'bb:156', 'type': 'control_flow'}, {'from': 'bb:41', 'to': 'bb:52', 'type': 'control_flow_fallthrough'}, {'from': 'bb:52', 'to': 'bb:199', 'type': 'control_flow'}, {'from': 'bb:52', 'to': 'bb:63', 'type': 'control_flow_fallthrough'}]
Traceback (most recent call last):
File "<stdin>", line 7, in <module>
KeyError: 1
thinking
**Inspecting edge references and block IDs**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# build id map
id_map={}
for b in blocks:
bid=b.get('id')
if not bid:
bid=f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
# find block id containing call pc 1098
call_pc=1098
call_block=None
for b in blocks:
s,e=b.get('start_pc'),b.get('end_pc')
if s is not None and e is not None and s<=call_pc<=e:
call_block=b
break
print('call block',call_block)
call_id=None
if call_block:
call_id=call_block.get('id') or f\"bb:{call_block.get('start_pc')}\"
print('call_id',call_id)
# find incoming edges to call_id
edges=j['analysis_artifact']['artifact_json']['cfg_edges']
incoming=[e for e in edges if e['to']==call_id]
print('incoming count',len(incoming))
for e in incoming[:10]:
b=id_map.get(e['from'])
print('from',e['from'],'start',b.get('start_pc'),'end',b.get('end_pc'))
PY" in /tmp/codex-86npeb2q succeeded in 25ms:
call block {'end_pc': 1103, 'hash': '0xe3015edd169714b7335ceeb3f2c07515fe613b18f6f7c0c017556185b0cd9c76', 'opcodes': ['JUMPDEST', 'PUSH1', 'OP_80', 'PUSH1', 'PUSH1', 'OP_84', 'MLOAD', 'PUSH1', 'OP_86', 'ADD', 'PUSH1', 'OP_88', 'GAS', 'CALL', 'OP_80', 'PUSH2', 'JUMPI'], 'start_pc': 1080}
call_id bb:1080
incoming count 1
from bb:1004 start 1004 end 1074
thinking
**Inspecting opcodes for block 1004**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
for b in blocks:
s=b.get('start_pc')
if s==1004:
print(b)
break
PY" in /tmp/codex-86npeb2q succeeded in 27ms:
{'end_pc': 1074, 'hash': '0xfbf6db55d25b26744b8985113b4e865948bc0bb35e5a2ced2e23a937c5ab7d02', 'opcodes': ['JUMPDEST', 'PUSH1', 'MLOAD', 'PUSH1', 'OP_81', 'OP_83', 'SUB', 'SUB', 'OP_81', 'MSTORE', 'OP_90', 'PUSH1', 'MSTORE', 'OP_91', 'POP', 'PUSH1', 'SHL', 'PUSH1', 'OP_82', 'ADD', 'OP_80', 'MLOAD', 'PUSH28', 'OP_83', 'OP_81', 'OP_83', 'AND', 'OR', 'OP_83', 'MSTORE', 'POP', 'POP', 'POP', 'POP', 'PUSH2', 'JUMP'], 'start_pc': 1004}
thinking
**Inspecting calldata assembly**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# get id map
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
edges=j['analysis_artifact']['artifact_json']['cfg_edges']
# find incoming to bb:1004
incoming=[e for e in edges if e['to']=='bb:1004']
print('incoming to 1004',incoming)
for e in incoming:
b=id_map.get(e['from'])
print('from',e['from'],'start',b.get('start_pc'),'end',b.get('end_pc'),'ops',b.get('opcodes'))
PY" in /tmp/codex-86npeb2q succeeded in 25ms:
incoming to 1004 []
thinking
**Analyzing dispatcher-to-function edges**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# create map from bb id to start_pc
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
edges=j['analysis_artifact']['artifact_json']['cfg_edges']
# list edges from dispatcher blocks (start <=77 maybe)
for e in edges:
fr=id_map.get(e['from'])
to=id_map.get(e['to'])
if fr and fr.get('start_pc') in (13,30,41,52,63,74):
print('from',fr.get('start_pc'),'to',to.get('start_pc'), 'type',e['type'])
PY" in /tmp/codex-86npeb2q succeeded in 27ms:
from 13 to 90 type control_flow
from 13 to 30 type control_flow_fallthrough
from 30 to 133 type control_flow
from 30 to 41 type control_flow_fallthrough
from 41 to 156 type control_flow
from 41 to 52 type control_flow_fallthrough
from 52 to 199 type control_flow
from 52 to 63 type control_flow_fallthrough
from 63 to 242 type control_flow
from 63 to 74 type control_flow_fallthrough
from 74 to 85 type control_flow
thinking
**Tracing function entries and calls**
exec
/bin/bash -lc "python - <<'PY'
import json
from collections import defaultdict, deque
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id to block, id
def block_id(b):
return b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map={block_id(b):b for b in blocks}
# build adjacency
adj=defaultdict(list)
for e in j['analysis_artifact']['artifact_json']['cfg_edges']:
adj[e['from']].append(e['to'])
# find block ids containing call pc
call_pcs=[584,1098]
call_blocks={}
for pc in call_pcs:
for b in blocks:
s,e=b.get('start_pc'),b.get('end_pc')
if s is not None and e is not None and s<=pc<=e:
call_blocks[pc]=block_id(b)
break
print('call blocks',call_blocks)
# entry blocks from dispatcher
entry_starts=[90,133,156,199,242]
entry_ids=[f\"bb:{s}\" for s in entry_starts]
# BFS from each entry to see if reaches call blocks
for entry in entry_ids:
for pc,cb in call_blocks.items():
# BFS
q=deque([entry]); seen=set([entry]); found=False
while q and not found:
cur=q.popleft()
if cur==cb:
found=True; break
for nxt in adj.get(cur,[]):
if nxt not in seen:
seen.add(nxt); q.append(nxt)
if found:
print('entry',entry,'reaches call pc',pc)
PY" in /tmp/codex-86npeb2q succeeded in 36ms:
call blocks {584: 'bb:502', 1098: 'bb:1080'}
entry bb:133 reaches call pc 584
thinking
**Analyzing unreachable call connections**
exec
/bin/bash -lc "python - <<'PY'
import json
from collections import defaultdict
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
# build incoming counts
incoming=defaultdict(int)
for e in j['analysis_artifact']['artifact_json']['cfg_edges']:
incoming[e['to']]+=1
# list blocks with no incoming
no_in=[bid for bid in id_map if incoming[bid]==0]
# show some with start_pc >= 200
for bid in sorted(no_in, key=lambda x: id_map[x].get('start_pc') or 0)[:20]:
b=id_map[bid]
print(bid,'start',b.get('start_pc'),'end',b.get('end_pc'))
print('total no incoming',len(no_in))
# show if bb:1004 or bb:1080 are no incoming
for bid in ['bb:1004','bb:1080']:
print(bid,'incoming',incoming[bid])
PY" in /tmp/codex-86npeb2q succeeded in 25ms:
bb:0 start 0 end 12
bb:111 start 111 end 123
bb:124 start 124 end 132
bb:154 start 154 end 155
bb:177 start 177 end 189
bb:190 start 190 end 198
bb:220 start 220 end 232
bb:233 start 233 end 241
bb:276 start 276 end 280
bb:281 start 281 end 282
bb:349 start 349 end 357
bb:493 start 493 end 501
bb:719 start 719 end 727
bb:863 start 863 end 871
bb:949 start 949 end 952
bb:953 start 953 end 1003
bb:1004 start 1004 end 1074
bb:1075 start 1075 end 1079
bb:1227 start 1227 end 1235
bb:1261 start 1261 end 1266
thinking
**Analyzing subgraph with BFS**
exec
/bin/bash -lc "python - <<'PY'
import json
from collections import defaultdict, deque
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
adj=defaultdict(list)
for e in j['analysis_artifact']['artifact_json']['cfg_edges']:
adj[e['from']].append(e['to'])
# find call block id
call_id=None
for b in blocks:
s,e=b.get('start_pc'),b.get('end_pc')
if s is not None and e is not None and s<=1098<=e:
call_id=b.get('id') or f\"bb:{s}\"
break
print('call block',call_id)
# try entries with no incoming near 900-1000
entries=[bid for bid,b in id_map.items() if b.get('start_pc') in (949,953,1004,1075)]
print('entries',entries)
for entry in entries:
# BFS
q=deque([entry]); seen=set([entry]); found=False
while q and not found:
cur=q.popleft()
if cur==call_id:
found=True; break
for nxt in adj.get(cur,[]):
if nxt not in seen:
seen.add(nxt); q.append(nxt)
print(entry,'reaches call',found)
PY" in /tmp/codex-86npeb2q succeeded in 59ms:
call block bb:1080
entries ['bb:949', 'bb:953', 'bb:1004', 'bb:1075']
bb:949 reaches call False
bb:953 reaches call False
bb:1004 reaches call True
bb:1075 reaches call False
thinking
**Analyzing function entry and computed jumps**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
for s in [872,949,953,1004,1075,1080,1104]:
for b in blocks:
if b.get('start_pc')==s:
print('block',s,b.get('end_pc'))
print(b.get('opcodes'))
break
PY" in /tmp/codex-86npeb2q succeeded in 76ms:
block 872 948
['JUMPDEST', 'PUSH2', 'PUSH1', 'PUSH1', 'OP_90', 'SLOAD', 'OP_90', 'PUSH2', 'EXP', 'OP_90', 'DIV', 'PUSH20', 'AND', 'OP_82', 'OP_84', 'PUSH20', 'AND', 'PUSH2', 'OP_90', 'OP_92', 'OP_91', 'OP_90', 'PUSH4', 'AND', 'JUMP']
block 949 952
['JUMPDEST', 'POP', 'POP', 'JUMP']
block 953 1003
['JUMPDEST', 'PUSH2', 'OP_83', 'OP_84', 'PUSH20', 'AND', 'PUSH4', 'OP_85', 'OP_85', 'PUSH1', 'MLOAD', 'PUSH1', 'ADD', 'PUSH2', 'OP_92', 'OP_91', 'OP_90', 'PUSH2', 'JUMP']
block 1004 1074
['JUMPDEST', 'PUSH1', 'MLOAD', 'PUSH1', 'OP_81', 'OP_83', 'SUB', 'SUB', 'OP_81', 'MSTORE', 'OP_90', 'PUSH1', 'MSTORE', 'OP_91', 'POP', 'PUSH1', 'SHL', 'PUSH1', 'OP_82', 'ADD', 'OP_80', 'MLOAD', 'PUSH28', 'OP_83', 'OP_81', 'OP_83', 'AND', 'OR', 'OP_83', 'MSTORE', 'POP', 'POP', 'POP', 'POP', 'PUSH2', 'JUMP']
block 1075 1079
['JUMPDEST', 'POP', 'POP', 'POP', 'JUMP']
block 1080 1103
['JUMPDEST', 'PUSH1', 'OP_80', 'PUSH1', 'PUSH1', 'OP_84', 'MLOAD', 'PUSH1', 'OP_86', 'ADD', 'PUSH1', 'OP_88', 'GAS', 'CALL', 'OP_80', 'PUSH2', 'JUMPI']
block 1104 1114
['PUSH1', 'MLOAD', 'RETURNDATASIZE', 'PUSH1', 'OP_82', 'RETURNDATACOPY', 'RETURNDATASIZE', 'OP_81', 'REVERT']
thinking
**Analyzing jump destinations and access control**
exec
/bin/bash -lc "python - <<'PY'
import json
from collections import defaultdict, deque
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
adj=defaultdict(list)
for e in j['analysis_artifact']['artifact_json']['cfg_edges']:
adj[e['from']].append(e['to'])
# find block ids for guard pcs 416 and 786 (within blocks 358-443 and 728-813)
pcs=[416,786]
for pc in pcs:
guard_block=None
for b in blocks:
s,e=b.get('start_pc'),b.get('end_pc')
if s is not None and e is not None and s<=pc<=e:
guard_block=b
break
gid=guard_block.get('id') or f\"bb:{guard_block.get('start_pc')}\"
print('guard pc',pc,'block',gid)
# find which entry reaches this block
entry_starts=[90,133,156,199,242]
entry_ids=[f\"bb:{s}\" for s in entry_starts]
for entry in entry_ids:
q=deque([entry]); seen=set([entry]); found=False
while q and not found:
cur=q.popleft()
if cur==gid:
found=True; break
for nxt in adj.get(cur,[]):
if nxt not in seen:
seen.add(nxt); q.append(nxt)
if found:
print(' reachable from',entry)
PY" in /tmp/codex-86npeb2q succeeded in 69ms:
guard pc 416 block bb:358
reachable from bb:133
guard pc 786 block bb:728
thinking
**Analyzing function guard reachability**
exec
/bin/bash -lc "python - <<'PY'
import json
from collections import defaultdict, deque
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
adj=defaultdict(list)
for e in j['analysis_artifact']['artifact_json']['cfg_edges']:
adj[e['from']].append(e['to'])
# find block id for guard 728
for b in blocks:
s,e=b.get('start_pc'),b.get('end_pc')
if s is not None and e is not None and s<=786<=e:
guard_id=b.get('id') or f\"bb:{s}\"
break
print('guard id',guard_id)
# check reachability from bb:953 or bb:1004 to guard
for entry in ['bb:953','bb:1004','bb:872']:
q=deque([entry]); seen=set([entry]); found=False
while q and not found:
cur=q.popleft()
if cur==guard_id:
found=True; break
for nxt in adj.get(cur,[]):
if nxt not in seen:
seen.add(nxt); q.append(nxt)
print(entry,'reaches guard',found)
# also check if guard leads to call 1098
# find call block id
call_id=None
for b in blocks:
s,e=b.get('start_pc'),b.get('end_pc')
if s is not None and e is not None and s<=1098<=e:
call_id=b.get('id') or f\"bb:{s}\"; break
for entry in ['bb:728','bb:872','bb:953','bb:1004']:
q=deque([entry]); seen=set([entry]); found=False
while q and not found:
cur=q.popleft()
if cur==call_id:
found=True; break
for nxt in adj.get(cur,[]):
if nxt not in seen:
seen.add(nxt); q.append(nxt)
print(entry,'reaches call',found)
PY" in /tmp/codex-86npeb2q succeeded in 62ms:
guard id bb:728
bb:953 reaches guard False
bb:1004 reaches guard False
bb:872 reaches guard False
bb:728 reaches call False
bb:872 reaches call False
bb:953 reaches call False
bb:1004 reaches call True
thinking
**Analyzing control flow and access checks**
I'm examining the control flow graph to see if the call at block 1098 is properly guarded or publicly accessible. The CFG might miss edges due to dynamic jumps, making the call potentially unguarded and risky. I’m checking related blocks for access control, like inspections for msg.sender checks and timestamp guards, and trying to clarify if the call target is constant or computed, which affects security risk.
exec
/bin/bash -lc "python - <<'PY'
import json
from collections import defaultdict, deque
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
adj=defaultdict(list)
for e in j['analysis_artifact']['artifact_json']['cfg_edges']:
adj[e['from']].append(e['to'])
# find blocks with TIMESTAMP
timestamp_blocks=[]
for b in blocks:
if b.get('opcodes') and 'TIMESTAMP' in b['opcodes']:
timestamp_blocks.append(b)
print('timestamp blocks:',[(b.get('start_pc'),b.get('end_pc')) for b in timestamp_blocks])
# check reachability from entries
entry_starts=[90,133,156,199,242]
entry_ids=[f\"bb:{s}\" for s in entry_starts]
for b in timestamp_blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
print('block',bid,'start',b.get('start_pc'))
for entry in entry_ids:
q=deque([entry]); seen=set([entry]); found=False
while q and not found:
cur=q.popleft()
if cur==bid:
found=True; break
for nxt in adj.get(cur,[]):
if nxt not in seen:
seen.add(nxt); q.append(nxt)
if found:
print(' reachable from',entry)
PY" in /tmp/codex-86npeb2q succeeded in 79ms:
timestamp blocks: [(289, 299), (609, 620), (659, 669)]
block bb:289 start 289
reachable from bb:133
block bb:609 start 609
reachable from bb:156
block bb:659 start 659
thinking
**Analyzing function reachability and dispatch structure**
exec
/bin/bash -lc "python - <<'PY'
import json
from collections import defaultdict, deque
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
adj=defaultdict(list)
for e in j['analysis_artifact']['artifact_json']['cfg_edges']:
adj[e['from']].append(e['to'])
entry_ids=[f\"bb:{s}\" for s in [90,133,156,199,242]]
for entry in entry_ids:
q=deque([entry]); seen=set([entry]); found=False
while q and not found:
cur=q.popleft()
if cur=='bb:1004':
found=True; break
for nxt in adj.get(cur,[]):
if nxt not in seen:
seen.add(nxt); q.append(nxt)
print(entry,'reaches bb:1004',found)
PY" in /tmp/codex-86npeb2q succeeded in 42ms:
bb:90 reaches bb:1004 False
bb:133 reaches bb:1004 False
bb:156 reaches bb:1004 False
bb:199 reaches bb:1004 False
bb:242 reaches bb:1004 False
thinking
**Investigating unreachable call possibility**
exec
/bin/bash -lc "python - <<'PY'
import json
from collections import defaultdict, deque
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
adj=defaultdict(list)
for e in j['analysis_artifact']['artifact_json']['cfg_edges']:
adj[e['from']].append(e['to'])
entry_ids=[f\"bb:{s}\" for s in [90,133,156,199,242]]
for target in ['bb:872','bb:953','bb:1004','bb:1080']:
print('target',target)
for entry in entry_ids:
q=deque([entry]); seen=set([entry]); found=False
while q and not found:
cur=q.popleft()
if cur==target:
found=True; break
for nxt in adj.get(cur,[]):
if nxt not in seen:
seen.add(nxt); q.append(nxt)
if found:
print(' reachable from',entry)
PY" in /tmp/codex-86npeb2q succeeded in 27ms:
target bb:872
target bb:953
target bb:1004
target bb:1080
thinking
**Analyzing potential external call risks**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
for s in [90,133,156,199,242]:
for b in blocks:
if b.get('start_pc')==s:
print('entry',s,'block',b)
break
PY" in /tmp/codex-86npeb2q succeeded in 24ms:
entry 90 block {'end_pc': 97, 'hash': '0xf3390630a44395cb6955e033b71fc24f358093e47b3892217eebdc03dd9d7ecb', 'opcodes': ['JUMPDEST', 'CALLVALUE', 'OP_80', 'ISZERO', 'PUSH2', 'JUMPI'], 'start_pc': 90}
entry 133 block {'end_pc': 140, 'hash': '0xfc809ed105a16fcfa37b0c3848197ffd55d12f8a97d2bb87625519634c594f41', 'opcodes': ['JUMPDEST', 'CALLVALUE', 'OP_80', 'ISZERO', 'PUSH2', 'JUMPI'], 'start_pc': 133}
entry 156 block {'end_pc': 163, 'hash': '0x0042ba403c0ea2b444d07a830d935b4b2a8d19b20eeaa30630cf09bdd254e24a', 'opcodes': ['JUMPDEST', 'CALLVALUE', 'OP_80', 'ISZERO', 'PUSH2', 'JUMPI'], 'start_pc': 156}
entry 199 block {'end_pc': 206, 'hash': '0xb8115f9d429165244f69142578786d138fd0e2793f347bf1d424cefa37344cb1', 'opcodes': ['JUMPDEST', 'CALLVALUE', 'OP_80', 'ISZERO', 'PUSH2', 'JUMPI'], 'start_pc': 199}
entry 242 block {'end_pc': 249, 'hash': '0x21618fb536719e05c47be2195cf35203989f4dedcffb885cc2bfb85f00a0b8dc', 'opcodes': ['JUMPDEST', 'CALLVALUE', 'OP_80', 'ISZERO', 'PUSH2', 'JUMPI'], 'start_pc': 242}
thinking
**Analyzing entry jump targets**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
edges=j['analysis_artifact']['artifact_json']['cfg_edges']
for entry in [90,133,156,199,242]:
bid=f\"bb:{entry}\"
outs=[e for e in edges if e['from']==bid]
print('entry',entry,'outs',[(id_map[o['to']].get('start_pc'),o['type']) for o in outs])
PY" in /tmp/codex-86npeb2q succeeded in 40ms:
entry 90 outs [(102, 'control_flow'), (98, 'control_flow_fallthrough')]
entry 133 outs [(145, 'control_flow'), (141, 'control_flow_fallthrough')]
entry 156 outs [(168, 'control_flow'), (164, 'control_flow_fallthrough')]
entry 199 outs [(211, 'control_flow'), (207, 'control_flow_fallthrough')]
entry 242 outs [(254, 'control_flow'), (250, 'control_flow_fallthrough')]
thinking
**Tracing entries through blocks**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
for s in [102,145,168,211,254]:
for b in blocks:
if b.get('start_pc')==s:
print('block',s,b.get('end_pc'),b.get('opcodes'))
break
PY" in /tmp/codex-86npeb2q succeeded in 38ms:
block 102 110 ['JUMPDEST', 'POP', 'PUSH2', 'PUSH2', 'JUMP']
block 145 153 ['JUMPDEST', 'POP', 'PUSH2', 'PUSH2', 'JUMP']
block 168 176 ['JUMPDEST', 'POP', 'PUSH2', 'PUSH2', 'JUMP']
block 211 219 ['JUMPDEST', 'POP', 'PUSH2', 'PUSH2', 'JUMP']
block 254 275 ['JUMPDEST', 'POP', 'PUSH2', 'PUSH1', 'OP_80', 'CALLDATASIZE', 'SUB', 'OP_81', 'ADD', 'OP_90', 'PUSH2', 'OP_91', 'OP_90', 'PUSH2', 'JUMP']
thinking
**Analyzing jump instructions and destinations**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
edges=j['analysis_artifact']['artifact_json']['cfg_edges']
for s in [102,145,168,211,254]:
bid=f\"bb:{s}\"
outs=[e for e in edges if e['from']==bid]
print('from',s,'outs',[(id_map[o['to']].get('start_pc'),o['type']) for o in outs])
PY" in /tmp/codex-86npeb2q succeeded in 27ms:
from 102 outs [(283, 'control_flow')]
from 145 outs [(289, 'control_flow')]
from 168 outs [(609, 'control_flow')]
from 211 outs [(621, 'control_flow')]
from 254 outs [(1551, 'control_flow')]
thinking
**Tracing function call paths**
exec
/bin/bash -lc "python - <<'PY'
import json
from collections import defaultdict, deque
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
# map id
id_map={}
for b in blocks:
bid=b.get('id') or f\"bb:{b.get('start_pc')}\"
id_map[bid]=b
adj=defaultdict(list)
for e in j['analysis_artifact']['artifact_json']['cfg_edges']:
adj[e['from']].append(e['to'])
# find call block id
call_id=None
for b in blocks:
s,e=b.get('start_pc'),b.get('end_pc')
if s is not None and e is not None and s<=1098<=e:
call_id=b.get('id') or f\"bb:{s}\"; break
# BFS from bb:1551
start='bb:1551'
q=deque([start]); seen=set([start]); found=False
while q and not found:
cur=q.popleft()
if cur==call_id:
found=True; break
for nxt in adj.get(cur,[]):
if nxt not in seen:
seen.add(nxt); q.append(nxt)
print('bb:1551 reaches call',found)
PY" in /tmp/codex-86npeb2q succeeded in 27ms:
bb:1551 reaches call False
thinking
**Investigating unreachable call and jump tables**
exec
/bin/bash -lc "python - <<'PY'
import json
j=json.load(open('BYTECODE_BUNDLE.json'))
blocks=j['analysis_artifact']['artifact_json']['basic_blocks']
for s in [1551,1566,1574,1588,1605,1615,1632,1673,1686,1697,1708,1733,1740,1781,1794,1805,1816,1841,1848,1857,1863,1884,1897,1904,1925,1932]:
for b in blocks:
if b.get('start_pc')==s:
print('block',s,'end',b.get('end_pc'), 'ops', b.get('opcodes'))
break
PY" in /tmp/codex-86npeb2q succeeded in 42ms:
block 1551 end 1565 ops ['JUMPDEST', 'PUSH1', 'OP_80', 'PUSH1', 'OP_83', 'OP_85', 'SUB', 'SLT', 'ISZERO', 'PUSH2', 'JUMPI']
block 1566 end 1572 ops ['PUSH2', 'PUSH2', 'JUMP']
block 1574 end 1587 ops ['JUMPDEST', 'PUSH1', 'PUSH2', 'OP_85', 'OP_82', 'OP_86', 'ADD', 'PUSH2', 'JUMP']
block 1588 end 1604 ops ['JUMPDEST', 'OP_92', 'POP', 'POP', 'PUSH1', 'PUSH2', 'OP_85', 'OP_82', 'OP_86', 'ADD', 'PUSH2', 'JUMP']
block 1605 end 1614 ops ['JUMPDEST', 'OP_91', 'POP', 'POP', 'OP_92', 'POP', 'OP_92', 'OP_90', 'POP', 'JUMP']
block 1615 end 1631 ops ['JUMPDEST', 'PUSH1', 'OP_82', 'OP_82', 'MSTORE', 'PUSH1', 'OP_82', 'ADD', 'OP_90', 'POP', 'OP_92', 'OP_91', 'POP', 'POP', 'JUMP']
block 1632 end 1672 ops ['JUMPDEST', 'PUSH32', 'PUSH1', 'OP_82', 'ADD', 'MSTORE', 'POP', 'JUMP']
block 1673 end 1685 ops ['JUMPDEST', 'PUSH1', 'PUSH2', 'PUSH1', 'OP_83', 'PUSH2', 'JUMP']
block 1686 end 1696 ops ['JUMPDEST', 'OP_91', 'POP', 'PUSH2', 'OP_82', 'PUSH2', 'JUMP']
block 1697 end 1707 ops ['JUMPDEST', 'PUSH1', 'OP_82', 'ADD', 'OP_90', 'POP', 'OP_91', 'OP_90', 'POP', 'JUMP']
block 1708 end 1732 ops ['JUMPDEST', 'PUSH1', 'PUSH1', 'OP_82', 'ADD', 'OP_90', 'POP', 'OP_81', 'OP_81', 'SUB', 'PUSH1', 'OP_83', 'ADD', 'MSTORE', 'PUSH2', 'OP_81', 'PUSH2', 'JUMP']
block 1733 end 1739 ops ['JUMPDEST', 'OP_90', 'POP', 'OP_91', 'OP_90', 'POP', 'JUMP']
block 1740 end 1780 ops ['JUMPDEST', 'PUSH32', 'PUSH1', 'OP_82', 'ADD', 'MSTORE', 'POP', 'JUMP']
block 1781 end 1793 ops ['JUMPDEST', 'PUSH1', 'PUSH2', 'PUSH1', 'OP_83', 'PUSH2', 'JUMP']
block 1794 end 1804 ops ['JUMPDEST', 'OP_91', 'POP', 'PUSH2', 'OP_82', 'PUSH2', 'JUMP']
block 1805 end 1815 ops ['JUMPDEST', 'PUSH1', 'OP_82', 'ADD', 'OP_90', 'POP', 'OP_91', 'OP_90', 'POP', 'JUMP']
block 1816 end 1840 ops ['JUMPDEST', 'PUSH1', 'PUSH1', 'OP_82', 'ADD', 'OP_90', 'POP', 'OP_81', 'OP_81', 'SUB', 'PUSH1', 'OP_83', 'ADD', 'MSTORE', 'PUSH2', 'OP_81', 'PUSH2', 'JUMP']
block 1841 end 1847 ops ['JUMPDEST', 'OP_90', 'POP', 'OP_91', 'OP_90', 'POP', 'JUMP']
block 1848 end 1856 ops ['JUMPDEST', 'PUSH2', 'OP_81', 'PUSH2', 'JUMP']
block 1857 end 1862 ops ['JUMPDEST', 'OP_82', 'MSTORE', 'POP', 'POP', 'JUMP']
thinking
**Summarizing bytecode security findings**
This analysis inspects two low-level CALLs with computed targets and values in the bytecode, highlighting uncertainty about reachability and access control. One call seems owner/time-locked and the other potentially unguarded, allowing arbitrary external calls with ETH transfer if reachable. I’m weighing severity as low to medium due to incomplete control-flow data, noting the lack of explicit caller checks near the second callsite and absence of delegatecall or storage writes, which limits risks. I'll consolidate these points into a JSON finding emphasizing potential unguarded calls, necessary guard checks, and uncertainty around dynamic jump paths.
codex
{"no_findings":false,"summary":"Two low-level CALL sites exist with computed target/value; one appears owner/time-locked, while the reachability/guarding of the pc:0x44a CALL is unclear from the bytecode graph and could allow arbitrary external calls if exposed.","findings":[{"severity":"medium","category":"access_control","title":"Potentially unguarded arbitrary CALL with ETH value (reachability unclear)","exploit_scenario":"If the code path containing the CALL at pc:0x44a (1098) is reachable from a public selector and its target/value are derived from calldata or other user-controlled inputs, any caller could trigger arbitrary external calls (and potentially transfer ETH) from this contract. This could allow draining ETH, token approvals/transfers, or interaction with attacker-controlled contracts. However, the control-flow graph does not clearly link this block to the dispatcher, and one dynamic trace observed a constant target (self), so actual exploitability is uncertain.","recommendation":"Confirm whether the pc:0x44a call path is reachable from any external selector and whether its target/value are user-controlled. If reachable, add explicit access control (owner/time-lock/allowlist) and validate target/value. If the call is intended to be a self-call or a fixed target, hardcode it or add assertions to prevent user-controlled targets.","evidence":"Bytecode analysis shows CALL at pc 1098 with target/value classified as computed (`callsites`), and no CALLER-based guard appears in nearby blocks (pc 953/1004/1080). Detector flagged both \"Untrusted CALL target/value reachable\" and \"ETH value transfer possible\" for this callsite; validation trace observed a constant target (self), so control is uncertain.","locations":["pc:0x44a","pc:0x3b8","pc:0x3ec"]}]}
tokens used
104,768
{
"codex": {
"ran_at": "2026-01-11T06:29:05.070471+00:00",
"returncode": 0
},
"project": {
"mode": "bytecode",
"bytecode_length": 3972,
"bytecode_truncated": false,
"capability_graph_present": true,
"analysis_artifact_present": true
},
"summary": "Two low-level CALL sites exist with computed target/value; one appears owner/time-locked, while the reachability/guarding of the pc:0x44a CALL is unclear from the bytecode graph and could allow arbitrary external calls if exposed.",
"raw_final": {
"summary": "Two low-level CALL sites exist with computed target/value; one appears owner/time-locked, while the reachability/guarding of the pc:0x44a CALL is unclear from the bytecode graph and could allow arbitrary external calls if exposed.",
"findings": [
{
"title": "Potentially unguarded arbitrary CALL with ETH value (reachability unclear)",
"category": "access_control",
"evidence": "Bytecode analysis shows CALL at pc 1098 with target/value classified as computed (`callsites`), and no CALLER-based guard appears in nearby blocks (pc 953/1004/1080). Detector flagged both \"Untrusted CALL target/value reachable\" and \"ETH value transfer possible\" for this callsite; validation trace observed a constant target (self), so control is uncertain.",
"severity": "medium",
"locations": [
"pc:0x44a",
"pc:0x3b8",
"pc:0x3ec"
],
"recommendation": "Confirm whether the pc:0x44a call path is reachable from any external selector and whether its target/value are user-controlled. If reachable, add explicit access control (owner/time-lock/allowlist) and validate target/value. If the call is intended to be a self-call or a fixed target, hardcode it or add assertions to prevent user-controlled targets.",
"exploit_scenario": "If the code path containing the CALL at pc:0x44a (1098) is reachable from a public selector and its target/value are derived from calldata or other user-controlled inputs, any caller could trigger arbitrary external calls (and potentially transfer ETH) from this contract. This could allow draining ETH, token approvals/transfers, or interaction with attacker-controlled contracts. However, the control-flow graph does not clearly link this block to the dispatcher, and one dynamic trace observed a constant target (self), so actual exploitability is uncertain."
}
],
"no_findings": false
},
"no_findings": false,
"schema_version": 1
}
00000000: PUSH1 0x80 00000002: PUSH1 0x40 00000004: MSTORE 00000005: PUSH1 0x04 00000007: CALLDATASIZE 00000008: LT 00000009: PUSH2 0x004e 0000000c: JUMPI 0000000d: PUSH1 0x00 0000000f: CALLDATALOAD 00000010: PUSH1 0xe0 00000012: SHR 00000013: DUP1 00000014: PUSH4 0x251c1aa3 00000019: EQ 0000001a: PUSH2 0x005a 0000001d: JUMPI 0000001e: DUP1 0000001f: PUSH4 0x3ccfd60b 00000024: EQ 00000025: PUSH2 0x0085 00000028: JUMPI 00000029: DUP1 0000002a: PUSH4 0x8380edb7 0000002f: EQ 00000030: PUSH2 0x009c 00000033: JUMPI 00000034: DUP1 00000035: PUSH4 0x8da5cb5b 0000003a: EQ 0000003b: PUSH2 0x00c7 0000003e: JUMPI 0000003f: DUP1 00000040: PUSH4 0xa1db9782 00000045: EQ 00000046: PUSH2 0x00f2 00000049: JUMPI 0000004a: PUSH2 0x0055 0000004d: JUMP 0000004e: JUMPDEST 0000004f: CALLDATASIZE 00000050: PUSH2 0x0055 00000053: JUMPI 00000054: STOP 00000055: JUMPDEST 00000056: PUSH1 0x00 00000058: DUP1 00000059: REVERT 0000005a: JUMPDEST 0000005b: CALLVALUE 0000005c: DUP1 0000005d: ISZERO 0000005e: PUSH2 0x0066 00000061: JUMPI 00000062: PUSH1 0x00 00000064: DUP1 00000065: REVERT 00000066: JUMPDEST 00000067: POP 00000068: PUSH2 0x006f 0000006b: PUSH2 0x011b 0000006e: JUMP 0000006f: JUMPDEST 00000070: PUSH1 0x40 00000072: MLOAD 00000073: PUSH2 0x007c 00000076: SWAP2 00000077: SWAP1 00000078: PUSH2 0x04f3 0000007b: JUMP 0000007c: JUMPDEST 0000007d: PUSH1 0x40 0000007f: MLOAD 00000080: DUP1 00000081: SWAP2 00000082: SUB 00000083: SWAP1 00000084: RETURN 00000085: JUMPDEST 00000086: CALLVALUE 00000087: DUP1 00000088: ISZERO 00000089: PUSH2 0x0091 0000008c: JUMPI 0000008d: PUSH1 0x00 0000008f: DUP1 00000090: REVERT 00000091: JUMPDEST 00000092: POP 00000093: PUSH2 0x009a 00000096: PUSH2 0x0121 00000099: JUMP 0000009a: JUMPDEST 0000009b: STOP 0000009c: JUMPDEST 0000009d: CALLVALUE 0000009e: DUP1 0000009f: ISZERO 000000a0: PUSH2 0x00a8 000000a3: JUMPI 000000a4: PUSH1 0x00 000000a6: DUP1 000000a7: REVERT 000000a8: JUMPDEST 000000a9: POP 000000aa: PUSH2 0x00b1 000000ad: PUSH2 0x0261 000000b0: JUMP 000000b1: JUMPDEST 000000b2: PUSH1 0x40 000000b4: MLOAD 000000b5: PUSH2 0x00be 000000b8: SWAP2 000000b9: SWAP1 000000ba: PUSH2 0x0529 000000bd: JUMP 000000be: JUMPDEST 000000bf: PUSH1 0x40 000000c1: MLOAD 000000c2: DUP1 000000c3: SWAP2 000000c4: SUB 000000c5: SWAP1 000000c6: RETURN 000000c7: JUMPDEST 000000c8: CALLVALUE 000000c9: DUP1 000000ca: ISZERO 000000cb: PUSH2 0x00d3 000000ce: JUMPI 000000cf: PUSH1 0x00 000000d1: DUP1 000000d2: REVERT 000000d3: JUMPDEST 000000d4: POP 000000d5: PUSH2 0x00dc 000000d8: PUSH2 0x026d 000000db: JUMP 000000dc: JUMPDEST 000000dd: PUSH1 0x40 000000df: MLOAD 000000e0: PUSH2 0x00e9 000000e3: SWAP2 000000e4: SWAP1 000000e5: PUSH2 0x0585 000000e8: JUMP 000000e9: JUMPDEST 000000ea: PUSH1 0x40 000000ec: MLOAD 000000ed: DUP1 000000ee: SWAP2 000000ef: SUB 000000f0: SWAP1 000000f1: RETURN 000000f2: JUMPDEST 000000f3: CALLVALUE 000000f4: DUP1 000000f5: ISZERO 000000f6: PUSH2 0x00fe 000000f9: JUMPI 000000fa: PUSH1 0x00 000000fc: DUP1 000000fd: REVERT 000000fe: JUMPDEST 000000ff: POP 00000100: PUSH2 0x0119 00000103: PUSH1 0x04 00000105: DUP1 00000106: CALLDATASIZE 00000107: SUB 00000108: DUP2 00000109: ADD 0000010a: SWAP1 0000010b: PUSH2 0x0114 0000010e: SWAP2 0000010f: SWAP1 00000110: PUSH2 0x060f 00000113: JUMP 00000114: JUMPDEST 00000115: PUSH2 0x0293 00000118: JUMP 00000119: JUMPDEST 0000011a: STOP 0000011b: JUMPDEST 0000011c: PUSH1 0x00 0000011e: SLOAD 0000011f: DUP2 00000120: JUMP 00000121: JUMPDEST 00000122: PUSH1 0x00 00000124: SLOAD 00000125: TIMESTAMP 00000126: LT 00000127: ISZERO 00000128: PUSH2 0x0166 0000012b: JUMPI 0000012c: PUSH1 0x40 0000012e: MLOAD 0000012f: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 00000150: DUP2 00000151: MSTORE 00000152: PUSH1 0x04 00000154: ADD 00000155: PUSH2 0x015d 00000158: SWAP1 00000159: PUSH2 0x06ac 0000015c: JUMP 0000015d: JUMPDEST 0000015e: PUSH1 0x40 00000160: MLOAD 00000161: DUP1 00000162: SWAP2 00000163: SUB 00000164: SWAP1 00000165: REVERT 00000166: JUMPDEST 00000167: PUSH1 0x01 00000169: PUSH1 0x00 0000016b: SWAP1 0000016c: SLOAD 0000016d: SWAP1 0000016e: PUSH2 0x0100 00000171: EXP 00000172: SWAP1 00000173: DIV 00000174: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000189: AND 0000018a: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 0000019f: AND 000001a0: CALLER 000001a1: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 000001b6: AND 000001b7: EQ 000001b8: PUSH2 0x01f6 000001bb: JUMPI 000001bc: PUSH1 0x40 000001be: MLOAD 000001bf: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 000001e0: DUP2 000001e1: MSTORE 000001e2: PUSH1 0x04 000001e4: ADD 000001e5: PUSH2 0x01ed 000001e8: SWAP1 000001e9: PUSH2 0x0718 000001ec: JUMP 000001ed: JUMPDEST 000001ee: PUSH1 0x40 000001f0: MLOAD 000001f1: DUP1 000001f2: SWAP2 000001f3: SUB 000001f4: SWAP1 000001f5: REVERT 000001f6: JUMPDEST 000001f7: PUSH1 0x01 000001f9: PUSH1 0x00 000001fb: SWAP1 000001fc: SLOAD 000001fd: SWAP1 000001fe: PUSH2 0x0100 00000201: EXP 00000202: SWAP1 00000203: DIV 00000204: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000219: AND 0000021a: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 0000022f: AND 00000230: PUSH2 0x08fc 00000233: SELFBALANCE 00000234: SWAP1 00000235: DUP2 00000236: ISZERO 00000237: MUL 00000238: SWAP1 00000239: PUSH1 0x40 0000023b: MLOAD 0000023c: PUSH1 0x00 0000023e: PUSH1 0x40 00000240: MLOAD 00000241: DUP1 00000242: DUP4 00000243: SUB 00000244: DUP2 00000245: DUP6 00000246: DUP9 00000247: DUP9 00000248: CALL 00000249: SWAP4 0000024a: POP 0000024b: POP 0000024c: POP 0000024d: POP 0000024e: ISZERO 0000024f: DUP1 00000250: ISZERO 00000251: PUSH2 0x025e 00000254: JUMPI 00000255: RETURNDATASIZE 00000256: PUSH1 0x00 00000258: DUP1 00000259: RETURNDATACOPY 0000025a: RETURNDATASIZE 0000025b: PUSH1 0x00 0000025d: REVERT 0000025e: JUMPDEST 0000025f: POP 00000260: JUMP 00000261: JUMPDEST 00000262: PUSH1 0x00 00000264: DUP1 00000265: SLOAD 00000266: TIMESTAMP 00000267: LT 00000268: ISZERO 00000269: SWAP1 0000026a: POP 0000026b: SWAP1 0000026c: JUMP 0000026d: JUMPDEST 0000026e: PUSH1 0x01 00000270: PUSH1 0x00 00000272: SWAP1 00000273: SLOAD 00000274: SWAP1 00000275: PUSH2 0x0100 00000278: EXP 00000279: SWAP1 0000027a: DIV 0000027b: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000290: AND 00000291: DUP2 00000292: JUMP 00000293: JUMPDEST 00000294: PUSH1 0x00 00000296: SLOAD 00000297: TIMESTAMP 00000298: LT 00000299: ISZERO 0000029a: PUSH2 0x02d8 0000029d: JUMPI 0000029e: PUSH1 0x40 000002a0: MLOAD 000002a1: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 000002c2: DUP2 000002c3: MSTORE 000002c4: PUSH1 0x04 000002c6: ADD 000002c7: PUSH2 0x02cf 000002ca: SWAP1 000002cb: PUSH2 0x06ac 000002ce: JUMP 000002cf: JUMPDEST 000002d0: PUSH1 0x40 000002d2: MLOAD 000002d3: DUP1 000002d4: SWAP2 000002d5: SUB 000002d6: SWAP1 000002d7: REVERT 000002d8: JUMPDEST 000002d9: PUSH1 0x01 000002db: PUSH1 0x00 000002dd: SWAP1 000002de: SLOAD 000002df: SWAP1 000002e0: PUSH2 0x0100 000002e3: EXP 000002e4: SWAP1 000002e5: DIV 000002e6: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 000002fb: AND 000002fc: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000311: AND 00000312: CALLER 00000313: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 00000328: AND 00000329: EQ 0000032a: PUSH2 0x0368 0000032d: JUMPI 0000032e: PUSH1 0x40 00000330: MLOAD 00000331: PUSH32 0x08c379a000000000000000000000000000000000000000000000000000000000 00000352: DUP2 00000353: MSTORE 00000354: PUSH1 0x04 00000356: ADD 00000357: PUSH2 0x035f 0000035a: SWAP1 0000035b: PUSH2 0x0718 0000035e: JUMP 0000035f: JUMPDEST 00000360: PUSH1 0x40 00000362: MLOAD 00000363: DUP1 00000364: SWAP2 00000365: SUB 00000366: SWAP1 00000367: REVERT 00000368: JUMPDEST 00000369: PUSH2 0x03b5 0000036c: PUSH1 0x01 0000036e: PUSH1 0x00 00000370: SWAP1 00000371: SLOAD 00000372: SWAP1 00000373: PUSH2 0x0100 00000376: EXP 00000377: SWAP1 00000378: DIV 00000379: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 0000038e: AND 0000038f: DUP3 00000390: DUP5 00000391: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 000003a6: AND 000003a7: PUSH2 0x03b9 000003aa: SWAP1 000003ab: SWAP3 000003ac: SWAP2 000003ad: SWAP1 000003ae: PUSH4 0xffffffff 000003b3: AND 000003b4: JUMP 000003b5: JUMPDEST 000003b6: POP 000003b7: POP 000003b8: JUMP 000003b9: JUMPDEST 000003ba: PUSH2 0x0433 000003bd: DUP4 000003be: DUP5 000003bf: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 000003d4: AND 000003d5: PUSH4 0xa9059cbb 000003da: DUP6 000003db: DUP6 000003dc: PUSH1 0x40 000003de: MLOAD 000003df: PUSH1 0x24 000003e1: ADD 000003e2: PUSH2 0x03ec 000003e5: SWAP3 000003e6: SWAP2 000003e7: SWAP1 000003e8: PUSH2 0x0747 000003eb: JUMP 000003ec: JUMPDEST 000003ed: PUSH1 0x40 000003ef: MLOAD 000003f0: PUSH1 0x20 000003f2: DUP2 000003f3: DUP4 000003f4: SUB 000003f5: SUB 000003f6: DUP2 000003f7: MSTORE 000003f8: SWAP1 000003f9: PUSH1 0x40 000003fb: MSTORE 000003fc: SWAP2 000003fd: POP 000003fe: PUSH1 0xe0 00000400: SHL 00000401: PUSH1 0x20 00000403: DUP3 00000404: ADD 00000405: DUP1 00000406: MLOAD 00000407: PUSH28 0xffffffffffffffffffffffffffffffffffffffffffffffffffffffff 00000424: DUP4 00000425: DUP2 00000426: DUP4 00000427: AND 00000428: OR 00000429: DUP4 0000042a: MSTORE 0000042b: POP 0000042c: POP 0000042d: POP 0000042e: POP 0000042f: PUSH2 0x0438 00000432: JUMP 00000433: JUMPDEST 00000434: POP 00000435: POP 00000436: POP 00000437: JUMP 00000438: JUMPDEST 00000439: PUSH1 0x00 0000043b: DUP1 0000043c: PUSH1 0x20 0000043e: PUSH1 0x00 00000440: DUP5 00000441: MLOAD 00000442: PUSH1 0x20 00000444: DUP7 00000445: ADD 00000446: PUSH1 0x00 00000448: DUP9 00000449: GAS 0000044a: CALL 0000044b: DUP1 0000044c: PUSH2 0x045b 0000044f: JUMPI 00000450: PUSH1 0x40 00000452: MLOAD 00000453: RETURNDATASIZE 00000454: PUSH1 0x00 00000456: DUP3 00000457: RETURNDATACOPY 00000458: RETURNDATASIZE 00000459: DUP2 0000045a: REVERT 0000045b: JUMPDEST 0000045c: RETURNDATASIZE 0000045d: SWAP3 0000045e: POP 0000045f: PUSH1 0x00 00000461: MLOAD 00000462: SWAP2 00000463: POP 00000464: POP 00000465: PUSH1 0x00 00000467: DUP3 00000468: EQ 00000469: PUSH2 0x0476 0000046c: JUMPI 0000046d: PUSH1 0x01 0000046f: DUP2 00000470: EQ 00000471: ISZERO 00000472: PUSH2 0x0492 00000475: JUMP 00000476: JUMPDEST 00000477: PUSH1 0x00 00000479: DUP5 0000047a: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 0000048f: AND 00000490: EXTCODESIZE 00000491: EQ 00000492: JUMPDEST 00000493: ISZERO 00000494: PUSH2 0x04d4 00000497: JUMPI 00000498: DUP4 00000499: PUSH1 0x40 0000049b: MLOAD 0000049c: PUSH32 0x5274afe700000000000000000000000000000000000000000000000000000000 000004bd: DUP2 000004be: MSTORE 000004bf: PUSH1 0x04 000004c1: ADD 000004c2: PUSH2 0x04cb 000004c5: SWAP2 000004c6: SWAP1 000004c7: PUSH2 0x0770 000004ca: JUMP 000004cb: JUMPDEST 000004cc: PUSH1 0x40 000004ce: MLOAD 000004cf: DUP1 000004d0: SWAP2 000004d1: SUB 000004d2: SWAP1 000004d3: REVERT 000004d4: JUMPDEST 000004d5: POP 000004d6: POP 000004d7: POP 000004d8: POP 000004d9: JUMP 000004da: JUMPDEST 000004db: PUSH1 0x00 000004dd: DUP2 000004de: SWAP1 000004df: POP 000004e0: SWAP2 000004e1: SWAP1 000004e2: POP 000004e3: JUMP 000004e4: JUMPDEST 000004e5: PUSH2 0x04ed 000004e8: DUP2 000004e9: PUSH2 0x04da 000004ec: JUMP 000004ed: JUMPDEST 000004ee: DUP3 000004ef: MSTORE 000004f0: POP 000004f1: POP 000004f2: JUMP 000004f3: JUMPDEST 000004f4: PUSH1 0x00 000004f6: PUSH1 0x20 000004f8: DUP3 000004f9: ADD 000004fa: SWAP1 000004fb: POP 000004fc: PUSH2 0x0508 000004ff: PUSH1 0x00 00000501: DUP4 00000502: ADD 00000503: DUP5 00000504: PUSH2 0x04e4 00000507: JUMP 00000508: JUMPDEST 00000509: SWAP3 0000050a: SWAP2 0000050b: POP 0000050c: POP 0000050d: JUMP 0000050e: JUMPDEST 0000050f: PUSH1 0x00 00000511: DUP2 00000512: ISZERO 00000513: ISZERO 00000514: SWAP1 00000515: POP 00000516: SWAP2 00000517: SWAP1 00000518: POP 00000519: JUMP 0000051a: JUMPDEST 0000051b: PUSH2 0x0523 0000051e: DUP2 0000051f: PUSH2 0x050e 00000522: JUMP 00000523: JUMPDEST 00000524: DUP3 00000525: MSTORE 00000526: POP 00000527: POP 00000528: JUMP 00000529: JUMPDEST 0000052a: PUSH1 0x00 0000052c: PUSH1 0x20 0000052e: DUP3 0000052f: ADD 00000530: SWAP1 00000531: POP 00000532: PUSH2 0x053e 00000535: PUSH1 0x00 00000537: DUP4 00000538: ADD 00000539: DUP5 0000053a: PUSH2 0x051a 0000053d: JUMP 0000053e: JUMPDEST 0000053f: SWAP3 00000540: SWAP2 00000541: POP 00000542: POP 00000543: JUMP 00000544: JUMPDEST 00000545: PUSH1 0x00 00000547: PUSH20 0xffffffffffffffffffffffffffffffffffffffff 0000055c: DUP3 0000055d: AND 0000055e: SWAP1 0000055f: POP 00000560: SWAP2 00000561: SWAP1 00000562: POP 00000563: JUMP 00000564: JUMPDEST 00000565: PUSH1 0x00 00000567: PUSH2 0x056f 0000056a: DUP3 0000056b: PUSH2 0x0544 0000056e: JUMP 0000056f: JUMPDEST 00000570: SWAP1 00000571: POP 00000572: SWAP2 00000573: SWAP1 00000574: POP 00000575: JUMP 00000576: JUMPDEST 00000577: PUSH2 0x057f 0000057a: DUP2 0000057b: PUSH2 0x0564 0000057e: JUMP 0000057f: JUMPDEST 00000580: DUP3 00000581: MSTORE 00000582: POP 00000583: POP 00000584: JUMP 00000585: JUMPDEST 00000586: PUSH1 0x00 00000588: PUSH1 0x20 0000058a: DUP3 0000058b: ADD 0000058c: SWAP1 0000058d: POP 0000058e: PUSH2 0x059a 00000591: PUSH1 0x00 00000593: DUP4 00000594: ADD 00000595: DUP5 00000596: PUSH2 0x0576 00000599: JUMP 0000059a: JUMPDEST 0000059b: SWAP3 0000059c: SWAP2 0000059d: POP 0000059e: POP 0000059f: JUMP 000005a0: JUMPDEST 000005a1: PUSH1 0x00 000005a3: DUP1 000005a4: REVERT 000005a5: JUMPDEST 000005a6: PUSH1 0x00 000005a8: PUSH2 0x05b0 000005ab: DUP3 000005ac: PUSH2 0x0544 000005af: JUMP 000005b0: JUMPDEST 000005b1: SWAP1 000005b2: POP 000005b3: SWAP2 000005b4: SWAP1 000005b5: POP 000005b6: JUMP 000005b7: JUMPDEST 000005b8: PUSH2 0x05c0 000005bb: DUP2 000005bc: PUSH2 0x05a5 000005bf: JUMP 000005c0: JUMPDEST 000005c1: DUP2 000005c2: EQ 000005c3: PUSH2 0x05cb 000005c6: JUMPI 000005c7: PUSH1 0x00 000005c9: DUP1 000005ca: REVERT 000005cb: JUMPDEST 000005cc: POP 000005cd: JUMP 000005ce: JUMPDEST 000005cf: PUSH1 0x00 000005d1: DUP2 000005d2: CALLDATALOAD 000005d3: SWAP1 000005d4: POP 000005d5: PUSH2 0x05dd 000005d8: DUP2 000005d9: PUSH2 0x05b7 000005dc: JUMP 000005dd: JUMPDEST 000005de: SWAP3 000005df: SWAP2 000005e0: POP 000005e1: POP 000005e2: JUMP 000005e3: JUMPDEST 000005e4: PUSH2 0x05ec 000005e7: DUP2 000005e8: PUSH2 0x04da 000005eb: JUMP 000005ec: JUMPDEST 000005ed: DUP2 000005ee: EQ 000005ef: PUSH2 0x05f7 000005f2: JUMPI 000005f3: PUSH1 0x00 000005f5: DUP1 000005f6: REVERT 000005f7: JUMPDEST 000005f8: POP 000005f9: JUMP 000005fa: JUMPDEST 000005fb: PUSH1 0x00 000005fd: DUP2 000005fe: CALLDATALOAD 000005ff: SWAP1 00000600: POP 00000601: PUSH2 0x0609 00000604: DUP2 00000605: PUSH2 0x05e3 00000608: JUMP 00000609: JUMPDEST 0000060a: SWAP3 0000060b: SWAP2 0000060c: POP 0000060d: POP 0000060e: JUMP 0000060f: JUMPDEST 00000610: PUSH1 0x00 00000612: DUP1 00000613: PUSH1 0x40 00000615: DUP4 00000616: DUP6 00000617: SUB 00000618: SLT 00000619: ISZERO 0000061a: PUSH2 0x0626 0000061d: JUMPI 0000061e: PUSH2 0x0625 00000621: PUSH2 0x05a0 00000624: JUMP 00000625: JUMPDEST 00000626: JUMPDEST 00000627: PUSH1 0x00 00000629: PUSH2 0x0634 0000062c: DUP6 0000062d: DUP3 0000062e: DUP7 0000062f: ADD 00000630: PUSH2 0x05ce 00000633: JUMP 00000634: JUMPDEST 00000635: SWAP3 00000636: POP 00000637: POP 00000638: PUSH1 0x20 0000063a: PUSH2 0x0645 0000063d: DUP6 0000063e: DUP3 0000063f: DUP7 00000640: ADD 00000641: PUSH2 0x05fa 00000644: JUMP 00000645: JUMPDEST 00000646: SWAP2 00000647: POP 00000648: POP 00000649: SWAP3 0000064a: POP 0000064b: SWAP3 0000064c: SWAP1 0000064d: POP 0000064e: JUMP 0000064f: JUMPDEST 00000650: PUSH1 0x00 00000652: DUP3 00000653: DUP3 00000654: MSTORE 00000655: PUSH1 0x20 00000657: DUP3 00000658: ADD 00000659: SWAP1 0000065a: POP 0000065b: SWAP3 0000065c: SWAP2 0000065d: POP 0000065e: POP 0000065f: JUMP 00000660: JUMPDEST 00000661: PUSH32 0x596f752063616e27742077697468647261772079657400000000000000000000 00000682: PUSH1 0x00 00000684: DUP3 00000685: ADD 00000686: MSTORE 00000687: POP 00000688: JUMP 00000689: JUMPDEST 0000068a: PUSH1 0x00 0000068c: PUSH2 0x0696 0000068f: PUSH1 0x16 00000691: DUP4 00000692: PUSH2 0x064f 00000695: JUMP 00000696: JUMPDEST 00000697: SWAP2 00000698: POP 00000699: PUSH2 0x06a1 0000069c: DUP3 0000069d: PUSH2 0x0660 000006a0: JUMP 000006a1: JUMPDEST 000006a2: PUSH1 0x20 000006a4: DUP3 000006a5: ADD 000006a6: SWAP1 000006a7: POP 000006a8: SWAP2 000006a9: SWAP1 000006aa: POP 000006ab: JUMP 000006ac: JUMPDEST 000006ad: PUSH1 0x00 000006af: PUSH1 0x20 000006b1: DUP3 000006b2: ADD 000006b3: SWAP1 000006b4: POP 000006b5: DUP2 000006b6: DUP2 000006b7: SUB 000006b8: PUSH1 0x00 000006ba: DUP4 000006bb: ADD 000006bc: MSTORE 000006bd: PUSH2 0x06c5 000006c0: DUP2 000006c1: PUSH2 0x0689 000006c4: JUMP 000006c5: JUMPDEST 000006c6: SWAP1 000006c7: POP 000006c8: SWAP2 000006c9: SWAP1 000006ca: POP 000006cb: JUMP 000006cc: JUMPDEST 000006cd: PUSH32 0x596f75206172656e277420746865206f776e6572000000000000000000000000 000006ee: PUSH1 0x00 000006f0: DUP3 000006f1: ADD 000006f2: MSTORE 000006f3: POP 000006f4: JUMP 000006f5: JUMPDEST 000006f6: PUSH1 0x00 000006f8: PUSH2 0x0702 000006fb: PUSH1 0x14 000006fd: DUP4 000006fe: PUSH2 0x064f 00000701: JUMP 00000702: JUMPDEST 00000703: SWAP2 00000704: POP 00000705: PUSH2 0x070d 00000708: DUP3 00000709: PUSH2 0x06cc 0000070c: JUMP 0000070d: JUMPDEST 0000070e: PUSH1 0x20 00000710: DUP3 00000711: ADD 00000712: SWAP1 00000713: POP 00000714: SWAP2 00000715: SWAP1 00000716: POP 00000717: JUMP 00000718: JUMPDEST 00000719: PUSH1 0x00 0000071b: PUSH1 0x20 0000071d: DUP3 0000071e: ADD 0000071f: SWAP1 00000720: POP 00000721: DUP2 00000722: DUP2 00000723: SUB 00000724: PUSH1 0x00 00000726: DUP4 00000727: ADD 00000728: MSTORE 00000729: PUSH2 0x0731 0000072c: DUP2 0000072d: PUSH2 0x06f5 00000730: JUMP 00000731: JUMPDEST 00000732: SWAP1 00000733: POP 00000734: SWAP2 00000735: SWAP1 00000736: POP 00000737: JUMP 00000738: JUMPDEST 00000739: PUSH2 0x0741 0000073c: DUP2 0000073d: PUSH2 0x05a5 00000740: JUMP 00000741: JUMPDEST 00000742: DUP3 00000743: MSTORE 00000744: POP 00000745: POP 00000746: JUMP 00000747: JUMPDEST 00000748: PUSH1 0x00 0000074a: PUSH1 0x40 0000074c: DUP3 0000074d: ADD 0000074e: SWAP1 0000074f: POP 00000750: PUSH2 0x075c 00000753: PUSH1 0x00 00000755: DUP4 00000756: ADD 00000757: DUP6 00000758: PUSH2 0x0738 0000075b: JUMP 0000075c: JUMPDEST 0000075d: PUSH2 0x0769 00000760: PUSH1 0x20 00000762: DUP4 00000763: ADD 00000764: DUP5 00000765: PUSH2 0x04e4 00000768: JUMP 00000769: JUMPDEST 0000076a: SWAP4 0000076b: SWAP3 0000076c: POP 0000076d: POP 0000076e: POP 0000076f: JUMP 00000770: JUMPDEST 00000771: PUSH1 0x00 00000773: PUSH1 0x20 00000775: DUP3 00000776: ADD 00000777: SWAP1 00000778: POP 00000779: PUSH2 0x0785 0000077c: PUSH1 0x00 0000077e: DUP4 0000077f: ADD 00000780: DUP5 00000781: PUSH2 0x0738 00000784: JUMP 00000785: JUMPDEST 00000786: SWAP3 00000787: SWAP2 00000788: POP 00000789: POP 0000078a: JUMP 0000078b: INVALID 0000078c: LOG2 0000078d: PUSH5 0x6970667358 00000793: UNKNOWN(0x22) 00000794: SLT 00000795: KECCAK256 00000796: UNKNOWN(0xEB) 00000797: UNKNOWN(0xB2) 00000798: SWAP9 00000799: UNKNOWN(0xE1) 0000079a: LOG4 0000079b: UNKNOWN(0x23) 0000079c: UNKNOWN(0x23) 0000079d: UNKNOWN(0x0F) 0000079e: PUSH30 0xfbc66c1cc7991da8417383cc4afc1d56a194e84b83264864736f6c634300 000007bd: ADDMOD 000007be: SHR 000007bf: STOP 000007c0: CALLER
| Severity | Category | Tool | Title | Confidence | Validated | Confirmed | Details |
|---|---|---|---|---|---|---|---|
| medium | access_control | codex | Potentially unguarded arbitrary CALL with ETH value (reachability unclear) | 75 | no | — |
viewevidence_json{
"evidence": "Bytecode analysis shows CALL at pc 1098 with target/value classified as computed (`callsites`), and no CALLER-based guard appears in nearby blocks (pc 953/1004/1080). Detector flagged both \"Untrusted CALL target/value reachable\" and \"ETH value transfer possible\" for this callsite; validation trace observed a constant target (self), so control is uncertain.",
"locations": [
"pc:0x44a",
"pc:0x3b8",
"pc:0x3ec"
],
"recommendation": "Confirm whether the pc:0x44a call path is reachable from any external selector and whether its target/value are user-controlled. If reachable, add explicit access control (owner/time-lock/allowlist) and validate target/value. If the call is intended to be a self-call or a fixed target, hardcode it or add assertions to prevent user-controlled targets.",
"exploit_scenario": "If the code path containing the CALL at pc:0x44a (1098) is reachable from a public selector and its target/value are derived from calldata or other user-controlled inputs, any caller could trigger arbitrary external calls (and potentially transfer ETH) from this contract. This could allow draining ETH, token approvals/transfers, or interaction with attacker-controlled contracts. However, the control-flow graph does not clearly link this block to the dispatcher, and one dynamic trace observed a constant target (self), so actual exploitability is uncertain."
}
|
| medium | other | detector | Untrusted CALL target/value reachable | 45 | no | no |
viewtrace_id: 0x09febf62874f57fb20e29e0f289c69e4bb05508ae453a3a747d87c873e5a727c
call_targets:
0x367ba7d034abf0b97bccdf07fd95217410c9dbe8
target_varies:
no
classification:
constant_target
validation_json{
"sink": "CALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0x09febf62874f57fb20e29e0f289c69e4bb05508ae453a3a747d87c873e5a727c",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0x367ba7d034abf0b97bccdf07fd95217410c9dbe8"
],
"matched_probe": null,
"target_varies": false,
"classification": "constant_target"
}
witness_json{
"notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
"constraints": [],
"entrypoints": [
{
"calldata": "0x251c1aa3",
"selector": "0x251c1aa3",
"calldata_variants": [
"0x251c1aa3",
"0x251c1aa30000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0x3ccfd60b",
"selector": "0x3ccfd60b",
"calldata_variants": [
"0x3ccfd60b",
"0x3ccfd60b0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0x8380edb7",
"selector": "0x8380edb7",
"calldata_variants": [
"0x8380edb7",
"0x8380edb70000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0x8da5cb5b",
"selector": "0x8da5cb5b",
"calldata_variants": [
"0x8da5cb5b",
"0x8da5cb5b0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xa1db9782",
"selector": "0xa1db9782",
"calldata_variants": [
"0xa1db9782",
"0xa1db97820000000000000000000000000000000000000000000000000000000000000000"
]
}
]
}
evidence_json{
"examples": [
{
"pc": 584,
"opcode": "CALL",
"returndata_checked": null,
"value_expression_category": "computed",
"target_expression_category": "computed"
},
{
"pc": 1098,
"opcode": "CALL",
"returndata_checked": null,
"value_expression_category": "computed",
"target_expression_category": "computed"
}
],
"call_count": 2,
"guards_detected": [
{
"pc": 416,
"type": "msg_sender_eq_const",
"value": "0xffffffffffffffffffffffffffffffffffffffff"
},
{
"pc": 786,
"type": "msg_sender_eq_const",
"value": "0xffffffffffffffffffffffffffffffffffffffff"
}
],
"untrusted_call_count": 2
}
|
| medium | other | detector | ETH value transfer possible | 40 | no | no |
viewtrace_id: 0x09febf62874f57fb20e29e0f289c69e4bb05508ae453a3a747d87c873e5a727c
call_targets:
0x367ba7d034abf0b97bccdf07fd95217410c9dbe8
target_varies:
no
classification:
constant_target
validation_json{
"sink": "CALL",
"errors": 0,
"status": "sink_reached",
"attempts": 1,
"trace_id": "0x09febf62874f57fb20e29e0f289c69e4bb05508ae453a3a747d87c873e5a727c",
"confirmed": false,
"trace_mode": "callTracer",
"call_targets": [
"0x367ba7d034abf0b97bccdf07fd95217410c9dbe8"
],
"matched_probe": null,
"target_varies": false,
"classification": "constant_target"
}
witness_json{
"notes": "heuristic witness; entrypoints are best-effort selectors with zeroed calldata",
"constraints": [],
"entrypoints": [
{
"calldata": "0x251c1aa3",
"selector": "0x251c1aa3",
"calldata_variants": [
"0x251c1aa3",
"0x251c1aa30000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0x3ccfd60b",
"selector": "0x3ccfd60b",
"calldata_variants": [
"0x3ccfd60b",
"0x3ccfd60b0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0x8380edb7",
"selector": "0x8380edb7",
"calldata_variants": [
"0x8380edb7",
"0x8380edb70000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0x8da5cb5b",
"selector": "0x8da5cb5b",
"calldata_variants": [
"0x8da5cb5b",
"0x8da5cb5b0000000000000000000000000000000000000000000000000000000000000000"
]
},
{
"calldata": "0xa1db9782",
"selector": "0xa1db9782",
"calldata_variants": [
"0xa1db9782",
"0xa1db97820000000000000000000000000000000000000000000000000000000000000000"
]
}
]
}
evidence_json{
"examples": [
{
"pc": 584,
"opcode": "CALL",
"returndata_checked": null,
"value_expression_category": "computed",
"target_expression_category": "computed"
},
{
"pc": 1098,
"opcode": "CALL",
"returndata_checked": null,
"value_expression_category": "computed",
"target_expression_category": "computed"
}
],
"eth_value_calls": 2,
"guards_detected": [
{
"pc": 416,
"type": "msg_sender_eq_const",
"value": "0xffffffffffffffffffffffffffffffffffffffff"
},
{
"pc": 786,
"type": "msg_sender_eq_const",
"value": "0xffffffffffffffffffffffffffffffffffffffff"
}
]
}
|