{"id":915,"date":"2024-09-12T11:08:38","date_gmt":"2024-09-12T09:08:38","guid":{"rendered":"https:\/\/ackee.xyz\/blog\/?p=915"},"modified":"2024-09-13T16:29:39","modified_gmt":"2024-09-13T14:29:39","slug":"introducing-manually-guided-fuzzing-a-new-approach-in-smart-contract-testing","status":"publish","type":"post","link":"https:\/\/ackee.xyz\/blog\/introducing-manually-guided-fuzzing-a-new-approach-in-smart-contract-testing\/","title":{"rendered":"Introducing Manually Guided Fuzzing: A New Approach in Smart Contract Testing"},"content":{"rendered":"<p><span style=\"font-weight: 400;\">Fuzzing is a well-known <\/span><span style=\"font-weight: 400;\">software testing technique, especially useful for testing<\/span><span style=\"font-weight: 400;\"> smart contracts. Traditionally, as fuzzing, testers understood mainly <\/span><b>Black Box Fuzzing<\/b><span style=\"font-weight: 400;\"> or, more advanced, <\/span><b>Property-Based Fuzzing<\/b><span style=\"font-weight: 400;\">. However, a new and innovative approach, <\/span><b>Manually Guided Fuzzing<\/b><span style=\"font-weight: 400;\">, offers enhanced efficiency and effectiveness, filling the gaps left by traditional fuzzing techniques. This article will explore how Guided Fuzzing differs from Black Box Fuzzing, Property-Based Fuzzing, Differential Fuzzing and other techniques.<\/span><\/p>\n<h2><span style=\"font-weight: 400;\">Understanding the Basics: Stateful vs. Stateless and Black Box vs. Grey Box vs. White Box Fuzzing<\/span><\/h2>\n<p><span style=\"font-weight: 400;\">Fuzzing techniques can be classified based on their statefulness and the level of guidance provided:<\/span><\/p>\n<h3><\/h3>\n<h3><b>Stateful vs. Stateless Fuzzing<\/b><span style=\"font-weight: 400;\">:<\/span><\/h3>\n<p><b>Stateless Fuzzing<\/b><span style=\"font-weight: 400;\">: Each test case is independent of the others in stateless fuzzing. The fuzzer does not maintain any knowledge of previous states or inputs, which limits its ability to discover bugs related to the sequence of operations.<\/span><\/p>\n<p><b>Stateful Fuzzing<\/b><span style=\"font-weight: 400;\">: This method considers the system&#8217;s state, meaning the outcome of one test case can influence the subsequent ones. Stateful fuzzing is more effective for testing complex systems like smart contracts, where state transitions are critical. See the difference on <\/span><a href=\"https:\/\/gist.github.com\/michprev\/d1c562634c5f27f5e9d6f2d0d1bbf9f5\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">this code snippet<\/span><\/a><span style=\"font-weight: 400;\"> written in <\/span><a href=\"https:\/\/github.com\/Ackee-Blockchain\/wake\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">Wake Framework<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/p>\n<h3><\/h3>\n<h3><b>Black Box vs. Grey Box vs. White Box<\/b><span style=\"font-weight: 400;\">:<\/span><\/h3>\n<p><b>Black Box Fuzzing<\/b><span style=\"font-weight: 400;\">: The fuzzer operates without knowing the tested system&#8217;s internal workings. It must know the interface, but it relies on random inputs and heuristic techniques to explore the state space to identify unexpected behavior or crashes. While effective in some cases, black box fuzzing is time and resource-consuming, often resulting in incomplete coverage.<\/span><\/p>\n<p><b>Grey Box Fuzzing<\/b><span style=\"font-weight: 400;\">: The fuzzer has some information about the internal workings of the tested system. The definition is very wide, an example is defined <\/span><b>Invariants <\/b><span style=\"font-weight: 400;\">(see Property-based Fuzzing).<\/span><\/p>\n<p><b>White Box<\/b><span style=\"font-weight: 400;\">: In contrast the fuzzer was provided all information about the tested system. This approach allows for more targeted and efficient fuzzing, focusing on critical code paths and specific scenarios, leading to quicker and more predictable bug detection (see Manually Guided Fuzzing).<\/span><\/p>\n<h3><\/h3>\n<h3><b>Getting more: Advanced Fuzzing Techniques<\/b><\/h3>\n<p><span style=\"font-weight: 400;\">There are more advanced techniques that allow testing in even more edge-case scenarios:<\/span><\/p>\n<p><b>Differential Fuzzing<\/b><span style=\"font-weight: 400;\">: The fuzzer compares the outputs of the tested system with those of a reference. The reference can be a model of the system implemented in a high-level language like Python. The subject of the test could also be a single function compared to an existing library implementing the same function. Differential fuzzing is an elegant technique mainly with mathematical functions that can reveal many rounding and precision errors of Solidity and becomes very powerful when combined with stateful fuzzing. An example of a differential fuzz test is the IPOR audit, where Ackee Blockchain Security compared the implementation of continuous compound interest rate calculations in contracts to a tailor-made Python model, which yielded one critical and two high-severity issues.<\/span><\/p>\n<p><b>Fork Testing<\/b><span style=\"font-weight: 400;\">: For testing projects with complex dependencies or integrations (Aave, Chainlink). Fork testing involves running tests on a development chain (like Anvil) that forks from a live network. This allows the system to interact with real-world data and states without needing to redeploy and mock contract storage. Using real-world data ensures more accurate and comprehensive testing than data mocking (a technique used in Formal Verification). An example of a fork fuzz test is the Lido Stonks audit, where Ackee Blockchain Security forked the Ethereum mainnet to test the protocol under realistic conditions. The use of a forked USDT contract led to an integration issue discovery of medium severity, see complete source code <\/span><a href=\"https:\/\/github.com\/Ackee-Blockchain\/tests-lido-stonks\/blob\/main\/tests\/test_fuzz.py\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">here<\/span><\/a><span style=\"font-weight: 400;\">.<\/span><\/p>\n<h3><\/h3>\n<h3><b>Exploring Property-Based Fuzzing<\/b><\/h3>\n<p><b>Property-based fuzzing<\/b><span style=\"font-weight: 400;\">, or <\/span><b>Invariant testing<\/b><span style=\"font-weight: 400;\">, falls between black box and white box fuzzing. Here, the tester (with some level of knowledge of the tested system) defines specific properties called <\/span><b>invariants<\/b><span style=\"font-weight: 400;\"> that the system must always satisfy, regardless of the inputs. The fuzzer then tries to generate inputs that violate these properties. With the tester&#8217;s introduction of invariants, the fuzzer operates with knowledge of the system&#8217;s properties, which gives the &#8220;gray-box&#8221; designation. While more controlled than black box fuzzing, property-based fuzzing still struggles with fully exploring complex state spaces, especially in stateful rich systems like complex smart contracts.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">An invariant is a test that is executed after each state change (transaction).<\/span><\/p>\n<pre><code class=\"language-python\">@invariant()\ndef invariant_balance(self) -&gt; None:\n\u00a0\u00a0\u00a0\u00a0assert self.token.balanceOf(self.admin) == self.balances[self.admin]<\/code><\/pre>\n<h2><span style=\"font-weight: 400;\">Introducing Manually Guided Fuzzing<\/span><\/h2>\n<p><b>Manually Guided Fuzzing<\/b><span style=\"font-weight: 400;\"> combines the strengths of Stateful Fuzzing and White Box Fuzzing, adding the concept of <\/span><b>Flows<\/b><span style=\"font-weight: 400;\"> to provide a structured approach to testing. This method requires the tester to fully understand the tested system and direct the fuzzing process, ensuring that the critical parts of the code are thoroughly examined.<\/span><\/p>\n<p><span style=\"font-weight: 400;\">A <\/span><b>Flow<\/b><span style=\"font-weight: 400;\"> is a sequence of actions or transactions within the system. For instance, a flow might involve transferring tokens from one account to another. The tester defines these flows to ensure the fuzzer targets important code paths.<\/span><\/p>\n<p><i><span style=\"font-weight: 400;\">A flow is a single test step executed in a test sequence. Flows are defined using the @flow decorator:<\/span><\/i><\/p>\n<pre><code class=\"language-python\">@flow(precondition=lambda self: self.count &gt; 0)\ndef flow_decrement(self) -&gt; None:\n    self.counter.decrement(from_=random_account())\n    self.count -= 1<\/code><\/pre>\n<h3><span style=\"font-weight: 400;\">How Manually Guided Fuzzing Works<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">Using the Wake Framework, Manually Guided Fuzzing involves several steps:<\/span><\/p>\n<ol>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Defining Invariants<\/b><span style=\"font-weight: 400;\">: Similar to property-based fuzzing, Manually Guided Fuzzing requires the definition of properties or invariants that should remain true after a flow is executed. For example, after a token transfer, the invariant could be that the total supply of tokens remains constant and that balances are updated correctly.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Defining Flows<\/b><span style=\"font-weight: 400;\">: Now the tester instructs how the fuzzer should interact with the contract. If we stick to the previous example, it will initiate the token transfer.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Combining Flows and Invariants<\/b><span style=\"font-weight: 400;\">: Manually Guided Fuzzing creates a more efficient fuzzing process by combining random flows, each flow followed by all invariant checks. Rather than randomly exploring the state space, this method allows the tester to focus on specific areas of the code, significantly reducing the time and computational resources needed.<\/span><\/li>\n<\/ol>\n<h3><\/h3>\n<h3><span style=\"font-weight: 400;\">(Dis)advantages of Manually Guided Fuzzing<\/span><\/h3>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Efficiency<\/b><span style=\"font-weight: 400;\">: By directing the fuzzer with specific flows and properties, Manually Guided Fuzzing significantly reduces the explored state space and, thus, the time required to find bugs.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>Flexibility<\/b><span style=\"font-weight: 400;\">: Testers have full control over the fuzzing process, enabling them to test specific scenarios, including stateful interactions, cross-chain transactions, and complex contract dependencies, which are often difficult to cover with black box fuzzing or formal verification.<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><b>With great power comes great responsibility<\/b><span style=\"font-weight: 400;\">: The tester must identify possible red flags in the code and decide to cover them with a fuzz test. Usually the tester defines flows for all public-state changing functions and chooses the right function\u2019s input values. If the tester misses those red flags or does not have an attack vector idea, the fuzzing campaign will not discover the vulnerabilities.<\/span><\/li>\n<\/ul>\n<h3><span style=\"font-weight: 400;\">Conclusion<\/span><\/h3>\n<p>Manually Guided Fuzzing represents a responsibility shift from testers using brute force or heuristic algorithms back to the hands of auditors and security researchers who have to guide fuzz tests toward attack vectors to discover vulnerabilities. Manually Guided Fuzzing offers a more efficient, precise, and scalable method for testing complex systems like heavily integrated smart contracts. Explore the mentioned techniques using the Wake framework which supports Manually Guided Fuzzing, fork testing, and differential testing.<\/p>\n<h3><span style=\"font-weight: 400;\">Additional Resources<\/span><\/h3>\n<p><span style=\"font-weight: 400;\">The following examples present real-world usage of different types of fuzz tests:<\/span><\/p>\n<ul>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Differential fuzz testing of Solidity Merkle tree against Python Merkle tree implementation &#8211; <\/span><a href=\"https:\/\/github.com\/Ackee-Blockchain\/tests-solady\/blob\/main\/tests\/test_merkle_proof_fuzz.py#L10\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">https:\/\/github.com\/Ackee-Blockchain\/tests-solady\/blob\/main\/tests\/test_merkle_proof_fuzz.py#L10<\/span><\/a><span style=\"font-weight: 400;\">\u00a0<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Fork testing using a real USDt token instead of a mocked token &#8211; <\/span><a href=\"https:\/\/github.com\/Ackee-Blockchain\/tests-lido-stonks\/blob\/main\/tests\/test_fuzz.py#L19\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">https:\/\/github.com\/Ackee-Blockchain\/tests-lido-stonks\/blob\/main\/tests\/test_fuzz.py#L19<\/span><\/a><span style=\"font-weight: 400;\">\u00a0<\/span><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Fuzzing flows with Wake &#8211; <\/span><a href=\"https:\/\/ackee.xyz\/wake\/docs\/latest\/testing-framework\/fuzzing\/#flows\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">https:\/\/ackee.xyz\/wake\/docs\/latest\/testing-framework\/fuzzing\/#flows<\/span><\/a><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Stateful invariant testing comparing Solidity values against Python model &#8211; <\/span><a href=\"https:\/\/github.com\/Ackee-Blockchain\/tests-lido-simple-delegation\/blob\/main\/tests\/test_fuzz.py#L605-L613\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">https:\/\/github.com\/Ackee-Blockchain\/tests-lido-simple-delegation\/blob\/main\/tests\/test_fuzz.py#L605-L613<\/span><\/a><\/li>\n<li style=\"font-weight: 400;\" aria-level=\"1\"><span style=\"font-weight: 400;\">Stateless invariants as assertions &#8211; <\/span><a href=\"https:\/\/github.com\/Ackee-Blockchain\/tests-lido-simple-delegation\/blob\/main\/tests\/test_fuzz.py#L382-L389\" target=\"_blank\" rel=\"noopener\"><span style=\"font-weight: 400;\">https:\/\/github.com\/Ackee-Blockchain\/tests-lido-simple-delegation\/blob\/main\/tests\/test_fuzz.py#L382-L389<\/span><\/a><span style=\"font-weight: 400;\">\u00a0<\/span><\/li>\n<\/ul>\n","protected":false},"excerpt":{"rendered":"<p>Fuzzing is a well-known software testing technique, especially useful for testing smart contracts. Traditionally, as fuzzing, testers understood mainly Black Box Fuzzing or, more advanced, Property-Based Fuzzing. However, a new and innovative approach, Manually Guided Fuzzing, offers enhanced efficiency and effectiveness, filling the gaps left by traditional fuzzing techniques. This article will explore how Guided Fuzzing differs from Black Box Fuzzing, Property-Based&hellip;<\/p>\n","protected":false},"author":5,"featured_media":916,"comment_status":"open","ping_status":"open","sticky":false,"template":"","format":"standard","meta":{"footnotes":""},"categories":[65,20,10,103],"tags":[96,24,104],"class_list":["post-915","post","type-post","status-publish","format-standard","has-post-thumbnail","hentry","category-announcements","category-audits","category-ethereum","category-wake","tag-educational","tag-ethereum","tag-wake"],"aioseo_notices":[],"featured_image_src":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/09\/Manual-Guided-Fuzzing-Ackee-2-600x400.png","featured_image_src_square":"https:\/\/ackee.xyz\/blog\/wp-content\/uploads\/2024\/09\/Manual-Guided-Fuzzing-Ackee-2-600x600.png","author_info":{"display_name":"Josef Gattermayer","author_link":"https:\/\/ackee.xyz\/blog\/author\/josef-gattermayerackeeblockchain-com\/"},"_links":{"self":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/915","targetHints":{"allow":["GET"]}}],"collection":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts"}],"about":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/types\/post"}],"author":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/users\/5"}],"replies":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/comments?post=915"}],"version-history":[{"count":0,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/posts\/915\/revisions"}],"wp:featuredmedia":[{"embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media\/916"}],"wp:attachment":[{"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/media?parent=915"}],"wp:term":[{"taxonomy":"category","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/categories?post=915"},{"taxonomy":"post_tag","embeddable":true,"href":"https:\/\/ackee.xyz\/blog\/wp-json\/wp\/v2\/tags?post=915"}],"curies":[{"name":"wp","href":"https:\/\/api.w.org\/{rel}","templated":true}]}}