初探web3安全审计
初探web3安全审计(1)
TL;DR
这是一篇初学solana安全审计的文章,从solana入门学习开始,自己记录了一些东西后续参考
Solana
首先是solana开发,强烈安利Programming on Solana - An Introduction | paulx这一篇文章,很多概念说的非常清楚。
program与account
因为在solana中program是无状态的,所以需要创建并管理一个program account来帮助存储数据。program通过对account的数据进行读写进而更新状态。
anchor入门
anchor test
客户端ts测试脚本中可以使用program.methods.xxx来访问#[program]中暴露出来的api函数。
anchor constraint
我理解has_one constraint的作用是对账户某个filed进行验证看是不是程序期待的那个field(比如对owner,signer等进行验证),看例子:
1 |
|
这里has_one constraint会检查puppet.authority = authority.key()
Cross-Program Invocations
1. 跨程序调用的时候,需要注意一个点是:在跨程序调用(Cross-Program Invocations,CPIs)中,通常情况下,你需要传递 AccountInfo
对象来表示外部程序的账户信息。这是因为 CPIs 通常涉及到与外部程序的智能合约交互,这些外部程序可能有它们自己的账户状态需要更新或操作。
当你在一个程序中调用另一个程序时,你需要提供被调用程序所需的账户信息,以便它可以访问和操作这些账户。因此,通常情况下,你会为 CPIs 传递 AccountInfo
对象,以表示外部程序的账户。
例子如下:
1 |
|
这里面set_data_ctx是一个在为跨程序调用函数准备Context
2.跨程序调用后,调用者里关于被调用程序的账户信息不会及时更新,比如上面例子中的puppet账户,如果要及时更新可以使用reload api。例子如下
1 | puppet::cpi::set_data(ctx.accounts.set_data_ctx(), data)?; |
3.获取被调用程序的返回值,可以通过set、get获取。需要注意的一点是:返回值的类型必须实现AnchorSerialize
和AnchorDeserialize
这两个特性。比如如果返回的是MyData,那MyData的实现就是如下:
1 |
|
跨程序调用获取MyData返回值的例子如下:
1 | pub fn pull_strings(ctx: Context<PullStrings>, data: u64) -> Result<MyData> { |
Solana程序的调试
链上程序调试较困难,可以多使用测试脚本进行测试
影响判断
1.漏洞的影响判断可以参考https://immunefi.com/immunefi-vulnerability-severity-classification-system-v2-3/
心得
1.对于任何用户都能调用的函数要警惕,重点审计
2.如果是无符号数,警惕加减等操作
3.如果都是整数,做除法留意,比如是不是向下取整,合不合逻辑。或者除零等操作。
4.to_u64这种情况,可以留意小数舍去方式是否符合当前的场景
5.realloc之前留意是否检查分配大小。
6.账户检查等问题
7.access_control宏不保留调用函数的属性,报错传不上来
8.对于用户可控参数要警惕。
9.anchor的一些实现,可以用cargo expand看看具体实现,比如clock: Sysvar<’info, Clock>anchor有没有对其进行owner的验证
踩坑
Cargo.toml中子依赖版本切换
在复现anchor-book中的tic-tac-toe项目使用anchor test测试时(anchor-book/programs/tic-tac-toe/programs/tic-tac-toe at master · coral-xyz/anchor-book (github.com)),对比实验发现高版本solana(1.16.10)会失败(报错:Error: failed to send transaction: Transaction simulation failed: Error processing Instruction 0: Program failed to complete)。
切换到低版本solana(1.14.11),不会出现该错误。可以把clone下来的项目跑起来。但是自己anchor init新建的项目不行,问题在于anchor-lang下面的3级子依赖项constant_time_eq的版本太高了。最终把子依赖及其父依赖一起降版本即可:
1 | anchor-lang = "=0.24.2" |
注意,一定要用”=”号指定版本,否则可能会是其他可兼容的版本,达不到效果。