<rss xmlns:atom="http://www.w3.org/2005/Atom" version="2.0"><channel><title>Fallback Function - Tag - Kyrin Labs</title><link>https://www.kyrin.one/tags/fallback-function/</link><description>Fallback Function - Tag - Kyrin Labs</description><generator>Hugo -- gohugo.io</generator><language>en-us</language><lastBuildDate>Sat, 08 Nov 2025 00:00:00 +0000</lastBuildDate><atom:link href="https://www.kyrin.one/tags/fallback-function/" rel="self" type="application/rss+xml"/><item><title>Ethernaut - 01 Fallback</title><link>https://www.kyrin.one/techshare/writeups/ethernaut/01_fallback/</link><pubDate>Sat, 08 Nov 2025 00:00:00 +0000</pubDate><author>Dalek</author><guid>https://www.kyrin.one/techshare/writeups/ethernaut/01_fallback/</guid><description><![CDATA[<h2 id="1-信息">1. 信息：</h2>
<h3 id="11-提示信息">1.1. 提示信息</h3>
<p>进入这一关后，首先可以看到提示：</p>
<blockquote>
<p>[!提示]</p>
<blockquote>
<p>Look carefully at the contract&rsquo;s code below.</p>
<p>You will beat this level if</p>
<ol>
<li>you claim ownership of the contract</li>
<li>you reduce its balance to 0</li>
</ol>
<p>  Things that might help</p>
<ul>
<li>How to send ether when interacting with an ABI</li>
<li>How to send ether outside of the ABI</li>
<li>Converting to and from wei/ether units (see <code>help()</code> command)</li>
<li>Fallback methods</li>
</ul>
</blockquote>
</blockquote>
<h3 id="12-合约代码">1.2. 合约代码</h3>
<p>直接看合约代码(直接在代码里通过注释的方式，分析代码的功能)：</p>
<div class="code-block code-line-numbers" style="counter-reset: code-block 0">
    <div class="code-header language-Solidity">
        <span class="code-title"><i class="arrow fas fa-angle-right" aria-hidden="true"></i></span>
        <span class="ellipses"><i class="fas fa-ellipsis-h" aria-hidden="true"></i></span>
        <span class="copy" title="Copy to clipboard"><i class="far fa-copy" aria-hidden="true"></i></span>
    </div><div class="highlight"><pre tabindex="0" class="chroma"><code class="language-Solidity" data-lang="Solidity"><span class="line"><span class="cl"><span class="c1">// SPDX-License-Identifier: MIT
</span></span></span><span class="line"><span class="cl"><span class="c1">// `pragma` specifies the compiler version of Solidity.
</span></span></span><span class="line"><span class="cl"><span class="k">pragma solidity</span> <span class="o">^</span><span class="mi">0</span><span class="p">.</span><span class="mi">8</span><span class="p">.</span><span class="mi">0</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl"><span class="kd">contract</span> <span class="nc">Fallback</span> <span class="p">{</span>    <span class="c1">// 合约名称为：`Fallback`
</span></span></span><span class="line"><span class="cl">	<span class="c1">// 将地址`address`映射到无符号256位整数`uint256`,这个映射的名字是 `contributions`.
</span></span></span><span class="line"><span class="cl">	<span class="c1">// 关键字 `public` 表示Solidity会自动为这个映射生成一个gatter函数，使外部和内部一样可以通过调用`contributions(address)`来查询某个地址对应的uint256值
</span></span></span><span class="line"><span class="cl">	<span class="c1">// 这个 `mapping` 的功能是记录每个地址的贡献值
</span></span></span><span class="line"><span class="cl">    <span class="kd">mapping</span><span class="p">(</span><span class="kt">address</span> <span class="o">=&gt;</span> <span class="kt">uint256</span><span class="p">)</span> <span class="k">public</span> <span class="n">contributions</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">    <span class="c1">// 声明一个公开的地址状态变量 `owner`，关键字 `public` 的作用还是生成gatter函数.
</span></span></span><span class="line"><span class="cl">    <span class="kt">address</span> <span class="k">public</span> <span class="n">owner</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// 构造函数，部署合约时只执行一次
</span></span></span><span class="line"><span class="cl">    <span class="kd">constructor</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">		<span class="c1">// 让部署者成为owner，`msg.sender` 中存储的是当前函数调用者的地址
</span></span></span><span class="line"><span class="cl">        <span class="n">owner</span> <span class="o">=</span> <span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="c1">// 调用这段代码开头的 `contributions` 函数，owner拥有1000ETH的初始贡献（注意：这里是以wei为单位，所以实际是1000*10^18 wei）
</span></span></span><span class="line"><span class="cl">        <span class="n">contributions</span><span class="p">[</span><span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span><span class="p">]</span> <span class="o">=</span> <span class="mi">1000</span> <span class="o">*</span> <span class="p">(</span><span class="mi">1</span> <span class="kc">ether</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// 这是一个修饰器
</span></span></span><span class="line"><span class="cl">    <span class="kd">modifier</span> <span class="nf">onlyOwner</span><span class="p">()</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">	    <span class="c1">// `require` 是用来条件检查语句，
</span></span></span><span class="line"><span class="cl">        <span class="nb">require</span><span class="p">(</span><span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span> <span class="o">==</span> <span class="n">owner</span><span class="p">,</span> <span class="s">&#34;caller is not the owner&#34;</span><span class="p">);</span><span class="c1">// 只有拥有者可以调用，否则回滚交易并清空所有Gas
</span></span></span><span class="line"><span class="cl">        <span class="k">_</span><span class="p">;</span>         <span class="c1">// 原函数体的插入位置
</span></span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// `payable` 函数是可以接收ETH的函数
</span></span></span><span class="line"><span class="cl">    <span class="kd">function</span> <span class="nf">contribute</span><span class="p">()</span> <span class="k">public</span> <span class="k">payable</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">require</span><span class="p">(</span><span class="nb">msg</span><span class="p">.</span><span class="nb">value</span> <span class="o">&lt;</span> <span class="mi">0</span><span class="p">.</span><span class="mi">001</span> <span class="kc">ether</span><span class="p">);</span>    <span class="c1">// 要求每次贡献的金额小于0.001 ether
</span></span></span><span class="line"><span class="cl">        <span class="n">contributions</span><span class="p">[</span><span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span><span class="p">]</span> <span class="o">+=</span> <span class="nb">msg</span><span class="p">.</span><span class="nb">value</span><span class="p">;</span>    <span class="c1">// 累加发送者的贡献值
</span></span></span><span class="line"><span class="cl">        <span class="c1">// 这个 `if` 用来判断如果当前发送者的贡献值超过当前拥有者的贡献值，则更新拥有者为当前发送者
</span></span></span><span class="line"><span class="cl">        <span class="k">if</span> <span class="p">(</span><span class="n">contributions</span><span class="p">[</span><span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span><span class="p">]</span> <span class="o">&gt;</span> <span class="n">contributions</span><span class="p">[</span><span class="n">owner</span><span class="p">])</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">            <span class="n">owner</span> <span class="o">=</span> <span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">        <span class="p">}</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// `view` 函数只读取数据，在链外调用时，不消耗Gas
</span></span></span><span class="line"><span class="cl">    <span class="kd">function</span> <span class="nf">getContribution</span><span class="p">()</span> <span class="k">public</span> <span class="k">view</span> <span class="k">returns</span> <span class="p">(</span><span class="kt">uint256</span><span class="p">)</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="c1">//  返回当前发送者的贡献值
</span></span></span><span class="line"><span class="cl">        <span class="k">return</span> <span class="n">contributions</span><span class="p">[</span><span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span><span class="p">];</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">// `onlyOwner` 修饰器负责权限控制，只用owner可以调用
</span></span></span><span class="line"><span class="cl">	<span class="c1">// **address(this).balance**：合约当前的ETH余额
</span></span></span><span class="line"><span class="cl">	<span class="c1">// **payable转换**：将address转换为payable address以接收ETH
</span></span></span><span class="line"><span class="cl">	<span class="c1">// **transfer函数**：发送ETH到指定地址（2300 Gas限制，自动失败）
</span></span></span><span class="line"><span class="cl">    <span class="kd">function</span> <span class="nf">withdraw</span><span class="p">()</span> <span class="k">public</span> <span class="n">onlyOwner</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="k">payable</span><span class="p">(</span><span class="n">owner</span><span class="p">).</span><span class="nb">transfer</span><span class="p">(</span><span class="kt">address</span><span class="p">(</span><span class="nb">this</span><span class="p">).</span><span class="nb">balance</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl">
</span></span><span class="line"><span class="cl">	<span class="c1">//- **receive函数**：专门处理纯ETH转账的特殊函数
</span></span></span><span class="line"><span class="cl">	<span class="c1">//- **external可见性**：只能从外部调用
</span></span></span><span class="line"><span class="cl">	<span class="c1">//- **payable**：可以接收ETH
</span></span></span><span class="line"><span class="cl">	<span class="c1">//- **条件限制**：需要发送ETH且发送者已有贡献记录
</span></span></span><span class="line"><span class="cl">	<span class="c1">//- **所有权转移**：满足条件时立即成为新owner
</span></span></span><span class="line"><span class="cl">    <span class="n">receive</span><span class="p">()</span> <span class="k">external</span> <span class="k">payable</span> <span class="p">{</span>
</span></span><span class="line"><span class="cl">        <span class="nb">require</span><span class="p">(</span><span class="nb">msg</span><span class="p">.</span><span class="nb">value</span> <span class="o">&gt;</span> <span class="mi">0</span> <span class="o">&amp;&amp;</span> <span class="n">contributions</span><span class="p">[</span><span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span><span class="p">]</span> <span class="o">&gt;</span> <span class="mi">0</span><span class="p">);</span>
</span></span><span class="line"><span class="cl">        <span class="n">owner</span> <span class="o">=</span> <span class="nb">msg</span><span class="p">.</span><span class="nb">sender</span><span class="p">;</span>
</span></span><span class="line"><span class="cl">    <span class="p">}</span>
</span></span><span class="line"><span class="cl"><span class="p">}</span></span></span></code></pre></div></div>
<h2 id="2-分析">2. 分析</h2>
<h3 id="21-合约代码逻辑分析">2.1. 合约代码逻辑分析</h3>
<blockquote>
<p>和合约代码逻辑 &amp;&amp; 漏洞利用逻辑
从函数 “contribute()&quot; 可以看出 &ldquo;owner&rdquo; 的贡献为 1000 ETH 。有一种变成 &ldquo;owner&rdquo; 的方式是贡献大于当前 &ldquo;owner&rdquo; 的 1000 ETH。显然不太可能。
那么还有一种方式就是通过 &ldquo;receive()&rdquo; 函数，观察代码可知，当贡献已大于0，并且再发送大于0的金额时，就会被设置成 &ldquo;owner&rdquo; 。</p>]]></description></item></channel></rss>