|
| 1 | +version: 1.0.6 |
| 2 | +author: "@Seecoalba" |
| 3 | +name: "Unverified from Address Usage in transferFrom" |
| 4 | +severity: "Low" |
| 5 | +precision: "Medium" |
| 6 | +description: "Employing an unverified from address in transferFrom or safeTransferFrom operations poses a substantial risk of fund loss. This risk arises from the potential for any party to execute token transfers from the specified from address, contingent upon having received adequate approval." |
| 7 | +impact: "Unauthorized token transactions emanating from the specified from address, if it has been authorized, which could lead to significant asset depletion." |
| 8 | +action-items: |
| 9 | + - "Verify that the from address used in calls to transferFrom or safeTransferFrom is validated properly." |
| 10 | +references: |
| 11 | + - "https://solidity.readthedocs.io/en/latest/types.html#members-of-addresses" |
| 12 | +reports: [] |
| 13 | +vulnerable_contracts: |
| 14 | + - "../vulnerable_contracts/unverified_from_address_in_transfer/" |
| 15 | +python: | |
| 16 | + results = [] |
| 17 | + # Retrieve all function call nodes from the AST. |
| 18 | + function_calls = get_nodes_by_types(ast_data, ["FunctionCall"]) |
| 19 | + |
| 20 | + # Define a function to check the validity of the 'from' argument in a function call. |
| 21 | + def check_argument_validity(function_call): |
| 22 | + # Extract arguments from the function call. |
| 23 | + args = function_call.get("arguments", []) |
| 24 | + # Determine the index of the 'from' argument based on the number of arguments. |
| 25 | + arg_index = 0 if len(args) == 3 else 1 if len(args) == 4 else -1 |
| 26 | + |
| 27 | + # Ensure the arg_index is valid. |
| 28 | + if arg_index not in [-1, len(args)]: |
| 29 | + # Get the 'from' argument based on its index. |
| 30 | + arg = args[arg_index] |
| 31 | + # If the 'from' argument is accessed via a member access, |
| 32 | + # verify if it securely originates from msg.sender. |
| 33 | + if arg.get("nodeType") == "MemberAccess": |
| 34 | + base_expr = arg.get("expression", {}) |
| 35 | + if arg.get("memberName") == "sender" and base_expr.get("name") == "msg": |
| 36 | + return False |
| 37 | + # If the 'from' argument is a result of a function call, |
| 38 | + # verify if it safely refers to 'this'. |
| 39 | + elif arg.get("nodeType") == "FunctionCall": |
| 40 | + expr = arg.get("expression", {}) |
| 41 | + if expr.get("typeName", {}).get("name") == "address" and \ |
| 42 | + arg.get("arguments", [])[0].get("name") == "this": |
| 43 | + return False |
| 44 | + # Consider the call valid if none of the above conditions are met. |
| 45 | + return True |
| 46 | + |
| 47 | + # Iterate through each function call found in the AST. |
| 48 | + for function_call in function_calls: |
| 49 | + # Extract the name of the function being called. |
| 50 | + function_name = function_call.get("expression", {}).get("memberName", "") |
| 51 | + # If the function is 'transferFrom' or 'safeTransferFrom', and the 'from' argument is considered valid, |
| 52 | + # add the function call to the results. |
| 53 | + if function_name in ["transferFrom", "safeTransferFrom"] and check_argument_validity(function_call): |
| 54 | + results.append(function_call) |
0 commit comments