A method for detecting vulnerabilities of smart contracts by cross-contract symbolic execution
By generating contract call graphs and control flow graphs, and combining the A* algorithm and depth-first search with bounds, the Z3 constraint solver is used to detect cross-contract vulnerabilities in smart contracts. This solves the problem of low efficiency in cross-contract vulnerability detection in existing tools and achieves more comprehensive security protection for smart contracts.
Patent Information
- Authority / Receiving Office
- CN · China
- Patent Type
- Patents(China)
- Current Assignee / Owner
- Filing Date
- 2022-11-01
- Publication Date
- 2026-03-27
AI Technical Summary
Existing smart contract vulnerability detection tools mainly target vulnerabilities in a single contract, neglecting cross-contract vulnerabilities. Furthermore, existing symbolic execution tools are inefficient in path selection and cannot effectively detect cross-contract vulnerabilities.
Symbolic execution technology is used to generate contract call graphs. Combined with control flow graphs, the A* algorithm and depth-first search strategy for path search are used. Cross-contract vulnerabilities, including reentrancy vulnerabilities, delegated calls, and tx-origin vulnerabilities, are detected by the Z3 constraint solver.
It enables in-depth exploration of cross-contract vulnerabilities, improves the security of smart contracts, covers cross-contract vulnerabilities that are ignored by most tools, and provides more comprehensive security protection.
Smart Images

Figure CN115906092B_ABST
Abstract
Description
TECHNICAL FIELD
[0001] The present application relates to the technical field of cross-contract detection of smart contract vulnerabilities, in particular to a symbolic execution method for cross-contract detection of smart contract vulnerabilities. BACKGROUND
[0002] Smart contract: Smart contract is a big feature of the era of blockchain 2.0. By storing code in the blockchain and being unable to tamper once deployed, it can run code to complete the behavior defined in advance when certain conditions are met, including transferring accounts, making decisions, and interacting with other contracts. Based on the characteristics of the blockchain, the running results of the smart contract are run and verified by a large number of computers to ensure credibility. Solidity is the most popular smart contract language at present, and the present method is only for Solidity language and EVM (Ethereum Virtual Machine). Symbolic execution: Symbolic execution technology is a classic program static analysis technology. Its basic idea is to replace the specific numerical value of the input program with a symbolic value, and "symbolically" simulate the execution of each program instruction, which is interpreted as a specific operation of a semantically equivalent symbolic value. Cross-contract vulnerability: a vulnerability existing in the execution of the calling operation of more than two smart contracts. For example, there are reentrant vulnerabilities, delegated calls, tx-origin, etc.
[0003] At present, the research similar to the present application mainly has the following schemes: Scheme one: the research [1] proposes Clairvoyance, a cross-contract static analysis technology to detect reentrant vulnerabilities in smart contracts. This scheme first realizes cross-contract analysis by tracking paths that may be contaminated, thereby reducing false positives. In addition, five major path protection techniques are summarized to support fast and accurate path feasibility checks. Scheme two: the research [2] proposes Oyente, which is a pioneering work of testing smart contracts using symbolic execution technology. It uses symbolic execution combined with Z3 SMT solver
[17] to traverse each path and detect vulnerability patterns. It also defines and detects four types of vulnerabilities. Scheme three: the research [3] proposes xFuzz, a fuzz testing tool guided by machine learning for cross-contract detection. This scheme trains an excellent machine learning model to filter the search space and guide the priority of the calculation path, thereby efficiently performing cross-contract fuzz testing.
[0004] The shortcomings of existing technologies are as follows: Solution 1: Although this solution is a cross-contract technology, it only detects reentrancy vulnerabilities and does not cover cross-contract vulnerabilities related to delegate calls and tx-origin. Furthermore, it causes redundant analysis and increases additional overhead. Solution 2: Oyente, as an earlier symbolic execution tool, only detects vulnerabilities within a single contract and cannot perform cross-contract operations. Additionally, its path selection is random, wasting significant execution time and reducing the efficiency of symbolic execution. Solution 3: While xFuzz performs cross-contract detection for all three vulnerabilities, it uses fuzzing technology, which is often plagued by invalid inputs, and many inputs are difficult to penetrate deep into the program path, thus affecting efficiency. To address these issues, we provide a symbolic execution method for cross-contract smart contract vulnerability detection. Summary of the Invention
[0005] The purpose of this invention is to provide a symbolic execution method for detecting smart contract vulnerabilities across contracts. Currently, many tools used for testing smart contracts only target vulnerabilities within a single contract, neglecting cross-contract vulnerability issues. This invention uses symbolic execution technology, specifically, static analysis to obtain inputs that allow execution of specific code regions. To obtain the specific values that trigger that branch, symbolic execution collects path constraints corresponding to the target branch during the exploration process, and then solves them using a constraint solver. Therefore, by generating the call graph of the contract under test to obtain the contract call chain, and then exploring each call chain using symbolic execution, cross-contract detection can be completed. Simultaneously, the internal branch paths of the contract can be obtained from the control flow graph to detect internal contract vulnerabilities. Furthermore, this invention establishes detection rules for cross-contract vulnerabilities.
[0006] To achieve the above objectives, the present invention provides the following technical solution: a symbolic execution method for detecting smart contract vulnerabilities across contracts, comprising the following steps:
[0007] Step 1: Collect open-source smart contract code: Collect the source code of smart contracts from Etherscan as a dataset, and decompile it into EVM bytecode as input for the program;
[0008] Step 2: Generate Control Flow Graph (CFG) and Call Graph: This module decomposes the EVM bytecode and reconstructs the CFG and call graph, which contain all basic blocks as nodes, as well as some edges representing jumps. The target jump can be determined by studying and locating the corresponding source node.
[0009] Step 3: Path Search Module: Path search is the core module of the symbolic execution tool. By searching and scanning the control flow graph and call graph in Step 2, it completes the analysis and processing of path constraints and other data of each path branch. Here, the path search module is divided into three parts.
[0010] Step 4: constraint solving: the constraint solving module uses the Z3 constraint solver to solve the combined constraints passed by the path constraint module, and detects whether there is a vulnerability;
[0011] Step 5: output results: according to the analysis results of the symbolic execution, the vulnerability detection results are output, in addition, the present application also establishes a vulnerability detection model, which analyzes the results generated by the symbolic execution to explore the vulnerabilities of the smart contract, in addition to the common internal vulnerabilities of the smart contract, the following mainly lists the cross-contract vulnerabilities and vulnerability characteristics:
[0012] Reentrancy vulnerability: reentrancy vulnerability occurs when a function calls another contract externally, using this vulnerability, the attacker can perform recursive callbacks of the main function, thereby producing repeated unexpected loops, for example, when a vulnerable smart contract contains a revoke function, the smart contract can be illegally called by the attacker revoke function countless times to exhaust any available balance contained in the contract;
[0013] Delegation call: code developers use call function (CALL) and delegation call function (DELEGATECALL) to modularize the code written, DELEGATE opcode consists of a function similar to the CALL message, but unlike calling the code of a smart contract, the sender (msg.sender) and value (msg.value) will not be changed, the DELEGATECALL feature indicates that when building custom libraries, defects may be introduced, which may also lead to new vulnerabilities;
[0014] tx-origin: tx.origin is a global variable in Solidity that can traverse the call stack and return the address of the account that originally sent the call, using the tx.origin variable for authentication will cause the contract to be vulnerable to phishing attacks, because the attacker can use the tx.origin global variable to bypass the restrictions and obtain certain permissions.
[0015] Preferably, in step 3, the first part of the graph maintenance module: mainly responsible for updating the control flow graph (CFG) and the call graph (CG) in the path exploration process;
[0016] The maintenance of CFG is implemented by two classes: CFG and CFGNode. The CFG has three data members. The root is the start node of the CFG, that is, the entry of the actual program. The all_visited is a visit flag, which is false by default. When all nodes are visited, the maintenance module modifies it to true, which also marks the end of symbolic execution. The node is a mapping of the name and pointer of all nodes in the CFG, which facilitates the fast retrieval of a certain node. The CFGNode is each node in the CFG. Each CFGNode has four data members. The name is the name of the node. The is_visited is a visit flag, which is false by default. After the node is visited, the maintenance module modifies it to true. The general_child_num indicates the number of child nodes, which is traversed or pruned during searching. The child is a set containing the pointers of the child nodes of the node.
[0017] The CG represents the calling relationship between sub-programs in a program. The data structure is similar to the CFG. However, each node in the CG represents a function, and each edge (f, g) represents that the function f calls the function g. Here, the function calling chain across contracts is mainly mapped, because the CFG inside the contract is explored for the path. If the same path is also contained in the CG, additional overhead will be caused.
[0018] Preferably, in step 3, the second part of the search strategy module: the strategy for path search is the key to the efficiency of symbolic execution. The strategy of the present application is a heuristic search based on coverage optimization and a bounded depth-first search strategy. The strategy can not only ensure high path coverage of symbolic execution and trigger branches at a deep level to capture vulnerabilities, but also reduce the number of program executions, making the algorithm more efficient.
[0019] Heuristic search based on coverage optimization: heuristic search refers to using the information related to the problem as heuristic information to improve the search efficiency and reduce the search times. The present application uses the A* algorithm in the heuristic search. In the A* algorithm, the evaluation function has two parts:
[0020] f(n) = g(n) + h(n)
[0021] Wherein, g(n) represents the cost value of the overhead from the start node to the current node n; h(n) represents the estimated minimum overhead value in the path from the current node n to the target node. Therefore, the evaluation function f(n) is the sum of the minimum overhead g(n) and the subsequent minimum overhead h(n). As long as the value of f(n) is minimized, it is optimal. The A* algorithm makes h(n) satisfy the admissibility and consistency, so that f(n) has an optimal solution.
[0022] Bounding depth-first search strategy: the core idea of this strategy is that after each symbolic execution of the program under test, a set of Boolean expressions containing symbolic constants that satisfy the branch decision conditions in the path can be obtained, that is, the path constraints collected this time; when searching for a path, the constraints at the front of the sequence that have not been negated are negated first, and are ANDed with all the constraints before them, and the solver is used to obtain the input values for the next symbolic execution:
[0023] For the input data, first of all, attention is paid to coverage. Compared with the traditional greedy path search strategy, the heuristic search can cover more branches in a shorter time. The input of the heuristic search is the path constraint (PC) and the updated CFG, CG. The graph is traversed and the encountered branch nodes are added to the sequence. Finally, it is judged whether the branch node sequence is empty. If it is empty, the search fails. Otherwise, branch node screening is performed. The process of branch node screening is to select branch nodes that satisfy the condition that only one of the two successor nodes has been visited from the branch node sequence obtained in the last step and add them to the sequence of branches to be negated. Similarly, it is judged whether the sequence of branches to be negated is empty. If it is empty, the search fails. Otherwise, constraint matching is performed to form new path constraints.
[0024] The heuristic search based on coverage optimization has two output conditions: the search for a new path fails or a new path constraint is obtained. If the latter, the constraint is solved, and then the next round of path search is entered. Otherwise, the bounding depth-first search strategy is scheduled to perform deeper search. Because the bounding depth-first search strategy has low execution efficiency and is prone to "path starvation" when facing large-scale complex programs, it is used as a supplementary search strategy for the heuristic search. At this time, only a small part of the deep branches of the complex program may not have been accessed.
[0025] The basic idea of the bounding depth-first search strategy is that during each path search, the constraint conditions corresponding to the branch nodes at a shallow level of the symbolic execution tree are negated as much as possible, that is, the constraint conditions with a large path depth are negated first, so as to guide the symbolic execution to explore new branch nodes to a greater extent. At the same time, by limiting, some non-optimal solutions are avoided, and the overhead is reduced. If such a constraint exists, the search is performed and a new path constraint is obtained. Otherwise, the path search ends.
[0026] Preferably, in step 3, the third part of the path constraint module: the path constraint module is mainly responsible for constraint negation and combination into new path constraints. Once a new path is found, this module will symbolically execute the path branch currently explored to collect path constraints, and perform preliminary processing on related data and path constraints to input the constraint solving module for solving;
[0027] The path constraint module receives two inputs, one is the negated constraint corresponding to the input value of the current program execution, and the serial number in the original path constraint; the other is a file recording each constraint condition, which contains the path constraint collected during program execution, and the statement type and code block name corresponding to each constraint. According to the principle of path depth first, the new path constraint is combined in turn according to the order of constraint collection.
[0028] Preferably, in step 4, the constraint solving module has two inputs, namely the file symbolic_file recording the symbolic constant used in the path constraint and its actual data type and the path constraint PCnew to be solved. The input format recognized by the Z3 solver is SMT-lib, so the symbolic_file and PCnew are converted into SMT-lib format by the Transform function to obtain z3_input before solving, and then the API function of Z3 is called to load z3_input into the solver z3_solver. The check function is used to judge whether the path constraint is solvable, and if not, the result "unsat" is returned, otherwise the specific value of each symbolic constant and its corresponding solution obtained by the z3_solver is returned to the path search module, but if the path search module fails to find a new path to solve, the result is used for vulnerability analysis.
[0029] Compared with the prior art, the present application has the following advantages:
[0030] The present application uses symbolic execution technology to detect cross-contract vulnerabilities of smart contracts for the first time, which can explore possible vulnerabilities between the tested contracts in a deeper level. The present application focuses on the cross-contract vulnerability problem ignored by many tools, which can more comprehensively guarantee the security of smart contracts. The key of the cross-contract detection of the present application is that the calling graph between smart contracts is generated through EVM bytecode while generating the control flow graph, so that the calling path is obtained for symbolic execution to explore, generate constraints and solve constraints. A symbolic execution method for detecting cross-contract vulnerabilities of smart contracts focuses on the cross-contract vulnerability problem ignored by many mainstream tools in the field of smart contract vulnerability detection, not only uses symbolic execution technology to explore deeper path branches, but also covers more comprehensive cross-contract vulnerabilities, and provides more comprehensive security protection for smart contracts. BRIEF DESCRIPTION OF DRAWINGS
[0031] Figure 1 The present application is a symbolic execution technology for detecting cross-contract vulnerabilities of smart contracts.
[0032] Figure 2 The present application is a path search strategy. DETAILED DESCRIPTION
[0033] The present invention will now be described in more detail by way of examples. These examples are merely illustrative and do not limit the scope of the present invention in any way.
[0034] This invention provides a technical solution: a symbolic execution method for detecting smart contract vulnerabilities across contracts, comprising the following steps:
[0035] Step 1: Collect open-source smart contract code: Collect the source code of smart contracts from Etherscan as a dataset, and decompile it into EVM bytecode as input for the program;
[0036] Step 2: Generate Control Flow Graph (CFG) and Call Graph: This module decomposes the EVM bytecode and reconstructs the CFG and call graph, which contain all basic blocks as nodes, as well as some edges representing jumps. The target jump can be determined by studying and locating the corresponding source node.
[0037] Step 3: Path Search Module: Path search is the core module of the symbolic execution tool. By searching and scanning the control flow graph and call graph in Step 2, it completes the analysis and processing of path constraints and other data of each path branch. Here, the path search module is divided into three parts.
[0038] Step 4: Constraint Solving: The constraint solving module uses the Z3 constraint solver to solve the combined constraints from the path constraint module and detects and analyzes whether there are any vulnerabilities.
[0039] Step 5: Output Results: Based on the analysis results of the symbolic execution, output the vulnerability detection results. Furthermore, this invention establishes a vulnerability detection model to discover smart contract vulnerabilities by analyzing the results of symbolic execution. Besides common smart contract internal vulnerabilities, the following mainly lists cross-contract vulnerabilities and their characteristics:
[0040] Reentrancy vulnerability: A reentrancy vulnerability occurs when a function calls another contract from the outside. By exploiting this vulnerability, an attacker can execute the recursive callback of the main function, thereby creating an unexpected loop that repeats multiple times. For example, when a vulnerable smart contract contains an undo function, the smart contract may be illegally called by an attacker countless times to exhaust any available balance contained in the contract.
[0041] Delegated Call: Code developers use CALL and DELEGATECALL to modularize the code they have written. The DELEGATE opcode consists of a function similar to the CALL message, but, unlike calling the code of a smart contract, the sender (msg.sender) and value (msg.value) are not changed. The DELEGATECALL feature indicates that, when building custom libraries, it may introduce flaws and lead to new vulnerabilities.
[0042] tx-origin: tx.origin is a global variable in Solidity that can traverse the call stack and return the address of the account that originally sent the call. Using the tx.origin variable for authentication can lead to the contract being vulnerable to phishing attacks, as attackers can use the tx.origin global variable to bypass restrictions and gain certain permissions.
[0043] Embodiment One:
[0044] Collecting open-source smart contract code: collect the source code of the smart contract from Etherscan as a dataset, and decompile it into EVM bytecode as the input of the program; generating control flow graph (CFG) and call graph: this module decomposes EVM bytecode and reconstructs CFG and call graph, which contains all basic blocks as nodes and some edges representing jumps, and the target jump can be located by studying the corresponding source node; path search module: path search is the core module of the symbolic execution tool, which scans the control flow graph and call graph in step 2 to collect path constraints and other data analysis and processing of each path branch, which is divided into three parts; constraint solving: the constraint solving module uses the Z3 constraint solver to solve the combined constraints from the path constraint module, and detects whether there is a vulnerability; output results: according to the analysis results of symbolic execution, output the vulnerability detection results, in addition, the present application also establishes a vulnerability detection model to analyze the results generated by symbolic execution to explore the vulnerabilities of smart contracts, in addition to common internal vulnerabilities of smart contracts, the following mainly lists cross-contract vulnerabilities and vulnerability characteristics: reentrant vulnerability: reentrant vulnerability occurs when a function calls another contract through external calls, using this vulnerability, the attacker can perform recursive callbacks of the main function, thereby producing repeated unexpected loops, for example, when a vulnerable smart contract contains a revoke function, the smart contract may be illegally called by the attacker revoke function countless times to exhaust any available balance contained in the contract; Delegation call: code developers use call function (CALL) and delegation call function (DELEGATECALL) to modularize the code they have written, DELEGATE opcode consists of a function similar to a CALL message, but unlike calling the code of a smart contract, the sender (msg.sender) and value (msg.value) will not be changed, the DELEGATECALL feature indicates that when building custom libraries, defects may be introduced, which may also lead to new vulnerabilities; tx-origin: tx.origin is a global variable in Solidity that can traverse the call stack and return the address of the account that originally sent the call, using the tx.origin variable for authentication will cause the contract to be vulnerable to phishing attacks, because the attacker can use the tx.origin global variable to bypass restrictions and obtain certain permissions.
[0045] Embodiment two:
[0046] In embodiment one, the following process is added:
[0047] In step 3, the first part of the graph maintenance module: mainly responsible for updating the control flow graph (CFG) and call graph (CG) during path exploration;
[0048] The maintenance of the CFG is implemented by two classes: CFG and CFGNode. The CFG has three data members. The root is the start node of the CFG, that is, the entry of the actual program. The all_visited is a visit flag, which is false by default. When all nodes are visited, the maintenance module modifies it to true, which also marks the end of symbolic execution. The node is a mapping of the name and pointer of all nodes in the CFG, which facilitates fast retrieval of a certain node. The CFGNode is each node in the CFG. Each CFGNode has four data members. The name is the name of the node. The is_visited is a visit flag, which is false by default. After the node is visited, the maintenance module modifies it to true. The general_child_num indicates the number of child nodes, which is traversed or pruned during searching. The child is a set containing pointers to each child node of the node.
[0049] The CG represents the calling relationship between subprograms in a program. The data structure is similar to the CFG. However, each node in the CG represents a function, and each edge (f, g) represents that the function f calls the function g. Here, the function calling chain across contracts is mainly mapped, because the CFG inside the contract is explored for the path. If the same path is also contained in the CG, additional overhead will be caused.
[0050] In step 3, the second part of the search strategy module: the strategy for path search is the key to the efficiency of symbolic execution. The strategy of the present application is a heuristic search based on coverage optimization and a bounded depth-first search strategy. The strategy can not only ensure high path coverage of symbolic execution and trigger branches at a deep level to capture vulnerabilities, but also reduce the number of program executions, making the algorithm more efficient.
[0051] Heuristic search based on coverage optimization: heuristic search refers to using current information related to the problem as heuristic information to improve search efficiency and reduce search times. The present application uses the A* algorithm in heuristic search. In the A* algorithm, the evaluation function has two parts:
[0052] f(n) = g(n) + h(n)
[0053] Wherein, g(n) represents the cost value of the overhead from the start node to the current node n; h(n) represents the estimated minimum overhead value in the path from the current node n to the target node. Therefore, the evaluation function f(n) is the sum of the minimum overhead g(n) and the subsequent minimum overhead h(n). As long as the value of f(n) is minimized, it is optimal. The A* algorithm makes h(n) satisfy the admissibility and consistency, so that f(n) has an optimal solution.
[0054] Bounding depth first search strategy: the core idea of this strategy is that after each symbolic execution of the program to be tested, a set of Boolean expressions containing symbolic constants that satisfy the branch decision conditions in the path can be obtained, that is, the path constraints collected this time; When searching for a path, the constraints at the front of the sequence that have not been negated are negated first, and are ANDed with all previous constraints. The solver is used to obtain the input value for the next symbolic execution:
[0055] For the input data, first of all, pay attention to coverage. Compared with the traditional greedy path search strategy, the heuristic search can cover more branches in a shorter time. The input of the heuristic search is the path constraint (PC) and the updated CFG, CG. The graph is traversed and the encountered branch nodes are added to the sequence. Finally, it is judged whether the branch node sequence is empty. If it is empty, the search fails. Otherwise, branch node screening is performed. The process of branch node screening is to select branch nodes that satisfy the condition that only one of the two successor nodes has been visited from the branch node sequence obtained in the last step and add them to the sequence of branches to be negated. Similarly, it is judged whether the sequence of branches to be negated is empty. If it is empty, the search fails. Otherwise, constraint matching is performed to form new path constraints.
[0056] The heuristic search based on coverage optimization has two output conditions: the search for a new path fails or a new path constraint is obtained. If the latter, the constraint is solved, and then the next round of path search is entered. Otherwise, the bounding depth first search strategy is scheduled to perform deeper search. Because the bounding depth first search strategy has low execution efficiency and is prone to "path starvation" when facing large-scale complex programs, it is used as a supplementary search strategy for the heuristic search. At this time, the complex program may only have a small part of the deep branches that have not been accessed.
[0057] The basic idea of the bounding depth first search strategy is that during each path search, the constraint conditions corresponding to the branch nodes at the shallow level of the symbolic execution tree are negated as much as possible, that is, the constraint conditions with large path depth are negated first, so as to guide the symbolic execution to explore new branch nodes to a greater extent. At the same time, through bounding, some non-optimal solutions are avoided, and the overhead is reduced. If such a constraint exists, the search is performed and a new path constraint is obtained. Otherwise, the path search ends.
[0058] In step 3, the third part of the path constraint module: the path constraint module is mainly responsible for constraint negation and combination into new path constraints. Once a new path is found, this module will symbolically execute the path branch currently explored to collect path constraints, and perform preliminary processing on related data and path constraints to input the constraint solving module for solving;
[0059] The path constraint module receives two inputs, one is the negated constraint corresponding to the input value of the current program execution, the serial number in the original path constraint; the other is a file recording each constraint condition, which contains the path constraint collected during program execution, and the statement type and code block name corresponding to each constraint. According to the principle of path depth first, the new path constraint is combined in turn by negating according to the order of constraint collection
[0060] Collecting open-source smart contract code: Collecting the source code of the smart contract from Etherscan as a dataset, and decompiling it into EVM bytecode as the input of the program; Generating control flow graph (CFG) and call graph: This module decomposes the EVM bytecode and reconstructs the CFG and call graph, which contains all basic blocks as nodes and some edges representing jumps, and the target jump can be located by studying the corresponding source node; Path search module: Path search is the core module of the symbolic execution tool, which scans the control flow graph and call graph in step 2 to collect path constraints and other data of each path branch, and the path search module is divided into three parts; The first part is the graph maintenance module: mainly responsible for updating the control flow graph (CFG) and call graph (CG) during path exploration; The maintenance of CFG is realized by two classes: CFG and CFGNode, CFG has three data members, root is the starting node of CFG, i.e. the entry of the actual program, all_visited is the access mark, which is false by default, when all nodes are visited, the maintenance module will modify it to true, which also marks the end of symbolic execution, node is a mapping of the name and pointer of all nodes in CFG, which facilitates fast retrieval of a node, CFGNode is each node in CFG, each CFGNode has four data members, name is the name of the node, is_visited is the access mark, which is false by default, the node is accessed, and the maintenance module modifies it to true, general_child_num indicates the number of its child nodes, which will be traversed or pruned during search, and child is a set containing pointers to each child node of the node; CG represents the calling relationship between subprograms in a program, and the data structure is similar to CFG, but each node in CG represents a function, and each edge (f, g) represents that function f calls function g, here we mainly build a graph for cross-contract function call chains, because the CFG inside the contract is explored, if the same path is also included in the CG, it will cause additional overhead. The second part is the search strategy module: the strategy for path search is the key to the efficiency of symbolic execution, the strategy of the present invention is a combination of heuristic search based on coverage optimization and bounded depth-first search strategy, which not only ensures high path coverage of symbolic execution and triggers branches at a deeper level to capture vulnerabilities, but also reduces the number of program executions, making the algorithm more efficient; Heuristic search based on coverage optimization: heuristic search refers to using current information related to the problem as heuristic information to improve search efficiency and reduce search frequency, the present invention uses A* algorithm in heuristic search, in A* algorithm, the evaluation function has two parts: f(n) = g(n) + h(n) where g(n) represents the cost value of the overhead from the starting node to the current node n;h(n) represents the estimated minimum cost value of the path from the current node n to the target node, so the evaluation function f(n) is the sum of the minimum cost g(n) and the subsequent minimum cost h(n), as long as the value of f(n) is minimized, it is optimal, and the A* algorithm makes h(n) satisfy the admissibility and consistency, so that f(n) always has an optimal solution; the bounded depth-first search strategy: the core idea of this strategy is that after each symbolic execution of the program to be tested, a set of Boolean expression sequences containing symbolic constants that satisfy the branch conditions in the path can be obtained, that is, the path constraints collected this time; during path search, the constraints at the front of the sequence that have not undergone negation operation are preferentially negated, and are ANDed with all the constraints before them, and the solver is used to obtain the input values for the next symbolic execution: for the input data, first of all, pay attention to coverage, compared with the traditional greedy path search strategy, heuristic search can cover more branches in a shorter time, the input of heuristic search is path constraint (PC) and updated CFG, CG, traverse the graph and add the encountered branch nodes to the sequence, finally judge whether the branch node sequence is empty, if empty, the search fails, otherwise, branch node screening is performed, the process of branch node screening is to select branch nodes that satisfy the condition that only one of the two successor nodes has been visited from the branch node sequence obtained in the last step, and add them to the sequence of branches to be negated, similarly, judge whether the sequence of branches to be negated is empty, if empty, feedback search failure; otherwise, perform constraint matching to form new path constraints; there are two output conditions for heuristic search based on coverage optimization: search for new paths fails or get new path constraints, if the latter, perform constraint solving, then enter the next round of path search, otherwise, the bounded depth-first search strategy is rescheduled as a supplementary search strategy for heuristic search, because the bounded depth-first search strategy has low execution efficiency and is prone to "path starvation" when dealing with large-scale complex programs, so it is used as a supplementary search strategy for heuristic search, at this time, the complex program may only have a small part of the deep branches not visited; the basic idea of the bounded depth-first search strategy is to negate the constraint conditions corresponding to the branch nodes at the shallow level of the symbolic execution tree as much as possible during each path search, that is, preferentially negate the constraint conditions corresponding to the branch nodes at the deep level, so as to guide the symbolic execution to explore new branch nodes to a greater extent, and at the same time, avoid some non-optimal solutions through limiting, reduce the cost, if there is such a constraint, search and get new path constraints, otherwise, the path search ends. The third part is the path constraint module: the path constraint module is mainly responsible for constraint negation and combination into new path constraints, once a new path is found, this module will symbolically execute the path branch currently explored to collect path constraints, and perform preliminary processing on related data and path constraints to input the constraint solving module for solving;The path constraint module receives two inputs, one is the negated constraint corresponding to the input value of this program execution, the serial number in the original path constraint; the other is a file recording each constraint condition, which contains the path constraint collected during program execution, and the statement type and code block name corresponding to each constraint, according to the principle of path depth first, in the order of constraint collection, in turn negated and combined into new path constraints; constraint solving: the constraint solving module uses the Z3 constraint solver to solve the combined constraint from the path constraint module, and detects and analyzes whether there is a vulnerability; output result: according to the analysis result of symbolic execution, the vulnerability detection result is output, in addition, the present application also establishes a vulnerability detection model, which analyzes the results generated by symbolic execution to explore the vulnerabilities of smart contracts, in addition to common internal vulnerabilities of smart contracts, the following mainly lists cross-contract vulnerabilities and vulnerability characteristics: reentrant vulnerability: reentrant vulnerability occurs when a function calls another contract through external call, using this vulnerability, the attacker can execute the recursive callback of the main function, thereby producing repeated unexpected loops, for example, when a vulnerable smart contract contains a revoke function, the smart contract may be illegally called by the attacker revoke function countless times to exhaust any available balance contained in the contract; Delegation call: code developers use call function (CALL) and delegation call function (DELEGATECALL) to modularize the code written, DELEGATE operation code is composed of a function similar to the CALL message, but, unlike calling the code of a smart contract, the sender (msg.sender) and value (msg.value) will not be changed, the DELEGATECALL feature indicates that when building custom libraries, defects may be introduced, which may also lead to new vulnerabilities; tx-origin: tx.origin is a global variable in Solidity, which can traverse the call stack and return the address of the account that originally sent the call, using the tx.origin variable for authentication will cause the contract to be vulnerable to phishing attacks, because the attacker can use the tx.origin global variable to bypass restrictions and obtain certain permissions.
[0061] Embodiment three:
[0062] In embodiment two, the following process is added:
[0063] In step 4, the constraint solving module has two inputs, the file symbolic_file recording the symbolic constants used in the path constraint and its actual data type and the path constraint PCnew to be solved. The input format recognized by the Z3 solver is SMT-lib, so the symbolic_file and PCnew are converted into SMT-lib format by the Transform function to obtain z3_input before solving. Then the API function of Z3 is called to load z3_input into the solver z3_solver. The left rear determines whether the path constraint is solvable by the check function. If not, "unsat" is returned. Otherwise, each symbolic constant and its corresponding specific value solved by the z3_solver are returned to the path search module. If the path search module fails to find a new path to solve, the result is analyzed for vulnerability.
[0064] Collecting open-source smart contract code: Collecting the source code of the smart contract from Etherscan as a dataset, and decompiling it into EVM bytecode as the input of the program; Generating control flow graph (CFG) and call graph: This module decomposes the EVM bytecode and reconstructs the CFG and call graph, which contains all basic blocks as nodes and some edges representing jumps, and the target jump can be located by studying the corresponding source node; Path search module: Path search is the core module of the symbolic execution tool, which scans the control flow graph and call graph in step 2 to collect path constraints and other data of each path branch, and the path search module is divided into three parts; The first part is the graph maintenance module: mainly responsible for updating the control flow graph (CFG) and call graph (CG) during path exploration; The maintenance of CFG is realized by two classes: CFG and CFGNode, CFG has three data members, root is the starting node of CFG, i.e. the entry of the actual program, all_visited is the access mark, which is false by default, when all nodes are visited, the maintenance module will modify it to true, which also marks the end of symbolic execution, node is a mapping of the name and pointer of all nodes in CFG, which facilitates fast retrieval of a node, CFGNode is each node in CFG, each CFGNode has four data members, name is the name of the node, is_visited is the access mark, which is false by default, the node is accessed, and the maintenance module modifies it to true, general_child_num indicates the number of its child nodes, which will be traversed or pruned during search, and child is a set containing pointers to each child node of the node; CG represents the calling relationship between subprograms in a program, and the data structure is similar to CFG, but each node in CG represents a function, and each edge (f, g) represents that function f calls function g, here we mainly build a graph for cross-contract function call chains, because the CFG inside the contract is explored, if the same path is also included in the CG, it will cause additional overhead. The second part is the search strategy module: the strategy of path search is the key to the efficiency of symbolic execution, the strategy of the present invention is a combination of heuristic search based on coverage optimization and bounded depth-first search strategy, which not only ensures high path coverage of symbolic execution and triggers branches at a deeper level to capture vulnerabilities, but also reduces the number of program executions, making the algorithm more efficient; Heuristic search based on coverage optimization: heuristic search refers to using current information related to the problem as heuristic information to improve search efficiency and reduce search frequency, the present invention uses A* algorithm in heuristic search, in A* algorithm, the evaluation function has two parts: f(n) = g(n) + h(n) where g(n) represents the cost value of the overhead from the starting node to the current node n;h(n) represents the estimated minimum cost value of the path from the current node n to the target node, so the evaluation function f(n) is the sum of the minimum cost g(n) and the subsequent minimum cost h(n), as long as the value of f(n) is minimized, it is optimal, and the A* algorithm makes h(n) satisfy the admissibility and consistency, so that f(n) always has an optimal solution; the bounded depth-first search strategy: the core idea of this strategy is that after each symbolic execution of the program to be tested, a set of Boolean expression sequences containing symbolic constants that satisfy the branch conditions in the path can be obtained, that is, the path constraints collected this time; during path search, the constraints at the front of the sequence that have not undergone negation operation are preferentially negated, and are ANDed with all the constraints before them, and the solver is used to obtain the input values for the next symbolic execution: for the input data, first of all, pay attention to coverage, compared with the traditional greedy path search strategy, heuristic search can cover more branches in a shorter time, the input of heuristic search is path constraint (PC) and updated CFG, CG, traverse the graph and add the encountered branch nodes to the sequence, finally judge whether the branch node sequence is empty, if empty, the search fails, otherwise, branch node screening is performed, the process of branch node screening is to select branch nodes that satisfy the condition that only one of the two successor nodes has been visited from the branch node sequence obtained in the last step, and add them to the sequence of branches to be negated, similarly, judge whether the sequence of branches to be negated is empty, if empty, feedback search failure; otherwise, perform constraint matching to form new path constraints; there are two output conditions for heuristic search based on coverage optimization: search for new paths fails or get new path constraints, if the latter, perform constraint solving, then enter the next round of path search, otherwise, the bounded depth-first search strategy is rescheduled as a supplementary search strategy for heuristic search, because the bounded depth-first search strategy has low execution efficiency and is prone to "path starvation" when facing large-scale complex programs, so it is used as a supplementary search strategy for heuristic search, at this time, the complex program may only have a small part of the deep branches not visited; the basic idea of the bounded depth-first search strategy is to negate the constraint conditions corresponding to the branch nodes at the shallow level of the symbolic execution tree as much as possible during each path search, that is, preferentially negate the constraint conditions corresponding to the branch nodes at the deep level, so as to guide the symbolic execution to explore new branch nodes to a greater extent, and at the same time, avoid some non-optimal solutions through limiting, reduce the cost, if there is such a constraint, search and get new path constraints, otherwise, the path search ends. The third part is the path constraint module: the path constraint module is mainly responsible for constraint negation and combination into new path constraints, once a new path is found, this module will symbolically execute the path branch currently explored to collect path constraints, and perform preliminary processing on related data and path constraints to input the constraint solving module for solving;The path constraint module receives two inputs, one is the negated constraint corresponding to the input value of this program execution, and the serial number in the original path constraint; the other is a file recording each constraint condition, which contains the path constraint collected during program execution, and the statement type and code block name corresponding to each constraint, according to the principle of path depth first, in the order of constraint collection, in turn negated and combined into new path constraints; constraint solving: the constraint solving module uses Z3 constraint solver to solve the combined constraint from the path constraint module, and detects and analyzes whether there is a vulnerability; the constraint solving module has two inputs, respectively symbolic_file and PCnew recording the symbolic constant used in the path constraint and its actual data type, and the path constraint to be solved, the input format recognized by the Z3 solver is SMT-lib, so before solving, first convert symbolic_file and PCnew into SMT-lib format through the Transform function to get z3_input, then call the API function of Z3 to load z3_input into the solver z3_solver; left back through the check function to judge whether the path constraint is solvable, if not, return "unsat", otherwise return each symbolic constant and its corresponding specific value solved by z3_solver, and return to the path search module, but if the path search module fails to find a new path to solve, then according to the result, analyze the vulnerability and output the result: according to the analysis result of symbolic execution, output the vulnerability detection result, in addition, the present application also establishes a vulnerability detection model, which analyzes the results generated by symbolic execution to explore the vulnerabilities of smart contracts, in addition to common internal vulnerabilities of smart contracts, the following mainly lists cross-contract vulnerabilities and vulnerability characteristics: reentrant vulnerability: reentrant vulnerability occurs when a function calls another contract through external call, using this vulnerability, the attacker can execute the recursive callback of the main function, thereby producing repeated unexpected loops, for example, when a vulnerable smart contract contains a revoke function, the smart contract may be illegally called by the attacker revoke function countless times to exhaust any available balance contained in the contract; Delegation call: code developers use call function (CALL) and delegation call function (DELEGATECALL) to modularize the code written, DELEGATE operation code is composed of a function similar to the CALL message, but unlike calling the code of a smart contract, the sender (msg.sender) and value (msg.value) will not be changed, the DELEGATECALL feature indicates that when building custom libraries, defects may be introduced, which may also lead to new vulnerabilities;tx-origin: tx.origin is a global variable in Solidity that can traverse the call stack and return the address of the account that originally sent the call. Using the tx.origin variable for authentication can lead to a contract being vulnerable to phishing attacks, as an attacker can use the tx.origin global variable to bypass restrictions and gain certain permissions.
[0065] While embodiments of the present application have been shown and described with reference to particular embodiments thereof, it will be understood by those of ordinary skill in the art that various changes in form and details can be made therein without departing from the spirit and scope of the application as defined by the following claims and their equivalents.
Claims
1. A method for detecting vulnerabilities of a smart contract by symbolic execution across contracts, the method comprising: receiving a smart contract; generating a symbolic execution trace of the smart contract; and detecting vulnerabilities of the smart contract based on the symbolic execution trace. The method comprises the following steps: Step 1: Collecting open-source smart contract code: Collecting the source code of the smart contract from Etherscan as a dataset, and decompiling it into EVM bytecode as the input of the program; Step 2: Generating control flow graph CFG and call graph: This module decomposes the EVM bytecode and reconstructs the CFG and call graph, which contains all basic blocks as nodes and some edges representing jumps, and determines the target jump by locating the corresponding source node through research; Step 3: Path search module: Path search is the core module of the symbolic execution tool, which scans the control flow graph and call graph in step 2 to collect path constraints and other data analysis and processing of each path branch, and the path search module is divided into three parts, the first part is the graph maintenance module: mainly responsible for updating the control flow graph CFG and the call graph CG during path exploration; The maintenance of CFG is realized by two classes: CFG and CFGNode, CFG has three data members, root is the starting node of CFG, that is, the entrance of the actual program, all_visited is the access mark, which is false by default, when all nodes are accessed, the maintenance module will modify it to true, which also marks the end of symbolic execution, node is a mapping, the name and pointer of all nodes in CFG are one-to-one corresponding, which is convenient for quickly retrieving a node, CFGNode is each node in CFG, each CFGNode has four data members, name is the name of the node, is_visited is the access mark, which is false by default, the node is accessed, and the maintenance module will modify it to true, general_child_num represents the number of its child nodes, which will be traversed or pruned during search, and child is a set containing pointers to each child node of the node; CG represents the calling relationship between subprograms in a program, and the data structure is similar to CFG, but each node in CG represents a function, and each edge (f, g) represents that function f calls function g, here the function call chain across contracts is graphed, because the CFG inside the contract is explored, if the same path is also included in the CG, it will cause additional overhead; Step 4: constraint solving: the constraint solving module uses the Z3 constraint solver to solve the combined constraints from the path constraint module to detect whether there is a vulnerability, the constraint solving module has two inputs, respectively, the file symbolic_file recording the symbolic constants used in the path constraint and its actual data type and the path constraint PCnew to be solved, the input format identified by the Z3 solver is SMT-lib, so before solving, first convert symbolic_file and PCnew into SMT-lib format through the Transform function to get z3_input, then call the API function of Z3 to load z3_input into the solver z3_solver; the left rear judges whether the path constraint is solvable through the check function, if not, returns "unsat", otherwise returns each symbolic constant and its corresponding specific value solved by z3_solver, and returns to the path search module, but if the path search module fails to find a new path to solve, then analyze the vulnerability according to the result; Step 5: output result: according to the analysis result of symbolic execution, output the vulnerability detection result. 2.The method of claim 1, wherein: In step 3, the second part of the search strategy module: comprehensive heuristic search based on coverage optimization and bounded depth-first search strategy; heuristic search based on coverage optimization: use A* algorithm in heuristic search, in A* algorithm, the evaluation function has two parts: f(n)=g(n)+h(n) Where, g(n) represents the cost value of the overhead from the starting node to the current node n; h(n) represents the estimated minimum overhead value in the path from the current node n to the target node, therefore, the evaluation function f(n) is the sum of the minimum overhead g(n) and the subsequent minimum overhead h(n), as long as the value of f(n) is minimized, it is optimal, and A* algorithm makes h(n) satisfy the admissibility and consistency, so that f(n) always has an optimal solution; Bounded depth-first search strategy: the core idea of this strategy is that after each symbolic execution of the program under test, a sequence of Boolean expressions containing symbolic constants that satisfy the branch conditions in the path can be obtained, that is, the path constraints collected this time; when searching the path, the constraints in the front of the sequence that have not been negated are negated first, and are ANDed with all the constraints before them, and the solver is used to solve to get the input value of the next symbolic execution: For the input data, first of all, attention to coverage, compared with the traditional greedy path search strategy, heuristic search can cover more branches in a shorter time, the input of heuristic search is path constraint PC and updated CFG, CG, traverse the graph and add the branch node encountered to the sequence, finally judge whether the branch node sequence is empty, if empty, the search fails, otherwise, branch node screening, the process of branch node screening is to select the branch node that meets the condition that only one of the two successor nodes is visited from the branch node sequence obtained in the last step, and add it to the sequence of branches to be negated in turn, similarly, judge whether the sequence of branches to be negated is empty, if empty, feedback search failure; Otherwise, constraint matching is performed to form new path constraints; Heuristic search based on coverage optimization has two output conditions: search for new path fails or gets new path constraint, if the latter, constraint solving is performed, and then the next round of path search is entered, otherwise, the limit depth first search strategy is scheduled again to search deeper, because the limit depth first search strategy has low execution efficiency and is easy to fall into "path starvation" state when facing large-scale complex programs, so it is used as a supplementary search strategy for heuristic search, at this time, the complex program may only have a small part of deep branches not visited; The basic idea of the limit depth first search strategy is to take the constraint condition corresponding to the branch node at a shallow level on the symbolic execution tree as a priority, that is, to take the constraint condition with large path depth as a priority, so as to guide the symbolic execution to explore new branch nodes to a greater extent, and at the same time, through limiting, some non-optimal solutions are avoided and the overhead is reduced, if such constraint exists, search and get new path constraint, otherwise, path search ends.
3. The method of claim 1, wherein the method further comprises: determining a contract violation of the smart contract based on the symbolic execution result. In step 3, the third part of the path constraint module: the path constraint module is responsible for constraint negation and combination into new path constraints, once a new path is found, this module will symbolically execute the current explored path branch to collect path constraints, and pre-process related data and path constraints for input into the constraint solving module for solving; The path constraint module receives two inputs, one is the negated constraint corresponding to the input value of the current program execution, and the sequence number in the original path constraint; The other is a file recording each constraint condition, which contains the path constraints collected during program execution, as well as the statement type and code block name corresponding to each constraint, according to the path depth first principle, the constraints are negated and combined into new path constraints in the order of collection.
Citation Information
Patent Citations
WASM smart contract vulnerability detection method based on symbolic execution
CN113672515A
Intelligent contract vulnerability detection method based on cross-modal knowledge distillation
CN114841318A