From 69d3c257b4285c0935c797ff99f52cb44448564e Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Wed, 28 Feb 2024 17:36:49 -0600 Subject: [PATCH 1/3] feat: add out-of-order-retryable ticket detector --- slither/detectors/all_detectors.py | 3 + .../functions/out_of_order_retryable.py | 143 ++++++++++++++++++ ...e_0_8_20_out_of_order_retryable_sol__0.txt | 8 + .../0.8.20/out_of_order_retryable.sol | 82 ++++++++++ .../out_of_order_retryable.sol-0.8.20.zip | Bin 0 -> 5285 bytes tests/e2e/detectors/test_detectors.py | 5 + 6 files changed, 241 insertions(+) create mode 100644 slither/detectors/functions/out_of_order_retryable.py create mode 100644 tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt create mode 100644 tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol create mode 100644 tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol-0.8.20.zip diff --git a/slither/detectors/all_detectors.py b/slither/detectors/all_detectors.py index fab9562d20..fc33ddfd38 100644 --- a/slither/detectors/all_detectors.py +++ b/slither/detectors/all_detectors.py @@ -97,3 +97,6 @@ from .operations.incorrect_exp import IncorrectOperatorExponentiation from .statements.tautological_compare import TautologicalCompare from .statements.return_bomb import ReturnBomb + + +from .functions.out_of_order_retryable import OutOfOrderRetryable diff --git a/slither/detectors/functions/out_of_order_retryable.py b/slither/detectors/functions/out_of_order_retryable.py new file mode 100644 index 0000000000..b273052fef --- /dev/null +++ b/slither/detectors/functions/out_of_order_retryable.py @@ -0,0 +1,143 @@ +from typing import List + +from slither.core.cfg.node import Node +from slither.core.declarations import Function, FunctionContract +from slither.slithir.operations import HighLevelCall +from slither.detectors.abstract_detector import AbstractDetector, DetectorClassification +from slither.utils.output import Output + + +class OutOfOrderRetryable(AbstractDetector): + + ARGUMENT = "out-of-order-retryable" + HELP = "Out-of-order retryable transactions" + IMPACT = DetectorClassification.MEDIUM + CONFIDENCE = DetectorClassification.MEDIUM + + WIKI = "https://github.com/crytic/slither/wiki/Detector-Documentation#out-of-order-retryable-transactions" + + WIKI_TITLE = "Out-of-order retryable transactions" + WIKI_DESCRIPTION = "Out-of-order retryable transactions" + + # region wiki_exploit_scenario + WIKI_EXPLOIT_SCENARIO = """ +```solidity +contract L1 { + function doStuffOnL2() external { + // Retryable A + IInbox(inbox).createRetryableTicket({ + to: l2contract, + l2CallValue: 0, + maxSubmissionCost: maxSubmissionCost, + excessFeeRefundAddress: msg.sender, + callValueRefundAddress: msg.sender, + gasLimit: gasLimit, + maxFeePerGas: maxFeePerGas, + data: abi.encodeCall(l2contract.claim_rewards, ()) + }); + // Retryable B + IInbox(inbox).createRetryableTicket({ + to: l2contract, + l2CallValue: 0, + maxSubmissionCost: maxSubmissionCost, + excessFeeRefundAddress: msg.sender, + callValueRefundAddress: msg.sender, + gasLimit: gas, + maxFeePerGas: maxFeePerGas, + data: abi.encodeCall(l2contract.unstake, ()) + }); + } +} + +contract L2 { + function claim_rewards() public { + // rewards is computed based on balance and staking period + uint unclaimed_rewards = _compute_and_update_rewards(); + token.safeTransfer(msg.sender, unclaimed_rewards); + } + + // Call claim_rewards before unstaking, otherwise you lose your rewards + function unstake() public { + _free_rewards(); // clean up rewards related variables + balance = balance[msg.sender]; + balance[msg.sender] = 0; + staked_token.safeTransfer(msg.sender, balance); + } +} +``` +Bob calls `doStuffOnL2` but the first retryable ticket calling `claim_rewards` fails. The second retryable ticket calling `unstake` is executed successfully. As a result, Bob loses his rewards.""" + # endregion wiki_exploit_scenario + + WIKI_RECOMMENDATION = "Do not rely on the order or successful execution of retryable tickets." + + key = "OUTOFORDERRETRYABLE" + + def _detect_multiple_tickets( + self, function: FunctionContract, node: Node, visited: List[Node] + ) -> None: + if node in visited: + return + + visited = visited + [node] + + fathers_context = [] + + for father in node.fathers: + if self.key in father.context: + fathers_context += father.context[self.key] + + # Exclude path that dont bring further information + if node in self.visited_all_paths: + if all(f_c in self.visited_all_paths[node] for f_c in fathers_context): + return + else: + self.visited_all_paths[node] = [] + + self.visited_all_paths[node] = self.visited_all_paths[node] + fathers_context + + if self.key not in node.context: + node.context[self.key] = fathers_context + + # analyze node + for ir in node.irs: + if ( + isinstance(ir, HighLevelCall) + and isinstance(ir.function, Function) + and ir.function.name == "createRetryableTicket" + ): + node.context[self.key].append(node) + + if len(node.context[self.key]) > 1: + self.results.append(node.context[self.key]) + return + + for son in node.sons: + self._detect_multiple_tickets(function, son, visited) + + def _detect(self) -> List[Output]: + results = [] + + # pylint: disable=attribute-defined-outside-init + self.results = [] + self.visited_all_paths = {} + + for contract in self.compilation_unit.contracts: + for function in contract.functions: + if ( + function.is_implemented + and function.contract_declarer == contract + and function.entry_point + ): + function.entry_point.context[self.key] = [] + self._detect_multiple_tickets(function, function.entry_point, []) + + for multiple_tickets in self.results: + info = ["Multiple retryable tickets created in the same function:\n"] + + for x in multiple_tickets: + info += ["\t -", x, "\n"] + + json = self.generate_result(info) + results.append(json) + + return results diff --git a/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt new file mode 100644 index 0000000000..4f07601b1b --- /dev/null +++ b/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt @@ -0,0 +1,8 @@ +Multiple retryable tickets created in the same function: + -Y(msg.sender).createRetryableTicket(address(1),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#62-70) + -Y(msg.sender).createRetryableTicket(address(2),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#72-80) + +Multiple retryable tickets created in the same function: + -Y(msg.sender).createRetryableTicket(address(1),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#40-48) + -Y(msg.sender).createRetryableTicket(address(2),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#50-58) + diff --git a/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol b/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol new file mode 100644 index 0000000000..afd4d274b8 --- /dev/null +++ b/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol @@ -0,0 +1,82 @@ +interface Y { + function createRetryableTicket( + address to, + uint256 l2CallValue, + uint256 maxSubmissionCost, + address excessFeeRefundAddress, + address callValueRefundAddress, + uint256 gasLimit, + uint256 maxFeePerGas, + bytes calldata data + ) external payable returns (uint256); +} + +contract X { +function good() external { + if (true) { + Y(msg.sender).createRetryableTicket( + address(1), + 0, + 0, + address(0), + address(0), + 0, + 0, + ""); + } else { + Y(msg.sender).createRetryableTicket( + address(2), + 0, + 0, + address(0), + address(0), + 0, + 0, + ""); + } +} +function bad1() external { + if (true) { + Y(msg.sender).createRetryableTicket( + address(1), + 0, + 0, + address(0), + address(0), + 0, + 0, + ""); + } + Y(msg.sender).createRetryableTicket( + address(2), + 0, + 0, + address(0), + address(0), + 0, + 0, + ""); + +} +function bad2() external { + Y(msg.sender).createRetryableTicket( + address(1), + 0, + 0, + address(0), + address(0), + 0, + 0, + ""); + + Y(msg.sender).createRetryableTicket( + address(2), + 0, + 0, + address(0), + address(0), + 0, + 0, + ""); +} +} \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol-0.8.20.zip b/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol-0.8.20.zip new file mode 100644 index 0000000000000000000000000000000000000000..1baadba84d201e0a4cecf23dae4f0c2af3f8445c GIT binary patch literal 5285 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 Date: Wed, 28 Feb 2024 17:58:10 -0600 Subject: [PATCH 2/3] make it interprocedural (only 1 call deep) --- slither/detectors/all_detectors.py | 2 -- .../functions/out_of_order_retryable.py | 17 ++++++++++--- ...e_0_8_20_out_of_order_retryable_sol__0.txt | 4 +++ .../0.8.20/out_of_order_retryable.sol | 23 ++++++++++++++++++ .../out_of_order_retryable.sol-0.8.20.zip | Bin 5285 -> 5942 bytes 5 files changed, 41 insertions(+), 5 deletions(-) diff --git a/slither/detectors/all_detectors.py b/slither/detectors/all_detectors.py index fc33ddfd38..4151759f0d 100644 --- a/slither/detectors/all_detectors.py +++ b/slither/detectors/all_detectors.py @@ -97,6 +97,4 @@ from .operations.incorrect_exp import IncorrectOperatorExponentiation from .statements.tautological_compare import TautologicalCompare from .statements.return_bomb import ReturnBomb - - from .functions.out_of_order_retryable import OutOfOrderRetryable diff --git a/slither/detectors/functions/out_of_order_retryable.py b/slither/detectors/functions/out_of_order_retryable.py index b273052fef..f483dc64aa 100644 --- a/slither/detectors/functions/out_of_order_retryable.py +++ b/slither/detectors/functions/out_of_order_retryable.py @@ -98,12 +98,23 @@ def _detect_multiple_tickets( if self.key not in node.context: node.context[self.key] = fathers_context - # analyze node - for ir in node.irs: + # include ops from internal function calls + internal_ops = [] + for internal_call in node.internal_calls: + if isinstance(internal_call, Function): + internal_ops += internal_call.all_slithir_operations() + + # analyze node for retryable tickets + for ir in node.irs + internal_ops: if ( isinstance(ir, HighLevelCall) and isinstance(ir.function, Function) - and ir.function.name == "createRetryableTicket" + and ir.function.name + in [ + "createRetryableTicket", + "outboundTransferCustomRefund", + "unsafeCreateRetryableTicket", + ] ): node.context[self.key].append(node) diff --git a/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt index 4f07601b1b..a54b2240d3 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt @@ -6,3 +6,7 @@ Multiple retryable tickets created in the same function: -Y(msg.sender).createRetryableTicket(address(1),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#40-48) -Y(msg.sender).createRetryableTicket(address(2),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#50-58) +Multiple retryable tickets created in the same function: + -Y(msg.sender).createRetryableTicket(address(1),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#83-91) + -good2() (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#92) + diff --git a/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol b/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol index afd4d274b8..af20814a57 100644 --- a/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol +++ b/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol @@ -79,4 +79,27 @@ function bad2() external { 0, ""); } +function bad3() external { + Y(msg.sender).createRetryableTicket( + address(1), + 0, + 0, + address(0), + address(0), + 0, + 0, + ""); + good2(); +} +function good2() internal { + Y(msg.sender).createRetryableTicket( + address(2), + 0, + 0, + address(0), + address(0), + 0, + 0, + ""); +} } \ No newline at end of file diff --git a/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol-0.8.20.zip b/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol-0.8.20.zip index 1baadba84d201e0a4cecf23dae4f0c2af3f8445c..604b64ae7a38a632dcaeafeae1bc773299210399 100644 GIT binary patch delta 5662 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 S004#slin6m1~U`@0000~g^O?i From 75c1159d7300e1eab9b924d83e69f415a964739f Mon Sep 17 00:00:00 2001 From: alpharush <0xalpharush@protonmail.com> Date: Thu, 29 Feb 2024 10:40:07 -0600 Subject: [PATCH 3/3] add test showing two internal call example is caught --- .../functions/out_of_order_retryable.py | 1 + ...e_0_8_20_out_of_order_retryable_sol__0.txt | 4 ++++ .../0.8.20/out_of_order_retryable.sol | 6 +++++- .../out_of_order_retryable.sol-0.8.20.zip | Bin 5942 -> 6058 bytes 4 files changed, 10 insertions(+), 1 deletion(-) diff --git a/slither/detectors/functions/out_of_order_retryable.py b/slither/detectors/functions/out_of_order_retryable.py index f483dc64aa..db9096f95f 100644 --- a/slither/detectors/functions/out_of_order_retryable.py +++ b/slither/detectors/functions/out_of_order_retryable.py @@ -72,6 +72,7 @@ class OutOfOrderRetryable(AbstractDetector): key = "OUTOFORDERRETRYABLE" + # pylint: disable=too-many-branches def _detect_multiple_tickets( self, function: FunctionContract, node: Node, visited: List[Node] ) -> None: diff --git a/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt b/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt index a54b2240d3..4b0371a8c2 100644 --- a/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt +++ b/tests/e2e/detectors/snapshots/detectors__detector_OutOfOrderRetryable_0_8_20_out_of_order_retryable_sol__0.txt @@ -2,6 +2,10 @@ Multiple retryable tickets created in the same function: -Y(msg.sender).createRetryableTicket(address(1),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#62-70) -Y(msg.sender).createRetryableTicket(address(2),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#72-80) +Multiple retryable tickets created in the same function: + -good2() (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#95) + -good2() (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#96) + Multiple retryable tickets created in the same function: -Y(msg.sender).createRetryableTicket(address(1),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#40-48) -Y(msg.sender).createRetryableTicket(address(2),0,0,address(0),address(0),0,0,) (tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol#50-58) diff --git a/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol b/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol index af20814a57..e3f8feb2e4 100644 --- a/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol +++ b/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol @@ -90,7 +90,11 @@ function bad3() external { 0, ""); good2(); -} +} +function bad4() external { + good2(); + good2(); +} function good2() internal { Y(msg.sender).createRetryableTicket( address(2), diff --git a/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol-0.8.20.zip b/tests/e2e/detectors/test_data/out-of-order-retryable/0.8.20/out_of_order_retryable.sol-0.8.20.zip index 604b64ae7a38a632dcaeafeae1bc773299210399..fd52cf1802ca8d58c5c02b660f1657cc9b69d2da 100644 GIT binary patch delta 5778 zcmV;D7H#RaE~+mZP)h>@KL7#%4gk$mU06gl(S7U|003!N0RS$O?-ntUOw)hZd$LTq z1L00*jpp*B#1-0o5Ba>xBHy|EAYo~Q0nf8>cfV34eKcM=TS=A|24Bhz1UGVrH$Eu@ zt8ouo`(v%IWaw zCGr`*k2YH5x?W{lTe27ew_Jbjcmp6Fbe0mI*K1X@;3ciMl9p|(!gdHpbZH9X0Szl> zsyAh+Y?;<{W^*fCCKi#injV1$27moTDI3d(4Gcvi`UWLsl#en)NBi)rjE;EUI%O{R zY%M}carh&+^CnFpFJY~`S$i;lnrDWNrPnDdUoF6iEXf1@J^y?#E_8q3)umV`RR%bEK{X6cc>)=xc_LXv_?JKdf~WlWTiYFtxaxx`&r&WyML zW_2AdKC^D*+vOwRxbrB%&)9g);VL`%Yw+OZ25OI+<6ZXxPOUfAYeVv;_!BE#o7*6T zF$ai}ln{6Qr8nz8mRx`E^(UhH@+xH8)$JxnujkJ9!pt4Bt(Tmgd9w$%Pp1f4`O_&_ z#`lGjwd3`GMfoozk!0D~Q{{=U)5R*Msgsi@{kFV)$jAW6k_OP3oD<;2mF~dqUa@A6 z#sHE{vqkjaD`(J6RkGjaRe6atYlDL5U@WmD0Zqx zFn{9(_$zZ7A%&e1DzIOvgdiqEyKinFaR&|fVnFLtX)U12kL-9jVdyyM(SaK8aQtLW zxG;$4ffyU)hJODsAkUGmlgI7RON&jhIj`HaZaH!`xzB$AOC_?se(^7?pa=T9YT9^2 zZ*9g-KAY=GWIBm!vYy5&ZY(enu2eofdD}NgOADuWe_8^^C@TvwZ+lzHQ5!xPB>}Uo zvY*4Uy%Xbe4ueh@xyv^X>tmV;Gltw+m1RUM%uW<;fU|&Fd`&vDST}Z1myDOx%Du)@ zfHu1|m9zZ+9+_V03~?!P${;+0qNVyGAd)l)mT3v2Yim_p*VuA}p(UB&&b-5;i- zVxN)I)b{A7w~|2YGalZEJ7xm?#8S%whTlAZ>=cZ4P&`sA?oyaelKUIe9&17y(9q(2 zlovOq@bPOVXX6a?XY(8?b3Y@o)kuRqKggkFm56`0h29cP*@9+MnA-A-SczJa+dEpR z8b4s=Sn)mXv;sMft7obdHR=*W{G23RBco36UE|PQx>`pq)+PWe)%ES0)`#>j`++L_ zZW$*X{)c+$ttEf@Q;KLrK%{ zM<#zRGCwbH>M0V(n&|W^*p~YzuomT_nIu3 z{5ciUOq$b}qDy$jchpvwgGO06Wb`o!Zn;6l^In4FRbolcUSzD>sS{BP6>qB>b9NI} zJ$B0E{nzTXcMFrz+JSuO*Pr@;S^ zYE4PiHr5V+1ee_Hbkb?<6fF$1Pu+bLe2F(=yS$xAm7DEvT4O*Uv9}OeMbCeF`+8!X z_v7v0%D@I~u;fbD?Jj}17huE_ei!Yl6(;;m8WkPrh@}I?EL^ThCD!svmAw04@F!F# zBzEx|I3u%p)(dW{7=8BK-rho8MbSmTXI<#IWi`D3=hbqyV?cbn9KXgM(P$n-P{>|< zHG@S?xHp{r9?eb~z*(kHS*(BOZOJ{*liu7{(7@~&J!ZYyD6860WAYSa&0Wn+J_2dB zt|L+ND3Im>g*2yo{znjo3vAe;gofWN7Y3VNguKBe&>0T%p8qE*pL=M?KHCjBjiyzi zXDj{74PN}g@gl7iy$hyrkZ zBY=L-XPo6rroWEyG64S)dp)}*Bd81}-o~{pfd5DE6eNYY!i9n?k)Gw_lXq5DJTW+Os$YZW};3 zp3i`WNAYbEMoJHNr?P)9-NczwViTlN_!5@(k*>PfB0V5gl@-L!r7_}e|FdkmGc8O& zmX7e=c`l>Wi{`80Pl{=+`MD<0juEy-AO^VT0}NDawTy%tc{G0@Osq#wMkV=41XKL{ zVdJX;(We1mA{n9mJfHbAV`64d+bCll)mR!Lt3!{`VRXB5k@9eIx5JhN7~=NE?2>Nq zGfdWDq@jy~)}zqKzc~T@UxvAJkFO7pCrsQ4M$MK5~` z0@W>mPQG6M`&fUndB*-ps(-__3`Lq6Y9t1Y7!yh*?N(61vT<(lZ$~0u{q!1N3kN(x zDw&veZbBsvq3nO^SvK%D@YtYiUI$n0B)sxo zKUK@@fRi0s9ljocOl{6Z6=&JChLU$kIw*bJP1(3(HJ2(})1WIfr7~+!w-8pKXJiv6 z8uMZyk*>ulx3_tPV(JS=6Yk8gNz#U4wS6v1jX#@4(>t)Hr61tn*jj$FH{o-?kb`;w z?Ht%X;q4R4nHos zuZUHfuye!K>oaF;rrV<&31C{Q*}5$c&=wc028~3aE2cLqpl8r}*5KgVTl=w-VN@LF zJY(s6(sygKX2t6$7I=rX!G>0CG4kFVlK_&ybFzPv3CCJ)T%g|sU$mEX@{*Xq3l+;Y zeYP|~B$69VCdGJC{7h^OZ(Pui+JJchMAjbY2ZFL9)}4BpYIP$gV}{jm@sWJW#)3E@CTc)TES4Ww!~DL zKIO;J6bEA^Pcnxp;j}x(^nqj3#i=P)R+93noPCIQyv{O#fTO$LMj5v1FEM4(mt#N z?W6m|$u&c>$k(x(&O);W`Pw3_k7~~ejxF}B-1rIuaZC_wtc&VFtbxC#yip#fg8YB+ zYFl6d>lIuHWH>)(LA!sG6faX{gHsgyr`h)xs-`u4G9J208BVoCp8QJH@1Go~@s_qAB&g_yMK zgyRg#5*g0SNRjW2`>eq_4Kb6c2 z1+(8J9+lMxTie>Ul;xe5p7*)lV?!*B@(SxZ)j&*WHc;LUXvRfWxrqCj)&kI~pbM(= zm9#6eP@j>iXEIuhm*uAVeji9DrTcncPSx7`yMy}19>$k>f7#P!Ue&eqG{t}3`e^6z)~BO3!_r7&6_XSdc*ENrMcGm&F)Zwks=77M?qnU zuo2Q@rl`D!NC3pC4m0t=UHE^QypHxmdkFbEr=q%UMfgos88d(5sNW9UW{sLvYldFN zqn?nbk&_j8JyZ&cf^Io{qLqt5#9Ghr5Bnp%ON&rc=W%X9gyisjGU=BXNLVdUW@vGc z5FHp_Byfd9w5+bkj#eWWwB(!WB9xTY8;3hVXsZSrxU)OoQHuD6UNL`v{Eq#*nh6Lh z?NkMhs1$)%+oXDP>g@|qgGwh=a`^XUYB25q-aP?{&+S82I${~_maI)a6-N%;)mo6k z-PaKX>Mk4DzK*A76kn4S~5KO`~`1|!(yuyyuR=VYOX)$SuHP~;$ z+ZF@0I($nlcO$RuV2ZuZnQg;k3O6BA`HGJgZ z{A}#&=X`3$c$j;#`Z2?E{cM?2K-Ml2{AG#DTbeG=#l6OCug-sL>Xu5Il0_g1eFcs0 z-&q{NQIFs7_0x_bwOef{WJm9;8T*dy)wCxIUS?56<)1Lf(CyB#4b3r$WJ48z*XRgVoC8F809GlzQMPOM<&U@mDrfqjL&^ zAJ69>7nOhe))35v@d*rhguH__*OxUh;@mJmcT_)%Bjr!3a%z1GQz{NPFvzWJsW`DF zfm;(!zw{bYVOXMs8m>V-<6#R8hru-KvuE=`@a|c7iIh)l0&xrR#m*2nCkRio|7;b% z3YdtVS0Z;xKHnD?=#iODOsM}j`v~flUvvP~LzaIOj62s5VN-%qF|-t%s1;ZIuEs}; z;@Y<>lkxOGAX-Fmk9}Au!@u`?zH_EE^48;||9TZ<4OySc0gw6La+1}gMVIOvc=G z{KbFQ3#m06r;GRC=ODo`I1=wGZBAj=x?3^;%Ww$#N2Vhx`cWj`oU_@8C8FZFHoZhYF1^c^9pwa1S;`x)ZI)0g`~`qHKas3hCE>#;oIX)OL;TnPxp~2IBU<39{ETlZBOCMRF<+1@A+^O1zz zBfkdvABSI(Yz`|ALrp?)xktx+&~{G{VKhF)V}BmH5Z={I$aDL-xcvnYEJf_{?sDlR zNnUa;OPQP(49sYmlW(dn(1d3poy~ue^s|BL1$<UH#)N_y%uDd;s)Ge$*Rw`r7(1UVQ7pXVwk}cMG(i z%v0Kc)$%D2uY+s5RPF9u%-QtnO}9-R)XUg9B`x5*RBxN4&$F{InVavJ(jXte1!)^YwL^ph6To8 z-`C@;;r&wd;(wm@l*S|^7nx)%(7HI8!2Wf%f)R3eGX(VWoWji7U6`MEa5%t7pqgo? zUr>%NV|E)5wCxfw6bL4$?B!34j3Q^zc4sn?MMLA968Mj%Zxbydny}N zZ%?WCPrb43U7KD7AwD%`y>ld6e1N$KX&ZR!R+oZQGt~SsRk4vgW3U(9*K`ncggU2w zTOS$QJIQa=c#xr!6F?#%AR`eIU%Yz#v5;U^%NK_Ggo?`a^PBLSlTf~EhifSX0;KErpQ7(AuP z2Pw1z4}kf+5`w^Z^$NB3x}h2IE4{M!AVJVLAKI9lT)*$T$}^p8&a3OCyUm03!O{Sv z8+K5vvL|jf>xpX@F|&VVucJPP2QG<7$^}NJIYn!vi-tsk7J*55c$d%S$y~r0pd*o^ zjWUiCUBqlrMX|ZNC9oG-egh}AtNv;5GhCeptmcQn!C!%5{w3z42-@D3?S2SvTy`C( zNK&wT$yw(HBy9Wg9hhrI95BuOK`j#0VDOvqw3#~%2r+zXjZ0yIHF?<2@Rx6NQQ zNSlK@DsJ~7*h8Cj$%35^*K;umI<6{09~AYs)52o$#(p!Q?^1snbV|FAa@0K);pM{l z8QaL{UfXU=t*c+;seaz)p{ouCv^t^}HYSxsWCVHNRqhwn^~yRz|HzDzf&fK`9^R!W z`{&nk$=pn3 QX;_oq7E%T{7XSbN01k^cP5=M^ delta 5676 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 S0001QlV=w_2E-Ns0000emIV0#